зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland
This commit is contained in:
Коммит
5a1596f8f2
|
@ -1,5 +1,5 @@
|
||||||
This is the PDF.js project output, https://github.com/mozilla/pdf.js
|
This is the PDF.js project output, https://github.com/mozilla/pdf.js
|
||||||
|
|
||||||
Current extension version is: 2.0.750
|
Current extension version is: 2.0.760
|
||||||
|
|
||||||
Taken from upstream commit: c8ee6331
|
Taken from upstream commit: 1268aea2
|
||||||
|
|
|
@ -123,8 +123,8 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
|
||||||
var pdfjsVersion = '2.0.750';
|
var pdfjsVersion = '2.0.760';
|
||||||
var pdfjsBuild = 'c8ee6331';
|
var pdfjsBuild = '1268aea2';
|
||||||
var pdfjsSharedUtil = __w_pdfjs_require__(1);
|
var pdfjsSharedUtil = __w_pdfjs_require__(1);
|
||||||
var pdfjsDisplayAPI = __w_pdfjs_require__(7);
|
var pdfjsDisplayAPI = __w_pdfjs_require__(7);
|
||||||
var pdfjsDisplayTextLayer = __w_pdfjs_require__(19);
|
var pdfjsDisplayTextLayer = __w_pdfjs_require__(19);
|
||||||
|
@ -4223,7 +4223,7 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
|
||||||
}
|
}
|
||||||
return worker.messageHandler.sendWithPromise('GetDocRequest', {
|
return worker.messageHandler.sendWithPromise('GetDocRequest', {
|
||||||
docId,
|
docId,
|
||||||
apiVersion: '2.0.750',
|
apiVersion: '2.0.760',
|
||||||
source: {
|
source: {
|
||||||
data: source.data,
|
data: source.data,
|
||||||
url: source.url,
|
url: source.url,
|
||||||
|
@ -5314,6 +5314,9 @@ var WorkerTransport = function WorkerTransportClosure() {
|
||||||
return this.messageHandler.sendWithPromise('GetDestinations', null);
|
return this.messageHandler.sendWithPromise('GetDestinations', null);
|
||||||
},
|
},
|
||||||
getDestination: function WorkerTransport_getDestination(id) {
|
getDestination: function WorkerTransport_getDestination(id) {
|
||||||
|
if (typeof id !== 'string') {
|
||||||
|
return Promise.reject(new Error('Invalid destination request.'));
|
||||||
|
}
|
||||||
return this.messageHandler.sendWithPromise('GetDestination', { id });
|
return this.messageHandler.sendWithPromise('GetDestination', { id });
|
||||||
},
|
},
|
||||||
getPageLabels: function WorkerTransport_getPageLabels() {
|
getPageLabels: function WorkerTransport_getPageLabels() {
|
||||||
|
@ -5563,8 +5566,8 @@ var InternalRenderTask = function InternalRenderTaskClosure() {
|
||||||
}();
|
}();
|
||||||
var version, build;
|
var version, build;
|
||||||
{
|
{
|
||||||
exports.version = version = '2.0.750';
|
exports.version = version = '2.0.760';
|
||||||
exports.build = build = 'c8ee6331';
|
exports.build = build = '1268aea2';
|
||||||
}
|
}
|
||||||
exports.getDocument = getDocument;
|
exports.getDocument = getDocument;
|
||||||
exports.LoopbackPort = LoopbackPort;
|
exports.LoopbackPort = LoopbackPort;
|
||||||
|
|
|
@ -123,8 +123,8 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
|
||||||
var pdfjsVersion = '2.0.750';
|
var pdfjsVersion = '2.0.760';
|
||||||
var pdfjsBuild = 'c8ee6331';
|
var pdfjsBuild = '1268aea2';
|
||||||
var pdfjsCoreWorker = __w_pdfjs_require__(1);
|
var pdfjsCoreWorker = __w_pdfjs_require__(1);
|
||||||
exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
|
exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
|
||||||
|
|
||||||
|
@ -327,7 +327,7 @@ var WorkerMessageHandler = {
|
||||||
var cancelXHRs = null;
|
var cancelXHRs = null;
|
||||||
var WorkerTasks = [];
|
var WorkerTasks = [];
|
||||||
let apiVersion = docParams.apiVersion;
|
let apiVersion = docParams.apiVersion;
|
||||||
let workerVersion = '2.0.750';
|
let workerVersion = '2.0.760';
|
||||||
if (apiVersion !== workerVersion) {
|
if (apiVersion !== workerVersion) {
|
||||||
throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
|
throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
|
||||||
}
|
}
|
||||||
|
@ -581,9 +581,9 @@ var WorkerMessageHandler = {
|
||||||
handler.on('GetStats', function wphSetupGetStats(data) {
|
handler.on('GetStats', function wphSetupGetStats(data) {
|
||||||
return pdfManager.pdfDocument.xref.stats;
|
return pdfManager.pdfDocument.xref.stats;
|
||||||
});
|
});
|
||||||
handler.on('GetAnnotations', function wphSetupGetAnnotations(data) {
|
handler.on('GetAnnotations', function ({ pageIndex, intent }) {
|
||||||
return pdfManager.getPage(data.pageIndex).then(function (page) {
|
return pdfManager.getPage(pageIndex).then(function (page) {
|
||||||
return pdfManager.ensure(page, 'getAnnotationsData', [data.intent]);
|
return page.getAnnotationsData(intent);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
handler.on('RenderPageRequest', function wphSetupRenderPage(data) {
|
handler.on('RenderPageRequest', function wphSetupRenderPage(data) {
|
||||||
|
@ -5393,8 +5393,7 @@ var Page = function PageClosure() {
|
||||||
return opList;
|
return opList;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
var annotationsPromise = this.pdfManager.ensure(this, 'annotations');
|
return Promise.all([pageListPromise, this._parsedAnnotations]).then(function ([pageOpList, annotations]) {
|
||||||
return Promise.all([pageListPromise, annotationsPromise]).then(function ([pageOpList, annotations]) {
|
|
||||||
if (annotations.length === 0) {
|
if (annotations.length === 0) {
|
||||||
pageOpList.flush(true);
|
pageOpList.flush(true);
|
||||||
return pageOpList;
|
return pageOpList;
|
||||||
|
@ -5444,27 +5443,37 @@ var Page = function PageClosure() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getAnnotationsData: function Page_getAnnotationsData(intent) {
|
getAnnotationsData(intent) {
|
||||||
var annotations = this.annotations;
|
return this._parsedAnnotations.then(function (annotations) {
|
||||||
var annotationsData = [];
|
let annotationsData = [];
|
||||||
for (var i = 0, n = annotations.length; i < n; ++i) {
|
for (let i = 0, ii = annotations.length; i < ii; i++) {
|
||||||
if (!intent || isAnnotationRenderable(annotations[i], intent)) {
|
if (!intent || isAnnotationRenderable(annotations[i], intent)) {
|
||||||
annotationsData.push(annotations[i].data);
|
annotationsData.push(annotations[i].data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return annotationsData;
|
return annotationsData;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
get annotations() {
|
get annotations() {
|
||||||
var annotations = [];
|
return (0, _util.shadow)(this, 'annotations', this._getInheritableProperty('Annots') || []);
|
||||||
var annotationRefs = this._getInheritableProperty('Annots') || [];
|
},
|
||||||
for (var i = 0, n = annotationRefs.length; i < n; ++i) {
|
get _parsedAnnotations() {
|
||||||
var annotationRef = annotationRefs[i];
|
const parsedAnnotations = this.pdfManager.ensure(this, 'annotations').then(() => {
|
||||||
var annotation = _annotation.AnnotationFactory.create(this.xref, annotationRef, this.pdfManager, this.idFactory);
|
const annotationRefs = this.annotations;
|
||||||
if (annotation) {
|
const annotationPromises = [];
|
||||||
annotations.push(annotation);
|
for (let i = 0, ii = annotationRefs.length; i < ii; i++) {
|
||||||
|
annotationPromises.push(_annotation.AnnotationFactory.create(this.xref, annotationRefs[i], this.pdfManager, this.idFactory));
|
||||||
}
|
}
|
||||||
}
|
return Promise.all(annotationPromises).then(function (annotations) {
|
||||||
return (0, _util.shadow)(this, 'annotations', annotations);
|
return annotations.filter(function isDefined(annotation) {
|
||||||
|
return !!annotation;
|
||||||
|
});
|
||||||
|
}, function (reason) {
|
||||||
|
(0, _util.warn)(`_parsedAnnotations: "${reason}".`);
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return (0, _util.shadow)(this, '_parsedAnnotations', parsedAnnotations);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return Page;
|
return Page;
|
||||||
|
@ -5759,6 +5768,9 @@ var _crypto = __w_pdfjs_require__(24);
|
||||||
|
|
||||||
var _colorspace = __w_pdfjs_require__(25);
|
var _colorspace = __w_pdfjs_require__(25);
|
||||||
|
|
||||||
|
function fetchDestination(dest) {
|
||||||
|
return (0, _primitives.isDict)(dest) ? dest.get('D') : dest;
|
||||||
|
}
|
||||||
var Catalog = function CatalogClosure() {
|
var Catalog = function CatalogClosure() {
|
||||||
function Catalog(pdfManager, xref) {
|
function Catalog(pdfManager, xref) {
|
||||||
this.pdfManager = pdfManager;
|
this.pdfManager = pdfManager;
|
||||||
|
@ -5898,62 +5910,36 @@ var Catalog = function CatalogClosure() {
|
||||||
return (0, _util.shadow)(this, 'numPages', obj);
|
return (0, _util.shadow)(this, 'numPages', obj);
|
||||||
},
|
},
|
||||||
get destinations() {
|
get destinations() {
|
||||||
function fetchDestination(dest) {
|
const obj = this._readDests(),
|
||||||
return (0, _primitives.isDict)(dest) ? dest.get('D') : dest;
|
dests = Object.create(null);
|
||||||
}
|
if (obj instanceof NameTree) {
|
||||||
var xref = this.xref;
|
const names = obj.getAll();
|
||||||
var dests = {},
|
for (let name in names) {
|
||||||
nameTreeRef,
|
|
||||||
nameDictionaryRef;
|
|
||||||
var obj = this.catDict.get('Names');
|
|
||||||
if (obj && obj.has('Dests')) {
|
|
||||||
nameTreeRef = obj.getRaw('Dests');
|
|
||||||
} else if (this.catDict.has('Dests')) {
|
|
||||||
nameDictionaryRef = this.catDict.get('Dests');
|
|
||||||
}
|
|
||||||
if (nameDictionaryRef) {
|
|
||||||
obj = nameDictionaryRef;
|
|
||||||
obj.forEach(function catalogForEach(key, value) {
|
|
||||||
if (!value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dests[key] = fetchDestination(value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (nameTreeRef) {
|
|
||||||
var nameTree = new NameTree(nameTreeRef, xref);
|
|
||||||
var names = nameTree.getAll();
|
|
||||||
for (var name in names) {
|
|
||||||
dests[name] = fetchDestination(names[name]);
|
dests[name] = fetchDestination(names[name]);
|
||||||
}
|
}
|
||||||
|
} else if (obj instanceof _primitives.Dict) {
|
||||||
|
obj.forEach(function (key, value) {
|
||||||
|
if (value) {
|
||||||
|
dests[key] = fetchDestination(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return (0, _util.shadow)(this, 'destinations', dests);
|
return (0, _util.shadow)(this, 'destinations', dests);
|
||||||
},
|
},
|
||||||
getDestination: function Catalog_getDestination(destinationId) {
|
getDestination(destinationId) {
|
||||||
function fetchDestination(dest) {
|
const obj = this._readDests();
|
||||||
return (0, _primitives.isDict)(dest) ? dest.get('D') : dest;
|
if (obj instanceof NameTree || obj instanceof _primitives.Dict) {
|
||||||
|
return fetchDestination(obj.get(destinationId) || null);
|
||||||
}
|
}
|
||||||
var xref = this.xref;
|
return null;
|
||||||
var dest = null,
|
},
|
||||||
nameTreeRef,
|
_readDests() {
|
||||||
nameDictionaryRef;
|
const obj = this.catDict.get('Names');
|
||||||
var obj = this.catDict.get('Names');
|
|
||||||
if (obj && obj.has('Dests')) {
|
if (obj && obj.has('Dests')) {
|
||||||
nameTreeRef = obj.getRaw('Dests');
|
return new NameTree(obj.getRaw('Dests'), this.xref);
|
||||||
} else if (this.catDict.has('Dests')) {
|
} else if (this.catDict.has('Dests')) {
|
||||||
nameDictionaryRef = this.catDict.get('Dests');
|
return this.catDict.get('Dests');
|
||||||
}
|
}
|
||||||
if (nameDictionaryRef) {
|
|
||||||
var value = nameDictionaryRef.get(destinationId);
|
|
||||||
if (value) {
|
|
||||||
dest = fetchDestination(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nameTreeRef) {
|
|
||||||
var nameTree = new NameTree(nameTreeRef, xref);
|
|
||||||
dest = fetchDestination(nameTree.get(destinationId));
|
|
||||||
}
|
|
||||||
return dest;
|
|
||||||
},
|
},
|
||||||
get pageLabels() {
|
get pageLabels() {
|
||||||
var obj = null;
|
var obj = null;
|
||||||
|
@ -17710,6 +17696,9 @@ var _stream = __w_pdfjs_require__(14);
|
||||||
|
|
||||||
class AnnotationFactory {
|
class AnnotationFactory {
|
||||||
static create(xref, ref, pdfManager, idFactory) {
|
static create(xref, ref, pdfManager, idFactory) {
|
||||||
|
return pdfManager.ensure(this, '_create', [xref, ref, pdfManager, idFactory]);
|
||||||
|
}
|
||||||
|
static _create(xref, ref, pdfManager, idFactory) {
|
||||||
let dict = xref.fetchIfRef(ref);
|
let dict = xref.fetchIfRef(ref);
|
||||||
if (!(0, _primitives.isDict)(dict)) {
|
if (!(0, _primitives.isDict)(dict)) {
|
||||||
return;
|
return;
|
||||||
|
@ -22812,17 +22801,15 @@ var Font = function FontClosure() {
|
||||||
} else if (isType1File(file)) {
|
} else if (isType1File(file)) {
|
||||||
if (composite) {
|
if (composite) {
|
||||||
fileType = 'CIDFontType0';
|
fileType = 'CIDFontType0';
|
||||||
} else if (type === 'MMType1') {
|
|
||||||
fileType = 'MMType1';
|
|
||||||
} else {
|
} else {
|
||||||
fileType = 'Type1';
|
fileType = type === 'MMType1' ? 'MMType1' : 'Type1';
|
||||||
}
|
}
|
||||||
} else if (isCFFFile(file)) {
|
} else if (isCFFFile(file)) {
|
||||||
if (composite) {
|
if (composite) {
|
||||||
fileType = 'CIDFontType0';
|
fileType = 'CIDFontType0';
|
||||||
fileSubtype = 'CIDFontType0C';
|
fileSubtype = 'CIDFontType0C';
|
||||||
} else {
|
} else {
|
||||||
fileType = 'Type1';
|
fileType = type === 'MMType1' ? 'MMType1' : 'Type1';
|
||||||
fileSubtype = 'Type1C';
|
fileSubtype = 'Type1C';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -20,7 +20,7 @@ origin:
|
||||||
|
|
||||||
# Human-readable identifier for this version/release
|
# Human-readable identifier for this version/release
|
||||||
# Generally "version NNN", "tag SSS", "bookmark SSS"
|
# Generally "version NNN", "tag SSS", "bookmark SSS"
|
||||||
release: version 2.0.719
|
release: version 2.0.760
|
||||||
|
|
||||||
# The package's license, where possible using the mnemonic from
|
# The package's license, where possible using the mnemonic from
|
||||||
# https://spdx.org/licenses/
|
# https://spdx.org/licenses/
|
||||||
|
|
|
@ -90,6 +90,10 @@ def checking(what, callback=None):
|
||||||
# that will cause the given path(s) to be searched rather than $PATH. Input
|
# that will cause the given path(s) to be searched rather than $PATH. Input
|
||||||
# paths may either be individual paths or delimited by os.pathsep, to allow
|
# paths may either be individual paths or delimited by os.pathsep, to allow
|
||||||
# passing $PATH (for example) as an element.
|
# passing $PATH (for example) as an element.
|
||||||
|
# - `paths_have_priority` means that any programs found early in the PATH
|
||||||
|
# will be prioritized over programs found later in the PATH. The default is
|
||||||
|
# False, meaning that any of the programs earlier in the program list will be
|
||||||
|
# given priority, no matter where in the PATH they are found.
|
||||||
#
|
#
|
||||||
# The simplest form is:
|
# The simplest form is:
|
||||||
# check_prog('PROG', ('a', 'b'))
|
# check_prog('PROG', ('a', 'b'))
|
||||||
|
@ -99,7 +103,7 @@ def checking(what, callback=None):
|
||||||
@template
|
@template
|
||||||
@imports(_from='mozbuild.shellutil', _import='quote')
|
@imports(_from='mozbuild.shellutil', _import='quote')
|
||||||
def check_prog(var, progs, what=None, input=None, allow_missing=False,
|
def check_prog(var, progs, what=None, input=None, allow_missing=False,
|
||||||
paths=None, when=None):
|
paths=None, paths_have_priority=False, when=None):
|
||||||
if input is not None:
|
if input is not None:
|
||||||
# Wrap input with type checking and normalization.
|
# Wrap input with type checking and normalization.
|
||||||
@depends(input, when=when)
|
@depends(input, when=when)
|
||||||
|
@ -131,6 +135,14 @@ def check_prog(var, progs, what=None, input=None, allow_missing=False,
|
||||||
if not isinstance(progs, (tuple, list)):
|
if not isinstance(progs, (tuple, list)):
|
||||||
configure_error('progs must resolve to a list or tuple!')
|
configure_error('progs must resolve to a list or tuple!')
|
||||||
|
|
||||||
|
if paths_have_priority:
|
||||||
|
for path in paths:
|
||||||
|
for prog in value or progs:
|
||||||
|
log.debug('%s: Trying %s', var.lower(), quote(prog))
|
||||||
|
result = find_program(prog, [path])
|
||||||
|
if result:
|
||||||
|
return result
|
||||||
|
else:
|
||||||
for prog in value or progs:
|
for prog in value or progs:
|
||||||
log.debug('%s: Trying %s', var.lower(), quote(prog))
|
log.debug('%s: Trying %s', var.lower(), quote(prog))
|
||||||
result = find_program(prog, paths)
|
result = find_program(prog, paths)
|
||||||
|
|
|
@ -35,7 +35,8 @@ def node_toolchain_search_path(host):
|
||||||
# exist on systems (probably linux distros) where there is a program in the path
|
# exist on systems (probably linux distros) where there is a program in the path
|
||||||
# called "node" that does something else.
|
# called "node" that does something else.
|
||||||
nodejs = check_prog('NODEJS', ('nodejs', 'node',),
|
nodejs = check_prog('NODEJS', ('nodejs', 'node',),
|
||||||
allow_missing=True, paths=node_toolchain_search_path)
|
allow_missing=True, paths=node_toolchain_search_path,
|
||||||
|
paths_have_priority=True)
|
||||||
|
|
||||||
|
|
||||||
@depends_if(nodejs)
|
@depends_if(nodejs)
|
||||||
|
|
|
@ -942,10 +942,8 @@ CopyingStructuredCloneReadCallback(JSContext* aCx,
|
||||||
if (aTag == SCTAG_DOM_MUTABLEFILE) {
|
if (aTag == SCTAG_DOM_MUTABLEFILE) {
|
||||||
MOZ_ASSERT(file.mType == StructuredCloneFile::eMutableFile);
|
MOZ_ASSERT(file.mType == StructuredCloneFile::eMutableFile);
|
||||||
|
|
||||||
RefPtr<IDBMutableFile> mutableFile = file.mMutableFile;
|
|
||||||
|
|
||||||
JS::Rooted<JS::Value> wrappedMutableFile(aCx);
|
JS::Rooted<JS::Value> wrappedMutableFile(aCx);
|
||||||
if (NS_WARN_IF(!ToJSValue(aCx, mutableFile, &wrappedMutableFile))) {
|
if (NS_WARN_IF(!ToJSValue(aCx, file.mMutableFile, &wrappedMutableFile))) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -956,9 +954,7 @@ CopyingStructuredCloneReadCallback(JSContext* aCx,
|
||||||
|
|
||||||
MOZ_ASSERT(file.mType == StructuredCloneFile::eWasmBytecode);
|
MOZ_ASSERT(file.mType == StructuredCloneFile::eWasmBytecode);
|
||||||
|
|
||||||
RefPtr<JS::WasmModule> module = file.mWasmModule;
|
JS::Rooted<JSObject*> wrappedModule(aCx, file.mWasmModule->createObject(aCx));
|
||||||
|
|
||||||
JS::Rooted<JSObject*> wrappedModule(aCx, module->createObject(aCx));
|
|
||||||
if (NS_WARN_IF(!wrappedModule)) {
|
if (NS_WARN_IF(!wrappedModule)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -671,7 +671,8 @@ AutoEntryScript::AutoEntryScript(JSObject* aObject,
|
||||||
bool aIsMainThread)
|
bool aIsMainThread)
|
||||||
: AutoEntryScript(xpc::NativeGlobal(aObject), aReason, aIsMainThread)
|
: AutoEntryScript(xpc::NativeGlobal(aObject), aReason, aIsMainThread)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!js::IsCrossCompartmentWrapper(aObject));
|
// xpc::NativeGlobal uses JS::GetNonCCWObjectGlobal, which asserts that
|
||||||
|
// aObject is not a CCW.
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoEntryScript::~AutoEntryScript()
|
AutoEntryScript::~AutoEntryScript()
|
||||||
|
|
|
@ -2702,24 +2702,25 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
||||||
case ParseNodeKind::Call:
|
case ParseNodeKind::Call:
|
||||||
case ParseNodeKind::SuperCall:
|
case ParseNodeKind::SuperCall:
|
||||||
{
|
{
|
||||||
ParseNode* next = pn->pn_head;
|
ParseNode* pn_callee = pn->pn_left;
|
||||||
MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
|
ParseNode* pn_args = pn->pn_right;
|
||||||
|
MOZ_ASSERT(pn->pn_pos.encloses(pn_callee->pn_pos));
|
||||||
|
|
||||||
RootedValue callee(cx);
|
RootedValue callee(cx);
|
||||||
if (pn->isKind(ParseNodeKind::SuperCall)) {
|
if (pn->isKind(ParseNodeKind::SuperCall)) {
|
||||||
MOZ_ASSERT(next->isKind(ParseNodeKind::SuperBase));
|
MOZ_ASSERT(pn_callee->isKind(ParseNodeKind::SuperBase));
|
||||||
if (!builder.super(&next->pn_pos, &callee))
|
if (!builder.super(&pn_callee->pn_pos, &callee))
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (!expression(next, &callee))
|
if (!expression(pn_callee, &callee))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeVector args(cx);
|
NodeVector args(cx);
|
||||||
if (!args.reserve(pn->pn_count - 1))
|
if (!args.reserve(pn_args->pn_count))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (next = next->pn_next; next; next = next->pn_next) {
|
for (ParseNode* next = pn_args->pn_head; next; next = next->pn_next) {
|
||||||
MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
|
MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
|
||||||
|
|
||||||
RootedValue arg(cx);
|
RootedValue arg(cx);
|
||||||
|
@ -2740,17 +2741,17 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
||||||
|
|
||||||
case ParseNodeKind::Dot:
|
case ParseNodeKind::Dot:
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos));
|
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
|
||||||
|
|
||||||
RootedValue expr(cx);
|
RootedValue expr(cx);
|
||||||
RootedValue propname(cx);
|
RootedValue propname(cx);
|
||||||
RootedAtom pnAtom(cx, pn->pn_atom);
|
RootedAtom pnAtom(cx, pn->pn_right->pn_atom);
|
||||||
|
|
||||||
if (pn->as<PropertyAccess>().isSuper()) {
|
if (pn->as<PropertyAccess>().isSuper()) {
|
||||||
if (!builder.super(&pn->pn_expr->pn_pos, &expr))
|
if (!builder.super(&pn->pn_left->pn_pos, &expr))
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (!expression(pn->pn_expr, &expr))
|
if (!expression(pn->pn_left, &expr))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,23 +79,23 @@ js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs&
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 20 (reordered).
|
/* Step 20 (reordered).
|
||||||
* Set the |index| property. (TemplateObject positions it in slot 0) */
|
* Set the |index| property. */
|
||||||
arr->setSlot(0, Int32Value(matches[0].start));
|
arr->setSlot(RegExpRealm::MatchResultObjectIndexSlot, Int32Value(matches[0].start));
|
||||||
|
|
||||||
/* Step 21 (reordered).
|
/* Step 21 (reordered).
|
||||||
* Set the |input| property. (TemplateObject positions it in slot 1) */
|
* Set the |input| property. */
|
||||||
arr->setSlot(1, StringValue(input));
|
arr->setSlot(RegExpRealm::MatchResultObjectInputSlot, StringValue(input));
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
RootedValue test(cx);
|
RootedValue test(cx);
|
||||||
RootedId id(cx, NameToId(cx->names().index));
|
RootedId id(cx, NameToId(cx->names().index));
|
||||||
if (!NativeGetProperty(cx, arr, id, &test))
|
if (!NativeGetProperty(cx, arr, id, &test))
|
||||||
return false;
|
return false;
|
||||||
MOZ_ASSERT(test == arr->getSlot(0));
|
MOZ_ASSERT(test == arr->getSlot(RegExpRealm::MatchResultObjectIndexSlot));
|
||||||
id = NameToId(cx->names().input);
|
id = NameToId(cx->names().input);
|
||||||
if (!NativeGetProperty(cx, arr, id, &test))
|
if (!NativeGetProperty(cx, arr, id, &test))
|
||||||
return false;
|
return false;
|
||||||
MOZ_ASSERT(test == arr->getSlot(1));
|
MOZ_ASSERT(test == arr->getSlot(RegExpRealm::MatchResultObjectInputSlot));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Step 25. */
|
/* Step 25. */
|
||||||
|
@ -1025,16 +1025,18 @@ js::RegExpMatcher(JSContext* cx, unsigned argc, Value* vp)
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
||||||
int32_t lastIndex,
|
int32_t maybeLastIndex,
|
||||||
MatchPairs* maybeMatches, MutableHandleValue output)
|
MatchPairs* maybeMatches, MutableHandleValue output)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(lastIndex >= 0);
|
|
||||||
|
|
||||||
// The MatchPairs will always be passed in, but RegExp execution was
|
// The MatchPairs will always be passed in, but RegExp execution was
|
||||||
// successful only if the pairs have actually been filled in.
|
// successful only if the pairs have actually been filled in.
|
||||||
if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0)
|
if (maybeMatches && maybeMatches->pairsRaw()[0] > MatchPair::NoMatch)
|
||||||
return CreateRegExpMatchResult(cx, input, *maybeMatches, output);
|
return CreateRegExpMatchResult(cx, input, *maybeMatches, output);
|
||||||
return RegExpMatcherImpl(cx, regexp, input, lastIndex, output);
|
|
||||||
|
// |maybeLastIndex| only contains a valid value when the RegExp execution
|
||||||
|
// was not successful.
|
||||||
|
MOZ_ASSERT(maybeLastIndex >= 0);
|
||||||
|
return RegExpMatcherImpl(cx, regexp, input, maybeLastIndex, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1106,7 +1108,7 @@ js::RegExpSearcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
||||||
|
|
||||||
// The MatchPairs will always be passed in, but RegExp execution was
|
// The MatchPairs will always be passed in, but RegExp execution was
|
||||||
// successful only if the pairs have actually been filled in.
|
// successful only if the pairs have actually been filled in.
|
||||||
if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0) {
|
if (maybeMatches && maybeMatches->pairsRaw()[0] > MatchPair::NoMatch) {
|
||||||
*result = CreateRegExpSearchResult(*maybeMatches);
|
*result = CreateRegExpSearchResult(*maybeMatches);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ RegExpMatcher(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
extern MOZ_MUST_USE bool
|
extern MOZ_MUST_USE bool
|
||||||
RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
||||||
int32_t lastIndex, MatchPairs* maybeMatches, MutableHandleValue output);
|
int32_t maybeLastIndex, MatchPairs* maybeMatches, MutableHandleValue output);
|
||||||
|
|
||||||
extern MOZ_MUST_USE bool
|
extern MOZ_MUST_USE bool
|
||||||
RegExpSearcher(JSContext* cx, unsigned argc, Value* vp);
|
RegExpSearcher(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
|
@ -1002,9 +1002,7 @@ StructMetaTypeDescr::createFromArrays(JSContext* cx,
|
||||||
if (!CreateTraceList(cx, descr))
|
if (!CreateTraceList(cx, descr))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (!cx->zone()->addTypeDescrObject(cx, descr) ||
|
if (!cx->zone()->addTypeDescrObject(cx, descr)) {
|
||||||
!cx->zone()->addTypeDescrObject(cx, fieldTypeVec))
|
|
||||||
{
|
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -394,6 +394,10 @@ function isOverridableField(initialCSU, csu, field)
|
||||||
return false;
|
return false;
|
||||||
if (field == "GetThreadFromPRThread")
|
if (field == "GetThreadFromPRThread")
|
||||||
return false;
|
return false;
|
||||||
|
if (field == "ConstructUbiNode")
|
||||||
|
return false;
|
||||||
|
if (initialCSU == 'nsIXPCScriptable' && field == "GetScriptableFlags")
|
||||||
|
return false;
|
||||||
if (initialCSU == 'nsIXPConnectJSObjectHolder' && field == 'GetJSObject')
|
if (initialCSU == 'nsIXPConnectJSObjectHolder' && field == 'GetJSObject')
|
||||||
return false;
|
return false;
|
||||||
if (initialCSU == 'nsIXPConnect' && field == 'GetSafeJSContext')
|
if (initialCSU == 'nsIXPConnect' && field == 'GetSafeJSContext')
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use IO::Handle;
|
use IO::Handle;
|
||||||
use File::Basename qw(dirname);
|
use File::Basename qw(basename dirname);
|
||||||
use Getopt::Long;
|
use Getopt::Long;
|
||||||
use Cwd;
|
use Cwd;
|
||||||
|
|
||||||
|
@ -249,13 +249,17 @@ sub run_build
|
||||||
# Tell the wrapper where to find the config
|
# Tell the wrapper where to find the config
|
||||||
$ENV{"XGILL_CONFIG"} = Cwd::abs_path($config_file);
|
$ENV{"XGILL_CONFIG"} = Cwd::abs_path($config_file);
|
||||||
|
|
||||||
# update the PATH so that the build will see the wrappers.
|
# If overriding $CC, use GCCDIR to tell the wrapper scripts where the
|
||||||
|
# real compiler is. If $CC is not set, then the wrapper script will
|
||||||
|
# search $PATH anyway.
|
||||||
if (exists $ENV{CC}) {
|
if (exists $ENV{CC}) {
|
||||||
$ENV{PATH} = dirname($ENV{CC}) . ":$ENV{PATH}";
|
$ENV{GCCDIR} = dirname($ENV{CC});
|
||||||
delete $ENV{CC};
|
|
||||||
delete $ENV{CXX};
|
|
||||||
}
|
}
|
||||||
$ENV{"PATH"} = "$wrap_dir:" . $ENV{"PATH"};
|
|
||||||
|
# Force the wrapper scripts to be run in place of the compiler during
|
||||||
|
# whatever build process we use.
|
||||||
|
$ENV{CC} = "$wrap_dir/" . basename($ENV{CC} // "gcc");
|
||||||
|
$ENV{CXX} = "$wrap_dir/" . basename($ENV{CXX} // "g++");
|
||||||
|
|
||||||
# do the build, cleaning if necessary.
|
# do the build, cleaning if necessary.
|
||||||
chdir $build_dir;
|
chdir $build_dir;
|
||||||
|
|
|
@ -3462,9 +3462,8 @@ BinASTParser<Tok>::parseInterfaceCallExpression(const size_t start, const BinKin
|
||||||
op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
|
op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto result = arguments;
|
|
||||||
result->setKind(ParseNodeKind::Call);
|
BINJS_TRY_DECL(result, factory_.newCall(callee, arguments));
|
||||||
result->prepend(callee);
|
|
||||||
result->setOp(op);
|
result->setOp(op);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -3742,7 +3741,7 @@ BinASTParser<Tok>::parseInterfaceComputedMemberAssignmentTarget(const size_t sta
|
||||||
|
|
||||||
BINJS_MOZ_TRY_DECL(expression, parseExpression());
|
BINJS_MOZ_TRY_DECL(expression, parseExpression());
|
||||||
|
|
||||||
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
|
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, tokenizer_->offset()));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3786,7 +3785,7 @@ BinASTParser<Tok>::parseInterfaceComputedMemberExpression(const size_t start, co
|
||||||
|
|
||||||
BINJS_MOZ_TRY_DECL(expression, parseExpression());
|
BINJS_MOZ_TRY_DECL(expression, parseExpression());
|
||||||
|
|
||||||
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
|
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, tokenizer_->offset()));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5677,10 +5676,7 @@ BinASTParser<Tok>::parseInterfaceNewExpression(const size_t start, const BinKind
|
||||||
|
|
||||||
BINJS_MOZ_TRY_DECL(arguments, parseArguments());
|
BINJS_MOZ_TRY_DECL(arguments, parseArguments());
|
||||||
|
|
||||||
auto result = arguments;
|
BINJS_TRY_DECL(result, factory_.newNewExpression(tokenizer_->pos(start).begin, callee, arguments));
|
||||||
result->setKind(ParseNodeKind::New);
|
|
||||||
result->prepend(callee);
|
|
||||||
result->setOp(JSOP_NEW);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6197,13 +6193,18 @@ BinASTParser<Tok>::parseInterfaceStaticMemberAssignmentTarget(const size_t start
|
||||||
const BinField expected_fields[2] = { BinField::Object, BinField::Property };
|
const BinField expected_fields[2] = { BinField::Object, BinField::Property };
|
||||||
MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
|
MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
|
||||||
#endif // defined(DEBUG)
|
#endif // defined(DEBUG)
|
||||||
|
size_t nameStart;
|
||||||
|
|
||||||
BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
|
BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
|
||||||
|
|
||||||
RootedAtom property(cx_);
|
RootedAtom property(cx_);
|
||||||
|
{
|
||||||
|
nameStart = tokenizer_->offset();
|
||||||
MOZ_TRY_VAR(property, tokenizer_->readAtom());
|
MOZ_TRY_VAR(property, tokenizer_->readAtom());
|
||||||
|
|
||||||
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
|
}
|
||||||
|
|
||||||
|
BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
|
||||||
|
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6242,13 +6243,18 @@ BinASTParser<Tok>::parseInterfaceStaticMemberExpression(const size_t start, cons
|
||||||
const BinField expected_fields[2] = { BinField::Object, BinField::Property };
|
const BinField expected_fields[2] = { BinField::Object, BinField::Property };
|
||||||
MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
|
MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
|
||||||
#endif // defined(DEBUG)
|
#endif // defined(DEBUG)
|
||||||
|
size_t nameStart;
|
||||||
|
|
||||||
BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
|
BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
|
||||||
|
|
||||||
RootedAtom property(cx_);
|
RootedAtom property(cx_);
|
||||||
|
{
|
||||||
|
nameStart = tokenizer_->offset();
|
||||||
MOZ_TRY_VAR(property, tokenizer_->readAtom());
|
MOZ_TRY_VAR(property, tokenizer_->readAtom());
|
||||||
|
|
||||||
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
|
}
|
||||||
|
|
||||||
|
BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
|
||||||
|
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7381,7 +7387,7 @@ BinASTParser<Tok>::parseArguments()
|
||||||
|
|
||||||
const auto start = tokenizer_->offset();
|
const auto start = tokenizer_->offset();
|
||||||
MOZ_TRY(tokenizer_->enterList(length, guard));
|
MOZ_TRY(tokenizer_->enterList(length, guard));
|
||||||
BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
|
BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::Arguments, tokenizer_->pos(start)));
|
||||||
|
|
||||||
for (uint32_t i = 0; i < length; ++i) {
|
for (uint32_t i = 0; i < length; ++i) {
|
||||||
BINJS_MOZ_TRY_DECL(item, parseSpreadElementOrExpression());
|
BINJS_MOZ_TRY_DECL(item, parseSpreadElementOrExpression());
|
||||||
|
|
|
@ -207,7 +207,7 @@ hpp:
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
init:
|
init:
|
||||||
BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
|
BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::Arguments, tokenizer_->pos(start)));
|
||||||
append:
|
append:
|
||||||
factory_.addList(/* list = */ result, /* kid = */ item);
|
factory_.addList(/* list = */ result, /* kid = */ item);
|
||||||
|
|
||||||
|
@ -428,9 +428,8 @@ CallExpression:
|
||||||
op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
|
op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto result = arguments;
|
|
||||||
result->setKind(ParseNodeKind::Call);
|
BINJS_TRY_DECL(result, factory_.newCall(callee, arguments));
|
||||||
result->prepend(callee);
|
|
||||||
result->setOp(op);
|
result->setOp(op);
|
||||||
|
|
||||||
|
|
||||||
|
@ -489,11 +488,11 @@ CompoundAssignmentExpression:
|
||||||
|
|
||||||
ComputedMemberAssignmentTarget:
|
ComputedMemberAssignmentTarget:
|
||||||
build: |
|
build: |
|
||||||
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
|
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, tokenizer_->offset()));
|
||||||
|
|
||||||
ComputedMemberExpression:
|
ComputedMemberExpression:
|
||||||
build: |
|
build: |
|
||||||
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
|
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, tokenizer_->offset()));
|
||||||
|
|
||||||
ConditionalExpression:
|
ConditionalExpression:
|
||||||
build: |
|
build: |
|
||||||
|
@ -831,10 +830,7 @@ LiteralStringExpression:
|
||||||
|
|
||||||
NewExpression:
|
NewExpression:
|
||||||
build: |
|
build: |
|
||||||
auto result = arguments;
|
BINJS_TRY_DECL(result, factory_.newNewExpression(tokenizer_->pos(start).begin, callee, arguments));
|
||||||
result->setKind(ParseNodeKind::New);
|
|
||||||
result->prepend(callee);
|
|
||||||
result->setOp(JSOP_NEW);
|
|
||||||
|
|
||||||
ObjectExpression:
|
ObjectExpression:
|
||||||
build:
|
build:
|
||||||
|
@ -922,12 +918,28 @@ SwitchStatementWithDefault:
|
||||||
BINJS_TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope, true));
|
BINJS_TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope, true));
|
||||||
|
|
||||||
StaticMemberAssignmentTarget:
|
StaticMemberAssignmentTarget:
|
||||||
|
init:
|
||||||
|
size_t nameStart;
|
||||||
|
fields:
|
||||||
|
property:
|
||||||
|
block:
|
||||||
|
before: |
|
||||||
|
nameStart = tokenizer_->offset();
|
||||||
build: |
|
build: |
|
||||||
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
|
BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
|
||||||
|
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
|
||||||
|
|
||||||
StaticMemberExpression:
|
StaticMemberExpression:
|
||||||
|
init:
|
||||||
|
size_t nameStart;
|
||||||
|
fields:
|
||||||
|
property:
|
||||||
|
block:
|
||||||
|
before: |
|
||||||
|
nameStart = tokenizer_->offset();
|
||||||
build: |
|
build: |
|
||||||
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
|
BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
|
||||||
|
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
|
||||||
|
|
||||||
ThisExpression:
|
ThisExpression:
|
||||||
build: |
|
build: |
|
||||||
|
|
|
@ -1022,7 +1022,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
||||||
|
|
||||||
// Watch out for getters!
|
// Watch out for getters!
|
||||||
case ParseNodeKind::Dot:
|
case ParseNodeKind::Dot:
|
||||||
MOZ_ASSERT(pn->isArity(PN_NAME));
|
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||||
*answer = true;
|
*answer = true;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -1260,6 +1260,14 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
||||||
case ParseNodeKind::Call:
|
case ParseNodeKind::Call:
|
||||||
case ParseNodeKind::TaggedTemplate:
|
case ParseNodeKind::TaggedTemplate:
|
||||||
case ParseNodeKind::SuperCall:
|
case ParseNodeKind::SuperCall:
|
||||||
|
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||||
|
*answer = true;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Function arg lists can contain arbitrary expressions. Technically
|
||||||
|
// this only causes side-effects if one of the arguments does, but since
|
||||||
|
// the call being made will always trigger side-effects, it isn't needed.
|
||||||
|
case ParseNodeKind::Arguments:
|
||||||
MOZ_ASSERT(pn->isArity(PN_LIST));
|
MOZ_ASSERT(pn->isArity(PN_LIST));
|
||||||
*answer = true;
|
*answer = true;
|
||||||
return true;
|
return true;
|
||||||
|
@ -1394,6 +1402,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
||||||
case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate
|
case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate
|
||||||
case ParseNodeKind::PosHolder: // by ParseNodeKind::NewTarget
|
case ParseNodeKind::PosHolder: // by ParseNodeKind::NewTarget
|
||||||
case ParseNodeKind::SuperBase: // by ParseNodeKind::Elem and others
|
case ParseNodeKind::SuperBase: // by ParseNodeKind::Elem and others
|
||||||
|
case ParseNodeKind::PropertyName: // by ParseNodeKind::Dot
|
||||||
MOZ_CRASH("handled by parent nodes");
|
MOZ_CRASH("handled by parent nodes");
|
||||||
|
|
||||||
case ParseNodeKind::Limit: // invalid sentinel value
|
case ParseNodeKind::Limit: // invalid sentinel value
|
||||||
|
@ -1865,11 +1874,11 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn)
|
||||||
MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
|
MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
|
||||||
MOZ_ASSERT(!pn->as<PropertyAccess>().isSuper());
|
MOZ_ASSERT(!pn->as<PropertyAccess>().isSuper());
|
||||||
|
|
||||||
ParseNode* pn2 = pn->pn_expr;
|
ParseNode* pn2 = pn->pn_left;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the object operand is also a dotted property reference, reverse the
|
* If the object operand is also a dotted property reference, reverse the
|
||||||
* list linked via pn_expr temporarily so we can iterate over it from the
|
* list linked via pn_left temporarily so we can iterate over it from the
|
||||||
* bottom up (reversing again as we go), to avoid excessive recursion.
|
* bottom up (reversing again as we go), to avoid excessive recursion.
|
||||||
*/
|
*/
|
||||||
if (pn2->isKind(ParseNodeKind::Dot) && !pn2->as<PropertyAccess>().isSuper()) {
|
if (pn2->isKind(ParseNodeKind::Dot) && !pn2->as<PropertyAccess>().isSuper()) {
|
||||||
|
@ -1877,9 +1886,9 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn)
|
||||||
ParseNode* pnup = nullptr;
|
ParseNode* pnup = nullptr;
|
||||||
ParseNode* pndown;
|
ParseNode* pndown;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* Reverse pndot->pn_expr to point up, not down. */
|
/* Reverse pndot->pn_left to point up, not down. */
|
||||||
pndown = pndot->pn_expr;
|
pndown = pndot->pn_left;
|
||||||
pndot->pn_expr = pnup;
|
pndot->pn_left = pnup;
|
||||||
if (!pndown->isKind(ParseNodeKind::Dot) || pndown->as<PropertyAccess>().isSuper())
|
if (!pndown->isKind(ParseNodeKind::Dot) || pndown->as<PropertyAccess>().isSuper())
|
||||||
break;
|
break;
|
||||||
pnup = pndot;
|
pnup = pndot;
|
||||||
|
@ -1892,12 +1901,12 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
/* Walk back up the list, emitting annotated name ops. */
|
/* Walk back up the list, emitting annotated name ops. */
|
||||||
if (!emitAtomOp(pndot, JSOP_GETPROP))
|
if (!emitAtomOp(pndot->pn_right, JSOP_GETPROP))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Reverse the pn_expr link again. */
|
/* Reverse the pn_left link again. */
|
||||||
pnup = pndot->pn_expr;
|
pnup = pndot->pn_left;
|
||||||
pndot->pn_expr = pndown;
|
pndot->pn_left = pndown;
|
||||||
pndown = pndot;
|
pndown = pndot;
|
||||||
} while ((pndot = pnup) != nullptr);
|
} while ((pndot = pnup) != nullptr);
|
||||||
return true;
|
return true;
|
||||||
|
@ -1922,7 +1931,7 @@ BytecodeEmitter::emitSuperPropLHS(ParseNode* superBase, bool isCall)
|
||||||
bool
|
bool
|
||||||
BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op)
|
BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(pn->isArity(PN_NAME));
|
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||||
|
|
||||||
if (!emitPropLHS(pn))
|
if (!emitPropLHS(pn))
|
||||||
return false;
|
return false;
|
||||||
|
@ -1930,7 +1939,7 @@ BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op)
|
||||||
if (op == JSOP_CALLPROP && !emit1(JSOP_DUP))
|
if (op == JSOP_CALLPROP && !emit1(JSOP_DUP))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!emitAtomOp(pn, op))
|
if (!emitAtomOp(pn->pn_right, op))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (op == JSOP_CALLPROP && !emit1(JSOP_SWAP))
|
if (op == JSOP_CALLPROP && !emit1(JSOP_SWAP))
|
||||||
|
@ -1946,7 +1955,7 @@ BytecodeEmitter::emitSuperGetProp(ParseNode* pn, bool isCall)
|
||||||
if (!emitSuperPropLHS(base, isCall))
|
if (!emitSuperPropLHS(base, isCall))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!emitAtomOp(pn, JSOP_GETPROP_SUPER))
|
if (!emitAtomOp(pn->pn_right, JSOP_GETPROP_SUPER))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (isCall && !emit1(JSOP_SWAP))
|
if (isCall && !emit1(JSOP_SWAP))
|
||||||
|
@ -1976,7 +1985,7 @@ BytecodeEmitter::emitPropIncDec(ParseNode* pn)
|
||||||
if (!emit1(JSOP_DUP)) // OBJ OBJ
|
if (!emit1(JSOP_DUP)) // OBJ OBJ
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!emitAtomOp(pn->pn_kid, isSuper? JSOP_GETPROP_SUPER : JSOP_GETPROP)) // OBJ V
|
if (!emitAtomOp(pn->pn_kid->pn_right, isSuper ? JSOP_GETPROP_SUPER : JSOP_GETPROP)) // OBJ V
|
||||||
return false;
|
return false;
|
||||||
if (!emit1(JSOP_POS)) // OBJ N
|
if (!emit1(JSOP_POS)) // OBJ N
|
||||||
return false;
|
return false;
|
||||||
|
@ -2002,7 +2011,7 @@ BytecodeEmitter::emitPropIncDec(ParseNode* pn)
|
||||||
|
|
||||||
JSOp setOp = isSuper ? sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER
|
JSOp setOp = isSuper ? sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER
|
||||||
: sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
|
: sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
|
||||||
if (!emitAtomOp(pn->pn_kid, setOp)) // N? N+1
|
if (!emitAtomOp(pn->pn_kid->pn_right, setOp)) // N? N+1
|
||||||
return false;
|
return false;
|
||||||
if (post && !emit1(JSOP_POP)) // RESULT
|
if (post && !emit1(JSOP_POP)) // RESULT
|
||||||
return false;
|
return false;
|
||||||
|
@ -2692,7 +2701,7 @@ BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, size_t* emitted)
|
||||||
return false;
|
return false;
|
||||||
*emitted = 2;
|
*emitted = 2;
|
||||||
} else {
|
} else {
|
||||||
if (!emitTree(target->pn_expr))
|
if (!emitTree(target->pn_left))
|
||||||
return false;
|
return false;
|
||||||
*emitted = 1;
|
*emitted = 1;
|
||||||
}
|
}
|
||||||
|
@ -2810,7 +2819,7 @@ BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, Destructuri
|
||||||
setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER;
|
setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER;
|
||||||
else
|
else
|
||||||
setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
|
setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
|
||||||
if (!emitAtomOp(target, setOp))
|
if (!emitAtomOp(target->pn_right, setOp))
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3923,11 +3932,11 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, ParseNodeKind pnk, ParseNode* rh
|
||||||
return false;
|
return false;
|
||||||
offset += 2;
|
offset += 2;
|
||||||
} else {
|
} else {
|
||||||
if (!emitTree(lhs->expr()))
|
if (!emitTree(lhs->pn_left))
|
||||||
return false;
|
return false;
|
||||||
offset += 1;
|
offset += 1;
|
||||||
}
|
}
|
||||||
if (!makeAtomIndex(lhs->pn_atom, &atomIndex))
|
if (!makeAtomIndex(lhs->pn_right->pn_atom, &atomIndex))
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case ParseNodeKind::Elem: {
|
case ParseNodeKind::Elem: {
|
||||||
|
@ -3976,7 +3985,7 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, ParseNodeKind pnk, ParseNode* rh
|
||||||
} else {
|
} else {
|
||||||
if (!emit1(JSOP_DUP))
|
if (!emit1(JSOP_DUP))
|
||||||
return false;
|
return false;
|
||||||
bool isLength = (lhs->pn_atom == cx->names().length);
|
bool isLength = (lhs->pn_right->pn_atom == cx->names().length);
|
||||||
getOp = isLength ? JSOP_LENGTH : JSOP_GETPROP;
|
getOp = isLength ? JSOP_LENGTH : JSOP_GETPROP;
|
||||||
}
|
}
|
||||||
if (!emitIndex32(getOp, atomIndex))
|
if (!emitIndex32(getOp, atomIndex))
|
||||||
|
@ -4353,12 +4362,10 @@ BytecodeEmitter::emitIf(ParseNode* pn)
|
||||||
{
|
{
|
||||||
IfEmitter ifThenElse(this);
|
IfEmitter ifThenElse(this);
|
||||||
|
|
||||||
if_again:
|
if (!ifThenElse.emitIf(Some(pn->pn_pos.begin)))
|
||||||
// Make sure this code is attributed to the "if" so that it gets a useful
|
|
||||||
// column number, instead of the default 0 value.
|
|
||||||
if (!updateSourceCoordNotes(pn->pn_pos.begin))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if_again:
|
||||||
/* Emit code for the condition before pushing stmtInfo. */
|
/* Emit code for the condition before pushing stmtInfo. */
|
||||||
if (!emitTree(pn->pn_kid1))
|
if (!emitTree(pn->pn_kid1))
|
||||||
return false;
|
return false;
|
||||||
|
@ -4380,7 +4387,7 @@ BytecodeEmitter::emitIf(ParseNode* pn)
|
||||||
if (elseNode->isKind(ParseNodeKind::If)) {
|
if (elseNode->isKind(ParseNodeKind::If)) {
|
||||||
pn = elseNode;
|
pn = elseNode;
|
||||||
|
|
||||||
if (!ifThenElse.emitElseIf())
|
if (!ifThenElse.emitElseIf(Some(pn->pn_pos.begin)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
goto if_again;
|
goto if_again;
|
||||||
|
@ -4821,7 +4828,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, const EmitterScope* headLexical
|
||||||
bool allowSelfHostedIter = false;
|
bool allowSelfHostedIter = false;
|
||||||
if (emitterMode == BytecodeEmitter::SelfHosting &&
|
if (emitterMode == BytecodeEmitter::SelfHosting &&
|
||||||
forHeadExpr->isKind(ParseNodeKind::Call) &&
|
forHeadExpr->isKind(ParseNodeKind::Call) &&
|
||||||
forHeadExpr->pn_head->name() == cx->names().allowContentIter)
|
forHeadExpr->pn_left->name() == cx->names().allowContentIter)
|
||||||
{
|
{
|
||||||
allowSelfHostedIter = true;
|
allowSelfHostedIter = true;
|
||||||
}
|
}
|
||||||
|
@ -6142,10 +6149,12 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn)
|
||||||
//
|
//
|
||||||
// argc is set to the amount of actually emitted args and the
|
// argc is set to the amount of actually emitted args and the
|
||||||
// emitting of args below is disabled by setting emitArgs to false.
|
// emitting of args below is disabled by setting emitArgs to false.
|
||||||
ParseNode* pn2 = pn->pn_head;
|
ParseNode* pn_callee = pn->pn_left;
|
||||||
const char* errorName = SelfHostedCallFunctionName(pn2->name(), cx);
|
ParseNode* pn_args = pn->pn_right;
|
||||||
|
|
||||||
if (pn->pn_count < 3) {
|
const char* errorName = SelfHostedCallFunctionName(pn_callee->name(), cx);
|
||||||
|
|
||||||
|
if (pn_args->pn_count < 2) {
|
||||||
reportError(pn, JSMSG_MORE_ARGS_NEEDED, errorName, "2", "s");
|
reportError(pn, JSMSG_MORE_ARGS_NEEDED, errorName, "2", "s");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -6156,8 +6165,8 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool constructing = pn2->name() == cx->names().constructContentFunction;
|
bool constructing = pn_callee->name() == cx->names().constructContentFunction;
|
||||||
ParseNode* funNode = pn2->pn_next;
|
ParseNode* funNode = pn_args->pn_head;
|
||||||
if (constructing) {
|
if (constructing) {
|
||||||
callOp = JSOP_NEW;
|
callOp = JSOP_NEW;
|
||||||
} else if (funNode->getKind() == ParseNodeKind::Name &&
|
} else if (funNode->getKind() == ParseNodeKind::Name &&
|
||||||
|
@ -6170,7 +6179,7 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn)
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (emitterMode == BytecodeEmitter::SelfHosting &&
|
if (emitterMode == BytecodeEmitter::SelfHosting &&
|
||||||
pn2->name() == cx->names().callFunction)
|
pn_callee->name() == cx->names().callFunction)
|
||||||
{
|
{
|
||||||
if (!emit1(JSOP_DEBUGCHECKSELFHOSTED))
|
if (!emit1(JSOP_DEBUGCHECKSELFHOSTED))
|
||||||
return false;
|
return false;
|
||||||
|
@ -6199,7 +6208,7 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t argc = pn->pn_count - 3;
|
uint32_t argc = pn_args->pn_count - 2;
|
||||||
if (!emitCall(callOp, argc))
|
if (!emitCall(callOp, argc))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -6210,15 +6219,15 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn)
|
||||||
bool
|
bool
|
||||||
BytecodeEmitter::emitSelfHostedResumeGenerator(ParseNode* pn)
|
BytecodeEmitter::emitSelfHostedResumeGenerator(ParseNode* pn)
|
||||||
{
|
{
|
||||||
|
ParseNode* pn_args = pn->pn_right;
|
||||||
|
|
||||||
// Syntax: resumeGenerator(gen, value, 'next'|'throw'|'return')
|
// Syntax: resumeGenerator(gen, value, 'next'|'throw'|'return')
|
||||||
if (pn->pn_count != 4) {
|
if (pn_args->pn_count != 3) {
|
||||||
reportError(pn, JSMSG_MORE_ARGS_NEEDED, "resumeGenerator", "1", "s");
|
reportError(pn, JSMSG_MORE_ARGS_NEEDED, "resumeGenerator", "1", "s");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseNode* funNode = pn->pn_head; // The resumeGenerator node.
|
ParseNode* genNode = pn_args->pn_head;
|
||||||
|
|
||||||
ParseNode* genNode = funNode->pn_next;
|
|
||||||
if (!emitTree(genNode))
|
if (!emitTree(genNode))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -6250,24 +6259,26 @@ BytecodeEmitter::emitSelfHostedForceInterpreter()
|
||||||
bool
|
bool
|
||||||
BytecodeEmitter::emitSelfHostedAllowContentIter(ParseNode* pn)
|
BytecodeEmitter::emitSelfHostedAllowContentIter(ParseNode* pn)
|
||||||
{
|
{
|
||||||
if (pn->pn_count != 2) {
|
ParseNode* pn_args = pn->pn_right;
|
||||||
|
|
||||||
|
if (pn_args->pn_count != 1) {
|
||||||
reportError(pn, JSMSG_MORE_ARGS_NEEDED, "allowContentIter", "1", "");
|
reportError(pn, JSMSG_MORE_ARGS_NEEDED, "allowContentIter", "1", "");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're just here as a sentinel. Pass the value through directly.
|
// We're just here as a sentinel. Pass the value through directly.
|
||||||
return emitTree(pn->pn_head->pn_next);
|
return emitTree(pn_args->pn_head);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn)
|
BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn)
|
||||||
{
|
{
|
||||||
// Only optimize when 3 arguments are passed (we use 4 to include |this|).
|
ParseNode* pn_args = pn->pn_right;
|
||||||
MOZ_ASSERT(pn->pn_count == 4);
|
|
||||||
|
|
||||||
ParseNode* funNode = pn->pn_head; // The _DefineDataProperty node.
|
// Only optimize when 3 arguments are passed.
|
||||||
|
MOZ_ASSERT(pn_args->pn_count == 3);
|
||||||
|
|
||||||
ParseNode* objNode = funNode->pn_next;
|
ParseNode* objNode = pn_args->pn_head;
|
||||||
if (!emitTree(objNode))
|
if (!emitTree(objNode))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -6288,14 +6299,14 @@ BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn)
|
||||||
bool
|
bool
|
||||||
BytecodeEmitter::emitSelfHostedHasOwn(ParseNode* pn)
|
BytecodeEmitter::emitSelfHostedHasOwn(ParseNode* pn)
|
||||||
{
|
{
|
||||||
if (pn->pn_count != 3) {
|
ParseNode* pn_args = pn->pn_right;
|
||||||
|
|
||||||
|
if (pn_args->pn_count != 2) {
|
||||||
reportError(pn, JSMSG_MORE_ARGS_NEEDED, "hasOwn", "2", "");
|
reportError(pn, JSMSG_MORE_ARGS_NEEDED, "hasOwn", "2", "");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseNode* funNode = pn->pn_head; // The hasOwn node.
|
ParseNode* idNode = pn_args->pn_head;
|
||||||
|
|
||||||
ParseNode* idNode = funNode->pn_next;
|
|
||||||
if (!emitTree(idNode))
|
if (!emitTree(idNode))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -6309,14 +6320,14 @@ BytecodeEmitter::emitSelfHostedHasOwn(ParseNode* pn)
|
||||||
bool
|
bool
|
||||||
BytecodeEmitter::emitSelfHostedGetPropertySuper(ParseNode* pn)
|
BytecodeEmitter::emitSelfHostedGetPropertySuper(ParseNode* pn)
|
||||||
{
|
{
|
||||||
if (pn->pn_count != 4) {
|
ParseNode* pn_args = pn->pn_right;
|
||||||
|
|
||||||
|
if (pn_args->pn_count != 3) {
|
||||||
reportError(pn, JSMSG_MORE_ARGS_NEEDED, "getPropertySuper", "3", "");
|
reportError(pn, JSMSG_MORE_ARGS_NEEDED, "getPropertySuper", "3", "");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseNode* funNode = pn->pn_head; // The getPropertySuper node.
|
ParseNode* objNode = pn_args->pn_head;
|
||||||
|
|
||||||
ParseNode* objNode = funNode->pn_next;
|
|
||||||
ParseNode* idNode = objNode->pn_next;
|
ParseNode* idNode = objNode->pn_next;
|
||||||
ParseNode* receiverNode = idNode->pn_next;
|
ParseNode* receiverNode = idNode->pn_next;
|
||||||
|
|
||||||
|
@ -6345,11 +6356,11 @@ BytecodeEmitter::isRestParameter(ParseNode* pn)
|
||||||
|
|
||||||
if (!pn->isKind(ParseNodeKind::Name)) {
|
if (!pn->isKind(ParseNodeKind::Name)) {
|
||||||
if (emitterMode == BytecodeEmitter::SelfHosting && pn->isKind(ParseNodeKind::Call)) {
|
if (emitterMode == BytecodeEmitter::SelfHosting && pn->isKind(ParseNodeKind::Call)) {
|
||||||
ParseNode* pn2 = pn->pn_head;
|
ParseNode* pn_callee = pn->pn_left;
|
||||||
if (pn2->getKind() == ParseNodeKind::Name &&
|
if (pn_callee->getKind() == ParseNodeKind::Name &&
|
||||||
pn2->name() == cx->names().allowContentIter)
|
pn_callee->name() == cx->names().allowContentIter)
|
||||||
{
|
{
|
||||||
return isRestParameter(pn2->pn_next);
|
return isRestParameter(pn->pn_right->pn_head);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -6479,101 +6490,22 @@ BytecodeEmitter::emitPipeline(ParseNode* pn)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */)
|
BytecodeEmitter::emitArguments(ParseNode* pn, bool callop, bool spread)
|
||||||
{
|
{
|
||||||
bool callop =
|
uint32_t argc = pn->pn_count;
|
||||||
pn->isKind(ParseNodeKind::Call) || pn->isKind(ParseNodeKind::TaggedTemplate);
|
|
||||||
/*
|
|
||||||
* Emit callable invocation or operator new (constructor call) code.
|
|
||||||
* First, emit code for the left operand to evaluate the callable or
|
|
||||||
* constructable object expression.
|
|
||||||
*
|
|
||||||
* For operator new, we emit JSOP_GETPROP instead of JSOP_CALLPROP, etc.
|
|
||||||
* This is necessary to interpose the lambda-initialized method read
|
|
||||||
* barrier -- see the code in jsinterp.cpp for JSOP_LAMBDA followed by
|
|
||||||
* JSOP_{SET,INIT}PROP.
|
|
||||||
*
|
|
||||||
* Then (or in a call case that has no explicit reference-base
|
|
||||||
* object) we emit JSOP_UNDEFINED to produce the undefined |this|
|
|
||||||
* value required for calls (which non-strict mode functions
|
|
||||||
* will box into the global object).
|
|
||||||
*/
|
|
||||||
uint32_t argc = pn->pn_count - 1;
|
|
||||||
|
|
||||||
if (argc >= ARGC_LIMIT) {
|
if (argc >= ARGC_LIMIT) {
|
||||||
reportError(pn, callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS);
|
reportError(pn, callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseNode* pn2 = pn->pn_head;
|
|
||||||
bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
|
|
||||||
|
|
||||||
if (pn2->isKind(ParseNodeKind::Name) && emitterMode == BytecodeEmitter::SelfHosting && !spread) {
|
|
||||||
// Calls to "forceInterpreter", "callFunction",
|
|
||||||
// "callContentFunction", or "resumeGenerator" in self-hosted
|
|
||||||
// code generate inline bytecode.
|
|
||||||
if (pn2->name() == cx->names().callFunction ||
|
|
||||||
pn2->name() == cx->names().callContentFunction ||
|
|
||||||
pn2->name() == cx->names().constructContentFunction)
|
|
||||||
{
|
|
||||||
return emitSelfHostedCallFunction(pn);
|
|
||||||
}
|
|
||||||
if (pn2->name() == cx->names().resumeGenerator)
|
|
||||||
return emitSelfHostedResumeGenerator(pn);
|
|
||||||
if (pn2->name() == cx->names().forceInterpreter)
|
|
||||||
return emitSelfHostedForceInterpreter();
|
|
||||||
if (pn2->name() == cx->names().allowContentIter)
|
|
||||||
return emitSelfHostedAllowContentIter(pn);
|
|
||||||
if (pn2->name() == cx->names().defineDataPropertyIntrinsic && pn->pn_count == 4)
|
|
||||||
return emitSelfHostedDefineDataProperty(pn);
|
|
||||||
if (pn2->name() == cx->names().hasOwn)
|
|
||||||
return emitSelfHostedHasOwn(pn);
|
|
||||||
if (pn2->name() == cx->names().getPropertySuper)
|
|
||||||
return emitSelfHostedGetPropertySuper(pn);
|
|
||||||
// Fall through
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!emitCallee(pn2, pn, &callop))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
bool isNewOp = pn->getOp() == JSOP_NEW || pn->getOp() == JSOP_SPREADNEW ||
|
|
||||||
pn->getOp() == JSOP_SUPERCALL || pn->getOp() == JSOP_SPREADSUPERCALL;
|
|
||||||
|
|
||||||
|
|
||||||
// Emit room for |this|.
|
|
||||||
if (!callop) {
|
|
||||||
if (isNewOp) {
|
|
||||||
if (!emit1(JSOP_IS_CONSTRUCTING))
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
if (!emit1(JSOP_UNDEFINED))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Emit code for each argument in order, then emit the JSOP_*CALL or
|
|
||||||
* JSOP_NEW bytecode with a two-byte immediate telling how many args
|
|
||||||
* were pushed on the operand stack.
|
|
||||||
*/
|
|
||||||
if (!spread) {
|
if (!spread) {
|
||||||
for (ParseNode* pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) {
|
for (ParseNode* pn3 = pn->pn_head; pn3; pn3 = pn3->pn_next) {
|
||||||
if (!emitTree(pn3))
|
if (!emitTree(pn3))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNewOp) {
|
|
||||||
if (pn->isKind(ParseNodeKind::SuperCall)) {
|
|
||||||
if (!emit1(JSOP_NEWTARGET))
|
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
// Repush the callee as new.target
|
ParseNode* args = pn->pn_head;
|
||||||
if (!emitDupAt(argc + 1))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ParseNode* args = pn2->pn_next;
|
|
||||||
bool emitOptCode = (argc == 1) && isRestParameter(args->pn_kid);
|
bool emitOptCode = (argc == 1) && isRestParameter(args->pn_kid);
|
||||||
InternalIfEmitter ifNotOptimizable(this);
|
InternalIfEmitter ifNotOptimizable(this);
|
||||||
|
|
||||||
|
@ -6613,29 +6545,159 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUs
|
||||||
if (!ifNotOptimizable.emitEnd())
|
if (!ifNotOptimizable.emitEnd())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */)
|
||||||
|
{
|
||||||
|
bool callop =
|
||||||
|
pn->isKind(ParseNodeKind::Call) || pn->isKind(ParseNodeKind::TaggedTemplate);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Emit callable invocation or operator new (constructor call) code.
|
||||||
|
* First, emit code for the left operand to evaluate the callable or
|
||||||
|
* constructable object expression.
|
||||||
|
*
|
||||||
|
* For operator new, we emit JSOP_GETPROP instead of JSOP_CALLPROP, etc.
|
||||||
|
* This is necessary to interpose the lambda-initialized method read
|
||||||
|
* barrier -- see the code in jsinterp.cpp for JSOP_LAMBDA followed by
|
||||||
|
* JSOP_{SET,INIT}PROP.
|
||||||
|
*
|
||||||
|
* Then (or in a call case that has no explicit reference-base
|
||||||
|
* object) we emit JSOP_UNDEFINED to produce the undefined |this|
|
||||||
|
* value required for calls (which non-strict mode functions
|
||||||
|
* will box into the global object).
|
||||||
|
*/
|
||||||
|
ParseNode* pn_callee = pn->pn_left;
|
||||||
|
ParseNode* pn_args = pn->pn_right;
|
||||||
|
|
||||||
|
bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
|
||||||
|
|
||||||
|
if (pn_callee->isKind(ParseNodeKind::Name) && emitterMode == BytecodeEmitter::SelfHosting && !spread) {
|
||||||
|
// Calls to "forceInterpreter", "callFunction",
|
||||||
|
// "callContentFunction", or "resumeGenerator" in self-hosted
|
||||||
|
// code generate inline bytecode.
|
||||||
|
if (pn_callee->name() == cx->names().callFunction ||
|
||||||
|
pn_callee->name() == cx->names().callContentFunction ||
|
||||||
|
pn_callee->name() == cx->names().constructContentFunction)
|
||||||
|
{
|
||||||
|
return emitSelfHostedCallFunction(pn);
|
||||||
|
}
|
||||||
|
if (pn_callee->name() == cx->names().resumeGenerator)
|
||||||
|
return emitSelfHostedResumeGenerator(pn);
|
||||||
|
if (pn_callee->name() == cx->names().forceInterpreter)
|
||||||
|
return emitSelfHostedForceInterpreter();
|
||||||
|
if (pn_callee->name() == cx->names().allowContentIter)
|
||||||
|
return emitSelfHostedAllowContentIter(pn);
|
||||||
|
if (pn_callee->name() == cx->names().defineDataPropertyIntrinsic && pn_args->pn_count == 3)
|
||||||
|
return emitSelfHostedDefineDataProperty(pn);
|
||||||
|
if (pn_callee->name() == cx->names().hasOwn)
|
||||||
|
return emitSelfHostedHasOwn(pn);
|
||||||
|
if (pn_callee->name() == cx->names().getPropertySuper)
|
||||||
|
return emitSelfHostedGetPropertySuper(pn);
|
||||||
|
// Fall through
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emitCallee(pn_callee, pn, &callop))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool isNewOp = pn->getOp() == JSOP_NEW || pn->getOp() == JSOP_SPREADNEW ||
|
||||||
|
pn->getOp() == JSOP_SUPERCALL || pn->getOp() == JSOP_SPREADSUPERCALL;
|
||||||
|
|
||||||
|
// Emit room for |this|.
|
||||||
|
if (!callop) {
|
||||||
|
if (isNewOp) {
|
||||||
|
if (!emit1(JSOP_IS_CONSTRUCTING))
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (!emit1(JSOP_UNDEFINED))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emitArguments(pn_args, callop, spread))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint32_t argc = pn_args->pn_count;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Emit code for each argument in order, then emit the JSOP_*CALL or
|
||||||
|
* JSOP_NEW bytecode with a two-byte immediate telling how many args
|
||||||
|
* were pushed on the operand stack.
|
||||||
|
*/
|
||||||
if (isNewOp) {
|
if (isNewOp) {
|
||||||
if (pn->isKind(ParseNodeKind::SuperCall)) {
|
if (pn->isKind(ParseNodeKind::SuperCall)) {
|
||||||
if (!emit1(JSOP_NEWTARGET))
|
if (!emit1(JSOP_NEWTARGET))
|
||||||
return false;
|
return false;
|
||||||
|
} else if (!spread) {
|
||||||
|
// Repush the callee as new.target
|
||||||
|
if (!emitDupAt(argc + 1))
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (!emitDupAt(2))
|
if (!emitDupAt(2))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParseNode* coordNode = pn;
|
||||||
|
if (pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL)) {
|
||||||
|
switch (pn_callee->getKind()) {
|
||||||
|
case ParseNodeKind::Dot: {
|
||||||
|
|
||||||
|
// Check if this member is a simple chain of simple chain of
|
||||||
|
// property accesses, e.g. x.y.z, this.x.y, super.x.y
|
||||||
|
bool simpleDotChain = false;
|
||||||
|
for (ParseNode* cur = pn_callee; cur->isKind(ParseNodeKind::Dot); cur = cur->pn_left) {
|
||||||
|
ParseNode* left = cur->pn_left;
|
||||||
|
if (left->isKind(ParseNodeKind::Name) || left->isKind(ParseNodeKind::This) ||
|
||||||
|
left->isKind(ParseNodeKind::SuperBase))
|
||||||
|
{
|
||||||
|
simpleDotChain = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!simpleDotChain) {
|
||||||
|
// obj().aprop() // expression
|
||||||
|
// ^ // column coord
|
||||||
|
//
|
||||||
|
// Note: Because of the constant folding logic in FoldElement,
|
||||||
|
// this case also applies for constant string properties.
|
||||||
|
//
|
||||||
|
// obj()['aprop']() // expression
|
||||||
|
// ^ // column coord
|
||||||
|
coordNode = pn_callee->pn_right;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ParseNodeKind::Elem:
|
||||||
|
// obj[expr]() // expression
|
||||||
|
// ^ // column coord
|
||||||
|
coordNode = pn_args;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!spread) {
|
if (!spread) {
|
||||||
if (pn->getOp() == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) {
|
if (pn->getOp() == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) {
|
||||||
if (!emitCall(JSOP_CALL_IGNORES_RV, argc, pn))
|
if (!emitCall(JSOP_CALL_IGNORES_RV, argc, coordNode))
|
||||||
return false;
|
return false;
|
||||||
checkTypeSet(JSOP_CALL_IGNORES_RV);
|
checkTypeSet(JSOP_CALL_IGNORES_RV);
|
||||||
} else {
|
} else {
|
||||||
if (!emitCall(pn->getOp(), argc, pn))
|
if (!emitCall(pn->getOp(), argc, coordNode))
|
||||||
return false;
|
return false;
|
||||||
checkTypeSet(pn->getOp());
|
checkTypeSet(pn->getOp());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (coordNode) {
|
||||||
|
if (!updateSourceCoordNotes(coordNode->pn_pos.begin))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!emit1(pn->getOp()))
|
if (!emit1(pn->getOp()))
|
||||||
return false;
|
return false;
|
||||||
checkTypeSet(pn->getOp());
|
checkTypeSet(pn->getOp());
|
||||||
|
@ -6846,26 +6908,28 @@ bool
|
||||||
BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional,
|
BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional,
|
||||||
ValueUsage valueUsage /* = ValueUsage::WantValue */)
|
ValueUsage valueUsage /* = ValueUsage::WantValue */)
|
||||||
{
|
{
|
||||||
/* Emit the condition, then branch if false to the else part. */
|
CondEmitter cond(this);
|
||||||
|
if (!cond.emitCond())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!emitTree(&conditional.condition()))
|
if (!emitTree(&conditional.condition()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
IfEmitter ifThenElse(this);
|
if (!cond.emitThenElse())
|
||||||
if (!ifThenElse.emitCond())
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!emitTree(&conditional.thenExpression(), valueUsage))
|
if (!emitTree(&conditional.thenExpression(), valueUsage))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!ifThenElse.emitElse())
|
if (!cond.emitElse())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!emitTree(&conditional.elseExpression(), valueUsage))
|
if (!emitTree(&conditional.elseExpression(), valueUsage))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!ifThenElse.emitEnd())
|
if (!cond.emitEnd())
|
||||||
return false;
|
return false;
|
||||||
MOZ_ASSERT(ifThenElse.pushed() == 1);
|
MOZ_ASSERT(cond.pushed() == 1);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -7204,7 +7268,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count)
|
||||||
|
|
||||||
if (emitterMode == BytecodeEmitter::SelfHosting &&
|
if (emitterMode == BytecodeEmitter::SelfHosting &&
|
||||||
expr->isKind(ParseNodeKind::Call) &&
|
expr->isKind(ParseNodeKind::Call) &&
|
||||||
expr->pn_head->name() == cx->names().allowContentIter)
|
expr->pn_left->name() == cx->names().allowContentIter)
|
||||||
{
|
{
|
||||||
allowSelfHostedIter = true;
|
allowSelfHostedIter = true;
|
||||||
}
|
}
|
||||||
|
@ -8252,8 +8316,9 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ParseNodeKind::PropertyName:
|
||||||
case ParseNodeKind::PosHolder:
|
case ParseNodeKind::PosHolder:
|
||||||
MOZ_FALLTHROUGH_ASSERT("Should never try to emit ParseNodeKind::PosHolder");
|
MOZ_FALLTHROUGH_ASSERT("Should never try to emit ParseNodeKind::PosHolder or ::Property");
|
||||||
|
|
||||||
default:
|
default:
|
||||||
MOZ_ASSERT(0);
|
MOZ_ASSERT(0);
|
||||||
|
|
|
@ -808,6 +808,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
||||||
|
|
||||||
bool isRestParameter(ParseNode* pn);
|
bool isRestParameter(ParseNode* pn);
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitArguments(ParseNode* pn, bool callop, bool spread);
|
||||||
MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue);
|
MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue);
|
||||||
MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn);
|
MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn);
|
||||||
MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn);
|
MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn);
|
||||||
|
|
|
@ -347,8 +347,10 @@ ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result)
|
||||||
case ParseNodeKind::Comma:
|
case ParseNodeKind::Comma:
|
||||||
case ParseNodeKind::Array:
|
case ParseNodeKind::Array:
|
||||||
case ParseNodeKind::Object:
|
case ParseNodeKind::Object:
|
||||||
|
case ParseNodeKind::PropertyName:
|
||||||
case ParseNodeKind::Dot:
|
case ParseNodeKind::Dot:
|
||||||
case ParseNodeKind::Elem:
|
case ParseNodeKind::Elem:
|
||||||
|
case ParseNodeKind::Arguments:
|
||||||
case ParseNodeKind::Call:
|
case ParseNodeKind::Call:
|
||||||
case ParseNodeKind::Name:
|
case ParseNodeKind::Name:
|
||||||
case ParseNodeKind::TemplateString:
|
case ParseNodeKind::TemplateString:
|
||||||
|
@ -1253,7 +1255,10 @@ FoldElement(JSContext* cx, ParseNode** nodePtr, PerHandlerParser<FullParseHandle
|
||||||
|
|
||||||
// Optimization 3: We have expr["foo"] where foo is not an index. Convert
|
// Optimization 3: We have expr["foo"] where foo is not an index. Convert
|
||||||
// to a property access (like expr.foo) that optimizes better downstream.
|
// to a property access (like expr.foo) that optimizes better downstream.
|
||||||
ParseNode* dottedAccess = parser.newPropertyAccess(expr, name, node->pn_pos.end);
|
ParseNode* nameNode = parser.newPropertyName(name, key->pn_pos);
|
||||||
|
if (!nameNode)
|
||||||
|
return false;
|
||||||
|
ParseNode* dottedAccess = parser.newPropertyAccess(expr, nameNode);
|
||||||
if (!dottedAccess)
|
if (!dottedAccess)
|
||||||
return false;
|
return false;
|
||||||
dottedAccess->setInParens(node->isInParens());
|
dottedAccess->setInParens(node->isInParens());
|
||||||
|
@ -1409,8 +1414,9 @@ FoldCall(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& par
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(node->isKind(ParseNodeKind::Call) ||
|
MOZ_ASSERT(node->isKind(ParseNodeKind::Call) ||
|
||||||
node->isKind(ParseNodeKind::SuperCall) ||
|
node->isKind(ParseNodeKind::SuperCall) ||
|
||||||
|
node->isKind(ParseNodeKind::New) ||
|
||||||
node->isKind(ParseNodeKind::TaggedTemplate));
|
node->isKind(ParseNodeKind::TaggedTemplate));
|
||||||
MOZ_ASSERT(node->isArity(PN_LIST));
|
MOZ_ASSERT(node->isArity(PN_BINARY));
|
||||||
|
|
||||||
// Don't fold a parenthesized callable component in an invocation, as this
|
// Don't fold a parenthesized callable component in an invocation, as this
|
||||||
// might cause a different |this| value to be used, changing semantics:
|
// might cause a different |this| value to be used, changing semantics:
|
||||||
|
@ -1423,10 +1429,26 @@ FoldCall(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& par
|
||||||
// assertEq(obj.f``, "obj");
|
// assertEq(obj.f``, "obj");
|
||||||
//
|
//
|
||||||
// See bug 537673 and bug 1182373.
|
// See bug 537673 and bug 1182373.
|
||||||
ParseNode** listp = &node->pn_head;
|
ParseNode** pn_callee = &node->pn_left;
|
||||||
if ((*listp)->isInParens())
|
if (node->isKind(ParseNodeKind::New) || !(*pn_callee)->isInParens()) {
|
||||||
listp = &(*listp)->pn_next;
|
if (!Fold(cx, pn_callee, parser))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseNode** pn_args = &node->pn_right;
|
||||||
|
if (!Fold(cx, pn_args, parser))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
FoldArguments(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(node->isKind(ParseNodeKind::Arguments));
|
||||||
|
MOZ_ASSERT(node->isArity(PN_LIST));
|
||||||
|
|
||||||
|
ParseNode** listp = &node->pn_head;
|
||||||
for (; *listp; listp = &(*listp)->pn_next) {
|
for (; *listp; listp = &(*listp)->pn_next) {
|
||||||
if (!Fold(cx, listp, parser))
|
if (!Fold(cx, listp, parser))
|
||||||
return false;
|
return false;
|
||||||
|
@ -1482,14 +1504,14 @@ static bool
|
||||||
FoldDottedProperty(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
|
FoldDottedProperty(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(node->isKind(ParseNodeKind::Dot));
|
MOZ_ASSERT(node->isKind(ParseNodeKind::Dot));
|
||||||
MOZ_ASSERT(node->isArity(PN_NAME));
|
MOZ_ASSERT(node->isArity(PN_BINARY));
|
||||||
|
|
||||||
// Iterate through a long chain of dotted property accesses to find the
|
// Iterate through a long chain of dotted property accesses to find the
|
||||||
// most-nested non-dotted property node, then fold that.
|
// most-nested non-dotted property node, then fold that.
|
||||||
ParseNode** nested = &node->pn_expr;
|
ParseNode** nested = &node->pn_left;
|
||||||
while ((*nested)->isKind(ParseNodeKind::Dot)) {
|
while ((*nested)->isKind(ParseNodeKind::Dot)) {
|
||||||
MOZ_ASSERT((*nested)->isArity(PN_NAME));
|
MOZ_ASSERT((*nested)->isArity(PN_BINARY));
|
||||||
nested = &(*nested)->pn_expr;
|
nested = &(*nested)->pn_left;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Fold(cx, nested, parser);
|
return Fold(cx, nested, parser);
|
||||||
|
@ -1642,7 +1664,6 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser<FullParseHandler>& parser)
|
||||||
case ParseNodeKind::InstanceOf:
|
case ParseNodeKind::InstanceOf:
|
||||||
case ParseNodeKind::In:
|
case ParseNodeKind::In:
|
||||||
case ParseNodeKind::Comma:
|
case ParseNodeKind::Comma:
|
||||||
case ParseNodeKind::New:
|
|
||||||
case ParseNodeKind::Array:
|
case ParseNodeKind::Array:
|
||||||
case ParseNodeKind::Object:
|
case ParseNodeKind::Object:
|
||||||
case ParseNodeKind::StatementList:
|
case ParseNodeKind::StatementList:
|
||||||
|
@ -1694,10 +1715,14 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser<FullParseHandler>& parser)
|
||||||
return FoldAdd(cx, pnp, parser);
|
return FoldAdd(cx, pnp, parser);
|
||||||
|
|
||||||
case ParseNodeKind::Call:
|
case ParseNodeKind::Call:
|
||||||
|
case ParseNodeKind::New:
|
||||||
case ParseNodeKind::SuperCall:
|
case ParseNodeKind::SuperCall:
|
||||||
case ParseNodeKind::TaggedTemplate:
|
case ParseNodeKind::TaggedTemplate:
|
||||||
return FoldCall(cx, pn, parser);
|
return FoldCall(cx, pn, parser);
|
||||||
|
|
||||||
|
case ParseNodeKind::Arguments:
|
||||||
|
return FoldArguments(cx, pn, parser);
|
||||||
|
|
||||||
case ParseNodeKind::Switch:
|
case ParseNodeKind::Switch:
|
||||||
case ParseNodeKind::Colon:
|
case ParseNodeKind::Colon:
|
||||||
case ParseNodeKind::Assign:
|
case ParseNodeKind::Assign:
|
||||||
|
@ -1777,6 +1802,9 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser<FullParseHandler>& parser)
|
||||||
MOZ_ASSERT(pn->isArity(PN_NAME));
|
MOZ_ASSERT(pn->isArity(PN_NAME));
|
||||||
return Fold(cx, &pn->pn_expr, parser);
|
return Fold(cx, &pn->pn_expr, parser);
|
||||||
|
|
||||||
|
case ParseNodeKind::PropertyName:
|
||||||
|
MOZ_CRASH("unreachable, handled by ::Dot");
|
||||||
|
|
||||||
case ParseNodeKind::Dot:
|
case ParseNodeKind::Dot:
|
||||||
return FoldDottedProperty(cx, pn, parser);
|
return FoldDottedProperty(cx, pn, parser);
|
||||||
|
|
||||||
|
|
|
@ -275,16 +275,20 @@ class FullParseHandler
|
||||||
addList(/* list = */ literal, /* child = */ element);
|
addList(/* list = */ literal, /* child = */ element);
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseNode* newCall(const TokenPos& pos) {
|
ParseNode* newCall(ParseNode* callee, ParseNode* args) {
|
||||||
return new_<ListNode>(ParseNodeKind::Call, JSOP_CALL, pos);
|
return new_<BinaryNode>(ParseNodeKind::Call, JSOP_CALL, callee, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseNode* newSuperCall(ParseNode* callee) {
|
ParseNode* newArguments(const TokenPos& pos) {
|
||||||
return new_<ListNode>(ParseNodeKind::SuperCall, JSOP_SUPERCALL, callee);
|
return new_<ListNode>(ParseNodeKind::Arguments, JSOP_NOP, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseNode* newTaggedTemplate(const TokenPos& pos) {
|
ParseNode* newSuperCall(ParseNode* callee, ParseNode* args) {
|
||||||
return new_<ListNode>(ParseNodeKind::TaggedTemplate, JSOP_CALL, pos);
|
return new_<BinaryNode>(ParseNodeKind::SuperCall, JSOP_SUPERCALL, callee, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseNode* newTaggedTemplate(ParseNode* tag, ParseNode* args) {
|
||||||
|
return new_<BinaryNode>(ParseNodeKind::TaggedTemplate, JSOP_CALL, tag, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseNode* newObjectLiteral(uint32_t begin) {
|
ParseNode* newObjectLiteral(uint32_t begin) {
|
||||||
|
@ -660,8 +664,12 @@ class FullParseHandler
|
||||||
return new_<DebuggerStatement>(pos);
|
return new_<DebuggerStatement>(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseNode* newPropertyAccess(ParseNode* expr, PropertyName* key, uint32_t end) {
|
ParseNode* newPropertyName(PropertyName* name, const TokenPos& pos) {
|
||||||
return new_<PropertyAccess>(expr, key, expr->pn_pos.begin, end);
|
return new_<NameNode>(ParseNodeKind::PropertyName, JSOP_NOP, name, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseNode* newPropertyAccess(ParseNode* expr, ParseNode* key) {
|
||||||
|
return new_<PropertyAccess>(expr, key, expr->pn_pos.begin, key->pn_pos.end);
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseNode* newPropertyByValue(ParseNode* lhs, ParseNode* index, uint32_t end) {
|
ParseNode* newPropertyByValue(ParseNode* lhs, ParseNode* index, uint32_t end) {
|
||||||
|
@ -735,13 +743,8 @@ class FullParseHandler
|
||||||
return new_<LexicalScopeNode>(bindings, body);
|
return new_<LexicalScopeNode>(bindings, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node newNewExpression(uint32_t begin, ParseNode* ctor) {
|
Node newNewExpression(uint32_t begin, ParseNode* ctor, ParseNode* args) {
|
||||||
ParseNode* newExpr = new_<ListNode>(ParseNodeKind::New, JSOP_NEW, TokenPos(begin, begin + 1));
|
return new_<BinaryNode>(ParseNodeKind::New, JSOP_NEW, TokenPos(begin, args->pn_pos.end), ctor, args);
|
||||||
if (!newExpr)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
addList(/* list = */ newExpr, /* child = */ ctor);
|
|
||||||
return newExpr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseNode* newAssignment(ParseNodeKind kind, ParseNode* lhs, ParseNode* rhs) {
|
ParseNode* newAssignment(ParseNodeKind kind, ParseNode* lhs, ParseNode* rhs) {
|
||||||
|
|
|
@ -13,15 +13,15 @@
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::frontend;
|
using namespace js::frontend;
|
||||||
|
|
||||||
IfEmitter::IfEmitter(BytecodeEmitter* bce, Kind kind)
|
using mozilla::Maybe;
|
||||||
|
|
||||||
|
BranchEmitterBase::BranchEmitterBase(BytecodeEmitter* bce, Kind kind)
|
||||||
: bce_(bce),
|
: bce_(bce),
|
||||||
thenDepth_(0),
|
|
||||||
kind_(kind)
|
kind_(kind)
|
||||||
#ifdef DEBUG
|
{}
|
||||||
, pushed_(0),
|
|
||||||
calculatedPushed_(false),
|
IfEmitter::IfEmitter(BytecodeEmitter* bce, Kind kind)
|
||||||
state_(State::Start)
|
: BranchEmitterBase(bce, kind)
|
||||||
#endif
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
IfEmitter::IfEmitter(BytecodeEmitter* bce)
|
IfEmitter::IfEmitter(BytecodeEmitter* bce)
|
||||||
|
@ -29,11 +29,8 @@ IfEmitter::IfEmitter(BytecodeEmitter* bce)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IfEmitter::emitIfInternal(SrcNoteType type)
|
BranchEmitterBase::emitThenInternal(SrcNoteType type)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome());
|
|
||||||
MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing());
|
|
||||||
|
|
||||||
// The end of TDZCheckCache for cond for else-if.
|
// The end of TDZCheckCache for cond for else-if.
|
||||||
if (kind_ == Kind::MayContainLexicalAccessInBranch)
|
if (kind_ == Kind::MayContainLexicalAccessInBranch)
|
||||||
tdzCache_.reset();
|
tdzCache_.reset();
|
||||||
|
@ -61,7 +58,7 @@ IfEmitter::emitIfInternal(SrcNoteType type)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
IfEmitter::calculateOrCheckPushed()
|
BranchEmitterBase::calculateOrCheckPushed()
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (!calculatedPushed_) {
|
if (!calculatedPushed_) {
|
||||||
|
@ -74,46 +71,7 @@ IfEmitter::calculateOrCheckPushed()
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IfEmitter::emitThen()
|
BranchEmitterBase::emitElseInternal()
|
||||||
{
|
|
||||||
MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf);
|
|
||||||
if (!emitIfInternal(SRC_IF))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
state_ = State::Then;
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
IfEmitter::emitCond()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(state_ == State::Start);
|
|
||||||
if (!emitIfInternal(SRC_COND))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
state_ = State::Cond;
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
IfEmitter::emitThenElse()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf);
|
|
||||||
if (!emitIfInternal(SRC_IF_ELSE))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
state_ = State::ThenElse;
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
IfEmitter::emitElseInternal()
|
|
||||||
{
|
{
|
||||||
calculateOrCheckPushed();
|
calculateOrCheckPushed();
|
||||||
|
|
||||||
|
@ -138,57 +96,17 @@ IfEmitter::emitElseInternal()
|
||||||
|
|
||||||
// Restore stack depth of the then part.
|
// Restore stack depth of the then part.
|
||||||
bce_->stackDepth = thenDepth_;
|
bce_->stackDepth = thenDepth_;
|
||||||
#ifdef DEBUG
|
|
||||||
state_ = State::Else;
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
IfEmitter::emitElse()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(state_ == State::ThenElse || state_ == State::Cond);
|
|
||||||
|
|
||||||
if (!emitElseInternal())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Enclose else-branch with TDZCheckCache.
|
// Enclose else-branch with TDZCheckCache.
|
||||||
if (kind_ == Kind::MayContainLexicalAccessInBranch)
|
if (kind_ == Kind::MayContainLexicalAccessInBranch)
|
||||||
tdzCache_.emplace(bce_);
|
tdzCache_.emplace(bce_);
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
state_ = State::Else;
|
|
||||||
#endif
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IfEmitter::emitElseIf()
|
BranchEmitterBase::emitEndInternal()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(state_ == State::ThenElse);
|
|
||||||
|
|
||||||
if (!emitElseInternal())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Enclose cond for else-if with TDZCheckCache.
|
|
||||||
if (kind_ == Kind::MayContainLexicalAccessInBranch)
|
|
||||||
tdzCache_.emplace(bce_);
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
state_ = State::ElseIf;
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
IfEmitter::emitEnd()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(state_ == State::Then || state_ == State::Else);
|
|
||||||
// If there was an else part for the last branch, jumpAroundThen_ is
|
|
||||||
// already fixed up when emitting the else part.
|
|
||||||
MOZ_ASSERT_IF(state_ == State::Then, jumpAroundThen_.offset != -1);
|
|
||||||
MOZ_ASSERT_IF(state_ == State::Else, jumpAroundThen_.offset == -1);
|
|
||||||
|
|
||||||
// The end of TDZCheckCache for then or else-clause.
|
// The end of TDZCheckCache for then or else-clause.
|
||||||
if (kind_ == Kind::MayContainLexicalAccessInBranch) {
|
if (kind_ == Kind::MayContainLexicalAccessInBranch) {
|
||||||
MOZ_ASSERT(tdzCache_.isSome());
|
MOZ_ASSERT(tdzCache_.isSome());
|
||||||
|
@ -208,6 +126,106 @@ IfEmitter::emitEnd()
|
||||||
if (!bce_->emitJumpTargetAndPatch(jumpsAroundElse_))
|
if (!bce_->emitJumpTargetAndPatch(jumpsAroundElse_))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IfEmitter::emitIf(const Maybe<uint32_t>& ifPos)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::Start);
|
||||||
|
|
||||||
|
if (ifPos) {
|
||||||
|
// Make sure this code is attributed to the "if" so that it gets a
|
||||||
|
// useful column number, instead of the default 0 value.
|
||||||
|
if (!bce_->updateSourceCoordNotes(*ifPos))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::If;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IfEmitter::emitThen()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::If || state_ == State::ElseIf);
|
||||||
|
MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome());
|
||||||
|
MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing());
|
||||||
|
|
||||||
|
if (!emitThenInternal(SRC_IF))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::Then;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IfEmitter::emitThenElse()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::If || state_ == State::ElseIf);
|
||||||
|
MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome());
|
||||||
|
MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing());
|
||||||
|
|
||||||
|
if (!emitThenInternal(SRC_IF_ELSE))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::ThenElse;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IfEmitter::emitElseIf(const Maybe<uint32_t>& ifPos)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::ThenElse);
|
||||||
|
|
||||||
|
if (!emitElseInternal())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ifPos) {
|
||||||
|
// Make sure this code is attributed to the "if" so that it gets a
|
||||||
|
// useful column number, instead of the default 0 value.
|
||||||
|
if (!bce_->updateSourceCoordNotes(*ifPos))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::ElseIf;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IfEmitter::emitElse()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::ThenElse);
|
||||||
|
|
||||||
|
if (!emitElseInternal())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::Else;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IfEmitter::emitEnd()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::Then || state_ == State::Else);
|
||||||
|
// If there was an else part for the last branch, jumpAroundThen_ is
|
||||||
|
// already fixed up when emitting the else part.
|
||||||
|
MOZ_ASSERT_IF(state_ == State::Then, jumpAroundThen_.offset != -1);
|
||||||
|
MOZ_ASSERT_IF(state_ == State::Else, jumpAroundThen_.offset == -1);
|
||||||
|
|
||||||
|
if (!emitEndInternal())
|
||||||
|
return false;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
state_ = State::End;
|
state_ = State::End;
|
||||||
#endif
|
#endif
|
||||||
|
@ -216,4 +234,65 @@ IfEmitter::emitEnd()
|
||||||
|
|
||||||
InternalIfEmitter::InternalIfEmitter(BytecodeEmitter* bce)
|
InternalIfEmitter::InternalIfEmitter(BytecodeEmitter* bce)
|
||||||
: IfEmitter(bce, Kind::NoLexicalAccessInBranch)
|
: IfEmitter(bce, Kind::NoLexicalAccessInBranch)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
// Skip emitIf (see the comment above InternalIfEmitter declaration).
|
||||||
|
state_ = State::If;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
CondEmitter::CondEmitter(BytecodeEmitter* bce)
|
||||||
|
: BranchEmitterBase(bce, Kind::MayContainLexicalAccessInBranch)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CondEmitter::emitCond()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::Start);
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::Cond;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CondEmitter::emitThenElse()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::Cond);
|
||||||
|
if (!emitThenInternal(SRC_COND))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::ThenElse;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CondEmitter::emitElse()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::ThenElse);
|
||||||
|
|
||||||
|
if (!emitElseInternal())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::Else;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CondEmitter::emitEnd()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::Else);
|
||||||
|
MOZ_ASSERT(jumpAroundThen_.offset == -1);
|
||||||
|
|
||||||
|
if (!emitEndInternal())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::End;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -21,58 +21,23 @@ namespace frontend {
|
||||||
|
|
||||||
struct BytecodeEmitter;
|
struct BytecodeEmitter;
|
||||||
|
|
||||||
// Class for emitting bytecode for blocks like if-then-else.
|
class MOZ_STACK_CLASS BranchEmitterBase
|
||||||
//
|
|
||||||
// This class can be used to emit single if-then-else block, or cascading
|
|
||||||
// else-if blocks.
|
|
||||||
//
|
|
||||||
// Usage: (check for the return value is omitted for simplicity)
|
|
||||||
//
|
|
||||||
// `if (cond) then_block`
|
|
||||||
// IfEmitter ifThen(this);
|
|
||||||
// emit(cond);
|
|
||||||
// ifThen.emitThen();
|
|
||||||
// emit(then_block);
|
|
||||||
// ifThen.emitEnd();
|
|
||||||
//
|
|
||||||
// `if (cond) then_block else else_block`
|
|
||||||
// IfEmitter ifThenElse(this);
|
|
||||||
// emit(cond);
|
|
||||||
// ifThenElse.emitThenElse();
|
|
||||||
// emit(then_block);
|
|
||||||
// ifThenElse.emitElse();
|
|
||||||
// emit(else_block);
|
|
||||||
// ifThenElse.emitEnd();
|
|
||||||
//
|
|
||||||
// `if (c1) b1 else if (c2) b2 else if (c3) b3 else b4`
|
|
||||||
// IfEmitter ifThenElse(this);
|
|
||||||
// emit(c1);
|
|
||||||
// ifThenElse.emitThenElse();
|
|
||||||
// emit(b1);
|
|
||||||
// ifThenElse.emitElseIf();
|
|
||||||
// emit(c2);
|
|
||||||
// ifThenElse.emitThenElse();
|
|
||||||
// emit(b2);
|
|
||||||
// ifThenElse.emitElseIf();
|
|
||||||
// emit(c3);
|
|
||||||
// ifThenElse.emitThenElse();
|
|
||||||
// emit(b3);
|
|
||||||
// ifThenElse.emitElse();
|
|
||||||
// emit(b4);
|
|
||||||
// ifThenElse.emitEnd();
|
|
||||||
//
|
|
||||||
// `cond ? then_expr : else_expr`
|
|
||||||
// IfEmitter condElse(this);
|
|
||||||
// emit(cond);
|
|
||||||
// condElse.emitCond();
|
|
||||||
// emit(then_block);
|
|
||||||
// condElse.emitElse();
|
|
||||||
// emit(else_block);
|
|
||||||
// condElse.emitEnd();
|
|
||||||
//
|
|
||||||
class MOZ_STACK_CLASS IfEmitter
|
|
||||||
{
|
{
|
||||||
public:
|
protected:
|
||||||
|
BytecodeEmitter* bce_;
|
||||||
|
|
||||||
|
// Jump around the then clause, to the beginning of the else clause.
|
||||||
|
JumpList jumpAroundThen_;
|
||||||
|
|
||||||
|
// Jump around the else clause, to the end of the entire branch.
|
||||||
|
JumpList jumpsAroundElse_;
|
||||||
|
|
||||||
|
// The stack depth before emitting the then block.
|
||||||
|
// Used for restoring stack depth before emitting the else block.
|
||||||
|
// Also used for assertion to make sure then and else blocks pushed the
|
||||||
|
// same number of values.
|
||||||
|
int32_t thenDepth_ = 0;
|
||||||
|
|
||||||
// Whether the then-clause, the else-clause, or else-if condition may
|
// Whether the then-clause, the else-clause, or else-if condition may
|
||||||
// contain declaration or access to lexical variables, which means they
|
// contain declaration or access to lexical variables, which means they
|
||||||
// should have their own TDZCheckCache. Basically TDZCheckCache should be
|
// should have their own TDZCheckCache. Basically TDZCheckCache should be
|
||||||
|
@ -92,92 +57,25 @@ class MOZ_STACK_CLASS IfEmitter
|
||||||
// inside then-clause, else-clause, nor else-if condition.
|
// inside then-clause, else-clause, nor else-if condition.
|
||||||
NoLexicalAccessInBranch
|
NoLexicalAccessInBranch
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
|
||||||
BytecodeEmitter* bce_;
|
|
||||||
|
|
||||||
// Jump around the then clause, to the beginning of the else clause.
|
|
||||||
JumpList jumpAroundThen_;
|
|
||||||
|
|
||||||
// Jump around the else clause, to the end of the entire branch.
|
|
||||||
JumpList jumpsAroundElse_;
|
|
||||||
|
|
||||||
// The stack depth before emitting the then block.
|
|
||||||
// Used for restoring stack depth before emitting the else block.
|
|
||||||
// Also used for assertion to make sure then and else blocks pushed the
|
|
||||||
// same number of values.
|
|
||||||
int32_t thenDepth_;
|
|
||||||
|
|
||||||
Kind kind_;
|
Kind kind_;
|
||||||
|
|
||||||
mozilla::Maybe<TDZCheckCache> tdzCache_;
|
mozilla::Maybe<TDZCheckCache> tdzCache_;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// The number of values pushed in the then and else blocks.
|
// The number of values pushed in the then and else blocks.
|
||||||
int32_t pushed_;
|
int32_t pushed_ = 0;
|
||||||
bool calculatedPushed_;
|
bool calculatedPushed_ = false;
|
||||||
|
|
||||||
// The state of this emitter.
|
|
||||||
//
|
|
||||||
// +-------+ emitCond +------+ emitElse +------+ emitEnd +-----+
|
|
||||||
// | Start |-+--------->| Cond |--------->| Else |------>+------->| End |
|
|
||||||
// +-------+ | +------+ +------+ ^ +-----+
|
|
||||||
// | |
|
|
||||||
// v emitThen +------+ |
|
|
||||||
// +->+--------->| Then |------------------------>+
|
|
||||||
// ^ | +------+ ^
|
|
||||||
// | | |
|
|
||||||
// | | +---+
|
|
||||||
// | | |
|
|
||||||
// | | emitThenElse +----------+ emitElse +------+ |
|
|
||||||
// | +------------->| ThenElse |-+--------->| Else |-+
|
|
||||||
// | +----------+ | +------+
|
|
||||||
// | |
|
|
||||||
// | | emitElseIf +--------+
|
|
||||||
// | +----------->| ElseIf |-+
|
|
||||||
// | +--------+ |
|
|
||||||
// | |
|
|
||||||
// +------------------------------------------------------+
|
|
||||||
enum class State {
|
|
||||||
// The initial state.
|
|
||||||
Start,
|
|
||||||
|
|
||||||
// After calling emitThen.
|
|
||||||
Then,
|
|
||||||
|
|
||||||
// After calling emitCond.
|
|
||||||
Cond,
|
|
||||||
|
|
||||||
// After calling emitThenElse.
|
|
||||||
ThenElse,
|
|
||||||
|
|
||||||
// After calling emitElse.
|
|
||||||
Else,
|
|
||||||
|
|
||||||
// After calling emitElseIf.
|
|
||||||
ElseIf,
|
|
||||||
|
|
||||||
// After calling emitEnd.
|
|
||||||
End
|
|
||||||
};
|
|
||||||
State state_;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// For InternalIfEmitter.
|
BranchEmitterBase(BytecodeEmitter* bce, Kind kind);
|
||||||
IfEmitter(BytecodeEmitter* bce, Kind kind);
|
|
||||||
|
MOZ_MUST_USE bool emitThenInternal(SrcNoteType type);
|
||||||
|
void calculateOrCheckPushed();
|
||||||
|
MOZ_MUST_USE bool emitElseInternal();
|
||||||
|
MOZ_MUST_USE bool emitEndInternal();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit IfEmitter(BytecodeEmitter* bce);
|
|
||||||
|
|
||||||
MOZ_MUST_USE bool emitThen();
|
|
||||||
MOZ_MUST_USE bool emitCond();
|
|
||||||
MOZ_MUST_USE bool emitThenElse();
|
|
||||||
|
|
||||||
MOZ_MUST_USE bool emitElse();
|
|
||||||
MOZ_MUST_USE bool emitElseIf();
|
|
||||||
|
|
||||||
MOZ_MUST_USE bool emitEnd();
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// Returns the number of values pushed onto the value stack inside
|
// Returns the number of values pushed onto the value stack inside
|
||||||
// `then_block` and `else_block`.
|
// `then_block` and `else_block`.
|
||||||
|
@ -193,11 +91,129 @@ class MOZ_STACK_CLASS IfEmitter
|
||||||
return -pushed_;
|
return -pushed_;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
// Class for emitting bytecode for blocks like if-then-else.
|
||||||
MOZ_MUST_USE bool emitIfInternal(SrcNoteType type);
|
//
|
||||||
void calculateOrCheckPushed();
|
// This class can be used to emit single if-then-else block, or cascading
|
||||||
MOZ_MUST_USE bool emitElseInternal();
|
// else-if blocks.
|
||||||
|
//
|
||||||
|
// Usage: (check for the return value is omitted for simplicity)
|
||||||
|
//
|
||||||
|
// `if (cond) then_block`
|
||||||
|
// IfEmitter ifThen(this);
|
||||||
|
// ifThen.emitIf(Some(offset_of_if));
|
||||||
|
// emit(cond);
|
||||||
|
// ifThen.emitThen();
|
||||||
|
// emit(then_block);
|
||||||
|
// ifThen.emitEnd();
|
||||||
|
//
|
||||||
|
// `if (cond) then_block else else_block`
|
||||||
|
// IfEmitter ifThenElse(this);
|
||||||
|
// ifThen.emitIf(Some(offset_of_if));
|
||||||
|
// emit(cond);
|
||||||
|
// ifThenElse.emitThenElse();
|
||||||
|
// emit(then_block);
|
||||||
|
// ifThenElse.emitElse();
|
||||||
|
// emit(else_block);
|
||||||
|
// ifThenElse.emitEnd();
|
||||||
|
//
|
||||||
|
// `if (c1) b1 else if (c2) b2 else if (c3) b3 else b4`
|
||||||
|
// IfEmitter ifThenElse(this);
|
||||||
|
// ifThen.emitIf(Some(offset_of_if));
|
||||||
|
// emit(c1);
|
||||||
|
// ifThenElse.emitThenElse();
|
||||||
|
// emit(b1);
|
||||||
|
// ifThenElse.emitElseIf(Some(offset_of_if));
|
||||||
|
// emit(c2);
|
||||||
|
// ifThenElse.emitThenElse();
|
||||||
|
// emit(b2);
|
||||||
|
// ifThenElse.emitElseIf(Some(offset_of_if));
|
||||||
|
// emit(c3);
|
||||||
|
// ifThenElse.emitThenElse();
|
||||||
|
// emit(b3);
|
||||||
|
// ifThenElse.emitElse();
|
||||||
|
// emit(b4);
|
||||||
|
// ifThenElse.emitEnd();
|
||||||
|
//
|
||||||
|
class MOZ_STACK_CLASS IfEmitter : public BranchEmitterBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
#ifdef DEBUG
|
||||||
|
// The state of this emitter.
|
||||||
|
//
|
||||||
|
// +-------+ emitIf +----+
|
||||||
|
// | Start |------->| If |-+
|
||||||
|
// +-------+ +----+ |
|
||||||
|
// |
|
||||||
|
// +--------------------+
|
||||||
|
// |
|
||||||
|
// v emitThen +------+ emitEnd +-----+
|
||||||
|
// +->+--------->| Then |---------------------------->+-------->| End |
|
||||||
|
// ^ | +------+ ^ +-----+
|
||||||
|
// | | |
|
||||||
|
// | | |
|
||||||
|
// | | |
|
||||||
|
// | | emitThenElse +----------+ emitElse +------+ |
|
||||||
|
// | +------------->| ThenElse |-+--------->| Else |-+
|
||||||
|
// | +----------+ | +------+
|
||||||
|
// | |
|
||||||
|
// | | emitElseIf +--------+
|
||||||
|
// | +----------->| ElseIf |-+
|
||||||
|
// | +--------+ |
|
||||||
|
// | |
|
||||||
|
// +------------------------------------------------------+
|
||||||
|
enum class State {
|
||||||
|
// The initial state.
|
||||||
|
Start,
|
||||||
|
|
||||||
|
// After calling emitIf.
|
||||||
|
If,
|
||||||
|
|
||||||
|
// After calling emitThen.
|
||||||
|
Then,
|
||||||
|
|
||||||
|
// After calling emitThenElse.
|
||||||
|
ThenElse,
|
||||||
|
|
||||||
|
// After calling emitElse.
|
||||||
|
Else,
|
||||||
|
|
||||||
|
// After calling emitElseIf.
|
||||||
|
ElseIf,
|
||||||
|
|
||||||
|
// After calling emitEnd.
|
||||||
|
End
|
||||||
|
};
|
||||||
|
State state_ = State::Start;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// For InternalIfEmitter.
|
||||||
|
IfEmitter(BytecodeEmitter* bce, Kind kind);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit IfEmitter(BytecodeEmitter* bce);
|
||||||
|
|
||||||
|
// `ifPos` is the offset in the source code for the character below:
|
||||||
|
//
|
||||||
|
// if ( cond ) { ... } else if ( cond2 ) { ... }
|
||||||
|
// ^ ^
|
||||||
|
// | |
|
||||||
|
// | ifPos for emitElseIf
|
||||||
|
// |
|
||||||
|
// ifPos for emitIf
|
||||||
|
//
|
||||||
|
// Can be Nothing() if not available.
|
||||||
|
MOZ_MUST_USE bool emitIf(const mozilla::Maybe<uint32_t>& ifPos);
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitThen();
|
||||||
|
MOZ_MUST_USE bool emitThenElse();
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitElseIf(const mozilla::Maybe<uint32_t>& ifPos);
|
||||||
|
MOZ_MUST_USE bool emitElse();
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitEnd();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Class for emitting bytecode for blocks like if-then-else which doesn't touch
|
// Class for emitting bytecode for blocks like if-then-else which doesn't touch
|
||||||
|
@ -205,12 +221,83 @@ class MOZ_STACK_CLASS IfEmitter
|
||||||
//
|
//
|
||||||
// See the comments above NoLexicalAccessInBranch for more details when to use
|
// See the comments above NoLexicalAccessInBranch for more details when to use
|
||||||
// this instead of IfEmitter.
|
// this instead of IfEmitter.
|
||||||
|
// Compared to IfEmitter, this class doesn't have emitIf method, given that
|
||||||
|
// it doesn't have syntactic `if`, and also the `cond` value can be already
|
||||||
|
// on the stack.
|
||||||
|
//
|
||||||
|
// Usage: (check for the return value is omitted for simplicity)
|
||||||
|
//
|
||||||
|
// `if (cond) then_block else else_block` (effectively)
|
||||||
|
// emit(cond);
|
||||||
|
// InternalIfEmitter ifThenElse(this);
|
||||||
|
// ifThenElse.emitThenElse();
|
||||||
|
// emit(then_block);
|
||||||
|
// ifThenElse.emitElse();
|
||||||
|
// emit(else_block);
|
||||||
|
// ifThenElse.emitEnd();
|
||||||
|
//
|
||||||
class MOZ_STACK_CLASS InternalIfEmitter : public IfEmitter
|
class MOZ_STACK_CLASS InternalIfEmitter : public IfEmitter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit InternalIfEmitter(BytecodeEmitter* bce);
|
explicit InternalIfEmitter(BytecodeEmitter* bce);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Class for emitting bytecode for conditional expression.
|
||||||
|
//
|
||||||
|
// Usage: (check for the return value is omitted for simplicity)
|
||||||
|
//
|
||||||
|
// `cond ? then_expr : else_expr`
|
||||||
|
// CondEmitter condElse(this);
|
||||||
|
// condElse.emitCond();
|
||||||
|
// emit(cond);
|
||||||
|
// condElse.emitThenElse();
|
||||||
|
// emit(then_expr);
|
||||||
|
// condElse.emitElse();
|
||||||
|
// emit(else_expr);
|
||||||
|
// condElse.emitEnd();
|
||||||
|
//
|
||||||
|
class MOZ_STACK_CLASS CondEmitter : public BranchEmitterBase
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
// The state of this emitter.
|
||||||
|
//
|
||||||
|
// +-------+ emitCond +------+ emitThenElse +----------+
|
||||||
|
// | Start |--------->| Cond |------------->| ThenElse |-+
|
||||||
|
// +-------+ +------+ +----------+ |
|
||||||
|
// |
|
||||||
|
// +-----------------+
|
||||||
|
// |
|
||||||
|
// | emitElse +------+ emitEnd +-----+
|
||||||
|
// +--------->| Else |-------->| End |
|
||||||
|
// +------+ +-----+
|
||||||
|
enum class State {
|
||||||
|
// The initial state.
|
||||||
|
Start,
|
||||||
|
|
||||||
|
// After calling emitCond.
|
||||||
|
Cond,
|
||||||
|
|
||||||
|
// After calling emitThenElse.
|
||||||
|
ThenElse,
|
||||||
|
|
||||||
|
// After calling emitElse.
|
||||||
|
Else,
|
||||||
|
|
||||||
|
// After calling emitEnd.
|
||||||
|
End
|
||||||
|
};
|
||||||
|
State state_ = State::Start;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CondEmitter(BytecodeEmitter* bce);
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitCond();
|
||||||
|
MOZ_MUST_USE bool emitThenElse();
|
||||||
|
MOZ_MUST_USE bool emitElse();
|
||||||
|
MOZ_MUST_USE bool emitEnd();
|
||||||
|
};
|
||||||
|
|
||||||
} /* namespace frontend */
|
} /* namespace frontend */
|
||||||
} /* namespace js */
|
} /* namespace js */
|
||||||
|
|
||||||
|
|
|
@ -75,11 +75,11 @@ class NameResolver
|
||||||
bool nameExpression(ParseNode* n, bool* foundName) {
|
bool nameExpression(ParseNode* n, bool* foundName) {
|
||||||
switch (n->getKind()) {
|
switch (n->getKind()) {
|
||||||
case ParseNodeKind::Dot:
|
case ParseNodeKind::Dot:
|
||||||
if (!nameExpression(n->expr(), foundName))
|
if (!nameExpression(n->pn_left, foundName))
|
||||||
return false;
|
return false;
|
||||||
if (!*foundName)
|
if (!*foundName)
|
||||||
return true;
|
return true;
|
||||||
return appendPropertyReference(n->pn_atom);
|
return appendPropertyReference(n->pn_right->pn_atom);
|
||||||
|
|
||||||
case ParseNodeKind::Name:
|
case ParseNodeKind::Name:
|
||||||
*foundName = true;
|
*foundName = true;
|
||||||
|
@ -315,17 +315,17 @@ class NameResolver
|
||||||
bool resolveTaggedTemplate(ParseNode* node, HandleAtom prefix) {
|
bool resolveTaggedTemplate(ParseNode* node, HandleAtom prefix) {
|
||||||
MOZ_ASSERT(node->isKind(ParseNodeKind::TaggedTemplate));
|
MOZ_ASSERT(node->isKind(ParseNodeKind::TaggedTemplate));
|
||||||
|
|
||||||
ParseNode* element = node->pn_head;
|
ParseNode* tag = node->pn_left;
|
||||||
|
|
||||||
// The list head is a leading expression, e.g. |tag| in |tag`foo`|,
|
// The leading expression, e.g. |tag| in |tag`foo`|,
|
||||||
// that might contain functions.
|
// that might contain functions.
|
||||||
if (!resolve(element, prefix))
|
if (!resolve(tag, prefix))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Next is the callsite object node. This node only contains
|
// The callsite object node is first. This node only contains
|
||||||
// internal strings or undefined and an array -- no user-controlled
|
// internal strings or undefined and an array -- no user-controlled
|
||||||
// expressions.
|
// expressions.
|
||||||
element = element->pn_next;
|
ParseNode* element = node->pn_right->pn_head;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(element->isKind(ParseNodeKind::CallSiteObj));
|
MOZ_ASSERT(element->isKind(ParseNodeKind::CallSiteObj));
|
||||||
|
@ -697,9 +697,6 @@ class NameResolver
|
||||||
case ParseNodeKind::Pow:
|
case ParseNodeKind::Pow:
|
||||||
case ParseNodeKind::Pipeline:
|
case ParseNodeKind::Pipeline:
|
||||||
case ParseNodeKind::Comma:
|
case ParseNodeKind::Comma:
|
||||||
case ParseNodeKind::New:
|
|
||||||
case ParseNodeKind::Call:
|
|
||||||
case ParseNodeKind::SuperCall:
|
|
||||||
case ParseNodeKind::Array:
|
case ParseNodeKind::Array:
|
||||||
case ParseNodeKind::StatementList:
|
case ParseNodeKind::StatementList:
|
||||||
case ParseNodeKind::ParamsBody:
|
case ParseNodeKind::ParamsBody:
|
||||||
|
@ -733,11 +730,32 @@ class NameResolver
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ParseNodeKind::TaggedTemplate:
|
case ParseNodeKind::TaggedTemplate:
|
||||||
MOZ_ASSERT(cur->isArity(PN_LIST));
|
MOZ_ASSERT(cur->isArity(PN_BINARY));
|
||||||
if (!resolveTaggedTemplate(cur, prefix))
|
if (!resolveTaggedTemplate(cur, prefix))
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ParseNodeKind::New:
|
||||||
|
case ParseNodeKind::Call:
|
||||||
|
case ParseNodeKind::SuperCall:
|
||||||
|
MOZ_ASSERT(cur->isArity(PN_BINARY));
|
||||||
|
if (!resolve(cur->pn_left, prefix))
|
||||||
|
return false;
|
||||||
|
if (!resolve(cur->pn_right, prefix))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Handles the arguments for new/call/supercall, but does _not_ handle
|
||||||
|
// the Arguments node used by tagged template literals, since that is
|
||||||
|
// special-cased inside of resolveTaggedTemplate.
|
||||||
|
case ParseNodeKind::Arguments:
|
||||||
|
MOZ_ASSERT(cur->isArity(PN_LIST));
|
||||||
|
for (ParseNode* element = cur->pn_head; element; element = element->pn_next) {
|
||||||
|
if (!resolve(element, prefix))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// Import/export spec lists contain import/export specs containing
|
// Import/export spec lists contain import/export specs containing
|
||||||
// only pairs of names. Alternatively, an export spec lists may
|
// only pairs of names. Alternatively, an export spec lists may
|
||||||
// contain a single export batch specifier.
|
// contain a single export batch specifier.
|
||||||
|
@ -766,12 +784,12 @@ class NameResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
case ParseNodeKind::Dot:
|
case ParseNodeKind::Dot:
|
||||||
MOZ_ASSERT(cur->isArity(PN_NAME));
|
MOZ_ASSERT(cur->isArity(PN_BINARY));
|
||||||
|
|
||||||
// Super prop nodes do not have a meaningful LHS
|
// Super prop nodes do not have a meaningful LHS
|
||||||
if (cur->as<PropertyAccess>().isSuper())
|
if (cur->as<PropertyAccess>().isSuper())
|
||||||
break;
|
break;
|
||||||
if (!resolve(cur->expr(), prefix))
|
if (!resolve(cur->pn_left, prefix))
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -810,6 +828,7 @@ class NameResolver
|
||||||
case ParseNodeKind::ExportSpec: // by ParseNodeKind::ExportSpecList
|
case ParseNodeKind::ExportSpec: // by ParseNodeKind::ExportSpecList
|
||||||
case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate
|
case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate
|
||||||
case ParseNodeKind::ClassNames: // by ParseNodeKind::Class
|
case ParseNodeKind::ClassNames: // by ParseNodeKind::Class
|
||||||
|
case ParseNodeKind::PropertyName: // by ParseNodeKind::Dot
|
||||||
MOZ_CRASH("should have been handled by a parent node");
|
MOZ_CRASH("should have been handled by a parent node");
|
||||||
|
|
||||||
case ParseNodeKind::Limit: // invalid sentinel value
|
case ParseNodeKind::Limit: // invalid sentinel value
|
||||||
|
|
|
@ -216,6 +216,21 @@ UnaryNode::dump(GenericPrinter& out, int indent)
|
||||||
void
|
void
|
||||||
BinaryNode::dump(GenericPrinter& out, int indent)
|
BinaryNode::dump(GenericPrinter& out, int indent)
|
||||||
{
|
{
|
||||||
|
if (isKind(ParseNodeKind::Dot)) {
|
||||||
|
out.put("(.");
|
||||||
|
|
||||||
|
DumpParseTree(pn_right, out, indent + 2);
|
||||||
|
|
||||||
|
out.putChar(' ');
|
||||||
|
if (as<PropertyAccess>().isSuper())
|
||||||
|
out.put("super");
|
||||||
|
else
|
||||||
|
DumpParseTree(pn_left, out, indent + 2);
|
||||||
|
|
||||||
|
out.printf(")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const char* name = parseNodeNames[size_t(getKind())];
|
const char* name = parseNodeNames[size_t(getKind())];
|
||||||
out.printf("(%s ", name);
|
out.printf("(%s ", name);
|
||||||
indent += strlen(name) + 2;
|
indent += strlen(name) + 2;
|
||||||
|
@ -288,10 +303,7 @@ DumpName(GenericPrinter& out, const CharT* s, size_t len)
|
||||||
void
|
void
|
||||||
NameNode::dump(GenericPrinter& out, int indent)
|
NameNode::dump(GenericPrinter& out, int indent)
|
||||||
{
|
{
|
||||||
if (isKind(ParseNodeKind::Name) || isKind(ParseNodeKind::Dot)) {
|
if (isKind(ParseNodeKind::Name) || isKind(ParseNodeKind::PropertyName)) {
|
||||||
if (isKind(ParseNodeKind::Dot))
|
|
||||||
out.put("(.");
|
|
||||||
|
|
||||||
if (!pn_atom) {
|
if (!pn_atom) {
|
||||||
out.put("#<null name>");
|
out.put("#<null name>");
|
||||||
} else if (getOp() == JSOP_GETARG && pn_atom->length() == 0) {
|
} else if (getOp() == JSOP_GETARG && pn_atom->length() == 0) {
|
||||||
|
@ -306,15 +318,6 @@ NameNode::dump(GenericPrinter& out, int indent)
|
||||||
else
|
else
|
||||||
DumpName(out, pn_atom->twoByteChars(nogc), pn_atom->length());
|
DumpName(out, pn_atom->twoByteChars(nogc), pn_atom->length());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isKind(ParseNodeKind::Dot)) {
|
|
||||||
out.putChar(' ');
|
|
||||||
if (as<PropertyAccess>().isSuper())
|
|
||||||
out.put("super");
|
|
||||||
else
|
|
||||||
DumpParseTree(expr(), out, indent + 2);
|
|
||||||
out.printf(")");
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ class ObjectBox;
|
||||||
F(PostIncrement) \
|
F(PostIncrement) \
|
||||||
F(PreDecrement) \
|
F(PreDecrement) \
|
||||||
F(PostDecrement) \
|
F(PostDecrement) \
|
||||||
|
F(PropertyName) \
|
||||||
F(Dot) \
|
F(Dot) \
|
||||||
F(Elem) \
|
F(Elem) \
|
||||||
F(Array) \
|
F(Array) \
|
||||||
|
@ -63,6 +64,7 @@ class ObjectBox;
|
||||||
F(Label) \
|
F(Label) \
|
||||||
F(Object) \
|
F(Object) \
|
||||||
F(Call) \
|
F(Call) \
|
||||||
|
F(Arguments) \
|
||||||
F(Name) \
|
F(Name) \
|
||||||
F(ObjectPropertyName) \
|
F(ObjectPropertyName) \
|
||||||
F(ComputedName) \
|
F(ComputedName) \
|
||||||
|
@ -371,9 +373,8 @@ IsTypeofKind(ParseNodeKind kind)
|
||||||
* PostIncrement,
|
* PostIncrement,
|
||||||
* PreDecrement,
|
* PreDecrement,
|
||||||
* PostDecrement
|
* PostDecrement
|
||||||
* New list pn_head: list of ctor, arg1, arg2, ... argN
|
* New binary pn_left: ctor expression on the left of the (
|
||||||
* pn_count: 1 + N (where N is number of args)
|
* pn_right: Arguments
|
||||||
* ctor is a MEMBER expr
|
|
||||||
* DeleteName unary pn_kid: Name expr
|
* DeleteName unary pn_kid: Name expr
|
||||||
* DeleteProp unary pn_kid: Dot expr
|
* DeleteProp unary pn_kid: Dot expr
|
||||||
* DeleteElem unary pn_kid: Elem expr
|
* DeleteElem unary pn_kid: Elem expr
|
||||||
|
@ -382,13 +383,15 @@ IsTypeofKind(ParseNodeKind kind)
|
||||||
* for a more-specific PNK_DELETE* unless constant
|
* for a more-specific PNK_DELETE* unless constant
|
||||||
* folding (or a similar parse tree manipulation) has
|
* folding (or a similar parse tree manipulation) has
|
||||||
* occurred
|
* occurred
|
||||||
* Dot name pn_expr: MEMBER expr to left of .
|
* PropertyName name pn_atom: property name being accessed
|
||||||
* pn_atom: name to right of .
|
* Dot binary pn_left: MEMBER expr to left of .
|
||||||
|
* pn_right: PropertyName to right of .
|
||||||
* Elem binary pn_left: MEMBER expr to left of [
|
* Elem binary pn_left: MEMBER expr to left of [
|
||||||
* pn_right: expr between [ and ]
|
* pn_right: expr between [ and ]
|
||||||
* Call list pn_head: list of call, arg1, arg2, ... argN
|
* Call binary pn_left: callee expression on the left of the (
|
||||||
* pn_count: 1 + N (where N is number of args)
|
* pn_right: Arguments
|
||||||
* call is a MEMBER expr naming a callable object
|
* Arguments list pn_head: list of arg1, arg2, ... argN
|
||||||
|
* pn_count: N (where N is number of args)
|
||||||
* Array list pn_head: list of pn_count array element exprs
|
* Array list pn_head: list of pn_count array element exprs
|
||||||
* [,,] holes are represented by Elision nodes
|
* [,,] holes are represented by Elision nodes
|
||||||
* pn_xflags: PN_ENDCOMMA if extra comma at end
|
* pn_xflags: PN_ENDCOMMA if extra comma at end
|
||||||
|
@ -408,8 +411,9 @@ IsTypeofKind(ParseNodeKind kind)
|
||||||
* list
|
* list
|
||||||
* TemplateString pn_atom: template string atom
|
* TemplateString pn_atom: template string atom
|
||||||
nullary pn_op: JSOP_NOP
|
nullary pn_op: JSOP_NOP
|
||||||
* TaggedTemplate pn_head: list of call, call site object, arg1, arg2, ... argN
|
* TaggedTemplate pn_left: tag expression
|
||||||
* list pn_count: 2 + N (N is the number of substitutions)
|
* binary pn_right: Arguments, with the first being the
|
||||||
|
* call site object, then arg1, arg2, ... argN
|
||||||
* CallSiteObj list pn_head: a Array node followed by
|
* CallSiteObj list pn_head: a Array node followed by
|
||||||
* list of pn_count - 1 TemplateString nodes
|
* list of pn_count - 1 TemplateString nodes
|
||||||
* RegExp nullary pn_objbox: RegExp model object
|
* RegExp nullary pn_objbox: RegExp model object
|
||||||
|
@ -421,7 +425,7 @@ IsTypeofKind(ParseNodeKind kind)
|
||||||
*
|
*
|
||||||
* This, unary pn_kid: '.this' Name if function `this`, else nullptr
|
* This, unary pn_kid: '.this' Name if function `this`, else nullptr
|
||||||
* SuperBase unary pn_kid: '.this' Name
|
* SuperBase unary pn_kid: '.this' Name
|
||||||
*
|
* SuperCall binary pn_left: SuperBase pn_right: Arguments
|
||||||
* SetThis binary pn_left: '.this' Name, pn_right: SuperCall
|
* SetThis binary pn_left: '.this' Name, pn_right: SuperCall
|
||||||
*
|
*
|
||||||
* LexicalScope scope pn_u.scope.bindings: scope bindings
|
* LexicalScope scope pn_u.scope.bindings: scope bindings
|
||||||
|
@ -571,8 +575,7 @@ class ParseNode
|
||||||
FunctionBox* funbox; /* function object */
|
FunctionBox* funbox; /* function object */
|
||||||
};
|
};
|
||||||
ParseNode* expr; /* module or function body, var
|
ParseNode* expr; /* module or function body, var
|
||||||
initializer, argument default, or
|
initializer, or argument default */
|
||||||
base object of ParseNodeKind::Dot */
|
|
||||||
} name;
|
} name;
|
||||||
struct {
|
struct {
|
||||||
LexicalScope::Data* bindings;
|
LexicalScope::Data* bindings;
|
||||||
|
@ -1176,30 +1179,33 @@ class RegExpLiteral : public NullaryNode
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class PropertyAccess : public ParseNode
|
class PropertyAccess : public BinaryNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PropertyAccess(ParseNode* lhs, PropertyName* name, uint32_t begin, uint32_t end)
|
/*
|
||||||
: ParseNode(ParseNodeKind::Dot, JSOP_NOP, PN_NAME, TokenPos(begin, end))
|
* PropertyAccess nodes can have any expression/'super' as left-hand
|
||||||
|
* side, but the name must be a ParseNodeKind::PropertyName node.
|
||||||
|
*/
|
||||||
|
PropertyAccess(ParseNode* lhs, ParseNode* name, uint32_t begin, uint32_t end)
|
||||||
|
: BinaryNode(ParseNodeKind::Dot, JSOP_NOP, TokenPos(begin, end), lhs, name)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(lhs != nullptr);
|
MOZ_ASSERT(lhs != nullptr);
|
||||||
MOZ_ASSERT(name != nullptr);
|
MOZ_ASSERT(name != nullptr);
|
||||||
pn_u.name.expr = lhs;
|
|
||||||
pn_u.name.atom = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool test(const ParseNode& node) {
|
static bool test(const ParseNode& node) {
|
||||||
bool match = node.isKind(ParseNodeKind::Dot);
|
bool match = node.isKind(ParseNodeKind::Dot);
|
||||||
MOZ_ASSERT_IF(match, node.isArity(PN_NAME));
|
MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
|
||||||
|
MOZ_ASSERT_IF(match, node.pn_right->isKind(ParseNodeKind::PropertyName));
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseNode& expression() const {
|
ParseNode& expression() const {
|
||||||
return *pn_u.name.expr;
|
return *pn_u.binary.left;
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyName& name() const {
|
PropertyName& name() const {
|
||||||
return *pn_u.name.atom->asPropertyName();
|
return *pn_u.binary.right->pn_atom->asPropertyName();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSuper() const {
|
bool isSuper() const {
|
||||||
|
|
|
@ -3397,13 +3397,13 @@ GeneralParser<ParseHandler, CharT>::addExprAndGetNextTemplStrToken(YieldHandling
|
||||||
|
|
||||||
template <class ParseHandler, typename CharT>
|
template <class ParseHandler, typename CharT>
|
||||||
bool
|
bool
|
||||||
GeneralParser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling, Node nodeList,
|
GeneralParser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling, Node tagArgsList,
|
||||||
TokenKind tt)
|
TokenKind tt)
|
||||||
{
|
{
|
||||||
Node callSiteObjNode = handler.newCallSiteObject(pos().begin);
|
Node callSiteObjNode = handler.newCallSiteObject(pos().begin);
|
||||||
if (!callSiteObjNode)
|
if (!callSiteObjNode)
|
||||||
return false;
|
return false;
|
||||||
handler.addList(nodeList, callSiteObjNode);
|
handler.addList(tagArgsList, callSiteObjNode);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (!appendToCallSiteObj(callSiteObjNode))
|
if (!appendToCallSiteObj(callSiteObjNode))
|
||||||
|
@ -3411,10 +3411,10 @@ GeneralParser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling,
|
||||||
if (tt != TokenKind::TemplateHead)
|
if (tt != TokenKind::TemplateHead)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt))
|
if (!addExprAndGetNextTemplStrToken(yieldHandling, tagArgsList, &tt))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
handler.setEndPosition(nodeList, callSiteObjNode);
|
handler.setEndPosition(tagArgsList, callSiteObjNode);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8643,24 +8643,27 @@ GeneralParser<ParseHandler, CharT>::assignExprWithoutYieldOrAwait(YieldHandling
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ParseHandler, typename CharT>
|
template <class ParseHandler, typename CharT>
|
||||||
bool
|
typename ParseHandler::Node
|
||||||
GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, Node listNode,
|
GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, bool* isSpread,
|
||||||
bool* isSpread,
|
|
||||||
PossibleError* possibleError /* = nullptr */)
|
PossibleError* possibleError /* = nullptr */)
|
||||||
{
|
{
|
||||||
|
Node argsList = handler.newArguments(pos());
|
||||||
|
if (!argsList)
|
||||||
|
return null();
|
||||||
|
|
||||||
bool matched;
|
bool matched;
|
||||||
if (!tokenStream.matchToken(&matched, TokenKind::Rp, TokenStream::Operand))
|
if (!tokenStream.matchToken(&matched, TokenKind::Rp, TokenStream::Operand))
|
||||||
return false;
|
return null();
|
||||||
if (matched) {
|
if (matched) {
|
||||||
handler.setEndPosition(listNode, pos().end);
|
handler.setEndPosition(argsList, pos().end);
|
||||||
return true;
|
return argsList;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
bool spread = false;
|
bool spread = false;
|
||||||
uint32_t begin = 0;
|
uint32_t begin = 0;
|
||||||
if (!tokenStream.matchToken(&matched, TokenKind::TripleDot, TokenStream::Operand))
|
if (!tokenStream.matchToken(&matched, TokenKind::TripleDot, TokenStream::Operand))
|
||||||
return false;
|
return null();
|
||||||
if (matched) {
|
if (matched) {
|
||||||
spread = true;
|
spread = true;
|
||||||
begin = pos().begin;
|
begin = pos().begin;
|
||||||
|
@ -8669,18 +8672,18 @@ GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, No
|
||||||
|
|
||||||
Node argNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError);
|
Node argNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError);
|
||||||
if (!argNode)
|
if (!argNode)
|
||||||
return false;
|
return null();
|
||||||
if (spread) {
|
if (spread) {
|
||||||
argNode = handler.newSpread(begin, argNode);
|
argNode = handler.newSpread(begin, argNode);
|
||||||
if (!argNode)
|
if (!argNode)
|
||||||
return false;
|
return null();
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.addList(listNode, argNode);
|
handler.addList(argsList, argNode);
|
||||||
|
|
||||||
bool matched;
|
bool matched;
|
||||||
if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::Operand))
|
if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::Operand))
|
||||||
return false;
|
return null();
|
||||||
if (!matched)
|
if (!matched)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -8693,8 +8696,8 @@ GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, No
|
||||||
|
|
||||||
MUST_MATCH_TOKEN_MOD(TokenKind::Rp, TokenStream::Operand, JSMSG_PAREN_AFTER_ARGS);
|
MUST_MATCH_TOKEN_MOD(TokenKind::Rp, TokenStream::Operand, JSMSG_PAREN_AFTER_ARGS);
|
||||||
|
|
||||||
handler.setEndPosition(listNode, pos().end);
|
handler.setEndPosition(argsList, pos().end);
|
||||||
return true;
|
return argsList;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -8740,21 +8743,28 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
|
||||||
if (!ctorExpr)
|
if (!ctorExpr)
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
lhs = handler.newNewExpression(newBegin, ctorExpr);
|
|
||||||
if (!lhs)
|
|
||||||
return null();
|
|
||||||
|
|
||||||
bool matched;
|
bool matched;
|
||||||
if (!tokenStream.matchToken(&matched, TokenKind::Lp))
|
if (!tokenStream.matchToken(&matched, TokenKind::Lp))
|
||||||
return null();
|
return null();
|
||||||
if (matched) {
|
|
||||||
bool isSpread = false;
|
bool isSpread = false;
|
||||||
if (!argumentList(yieldHandling, lhs, &isSpread))
|
Node args;
|
||||||
|
if (matched) {
|
||||||
|
args = argumentList(yieldHandling, &isSpread);
|
||||||
|
} else {
|
||||||
|
args = handler.newArguments(pos());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!args)
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
|
lhs = handler.newNewExpression(newBegin, ctorExpr, args);
|
||||||
|
if (!lhs)
|
||||||
|
return null();
|
||||||
|
|
||||||
if (isSpread)
|
if (isSpread)
|
||||||
handler.setOp(lhs, JSOP_SPREADNEW);
|
handler.setOp(lhs, JSOP_SPREADNEW);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else if (tt == TokenKind::Super) {
|
} else if (tt == TokenKind::Super) {
|
||||||
Node thisName = newThisName();
|
Node thisName = newThisName();
|
||||||
if (!thisName)
|
if (!thisName)
|
||||||
|
@ -8790,7 +8800,12 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
|
||||||
error(JSMSG_BAD_SUPERPROP, "property");
|
error(JSMSG_BAD_SUPERPROP, "property");
|
||||||
return null();
|
return null();
|
||||||
}
|
}
|
||||||
nextMember = handler.newPropertyAccess(lhs, field, pos().end);
|
|
||||||
|
Node name = handler.newPropertyName(field, pos());
|
||||||
|
if (!name)
|
||||||
|
return null();
|
||||||
|
|
||||||
|
nextMember = handler.newPropertyAccess(lhs, name);
|
||||||
if (!nextMember)
|
if (!nextMember)
|
||||||
return null();
|
return null();
|
||||||
} else {
|
} else {
|
||||||
|
@ -8826,15 +8841,16 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
|
||||||
return null();
|
return null();
|
||||||
}
|
}
|
||||||
|
|
||||||
nextMember = handler.newSuperCall(lhs);
|
|
||||||
if (!nextMember)
|
|
||||||
return null();
|
|
||||||
|
|
||||||
// Despite the fact that it's impossible to have |super()| in a
|
// Despite the fact that it's impossible to have |super()| in a
|
||||||
// generator, we still inherit the yieldHandling of the
|
// generator, we still inherit the yieldHandling of the
|
||||||
// memberExpression, per spec. Curious.
|
// memberExpression, per spec. Curious.
|
||||||
bool isSpread = false;
|
bool isSpread = false;
|
||||||
if (!argumentList(yieldHandling, nextMember, &isSpread))
|
Node args = argumentList(yieldHandling, &isSpread);
|
||||||
|
if (!args)
|
||||||
|
return null();
|
||||||
|
|
||||||
|
nextMember = handler.newSuperCall(lhs, args);
|
||||||
|
if (!nextMember)
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
if (isSpread)
|
if (isSpread)
|
||||||
|
@ -8853,13 +8869,6 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
|
||||||
return null();
|
return null();
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenPos nextMemberPos = pos();
|
|
||||||
nextMember = tt == TokenKind::Lp
|
|
||||||
? handler.newCall(nextMemberPos)
|
|
||||||
: handler.newTaggedTemplate(nextMemberPos);
|
|
||||||
if (!nextMember)
|
|
||||||
return null();
|
|
||||||
|
|
||||||
JSOp op = JSOP_CALL;
|
JSOp op = JSOP_CALL;
|
||||||
bool maybeAsyncArrow = false;
|
bool maybeAsyncArrow = false;
|
||||||
if (PropertyName* prop = handler.maybeDottedProperty(lhs)) {
|
if (PropertyName* prop = handler.maybeDottedProperty(lhs)) {
|
||||||
|
@ -8901,13 +8910,11 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.setBeginPosition(nextMember, lhs);
|
|
||||||
handler.addList(nextMember, lhs);
|
|
||||||
|
|
||||||
if (tt == TokenKind::Lp) {
|
if (tt == TokenKind::Lp) {
|
||||||
bool isSpread = false;
|
bool isSpread = false;
|
||||||
PossibleError* asyncPossibleError = maybeAsyncArrow ? possibleError : nullptr;
|
PossibleError* asyncPossibleError = maybeAsyncArrow ? possibleError : nullptr;
|
||||||
if (!argumentList(yieldHandling, nextMember, &isSpread, asyncPossibleError))
|
Node args = argumentList(yieldHandling, &isSpread, asyncPossibleError);
|
||||||
|
if (!args)
|
||||||
return null();
|
return null();
|
||||||
if (isSpread) {
|
if (isSpread) {
|
||||||
if (op == JSOP_EVAL)
|
if (op == JSOP_EVAL)
|
||||||
|
@ -8917,8 +8924,20 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
|
||||||
else
|
else
|
||||||
op = JSOP_SPREADCALL;
|
op = JSOP_SPREADCALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nextMember = handler.newCall(lhs, args);
|
||||||
|
if (!nextMember)
|
||||||
|
return null();
|
||||||
} else {
|
} else {
|
||||||
if (!taggedTemplate(yieldHandling, nextMember, tt))
|
Node args = handler.newArguments(pos());
|
||||||
|
if (!args)
|
||||||
|
return null();
|
||||||
|
|
||||||
|
if (!taggedTemplate(yieldHandling, args, tt))
|
||||||
|
return null();
|
||||||
|
|
||||||
|
nextMember = handler.newTaggedTemplate(lhs, args);
|
||||||
|
if (!nextMember)
|
||||||
return null();
|
return null();
|
||||||
}
|
}
|
||||||
handler.setOp(nextMember, op);
|
handler.setOp(nextMember, op);
|
||||||
|
|
|
@ -569,8 +569,12 @@ class MOZ_STACK_CLASS PerHandlerParser
|
||||||
bool isValidSimpleAssignmentTarget(Node node,
|
bool isValidSimpleAssignmentTarget(Node node,
|
||||||
FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
|
FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
|
||||||
|
|
||||||
Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) {
|
Node newPropertyName(PropertyName* key, const TokenPos& pos) {
|
||||||
return handler.newPropertyAccess(expr, key, end);
|
return handler.newPropertyName(key, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node newPropertyAccess(Node expr, Node key) {
|
||||||
|
return handler.newPropertyAccess(expr, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
|
FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
|
||||||
|
@ -1155,7 +1159,7 @@ class MOZ_STACK_CLASS GeneralParser
|
||||||
|
|
||||||
Node condition(InHandling inHandling, YieldHandling yieldHandling);
|
Node condition(InHandling inHandling, YieldHandling yieldHandling);
|
||||||
|
|
||||||
bool argumentList(YieldHandling yieldHandling, Node listNode, bool* isSpread,
|
Node argumentList(YieldHandling yieldHandling, bool* isSpread,
|
||||||
PossibleError* possibleError = nullptr);
|
PossibleError* possibleError = nullptr);
|
||||||
Node destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling,
|
Node destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling,
|
||||||
TokenKind tt);
|
TokenKind tt);
|
||||||
|
|
|
@ -248,9 +248,11 @@ class SyntaxParseHandler
|
||||||
MOZ_MUST_USE bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; }
|
MOZ_MUST_USE bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; }
|
||||||
void addArrayElement(Node literal, Node element) { }
|
void addArrayElement(Node literal, Node element) { }
|
||||||
|
|
||||||
Node newCall(const TokenPos& pos) { return NodeFunctionCall; }
|
Node newArguments(const TokenPos& pos) { return NodeGeneric; }
|
||||||
Node newSuperCall(Node callee) { return NodeGeneric; }
|
Node newCall(Node callee, Node args) { return NodeFunctionCall; }
|
||||||
Node newTaggedTemplate(const TokenPos& pos) { return NodeGeneric; }
|
|
||||||
|
Node newSuperCall(Node callee, Node args) { return NodeGeneric; }
|
||||||
|
Node newTaggedTemplate(Node callee, Node args) { return NodeGeneric; }
|
||||||
|
|
||||||
Node newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; }
|
Node newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; }
|
||||||
Node newClassMethodList(uint32_t begin) { return NodeGeneric; }
|
Node newClassMethodList(uint32_t begin) { return NodeGeneric; }
|
||||||
|
@ -332,8 +334,12 @@ class SyntaxParseHandler
|
||||||
}
|
}
|
||||||
Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
|
Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
|
||||||
|
|
||||||
Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) {
|
Node newPropertyName(PropertyName* name, const TokenPos& pos) {
|
||||||
lastAtom = key;
|
lastAtom = name;
|
||||||
|
return NodeGeneric;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node newPropertyAccess(Node expr, Node key) {
|
||||||
return NodeDottedProperty;
|
return NodeDottedProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,7 +434,7 @@ class SyntaxParseHandler
|
||||||
list == NodeFunctionCall);
|
list == NodeFunctionCall);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node newNewExpression(uint32_t begin, Node ctor) {
|
Node newNewExpression(uint32_t begin, Node ctor, Node args) {
|
||||||
return NodeGeneric;
|
return NodeGeneric;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2778,9 +2778,21 @@ ForegroundUpdateKinds(AllocKinds kinds)
|
||||||
void
|
void
|
||||||
GCRuntime::updateTypeDescrObjects(MovingTracer* trc, Zone* zone)
|
GCRuntime::updateTypeDescrObjects(MovingTracer* trc, Zone* zone)
|
||||||
{
|
{
|
||||||
|
// We need to update each type descriptor object and any objects stored in
|
||||||
|
// its slots, since some of these contain array objects which also need to
|
||||||
|
// be updated.
|
||||||
|
|
||||||
zone->typeDescrObjects().sweep();
|
zone->typeDescrObjects().sweep();
|
||||||
for (auto r = zone->typeDescrObjects().all(); !r.empty(); r.popFront())
|
|
||||||
UpdateCellPointers(trc, r.front());
|
for (auto r = zone->typeDescrObjects().all(); !r.empty(); r.popFront()) {
|
||||||
|
NativeObject* obj = &r.front()->as<NativeObject>();
|
||||||
|
UpdateCellPointers(trc, obj);
|
||||||
|
for (size_t i = 0; i < obj->slotSpan(); i++) {
|
||||||
|
Value value = obj->getSlot(i);
|
||||||
|
if (value.isObject())
|
||||||
|
UpdateCellPointers(trc, &value.toObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -33,7 +33,12 @@ function assertOffsetColumns(code, expectedBpts, expectedOrdering = null) {
|
||||||
|
|
||||||
// Set breakpoints everywhere and call the function.
|
// Set breakpoints everywhere and call the function.
|
||||||
const dbg = new Debugger;
|
const dbg = new Debugger;
|
||||||
const script = dbg.addDebuggee(global).makeDebuggeeValue(global.f).script;
|
let debuggeeFn = dbg.addDebuggee(global).makeDebuggeeValue(global.f);
|
||||||
|
if (debuggeeFn.isBoundFunction) {
|
||||||
|
debuggeeFn = debuggeeFn.boundTargetFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { script } = debuggeeFn;
|
||||||
for (const offset of script.getAllColumnOffsets()) {
|
for (const offset of script.getAllColumnOffsets()) {
|
||||||
assertEq(offset.lineNumber, 1);
|
assertEq(offset.lineNumber, 1);
|
||||||
assertEq(offset.columnNumber < execCode.length, true);
|
assertEq(offset.columnNumber < execCode.length, true);
|
||||||
|
|
|
@ -1,16 +1,193 @@
|
||||||
function warmup(fun, input, expected) {
|
setJitCompilerOption('ion.forceinlineCaches', 1);
|
||||||
assertEq(input.length, expected.length);
|
|
||||||
|
function warmup(fun, input_array) {
|
||||||
|
for (var index = 0; index < input_array.length; index++) {
|
||||||
|
input = input_array[index];
|
||||||
|
input_lhs = input[0];
|
||||||
|
input_rhs = input[1];
|
||||||
|
output = input[2];
|
||||||
for (var i = 0; i < 30; i++) {
|
for (var i = 0; i < 30; i++) {
|
||||||
for (var j = 0; j < input.length; j++) {
|
var str = "";
|
||||||
lhs = input[j][0];
|
var y = fun(input_lhs, input_rhs);
|
||||||
rhs = input[j][1];
|
if (y != output) {
|
||||||
assertEq(fun(lhs,rhs), expected[j]);
|
str = "Computed: "+y+ ", expected: "+ output + " ( " + fun + " lhs: "+ input_lhs + " rhs: " + input_rhs +")";
|
||||||
|
}
|
||||||
|
assertEq(str, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var strictCompare = function(a,b) { return a === b; }
|
|
||||||
warmup(strictCompare, [[1,1], [3,3], [3,strictCompare],[strictCompare, {}], [3.2, 1],
|
// Int32 + Int32
|
||||||
[0, -0]],
|
var Int32Int32Fun_GT1 = (a, b) => { return a > b; }
|
||||||
[true, true, false, false, false,
|
warmup(Int32Int32Fun_GT1, [[1,2, false], [1,1, false], [3,4, false], [4294967295, 2, true],
|
||||||
true]);
|
[NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_GTE1 = (a, b) => { return a >= b; }
|
||||||
|
warmup(Int32Int32Fun_GTE1, [[1,2, false], [1,1, true], [3,4, false], [4294967295, 2, true],
|
||||||
|
[NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_LT1 = (a, b) => { return a < b; }
|
||||||
|
warmup(Int32Int32Fun_LT1, [[1,2, true], [1,1, false], [3,4, true], [4294967295, 2, false],
|
||||||
|
[NaN, 2, false],[-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_LTE1 = (a, b) => { return a <= b; }
|
||||||
|
warmup(Int32Int32Fun_LTE1, [[1,2, true], [1,1, true], [3,4, true], [4294967295, 2, false],
|
||||||
|
[NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_EQ1 = (a, b) => { return a == b; }
|
||||||
|
warmup(Int32Int32Fun_EQ1, [[1,2, false], [1,1, true], [3,4, false], [4294967295, 2, false],
|
||||||
|
[NaN, 2, false], [-1000, NaN, false], [undefined, null, true],
|
||||||
|
['0', 0, true], [new String('0'), 0, true], [10, undefined, false]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_EQ2 = (a, b) => { return a == b; }
|
||||||
|
warmup(Int32Int32Fun_EQ2, [[1, NaN, false], [1, undefined, false], [1, null, false]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_EQ3 = (a, b) => { return a == b; }
|
||||||
|
warmup(Int32Int32Fun_EQ3, [[{a: 1}, NaN, false], [{a: 1}, undefined, false], [{a: 1}, null, false]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_EQ4 = (a, b) => { return a == b; }
|
||||||
|
warmup(Int32Int32Fun_EQ4, [[undefined, undefined, true], [null, null, true], [null, undefined, true], [undefined, null, true]]);
|
||||||
|
|
||||||
|
|
||||||
|
var Int32Int32Fun_NEQ1 = (a, b) => { return a != b; }
|
||||||
|
warmup(Int32Int32Fun_NEQ1, [[1,2, true], [1,1, false], [3,4, true], [4294967295, 2, true],
|
||||||
|
[NaN, 2, true], [-1000, NaN, true], [undefined, null, false],
|
||||||
|
['0', 0, false], [new String('0'), 0, false], [10, undefined, true]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_NEQ2 = (a, b) => { return a != b; }
|
||||||
|
warmup(Int32Int32Fun_NEQ2, [[1, NaN, true], [1, undefined, true], [1, null, true]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_NEQ3 = (a, b) => { return a != b; }
|
||||||
|
warmup(Int32Int32Fun_NEQ3, [[{a: 1}, NaN, true], [{a: 1}, undefined, true], [{a: 1}, null, true]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_NEQ4 = (a, b) => { return a != b; }
|
||||||
|
warmup(Int32Int32Fun_NEQ4, [[undefined, undefined, false], [null, null, false], [null, undefined, false], [undefined, null, false]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_SEQ1 = (a, b) => { return a === b; }
|
||||||
|
warmup(Int32Int32Fun_SEQ1, [[1,2, false], [1,1, true], [3,4, false], [4294967295, 2, false],
|
||||||
|
[NaN, 2, false], [-1000, NaN, false], [undefined, null, false],
|
||||||
|
['0', 0, false], [new String('0'), 0, false], [10, undefined, false]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_SEQ2 = (a, b) => { return a === b; }
|
||||||
|
warmup(Int32Int32Fun_SEQ2, [[1, NaN, false], [1, undefined, false], [1, null, false]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_SEQ3 = (a, b) => { return a === b; }
|
||||||
|
warmup(Int32Int32Fun_SEQ3, [[{a: 1}, NaN, false], [{a: 1}, undefined, false], [{a: 1}, null, false]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_SEQ4 = (a, b) => { return a === b; }
|
||||||
|
warmup(Int32Int32Fun_SEQ4, [[undefined, undefined, true], [null, null, true], [null, undefined, false], [undefined, null, false] ]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_SEQ5 = (a, b) => { return a === b; }
|
||||||
|
warmup(Int32Int32Fun_SEQ5, [[1, true, false], [1, false, false], [false, 1, false], [true, 0, false], [true, true, true]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_SNEQ1 = (a, b) => { return a !== b; }
|
||||||
|
warmup(Int32Int32Fun_SNEQ1, [[1,2, true], [1,1, false], [3,4, true], [4294967295, 2, true],
|
||||||
|
[NaN, 2, true], [-1000, NaN, true], [undefined, null, true],
|
||||||
|
['0', 0, true], [new String('0'), 0, true], [10, undefined, true]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_SNEQ2 = (a, b) => { return a !== b; }
|
||||||
|
warmup(Int32Int32Fun_SNEQ2, [[1, NaN, true], [1, undefined, true], [1, null, true]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_SNEQ3 = (a, b) => { return a !== b; }
|
||||||
|
warmup(Int32Int32Fun_SNEQ3, [[{a: 1}, NaN, true], [{a: 1}, undefined, true], [{a: 1}, null, true]]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_SNEQ4 = (a, b) => { return a !== b; }
|
||||||
|
warmup(Int32Int32Fun_SNEQ4, [[undefined, undefined, false], [null, null, false], [null, undefined, true], [undefined, null, true] ]);
|
||||||
|
|
||||||
|
var Int32Int32Fun_SNEQ5 = (a, b) => { return a !== b; }
|
||||||
|
warmup(Int32Int32Fun_SNEQ5, [[1, true, true], [1, false, true], [false, 1, true], [true, 0, true]]);
|
||||||
|
|
||||||
|
|
||||||
|
// Number Number
|
||||||
|
var NumberNumberFun_GT1 = (a, b) => { return a > b; }
|
||||||
|
warmup(NumberNumberFun_GT1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false],
|
||||||
|
[1,1, false], [3,4, false], [4294967295, 2, true],
|
||||||
|
[NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10.2, undefined, false]]);
|
||||||
|
|
||||||
|
var NumberNumberFun_GTE1 = (a, b) => { return a >= b; }
|
||||||
|
warmup(NumberNumberFun_GTE1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false],
|
||||||
|
[1,1, true], [3,4, false], [4294967295, 2, true],
|
||||||
|
[NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10.2, undefined, false]]);
|
||||||
|
|
||||||
|
var NumberNumberFun_LT1 = (a, b) => { return a < b; }
|
||||||
|
warmup(NumberNumberFun_LT1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true],
|
||||||
|
[1,1, false], [3,4, true], [4294967295, 2, false],
|
||||||
|
[NaN, 2, false],[-1000, NaN, false], [NaN, NaN, false, [10.2, undefined, false]]]);
|
||||||
|
|
||||||
|
var NumberNumberFun_LTE1 = (a, b) => { return a <= b; }
|
||||||
|
warmup(NumberNumberFun_LTE1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true],
|
||||||
|
[1,1, true], [3,4, true], [4294967295, 2, false],
|
||||||
|
[NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10.2, undefined, false]]);
|
||||||
|
|
||||||
|
var NumberNumberFun_EQ1 = (a, b) => { return a == b; }
|
||||||
|
warmup(NumberNumberFun_EQ1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false],
|
||||||
|
[1,1, true], [3,4, false], [4294967295, 2, false],
|
||||||
|
[NaN, 2, false], [-1000, NaN, false], [undefined, null, true],
|
||||||
|
['0', 0, true], [new String('0'), 0, true], [10.2, undefined, false]]);
|
||||||
|
|
||||||
|
var NumberNumberFun_NEQ1 = (a, b) => { return a != b; }
|
||||||
|
warmup(NumberNumberFun_NEQ1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true],
|
||||||
|
[1,1, false], [3,4, true], [4294967295, 2, true],
|
||||||
|
[NaN, 2, true], [-1000, NaN, true], [undefined, null, false],
|
||||||
|
['0', 0, false], [new String('0'), 0, false], [10.2, undefined, true]]);
|
||||||
|
|
||||||
|
var NumberNumberFun_SEQ1 = (a, b) => { return a === b; }
|
||||||
|
warmup(NumberNumberFun_SEQ1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false],
|
||||||
|
[1,1, true], [3,4, false], [4294967295, 2, false],
|
||||||
|
[NaN, 2, false], [-1000, NaN, false], [undefined, null, false],
|
||||||
|
['0', 0, false], [new String('0'), 0, false], [10.2, undefined, false]]);
|
||||||
|
|
||||||
|
var NumberNumberFun_SNEQ1 = (a, b) => { return a !== b; }
|
||||||
|
warmup(NumberNumberFun_SNEQ1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true],
|
||||||
|
[1,1, false], [3,4, true], [4294967295, 2, true],
|
||||||
|
[NaN, 2, true], [-1000, NaN, true], [undefined, null, true],
|
||||||
|
['0', 0, true], [new String('0'), 0, true], [10.2, undefined, true]]);
|
||||||
|
|
||||||
|
// Boolean + Int32
|
||||||
|
var BooleanFun_GT1 = (a, b) => { return a > b; }
|
||||||
|
warmup(BooleanFun_GT1, [[1,2, false], [true, 2, false], [1,1, false], [true,true, false], [3,4, false], ]);
|
||||||
|
|
||||||
|
var BooleanFun_GTE1 = (a, b) => { return a >= b; }
|
||||||
|
warmup(BooleanFun_GTE1, [[1,2, false], [true, 2, false], [1,1, true], [true,true, true], [3,4, false]]);
|
||||||
|
|
||||||
|
var BooleanFun_LT1 = (a, b) => { return a < b; }
|
||||||
|
warmup(BooleanFun_LT1, [[1,2, true], [true, 2, true], [1,1, false], [true,true, false], [3,4, true]]);
|
||||||
|
|
||||||
|
var BooleanFun_LTE1 = (a, b) => { return a <= b; }
|
||||||
|
warmup(BooleanFun_LTE1, [[1,2, true], [true, 2, true], [1,1, true], [true,true, true], [3,4, true]]);
|
||||||
|
|
||||||
|
var BooleanFun_EQ1 = (a, b) => { return a == b; }
|
||||||
|
warmup(BooleanFun_EQ1, [[1,2, false], [true, 2, false], [1,1, true], [true,true, true], [3,4, false],
|
||||||
|
['0', 0, true], [new String('0'), 0, true], [10, undefined, false]]);
|
||||||
|
|
||||||
|
var BooleanFun_NEQ1 = (a, b) => { return a != b; }
|
||||||
|
warmup(BooleanFun_NEQ1, [[1,2, true], [true, 2, true], [1,1, false], [true,true, false], [3,4, true],
|
||||||
|
['0', 0, false], [new String('0'), 0, false], [10, undefined, true]]);
|
||||||
|
|
||||||
|
var BooleanFun_SEQ1 = (a, b) => { return a === b; }
|
||||||
|
warmup(BooleanFun_SEQ1, [[1,2, false], [true, 2, false], [1,1, true], [true,true, true], [3,4, false]]);
|
||||||
|
|
||||||
|
var BooleanFun_SNEQ1 = (a, b) => { return a !== b; }
|
||||||
|
warmup(BooleanFun_SNEQ1, [[1,2, true], [true, 2, true], [1,1, false], [true,true, false], [3,4, true]]);
|
||||||
|
|
||||||
|
// Undefined / Null equality.
|
||||||
|
var UndefNull_EQ1 = (a, b) => { return a == b; }
|
||||||
|
warmup(UndefNull_EQ1, [[undefined, null, true], [undefined, undefined, true], [null, undefined, true],
|
||||||
|
[null, null, true], [{a: 10}, undefined, false], [{a: 10}, null, false]]);
|
||||||
|
|
||||||
|
var UndefNull_NEQ1 = (a, b) => { return a != b; }
|
||||||
|
warmup(UndefNull_NEQ1, [[undefined, null, false], [undefined, undefined, false], [null, undefined, false],
|
||||||
|
[null, null, false], [{a: 10}, undefined, true], [{a: 10}, null, true]]);
|
||||||
|
|
||||||
|
var UndefNull_SEQ1 = (a, b) => { return a === b; }
|
||||||
|
warmup(UndefNull_SEQ1, [[undefined, null, false], [undefined, undefined, true], [null, undefined, false],
|
||||||
|
[null, null, true], [{a: 10}, undefined, false], [{a: 10}, null, false]]);
|
||||||
|
|
||||||
|
var UndefNull_SNEQ1 = (a, b) => { return a !== b; }
|
||||||
|
warmup(UndefNull_SNEQ1, [[undefined, null, true], [undefined, undefined, false], [null, undefined, true],
|
||||||
|
[null, null, false], [{a: 10}, undefined, true], [{a: 10}, null, true]]);
|
||||||
|
|
||||||
|
var typeDifference = function(a,b) { return a === b; }
|
||||||
|
warmup(typeDifference, [[1,1, true], [3,3, true], [3, typeDifference, false],[typeDifference, {}, false],
|
||||||
|
[3.2, 1, false], [0, -0, true]]);
|
|
@ -83,3 +83,58 @@ assertOffsetColumns(
|
||||||
"function f(n) { do { print(n); } while(false); }",
|
"function f(n) { do { print(n); } while(false); }",
|
||||||
" ^ ^ ^",
|
" ^ ^ ^",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// getColumnOffsets correctly places the part of normal ::Dot node with identifier root.
|
||||||
|
assertOffsetColumns(
|
||||||
|
"var args = [];\n" +
|
||||||
|
"var obj = { base: { a(){ return { b(){} }; } } };\n" +
|
||||||
|
"function f(n) { obj.base.a().b(...args); }",
|
||||||
|
" ^ ^ ^ ^",
|
||||||
|
"0 2 1 3",
|
||||||
|
);
|
||||||
|
|
||||||
|
// getColumnOffsets correctly places the part of normal ::Dot node with "this" root.
|
||||||
|
assertOffsetColumns(
|
||||||
|
"var args = [];\n" +
|
||||||
|
"var obj = { base: { a(){ return { b(){} }; } } };\n" +
|
||||||
|
"var f = function() { this.base.a().b(...args); }.bind(obj);",
|
||||||
|
" ^ ^ ^ ^",
|
||||||
|
"0 2 1 3",
|
||||||
|
);
|
||||||
|
|
||||||
|
// getColumnOffsets correctly places the part of normal ::Dot node with "super" base.
|
||||||
|
assertOffsetColumns(
|
||||||
|
"var args = [];\n" +
|
||||||
|
"var obj = { base: { a(){ return { b(){} }; } } };\n" +
|
||||||
|
"var f = { __proto__: obj, f(n) { super.base.a().b(...args); } }.f;",
|
||||||
|
" ^ ^ ^ ^",
|
||||||
|
"0 2 1 3",
|
||||||
|
);
|
||||||
|
|
||||||
|
// getColumnOffsets correctly places the part of normal ::Dot node with other base.
|
||||||
|
assertOffsetColumns(
|
||||||
|
"var args = [];\n" +
|
||||||
|
"var obj = { base: { a(){ return { b(){} }; } } };\n" +
|
||||||
|
"function f(n) { (0, obj).base.a().b(...args); }",
|
||||||
|
" ^ ^ ^ ^ ^ ^",
|
||||||
|
"0 1 2 4 3 5",
|
||||||
|
);
|
||||||
|
|
||||||
|
// getColumnOffsets correctly places the part of folded ::Elem node.
|
||||||
|
assertOffsetColumns(
|
||||||
|
"var args = [];\n" +
|
||||||
|
"var obj = { base: { a(){ return { b(){} }; } } };\n" +
|
||||||
|
// Constant folding makes the static string behave like a dot access.
|
||||||
|
"function f(n) { obj.base['a']()['b'](...args); }",
|
||||||
|
" ^ ^ ^ ^",
|
||||||
|
"0 2 1 3",
|
||||||
|
);
|
||||||
|
|
||||||
|
// getColumnOffsets correctly places the part of computed ::Elem node.
|
||||||
|
assertOffsetColumns(
|
||||||
|
"var args = [], a = 'a', b = 'b';\n" +
|
||||||
|
"var obj = { base: { a(){ return { b(){} }; } } };\n" +
|
||||||
|
"function f(n) { obj.base[a]()[b](...args); }",
|
||||||
|
" ^ ^ ^^ ^",
|
||||||
|
"0 1 3 2 4",
|
||||||
|
);
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
v = new new TypedObject.StructType({
|
||||||
|
f: TypedObject.Any
|
||||||
|
})
|
||||||
|
gczeal(14);
|
||||||
|
var lfOffThreadGlobal = newGlobal();
|
|
@ -5014,6 +5014,127 @@ ICBinaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
||||||
return tailCallVM(DoBinaryArithFallbackInfo, masm);
|
return tailCallVM(DoBinaryArithFallbackInfo, masm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Compare_Fallback
|
||||||
|
//
|
||||||
|
static bool
|
||||||
|
DoCompareFallback(JSContext* cx, BaselineFrame* frame, ICCompare_Fallback* stub_, HandleValue lhs,
|
||||||
|
HandleValue rhs, MutableHandleValue ret)
|
||||||
|
{
|
||||||
|
// This fallback stub may trigger debug mode toggling.
|
||||||
|
DebugModeOSRVolatileStub<ICCompare_Fallback*> stub(ICStubEngine::Baseline, frame, stub_);
|
||||||
|
|
||||||
|
RootedScript script(cx, frame->script());
|
||||||
|
jsbytecode* pc = stub->icEntry()->pc(script);
|
||||||
|
JSOp op = JSOp(*pc);
|
||||||
|
|
||||||
|
FallbackICSpew(cx, stub, "Compare(%s)", CodeName[op]);
|
||||||
|
|
||||||
|
// Case operations in a CONDSWITCH are performing strict equality.
|
||||||
|
if (op == JSOP_CASE)
|
||||||
|
op = JSOP_STRICTEQ;
|
||||||
|
|
||||||
|
// Don't pass lhs/rhs directly, we need the original values when
|
||||||
|
// generating stubs.
|
||||||
|
RootedValue lhsCopy(cx, lhs);
|
||||||
|
RootedValue rhsCopy(cx, rhs);
|
||||||
|
|
||||||
|
// Perform the compare operation.
|
||||||
|
bool out;
|
||||||
|
switch (op) {
|
||||||
|
case JSOP_LT:
|
||||||
|
if (!LessThan(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case JSOP_LE:
|
||||||
|
if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case JSOP_GT:
|
||||||
|
if (!GreaterThan(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case JSOP_GE:
|
||||||
|
if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case JSOP_EQ:
|
||||||
|
if (!LooselyEqual<true>(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case JSOP_NE:
|
||||||
|
if (!LooselyEqual<false>(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case JSOP_STRICTEQ:
|
||||||
|
if (!StrictlyEqual<true>(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case JSOP_STRICTNE:
|
||||||
|
if (!StrictlyEqual<false>(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_ASSERT_UNREACHABLE("Unhandled baseline compare op");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.setBoolean(out);
|
||||||
|
|
||||||
|
// Check if debug mode toggling made the stub invalid.
|
||||||
|
if (stub.invalid())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Check to see if a new stub should be generated.
|
||||||
|
if (stub->numOptimizedStubs() >= ICCompare_Fallback::MAX_OPTIMIZED_STUBS) {
|
||||||
|
// TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
|
||||||
|
// But for now we just bail.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stub->state().canAttachStub()) {
|
||||||
|
CompareIRGenerator gen(cx, script, pc, stub->state().mode(), op, lhs, rhs);
|
||||||
|
bool attached = false;
|
||||||
|
if (gen.tryAttachStub()) {
|
||||||
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Regular,
|
||||||
|
ICStubEngine::Baseline, script, stub, &attached);
|
||||||
|
if (newStub)
|
||||||
|
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stub->noteUnoptimizableAccess();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef bool (*DoCompareFallbackFn)(JSContext*, BaselineFrame*, ICCompare_Fallback*,
|
||||||
|
HandleValue, HandleValue, MutableHandleValue);
|
||||||
|
static const VMFunction DoCompareFallbackInfo =
|
||||||
|
FunctionInfo<DoCompareFallbackFn>(DoCompareFallback, "DoCompareFallback", TailCall,
|
||||||
|
PopValues(2));
|
||||||
|
|
||||||
|
bool
|
||||||
|
ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(R0 == JSReturnOperand);
|
||||||
|
|
||||||
|
// Restore the tail call register.
|
||||||
|
EmitRestoreTailCallReg(masm);
|
||||||
|
|
||||||
|
// Ensure stack is fully synced for the expression decompiler.
|
||||||
|
masm.pushValue(R0);
|
||||||
|
masm.pushValue(R1);
|
||||||
|
|
||||||
|
// Push arguments.
|
||||||
|
masm.pushValue(R1);
|
||||||
|
masm.pushValue(R0);
|
||||||
|
masm.push(ICStubReg);
|
||||||
|
pushStubPayload(masm, R0.scratchReg());
|
||||||
|
return tailCallVM(DoCompareFallbackInfo, masm);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// NewArray_Fallback
|
// NewArray_Fallback
|
||||||
//
|
//
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "jit/BaselineInspector.h"
|
#include "jit/BaselineInspector.h"
|
||||||
|
|
||||||
|
#include "mozilla/Array.h"
|
||||||
#include "mozilla/DebugOnly.h"
|
#include "mozilla/DebugOnly.h"
|
||||||
|
|
||||||
#include "jit/BaselineIC.h"
|
#include "jit/BaselineIC.h"
|
||||||
|
@ -412,20 +413,144 @@ BaselineInspector::expectedResultType(jsbytecode* pc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whether a baseline stub kind is suitable for a double comparison that
|
// Return the MIRtype corresponding to the guard the reader is pointing
|
||||||
// converts its operands to doubles.
|
// to, and ensure that afterwards the reader is pointing to the next op
|
||||||
|
// (consume operands).
|
||||||
|
//
|
||||||
|
// An expected parameter is provided to allow GuardType to check we read
|
||||||
|
// the guard from the expected operand in debug builds.
|
||||||
static bool
|
static bool
|
||||||
CanUseDoubleCompare(ICStub::Kind kind)
|
GuardType(CacheIRReader& reader, mozilla::Array<MIRType,2>& guardType)
|
||||||
{
|
{
|
||||||
return kind == ICStub::Compare_Double || kind == ICStub::Compare_NumberWithUndefined;
|
CacheOp op = reader.readOp();
|
||||||
|
uint8_t guardOperand = reader.readByte();
|
||||||
|
|
||||||
|
// We only have two entries for guard types.
|
||||||
|
if (guardOperand > 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Already assigned this guard a type, fail.
|
||||||
|
if (guardType[guardOperand] != MIRType::None)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
// 0 Skip cases
|
||||||
|
case CacheOp::GuardIsString:
|
||||||
|
guardType[guardOperand] = MIRType::String;
|
||||||
|
break;
|
||||||
|
case CacheOp::GuardIsSymbol:
|
||||||
|
guardType[guardOperand] = MIRType::Symbol;
|
||||||
|
break;
|
||||||
|
case CacheOp::GuardIsNumber:
|
||||||
|
guardType[guardOperand] = MIRType::Double;
|
||||||
|
break;
|
||||||
|
case CacheOp::GuardIsUndefined:
|
||||||
|
guardType[guardOperand] = MIRType::Undefined;
|
||||||
|
break;
|
||||||
|
// 1 skip
|
||||||
|
case CacheOp::GuardIsInt32:
|
||||||
|
guardType[guardOperand] = MIRType::Int32;
|
||||||
|
// Skip over result
|
||||||
|
reader.skip();
|
||||||
|
break;
|
||||||
|
case CacheOp::GuardIsBoolean:
|
||||||
|
guardType[guardOperand] = MIRType::Boolean;
|
||||||
|
// Skip over result
|
||||||
|
reader.skip();
|
||||||
|
break;
|
||||||
|
// Unknown op --
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whether a baseline stub kind is suitable for an int32 comparison that
|
// This code works for all Compare ICs where the pattern is
|
||||||
// converts its operands to int32.
|
//
|
||||||
static bool
|
// <Guard LHS/RHS>
|
||||||
CanUseInt32Compare(ICStub::Kind kind)
|
// <Guard RHS/LHS>
|
||||||
|
// <CompareResult>
|
||||||
|
//
|
||||||
|
// in other cases (like StrictlyDifferentTypes) it will just
|
||||||
|
// return CompareUnknown
|
||||||
|
static MCompare::CompareType
|
||||||
|
ParseCacheIRStubForCompareType(ICCacheIR_Regular* stub)
|
||||||
{
|
{
|
||||||
return kind == ICStub::Compare_Int32 || kind == ICStub::Compare_Int32WithBoolean;
|
CacheIRReader reader(stub->stubInfo());
|
||||||
|
|
||||||
|
// Two element array to allow parsing the guards
|
||||||
|
// in whichever order they appear.
|
||||||
|
mozilla::Array<MIRType, 2> guards = { MIRType::None, MIRType::None };
|
||||||
|
|
||||||
|
// Parse out two guards
|
||||||
|
if (!GuardType(reader, guards))
|
||||||
|
return MCompare::Compare_Unknown;
|
||||||
|
if (!GuardType(reader, guards))
|
||||||
|
return MCompare::Compare_Unknown;
|
||||||
|
|
||||||
|
// The lhs and rhs ids are asserted in
|
||||||
|
// CompareIRGenerator::tryAttachStub.
|
||||||
|
MIRType lhs_guard = guards[0];
|
||||||
|
MIRType rhs_guard = guards[1];
|
||||||
|
|
||||||
|
if (lhs_guard == rhs_guard)
|
||||||
|
{
|
||||||
|
if (lhs_guard == MIRType::Int32)
|
||||||
|
return MCompare::Compare_Int32;
|
||||||
|
if (lhs_guard == MIRType::Double)
|
||||||
|
return MCompare::Compare_Double;
|
||||||
|
return MCompare::Compare_Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((lhs_guard == MIRType::Int32 && rhs_guard == MIRType::Boolean) ||
|
||||||
|
(lhs_guard == MIRType::Boolean && rhs_guard == MIRType::Int32))
|
||||||
|
{
|
||||||
|
// RHS is converting
|
||||||
|
if (rhs_guard == MIRType::Boolean)
|
||||||
|
return MCompare::Compare_Int32MaybeCoerceRHS;
|
||||||
|
|
||||||
|
return MCompare::Compare_Int32MaybeCoerceLHS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((lhs_guard == MIRType::Double && rhs_guard == MIRType::Undefined) ||
|
||||||
|
(lhs_guard == MIRType::Undefined && rhs_guard == MIRType::Double))
|
||||||
|
{
|
||||||
|
// RHS is converting
|
||||||
|
if (rhs_guard == MIRType::Undefined)
|
||||||
|
return MCompare::Compare_DoubleMaybeCoerceRHS;
|
||||||
|
|
||||||
|
return MCompare::Compare_DoubleMaybeCoerceLHS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MCompare::Compare_Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
CoercingCompare(MCompare::CompareType type)
|
||||||
|
{
|
||||||
|
//Prefer the coercing types if they exist, otherwise just use first's type.
|
||||||
|
if (type == MCompare::Compare_DoubleMaybeCoerceLHS ||
|
||||||
|
type == MCompare::Compare_DoubleMaybeCoerceRHS ||
|
||||||
|
type == MCompare::Compare_Int32MaybeCoerceLHS ||
|
||||||
|
type == MCompare::Compare_Int32MaybeCoerceRHS)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MCompare::CompareType
|
||||||
|
CompatibleType(MCompare::CompareType first, MCompare::CompareType second)
|
||||||
|
{
|
||||||
|
// Caller should have dealt with this case.
|
||||||
|
MOZ_ASSERT(first != second);
|
||||||
|
|
||||||
|
//Prefer the coercing types if they exist, otherwise just use first's type.
|
||||||
|
if (CoercingCompare(first))
|
||||||
|
return first;
|
||||||
|
|
||||||
|
if (CoercingCompare(second))
|
||||||
|
return second;
|
||||||
|
|
||||||
|
return first;
|
||||||
}
|
}
|
||||||
|
|
||||||
MCompare::CompareType
|
MCompare::CompareType
|
||||||
|
@ -442,37 +567,19 @@ BaselineInspector::expectedCompareType(jsbytecode* pc)
|
||||||
return MCompare::Compare_Unknown;
|
return MCompare::Compare_Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CanUseInt32Compare(first->kind()) && (!second || CanUseInt32Compare(second->kind()))) {
|
MCompare::CompareType first_type = ParseCacheIRStubForCompareType(first->toCacheIR_Regular());
|
||||||
ICCompare_Int32WithBoolean* coerce =
|
if (!second)
|
||||||
first->isCompare_Int32WithBoolean()
|
return first_type;
|
||||||
? first->toCompare_Int32WithBoolean()
|
|
||||||
: ((second && second->isCompare_Int32WithBoolean())
|
|
||||||
? second->toCompare_Int32WithBoolean()
|
|
||||||
: nullptr);
|
|
||||||
if (coerce) {
|
|
||||||
return coerce->lhsIsInt32()
|
|
||||||
? MCompare::Compare_Int32MaybeCoerceRHS
|
|
||||||
: MCompare::Compare_Int32MaybeCoerceLHS;
|
|
||||||
}
|
|
||||||
return MCompare::Compare_Int32;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CanUseDoubleCompare(first->kind()) && (!second || CanUseDoubleCompare(second->kind()))) {
|
MCompare::CompareType second_type = ParseCacheIRStubForCompareType(second->toCacheIR_Regular());
|
||||||
ICCompare_NumberWithUndefined* coerce =
|
|
||||||
first->isCompare_NumberWithUndefined()
|
|
||||||
? first->toCompare_NumberWithUndefined()
|
|
||||||
: (second && second->isCompare_NumberWithUndefined())
|
|
||||||
? second->toCompare_NumberWithUndefined()
|
|
||||||
: nullptr;
|
|
||||||
if (coerce) {
|
|
||||||
return coerce->lhsIsUndefined()
|
|
||||||
? MCompare::Compare_DoubleMaybeCoerceLHS
|
|
||||||
: MCompare::Compare_DoubleMaybeCoerceRHS;
|
|
||||||
}
|
|
||||||
return MCompare::Compare_Double;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (first_type == MCompare::Compare_Unknown || second_type == MCompare::Compare_Unknown)
|
||||||
return MCompare::Compare_Unknown;
|
return MCompare::Compare_Unknown;
|
||||||
|
|
||||||
|
if (first_type == second_type)
|
||||||
|
return first_type;
|
||||||
|
|
||||||
|
return CompatibleType(first_type, second_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
|
@ -37,6 +37,33 @@ CacheIRWriter::assertSameCompartment(JSObject* obj) {
|
||||||
assertSameCompartmentDebugOnly(cx_, obj);
|
assertSameCompartmentDebugOnly(cx_, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StubField
|
||||||
|
CacheIRWriter::readStubFieldForIon(uint32_t offset, StubField::Type type) const
|
||||||
|
{
|
||||||
|
size_t index = 0;
|
||||||
|
size_t currentOffset = 0;
|
||||||
|
|
||||||
|
// If we've seen an offset earlier than this before, we know we can start the search
|
||||||
|
// there at least, otherwise, we start the search from the beginning.
|
||||||
|
if (lastOffset_ < offset) {
|
||||||
|
currentOffset = lastOffset_;
|
||||||
|
index = lastIndex_;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (currentOffset != offset) {
|
||||||
|
currentOffset += StubField::sizeInBytes(stubFields_[index].type());
|
||||||
|
index++;
|
||||||
|
MOZ_ASSERT(index < stubFields_.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(stubFields_[index].type() == type);
|
||||||
|
|
||||||
|
lastOffset_ = currentOffset;
|
||||||
|
lastIndex_ = index;
|
||||||
|
|
||||||
|
return stubFields_[index];
|
||||||
|
}
|
||||||
|
|
||||||
IRGenerator::IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
|
IRGenerator::IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
|
||||||
ICState::Mode mode)
|
ICState::Mode mode)
|
||||||
: writer(cx),
|
: writer(cx),
|
||||||
|
@ -4813,6 +4840,122 @@ CompareIRGenerator::tryAttachStrictDifferentTypes(ValOperandId lhsId, ValOperand
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CompareIRGenerator::tryAttachInt32(ValOperandId lhsId, ValOperandId rhsId)
|
||||||
|
{
|
||||||
|
if ((!lhsVal_.isInt32() && !lhsVal_.isBoolean()) ||
|
||||||
|
(!rhsVal_.isInt32() && !rhsVal_.isBoolean()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Int32OperandId lhsIntId = lhsVal_.isBoolean() ? writer.guardIsBoolean(lhsId)
|
||||||
|
: writer.guardIsInt32(lhsId);
|
||||||
|
Int32OperandId rhsIntId = rhsVal_.isBoolean() ? writer.guardIsBoolean(rhsId)
|
||||||
|
: writer.guardIsInt32(rhsId);
|
||||||
|
|
||||||
|
// Strictly different types should have been handed by
|
||||||
|
// tryAttachStrictDifferentTypes
|
||||||
|
MOZ_ASSERT_IF(op_ == JSOP_STRICTEQ || op_ == JSOP_STRICTNE,
|
||||||
|
lhsVal_.isInt32() == rhsVal_.isInt32());
|
||||||
|
|
||||||
|
writer.compareInt32Result(op_, lhsIntId, rhsIntId);
|
||||||
|
writer.returnFromIC();
|
||||||
|
|
||||||
|
trackAttached(lhsVal_.isBoolean() ? "Boolean" : "Int32");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CompareIRGenerator::tryAttachNumber(ValOperandId lhsId, ValOperandId rhsId)
|
||||||
|
{
|
||||||
|
if (!cx_->runtime()->jitSupportsFloatingPoint)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!lhsVal_.isNumber() || !rhsVal_.isNumber())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
writer.guardIsNumber(lhsId);
|
||||||
|
writer.guardIsNumber(rhsId);
|
||||||
|
writer.compareDoubleResult(op_, lhsId, rhsId);
|
||||||
|
writer.returnFromIC();
|
||||||
|
|
||||||
|
trackAttached("Number");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CompareIRGenerator::tryAttachObjectUndefined(ValOperandId lhsId, ValOperandId rhsId)
|
||||||
|
{
|
||||||
|
if (!(lhsVal_.isNullOrUndefined() && rhsVal_.isObject()) &&
|
||||||
|
!(rhsVal_.isNullOrUndefined() && lhsVal_.isObject()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (op_ != JSOP_EQ && op_ != JSOP_NE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ValOperandId obj = rhsVal_.isObject() ? rhsId : lhsId;
|
||||||
|
ValOperandId undefOrNull = rhsVal_.isObject() ? lhsId : rhsId;
|
||||||
|
|
||||||
|
writer.guardIsNullOrUndefined(undefOrNull);
|
||||||
|
ObjOperandId objOperand = writer.guardIsObject(obj);
|
||||||
|
writer.compareObjectUndefinedNullResult(op_, objOperand);
|
||||||
|
writer.returnFromIC();
|
||||||
|
|
||||||
|
trackAttached("ObjectUndefined");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle NumberUndefined comparisons
|
||||||
|
bool
|
||||||
|
CompareIRGenerator::tryAttachNumberUndefined(ValOperandId lhsId, ValOperandId rhsId)
|
||||||
|
{
|
||||||
|
if (!(lhsVal_.isUndefined() && rhsVal_.isNumber()) &&
|
||||||
|
!(rhsVal_.isUndefined() && lhsVal_.isNumber()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lhsVal_.isNumber() ? writer.guardIsNumber(lhsId) : writer.guardIsUndefined(lhsId);
|
||||||
|
rhsVal_.isNumber() ? writer.guardIsNumber(rhsId) : writer.guardIsUndefined(rhsId);
|
||||||
|
|
||||||
|
// Comparing a number with undefined will always be true for NE/STRICTNE,
|
||||||
|
// and always be false for other compare ops.
|
||||||
|
writer.loadBooleanResult(op_ == JSOP_NE || op_ == JSOP_STRICTNE);
|
||||||
|
writer.returnFromIC();
|
||||||
|
|
||||||
|
trackAttached("NumberUndefined");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle {null/undefined} x {null,undefined} equality comparisons
|
||||||
|
bool
|
||||||
|
CompareIRGenerator::tryAttachNullUndefined(ValOperandId lhsId, ValOperandId rhsId)
|
||||||
|
{
|
||||||
|
if (!lhsVal_.isNullOrUndefined() || !rhsVal_.isNullOrUndefined())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (op_ == JSOP_EQ || op_ == JSOP_NE) {
|
||||||
|
writer.guardIsNullOrUndefined(lhsId);
|
||||||
|
writer.guardIsNullOrUndefined(rhsId);
|
||||||
|
// Sloppy equality means we actually only care about the op:
|
||||||
|
writer.loadBooleanResult(op_ == JSOP_EQ);
|
||||||
|
trackAttached("SloppyNullUndefined");
|
||||||
|
} else {
|
||||||
|
// Strict equality only hits this branch, and only in the
|
||||||
|
// undef {!,=}== undef and null {!,=}== null cases.
|
||||||
|
// The other cases should have hit compareStrictlyDifferentTypes.
|
||||||
|
MOZ_ASSERT(lhsVal_.isNull() == rhsVal_.isNull());
|
||||||
|
lhsVal_.isNull() ? writer.guardIsNull(lhsId) : writer.guardIsUndefined(lhsId);
|
||||||
|
rhsVal_.isNull() ? writer.guardIsNull(rhsId) : writer.guardIsUndefined(rhsId);
|
||||||
|
writer.loadBooleanResult(op_ == JSOP_STRICTEQ);
|
||||||
|
trackAttached("StrictNullUndefinedEquality");
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.returnFromIC();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CompareIRGenerator::tryAttachStub()
|
CompareIRGenerator::tryAttachStub()
|
||||||
{
|
{
|
||||||
|
@ -4823,6 +4966,12 @@ CompareIRGenerator::tryAttachStub()
|
||||||
|
|
||||||
AutoAssertNoPendingException aanpe(cx_);
|
AutoAssertNoPendingException aanpe(cx_);
|
||||||
|
|
||||||
|
constexpr uint8_t lhsIndex = 0;
|
||||||
|
constexpr uint8_t rhsIndex = 1;
|
||||||
|
|
||||||
|
static_assert(lhsIndex == 0 && rhsIndex == 1,
|
||||||
|
"Indexes relied upon by baseline inspector");
|
||||||
|
|
||||||
ValOperandId lhsId(writer.setInputOperandId(0));
|
ValOperandId lhsId(writer.setInputOperandId(0));
|
||||||
ValOperandId rhsId(writer.setInputOperandId(1));
|
ValOperandId rhsId(writer.setInputOperandId(1));
|
||||||
|
|
||||||
|
@ -4833,13 +4982,30 @@ CompareIRGenerator::tryAttachStub()
|
||||||
return true;
|
return true;
|
||||||
if (tryAttachSymbol(lhsId, rhsId))
|
if (tryAttachSymbol(lhsId, rhsId))
|
||||||
return true;
|
return true;
|
||||||
|
if (tryAttachObjectUndefined(lhsId, rhsId))
|
||||||
|
return true;
|
||||||
if (tryAttachStrictDifferentTypes(lhsId, rhsId))
|
if (tryAttachStrictDifferentTypes(lhsId, rhsId))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
trackAttached(IRGenerator::NotAttached);
|
// This should come after strictDifferent types to
|
||||||
return false;
|
// allow it to only handle sloppy equality.
|
||||||
|
if (tryAttachNullUndefined(lhsId, rhsId))
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This should preceed the Int32/Number cases to allow
|
||||||
|
// them to not concern themselves with handling undefined
|
||||||
|
// or null.
|
||||||
|
if (tryAttachNumberUndefined(lhsId, rhsId))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// We want these to be last, to allow us to bypass the
|
||||||
|
// strictly-different-types cases in the below attachment code
|
||||||
|
if (tryAttachInt32(lhsId, rhsId))
|
||||||
|
return true;
|
||||||
|
if (tryAttachNumber(lhsId, rhsId))
|
||||||
|
return true;
|
||||||
|
|
||||||
trackAttached(IRGenerator::NotAttached);
|
trackAttached(IRGenerator::NotAttached);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -4851,6 +5017,7 @@ CompareIRGenerator::trackAttached(const char* name)
|
||||||
if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
|
if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
|
||||||
sp.valueProperty("lhs", lhsVal_);
|
sp.valueProperty("lhs", lhsVal_);
|
||||||
sp.valueProperty("rhs", rhsVal_);
|
sp.valueProperty("rhs", rhsVal_);
|
||||||
|
sp.opcodeProperty("op", op_);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,6 +182,9 @@ extern const char* CacheKindNames[];
|
||||||
_(GuardIsObject) \
|
_(GuardIsObject) \
|
||||||
_(GuardIsObjectOrNull) \
|
_(GuardIsObjectOrNull) \
|
||||||
_(GuardIsNullOrUndefined) \
|
_(GuardIsNullOrUndefined) \
|
||||||
|
_(GuardIsNotNullOrUndefined) \
|
||||||
|
_(GuardIsNull) \
|
||||||
|
_(GuardIsUndefined) \
|
||||||
_(GuardIsBoolean) \
|
_(GuardIsBoolean) \
|
||||||
_(GuardIsString) \
|
_(GuardIsString) \
|
||||||
_(GuardIsSymbol) \
|
_(GuardIsSymbol) \
|
||||||
|
@ -324,6 +327,9 @@ extern const char* CacheKindNames[];
|
||||||
_(CompareStringResult) \
|
_(CompareStringResult) \
|
||||||
_(CompareObjectResult) \
|
_(CompareObjectResult) \
|
||||||
_(CompareSymbolResult) \
|
_(CompareSymbolResult) \
|
||||||
|
_(CompareInt32Result) \
|
||||||
|
_(CompareDoubleResult) \
|
||||||
|
_(CompareObjectUndefinedNullResult) \
|
||||||
\
|
\
|
||||||
_(CallPrintString) \
|
_(CallPrintString) \
|
||||||
_(Breakpoint) \
|
_(Breakpoint) \
|
||||||
|
@ -438,6 +444,10 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
||||||
static const size_t MaxStubDataSizeInBytes = 20 * sizeof(uintptr_t);
|
static const size_t MaxStubDataSizeInBytes = 20 * sizeof(uintptr_t);
|
||||||
bool tooLarge_;
|
bool tooLarge_;
|
||||||
|
|
||||||
|
// Basic caching to avoid quadatic lookup behaviour in readStubFieldForIon.
|
||||||
|
mutable uint32_t lastOffset_;
|
||||||
|
mutable uint32_t lastIndex_;
|
||||||
|
|
||||||
void assertSameCompartment(JSObject*);
|
void assertSameCompartment(JSObject*);
|
||||||
|
|
||||||
void writeOp(CacheOp op) {
|
void writeOp(CacheOp op) {
|
||||||
|
@ -501,7 +511,9 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
||||||
nextInstructionId_(0),
|
nextInstructionId_(0),
|
||||||
numInputOperands_(0),
|
numInputOperands_(0),
|
||||||
stubDataSize_(0),
|
stubDataSize_(0),
|
||||||
tooLarge_(false)
|
tooLarge_(false),
|
||||||
|
lastOffset_(0),
|
||||||
|
lastIndex_(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool failed() const { return buffer_.oom() || tooLarge_; }
|
bool failed() const { return buffer_.oom() || tooLarge_; }
|
||||||
|
@ -551,10 +563,7 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
||||||
|
|
||||||
// This should not be used when compiling Baseline code, as Baseline code
|
// This should not be used when compiling Baseline code, as Baseline code
|
||||||
// shouldn't bake in stub values.
|
// shouldn't bake in stub values.
|
||||||
StubField readStubFieldForIon(size_t i, StubField::Type type) const {
|
StubField readStubFieldForIon(uint32_t offset, StubField::Type type) const;
|
||||||
MOZ_ASSERT(stubFields_[i].type() == type);
|
|
||||||
return stubFields_[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjOperandId guardIsObject(ValOperandId val) {
|
ObjOperandId guardIsObject(ValOperandId val) {
|
||||||
writeOpWithOperandId(CacheOp::GuardIsObject, val);
|
writeOpWithOperandId(CacheOp::GuardIsObject, val);
|
||||||
|
@ -600,6 +609,15 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
||||||
void guardIsNullOrUndefined(ValOperandId val) {
|
void guardIsNullOrUndefined(ValOperandId val) {
|
||||||
writeOpWithOperandId(CacheOp::GuardIsNullOrUndefined, val);
|
writeOpWithOperandId(CacheOp::GuardIsNullOrUndefined, val);
|
||||||
}
|
}
|
||||||
|
void guardIsNotNullOrUndefined(ValOperandId val) {
|
||||||
|
writeOpWithOperandId(CacheOp::GuardIsNotNullOrUndefined, val);
|
||||||
|
}
|
||||||
|
void guardIsNull(ValOperandId val) {
|
||||||
|
writeOpWithOperandId(CacheOp::GuardIsNull, val);
|
||||||
|
}
|
||||||
|
void guardIsUndefined(ValOperandId val) {
|
||||||
|
writeOpWithOperandId(CacheOp::GuardIsUndefined, val);
|
||||||
|
}
|
||||||
void guardShape(ObjOperandId obj, Shape* shape) {
|
void guardShape(ObjOperandId obj, Shape* shape) {
|
||||||
MOZ_ASSERT(shape);
|
MOZ_ASSERT(shape);
|
||||||
writeOpWithOperandId(CacheOp::GuardShape, obj);
|
writeOpWithOperandId(CacheOp::GuardShape, obj);
|
||||||
|
@ -1257,11 +1275,25 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
||||||
writeOperandId(rhs);
|
writeOperandId(rhs);
|
||||||
buffer_.writeByte(uint32_t(op));
|
buffer_.writeByte(uint32_t(op));
|
||||||
}
|
}
|
||||||
|
void compareObjectUndefinedNullResult(uint32_t op, ObjOperandId object) {
|
||||||
|
writeOpWithOperandId(CacheOp::CompareObjectUndefinedNullResult, object);
|
||||||
|
buffer_.writeByte(uint32_t(op));
|
||||||
|
}
|
||||||
void compareSymbolResult(uint32_t op, SymbolOperandId lhs, SymbolOperandId rhs) {
|
void compareSymbolResult(uint32_t op, SymbolOperandId lhs, SymbolOperandId rhs) {
|
||||||
writeOpWithOperandId(CacheOp::CompareSymbolResult, lhs);
|
writeOpWithOperandId(CacheOp::CompareSymbolResult, lhs);
|
||||||
writeOperandId(rhs);
|
writeOperandId(rhs);
|
||||||
buffer_.writeByte(uint32_t(op));
|
buffer_.writeByte(uint32_t(op));
|
||||||
}
|
}
|
||||||
|
void compareInt32Result(uint32_t op, Int32OperandId lhs, Int32OperandId rhs) {
|
||||||
|
writeOpWithOperandId(CacheOp::CompareInt32Result, lhs);
|
||||||
|
writeOperandId(rhs);
|
||||||
|
buffer_.writeByte(uint32_t(op));
|
||||||
|
}
|
||||||
|
void compareDoubleResult(uint32_t op, ValOperandId lhs, ValOperandId rhs) {
|
||||||
|
writeOpWithOperandId(CacheOp::CompareDoubleResult, lhs);
|
||||||
|
writeOperandId(rhs);
|
||||||
|
buffer_.writeByte(uint32_t(op));
|
||||||
|
}
|
||||||
|
|
||||||
void callPrintString(const char* str) {
|
void callPrintString(const char* str) {
|
||||||
writeOp(CacheOp::CallPrintString);
|
writeOp(CacheOp::CallPrintString);
|
||||||
|
@ -1816,6 +1848,11 @@ class MOZ_RAII CompareIRGenerator : public IRGenerator
|
||||||
bool tryAttachObject(ValOperandId lhsId, ValOperandId rhsId);
|
bool tryAttachObject(ValOperandId lhsId, ValOperandId rhsId);
|
||||||
bool tryAttachSymbol(ValOperandId lhsId, ValOperandId rhsId);
|
bool tryAttachSymbol(ValOperandId lhsId, ValOperandId rhsId);
|
||||||
bool tryAttachStrictDifferentTypes(ValOperandId lhsId, ValOperandId rhsId);
|
bool tryAttachStrictDifferentTypes(ValOperandId lhsId, ValOperandId rhsId);
|
||||||
|
bool tryAttachInt32(ValOperandId lhsId, ValOperandId rhsId);
|
||||||
|
bool tryAttachNumber(ValOperandId lhsId, ValOperandId rhsId);
|
||||||
|
bool tryAttachNumberUndefined(ValOperandId lhsId, ValOperandId rhsId);
|
||||||
|
bool tryAttachObjectUndefined(ValOperandId lhsId, ValOperandId rhsId);
|
||||||
|
bool tryAttachNullUndefined(ValOperandId lhsId, ValOperandId rhsId);
|
||||||
|
|
||||||
void trackAttached(const char* name);
|
void trackAttached(const char* name);
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ CacheRegisterAllocator::useValueRegister(MacroAssembler& masm, ValOperandId op)
|
||||||
// Load a value operand directly into a float register. Caller must have
|
// Load a value operand directly into a float register. Caller must have
|
||||||
// guarded isNumber on the provided val.
|
// guarded isNumber on the provided val.
|
||||||
void
|
void
|
||||||
CacheRegisterAllocator::loadDouble(MacroAssembler& masm, ValOperandId op, FloatRegister dest)
|
CacheRegisterAllocator::ensureDoubleRegister(MacroAssembler& masm, ValOperandId op, FloatRegister dest)
|
||||||
{
|
{
|
||||||
OperandLocation& loc = operandLocations_[op.id()];
|
OperandLocation& loc = operandLocations_[op.id()];
|
||||||
|
|
||||||
|
@ -125,12 +125,12 @@ CacheRegisterAllocator::loadDouble(MacroAssembler& masm, ValOperandId op, FloatR
|
||||||
case OperandLocation::PayloadStack:
|
case OperandLocation::PayloadStack:
|
||||||
case OperandLocation::PayloadReg:
|
case OperandLocation::PayloadReg:
|
||||||
case OperandLocation::Uninitialized:
|
case OperandLocation::Uninitialized:
|
||||||
MOZ_CRASH("Unhandled operand type in loadDouble");
|
MOZ_CRASH("Unhandled operand type in ensureDoubleRegister");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
masm.jump(&done);
|
masm.jump(&done);
|
||||||
masm.bind(&failure);
|
masm.bind(&failure);
|
||||||
masm.assumeUnreachable("Missing guard allowed non-number to hit loadDouble");
|
masm.assumeUnreachable("Missing guard allowed non-number to hit ensureDoubleRegister");
|
||||||
masm.bind(&done);
|
masm.bind(&done);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1377,6 +1377,63 @@ CacheIRCompiler::emitGuardIsNullOrUndefined()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
CacheIRCompiler::emitGuardIsNotNullOrUndefined()
|
||||||
|
{
|
||||||
|
ValOperandId inputId = reader.valOperandId();
|
||||||
|
JSValueType knownType = allocator.knownType(inputId);
|
||||||
|
if (knownType == JSVAL_TYPE_UNDEFINED || knownType == JSVAL_TYPE_NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ValueOperand input = allocator.useValueRegister(masm, inputId);
|
||||||
|
FailurePath* failure;
|
||||||
|
if (!addFailurePath(&failure))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
masm.branchTestNull(Assembler::Equal, input, failure->label());
|
||||||
|
masm.branchTestUndefined(Assembler::Equal, input, failure->label());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
CacheIRCompiler::emitGuardIsNull()
|
||||||
|
{
|
||||||
|
ValOperandId inputId = reader.valOperandId();
|
||||||
|
JSValueType knownType = allocator.knownType(inputId);
|
||||||
|
if (knownType == JSVAL_TYPE_NULL)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
ValueOperand input = allocator.useValueRegister(masm, inputId);
|
||||||
|
FailurePath* failure;
|
||||||
|
if (!addFailurePath(&failure))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Label success;
|
||||||
|
masm.branchTestNull(Assembler::NotEqual, input, failure->label());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CacheIRCompiler::emitGuardIsUndefined()
|
||||||
|
{
|
||||||
|
ValOperandId inputId = reader.valOperandId();
|
||||||
|
JSValueType knownType = allocator.knownType(inputId);
|
||||||
|
if (knownType == JSVAL_TYPE_UNDEFINED)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
ValueOperand input = allocator.useValueRegister(masm, inputId);
|
||||||
|
FailurePath* failure;
|
||||||
|
if (!addFailurePath(&failure))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
masm.branchTestUndefined(Assembler::NotEqual, input, failure->label());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CacheIRCompiler::emitGuardIsObjectOrNull()
|
CacheIRCompiler::emitGuardIsObjectOrNull()
|
||||||
{
|
{
|
||||||
|
@ -1994,8 +2051,8 @@ CacheIRCompiler::emitDoubleAddResult()
|
||||||
// Float register must be preserved. The BinaryArith ICs use
|
// Float register must be preserved. The BinaryArith ICs use
|
||||||
// the fact that baseline has them available, as well as fixed temps on
|
// the fact that baseline has them available, as well as fixed temps on
|
||||||
// LBinaryCache.
|
// LBinaryCache.
|
||||||
allocator.loadDouble(masm, reader.valOperandId(), FloatReg0);
|
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
|
||||||
allocator.loadDouble(masm, reader.valOperandId(), FloatReg1);
|
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
|
||||||
|
|
||||||
masm.addDouble(FloatReg1, FloatReg0);
|
masm.addDouble(FloatReg1, FloatReg0);
|
||||||
masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
|
masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
|
||||||
|
@ -2007,8 +2064,8 @@ CacheIRCompiler::emitDoubleSubResult()
|
||||||
{
|
{
|
||||||
AutoOutputRegister output(*this);
|
AutoOutputRegister output(*this);
|
||||||
|
|
||||||
allocator.loadDouble(masm, reader.valOperandId(), FloatReg0);
|
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
|
||||||
allocator.loadDouble(masm, reader.valOperandId(), FloatReg1);
|
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
|
||||||
|
|
||||||
masm.subDouble(FloatReg1, FloatReg0);
|
masm.subDouble(FloatReg1, FloatReg0);
|
||||||
masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
|
masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
|
||||||
|
@ -2020,8 +2077,8 @@ CacheIRCompiler::emitDoubleMulResult()
|
||||||
{
|
{
|
||||||
AutoOutputRegister output(*this);
|
AutoOutputRegister output(*this);
|
||||||
|
|
||||||
allocator.loadDouble(masm, reader.valOperandId(), FloatReg0);
|
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
|
||||||
allocator.loadDouble(masm, reader.valOperandId(), FloatReg1);
|
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
|
||||||
|
|
||||||
masm.mulDouble(FloatReg1, FloatReg0);
|
masm.mulDouble(FloatReg1, FloatReg0);
|
||||||
masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
|
masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
|
||||||
|
@ -2033,8 +2090,8 @@ CacheIRCompiler::emitDoubleDivResult()
|
||||||
{
|
{
|
||||||
AutoOutputRegister output(*this);
|
AutoOutputRegister output(*this);
|
||||||
|
|
||||||
allocator.loadDouble(masm, reader.valOperandId(), FloatReg0);
|
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
|
||||||
allocator.loadDouble(masm, reader.valOperandId(), FloatReg1);
|
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
|
||||||
|
|
||||||
masm.divDouble(FloatReg1, FloatReg0);
|
masm.divDouble(FloatReg1, FloatReg0);
|
||||||
masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
|
masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
|
||||||
|
@ -2047,8 +2104,8 @@ CacheIRCompiler::emitDoubleModResult()
|
||||||
AutoOutputRegister output(*this);
|
AutoOutputRegister output(*this);
|
||||||
AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
|
AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
|
||||||
|
|
||||||
allocator.loadDouble(masm, reader.valOperandId(), FloatReg0);
|
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
|
||||||
allocator.loadDouble(masm, reader.valOperandId(), FloatReg1);
|
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
|
||||||
|
|
||||||
LiveRegisterSet save(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
|
LiveRegisterSet save(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
|
||||||
masm.PushRegsInMask(save);
|
masm.PushRegsInMask(save);
|
||||||
|
@ -3092,6 +3149,79 @@ CacheIRCompiler::emitCompareSymbolResult()
|
||||||
return emitComparePointerResultShared(true);
|
return emitComparePointerResultShared(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CacheIRCompiler::emitCompareInt32Result()
|
||||||
|
{
|
||||||
|
AutoOutputRegister output(*this);
|
||||||
|
Register left = allocator.useRegister(masm, reader.int32OperandId());
|
||||||
|
Register right = allocator.useRegister(masm, reader.int32OperandId());
|
||||||
|
JSOp op = reader.jsop();
|
||||||
|
|
||||||
|
Label ifTrue, done;
|
||||||
|
masm.branch32(JSOpToCondition(op, /* signed = */true), left, right, &ifTrue);
|
||||||
|
|
||||||
|
masm.moveValue(BooleanValue(false), output.valueReg());
|
||||||
|
masm.jump(&done);
|
||||||
|
|
||||||
|
masm.bind(&ifTrue);
|
||||||
|
masm.moveValue(BooleanValue(true), output.valueReg());
|
||||||
|
masm.bind(&done);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CacheIRCompiler::emitCompareDoubleResult()
|
||||||
|
{
|
||||||
|
AutoOutputRegister output(*this);
|
||||||
|
|
||||||
|
FailurePath* failure;
|
||||||
|
if (!addFailurePath(&failure))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
|
||||||
|
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
|
||||||
|
JSOp op = reader.jsop();
|
||||||
|
|
||||||
|
Label done, ifTrue;
|
||||||
|
masm.branchDouble(JSOpToDoubleCondition(op), FloatReg0, FloatReg1, &ifTrue);
|
||||||
|
masm.moveValue(BooleanValue(false), output.valueReg());
|
||||||
|
masm.jump(&done);
|
||||||
|
|
||||||
|
masm.bind(&ifTrue);
|
||||||
|
masm.moveValue(BooleanValue(true), output.valueReg());
|
||||||
|
masm.bind(&done);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CacheIRCompiler::emitCompareObjectUndefinedNullResult()
|
||||||
|
{
|
||||||
|
AutoOutputRegister output(*this);
|
||||||
|
|
||||||
|
Register obj = allocator.useRegister(masm, reader.objOperandId());
|
||||||
|
JSOp op = reader.jsop();
|
||||||
|
|
||||||
|
FailurePath* failure;
|
||||||
|
if (!addFailurePath(&failure))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (op == JSOP_STRICTEQ || op == JSOP_STRICTNE) {
|
||||||
|
// obj !== undefined/null for all objects.
|
||||||
|
masm.moveValue(BooleanValue(op == JSOP_STRICTNE), output.valueReg());
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE);
|
||||||
|
AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
|
||||||
|
Label done, emulatesUndefined;
|
||||||
|
masm.branchIfObjectEmulatesUndefined(obj, scratch, failure->label(), &emulatesUndefined);
|
||||||
|
masm.moveValue(BooleanValue(op == JSOP_NE), output.valueReg());
|
||||||
|
masm.jump(&done);
|
||||||
|
masm.bind(&emulatesUndefined);
|
||||||
|
masm.moveValue(BooleanValue(op == JSOP_EQ), output.valueReg());
|
||||||
|
masm.bind(&done);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CacheIRCompiler::emitCallPrintString()
|
CacheIRCompiler::emitCallPrintString()
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,6 +19,9 @@ namespace jit {
|
||||||
#define CACHE_IR_SHARED_OPS(_) \
|
#define CACHE_IR_SHARED_OPS(_) \
|
||||||
_(GuardIsObject) \
|
_(GuardIsObject) \
|
||||||
_(GuardIsNullOrUndefined) \
|
_(GuardIsNullOrUndefined) \
|
||||||
|
_(GuardIsNotNullOrUndefined) \
|
||||||
|
_(GuardIsNull) \
|
||||||
|
_(GuardIsUndefined) \
|
||||||
_(GuardIsObjectOrNull) \
|
_(GuardIsObjectOrNull) \
|
||||||
_(GuardIsBoolean) \
|
_(GuardIsBoolean) \
|
||||||
_(GuardIsString) \
|
_(GuardIsString) \
|
||||||
|
@ -93,6 +96,9 @@ namespace jit {
|
||||||
_(LoadObjectTruthyResult) \
|
_(LoadObjectTruthyResult) \
|
||||||
_(CompareObjectResult) \
|
_(CompareObjectResult) \
|
||||||
_(CompareSymbolResult) \
|
_(CompareSymbolResult) \
|
||||||
|
_(CompareInt32Result) \
|
||||||
|
_(CompareDoubleResult) \
|
||||||
|
_(CompareObjectUndefinedNullResult) \
|
||||||
_(ArrayJoinResult) \
|
_(ArrayJoinResult) \
|
||||||
_(CallPrintString) \
|
_(CallPrintString) \
|
||||||
_(Breakpoint) \
|
_(Breakpoint) \
|
||||||
|
@ -488,8 +494,10 @@ class MOZ_RAII CacheRegisterAllocator
|
||||||
Register defineRegister(MacroAssembler& masm, TypedOperandId typedId);
|
Register defineRegister(MacroAssembler& masm, TypedOperandId typedId);
|
||||||
ValueOperand defineValueRegister(MacroAssembler& masm, ValOperandId val);
|
ValueOperand defineValueRegister(MacroAssembler& masm, ValOperandId val);
|
||||||
|
|
||||||
// Loads (and unboxes) a value into a float register (caller guarded)
|
// Loads (potentially coercing) and unboxes a value into a float register
|
||||||
void loadDouble(MacroAssembler&, ValOperandId, FloatRegister);
|
// This is infallible, as there should have been a previous guard
|
||||||
|
// to ensure the ValOperandId is already a number.
|
||||||
|
void ensureDoubleRegister(MacroAssembler&, ValOperandId, FloatRegister);
|
||||||
|
|
||||||
// Returns |val|'s JSValueType or JSVAL_TYPE_UNKNOWN.
|
// Returns |val|'s JSValueType or JSVAL_TYPE_UNKNOWN.
|
||||||
JSValueType knownType(ValOperandId val) const;
|
JSValueType knownType(ValOperandId val) const;
|
||||||
|
@ -634,8 +642,6 @@ class MOZ_RAII CacheIRCompiler
|
||||||
// sizeof(stubType)
|
// sizeof(stubType)
|
||||||
uint32_t stubDataOffset_;
|
uint32_t stubDataOffset_;
|
||||||
|
|
||||||
uint32_t nextStubField_;
|
|
||||||
|
|
||||||
enum class StubFieldPolicy {
|
enum class StubFieldPolicy {
|
||||||
Address,
|
Address,
|
||||||
Constant
|
Constant
|
||||||
|
@ -651,7 +657,6 @@ class MOZ_RAII CacheIRCompiler
|
||||||
liveFloatRegs_(FloatRegisterSet::All()),
|
liveFloatRegs_(FloatRegisterSet::All()),
|
||||||
mode_(mode),
|
mode_(mode),
|
||||||
stubDataOffset_(stubDataOffset),
|
stubDataOffset_(stubDataOffset),
|
||||||
nextStubField_(0),
|
|
||||||
stubFieldPolicy_(policy)
|
stubFieldPolicy_(policy)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!writer.failed());
|
MOZ_ASSERT(!writer.failed());
|
||||||
|
@ -716,15 +721,12 @@ class MOZ_RAII CacheIRCompiler
|
||||||
uintptr_t readStubWord(uint32_t offset, StubField::Type type) {
|
uintptr_t readStubWord(uint32_t offset, StubField::Type type) {
|
||||||
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
||||||
MOZ_ASSERT((offset % sizeof(uintptr_t)) == 0);
|
MOZ_ASSERT((offset % sizeof(uintptr_t)) == 0);
|
||||||
// We use nextStubField_ to access the data as it's stored in an as-of-yet
|
return writer_.readStubFieldForIon(offset, type).asWord();
|
||||||
// unpacked vector, and so using the offset can be incorrect where the index
|
|
||||||
// would change as a result of packing.
|
|
||||||
return writer_.readStubFieldForIon(nextStubField_++, type).asWord();
|
|
||||||
}
|
}
|
||||||
uint64_t readStubInt64(uint32_t offset, StubField::Type type) {
|
uint64_t readStubInt64(uint32_t offset, StubField::Type type) {
|
||||||
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
||||||
MOZ_ASSERT((offset % sizeof(uintptr_t)) == 0);
|
MOZ_ASSERT((offset % sizeof(uintptr_t)) == 0);
|
||||||
return writer_.readStubFieldForIon(nextStubField_++, type).asInt64();
|
return writer_.readStubFieldForIon(offset, type).asInt64();
|
||||||
}
|
}
|
||||||
int32_t int32StubField(uint32_t offset) {
|
int32_t int32StubField(uint32_t offset) {
|
||||||
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -5830,7 +5830,7 @@ IonBuilder::jsop_compare(JSOp op, MDefinition* left, MDefinition* right)
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_TRY(compareTrySharedStub(&emitted, left, right));
|
MOZ_TRY(compareTryBinaryStub(&emitted, left, right));
|
||||||
if (emitted)
|
if (emitted)
|
||||||
return Ok();
|
return Ok();
|
||||||
|
|
||||||
|
@ -6025,7 +6025,7 @@ IonBuilder::compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDe
|
||||||
}
|
}
|
||||||
|
|
||||||
AbortReasonOr<Ok>
|
AbortReasonOr<Ok>
|
||||||
IonBuilder::compareTrySharedStub(bool* emitted, MDefinition* left, MDefinition* right)
|
IonBuilder::compareTryBinaryStub(bool* emitted, MDefinition* left, MDefinition* right)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(*emitted == false);
|
MOZ_ASSERT(*emitted == false);
|
||||||
|
|
||||||
|
@ -6037,9 +6037,7 @@ IonBuilder::compareTrySharedStub(bool* emitted, MDefinition* left, MDefinition*
|
||||||
if (JSOp(*pc) == JSOP_CASE)
|
if (JSOp(*pc) == JSOP_CASE)
|
||||||
return Ok();
|
return Ok();
|
||||||
|
|
||||||
trackOptimizationAttempt(TrackedStrategy::Compare_SharedCache);
|
MBinaryCache* stub = MBinaryCache::New(alloc(), left, right);
|
||||||
|
|
||||||
MBinarySharedStub* stub = MBinarySharedStub::New(alloc(), left, right);
|
|
||||||
current->add(stub);
|
current->add(stub);
|
||||||
current->push(stub);
|
current->push(stub);
|
||||||
MOZ_TRY(resumeAfter(stub));
|
MOZ_TRY(resumeAfter(stub));
|
||||||
|
|
|
@ -332,7 +332,7 @@ class IonBuilder
|
||||||
AbortReasonOr<Ok> compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
|
AbortReasonOr<Ok> compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
|
||||||
MDefinition* left,
|
MDefinition* left,
|
||||||
MDefinition* right);
|
MDefinition* right);
|
||||||
AbortReasonOr<Ok> compareTrySharedStub(bool* emitted, MDefinition* left, MDefinition* right);
|
AbortReasonOr<Ok> compareTryBinaryStub(bool* emitted, MDefinition* left, MDefinition* right);
|
||||||
|
|
||||||
// jsop_newarray helpers.
|
// jsop_newarray helpers.
|
||||||
AbortReasonOr<Ok> newArrayTryTemplateObject(bool* emitted, JSObject* templateObject,
|
AbortReasonOr<Ok> newArrayTryTemplateObject(bool* emitted, JSObject* templateObject,
|
||||||
|
|
|
@ -537,8 +537,21 @@ IonCacheIRCompiler::init()
|
||||||
allocator.initInputLocation(1, ic->rhs());
|
allocator.initInputLocation(1, ic->rhs());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CacheKind::Compare: {
|
||||||
|
IonCompareIC *ic = ic_->asCompareIC();
|
||||||
|
ValueOperand output = ic->output();
|
||||||
|
|
||||||
|
available.add(output);
|
||||||
|
|
||||||
|
liveRegs_.emplace(ic->liveRegs());
|
||||||
|
outputUnchecked_.emplace(TypedOrValueRegister(output));
|
||||||
|
|
||||||
|
MOZ_ASSERT(numInputs == 2);
|
||||||
|
allocator.initInputLocation(0, ic->lhs());
|
||||||
|
allocator.initInputLocation(1, ic->rhs());
|
||||||
|
break;
|
||||||
|
}
|
||||||
case CacheKind::Call:
|
case CacheKind::Call:
|
||||||
case CacheKind::Compare:
|
|
||||||
case CacheKind::TypeOf:
|
case CacheKind::TypeOf:
|
||||||
case CacheKind::ToBool:
|
case CacheKind::ToBool:
|
||||||
case CacheKind::GetIntrinsic:
|
case CacheKind::GetIntrinsic:
|
||||||
|
@ -579,8 +592,6 @@ IonCacheIRCompiler::compile()
|
||||||
allocator.nextOp();
|
allocator.nextOp();
|
||||||
} while (reader.more());
|
} while (reader.more());
|
||||||
|
|
||||||
MOZ_RELEASE_ASSERT(nextStubField_ == writer_.numStubFields());
|
|
||||||
|
|
||||||
masm.assumeUnreachable("Should have returned from IC");
|
masm.assumeUnreachable("Should have returned from IC");
|
||||||
|
|
||||||
// Done emitting the main IC code. Now emit the failure paths.
|
// Done emitting the main IC code. Now emit the failure paths.
|
||||||
|
|
|
@ -60,8 +60,9 @@ IonIC::scratchRegisterForEntryJump()
|
||||||
return asUnaryArithIC()->output().scratchReg();
|
return asUnaryArithIC()->output().scratchReg();
|
||||||
case CacheKind::BinaryArith:
|
case CacheKind::BinaryArith:
|
||||||
return asBinaryArithIC()->output().scratchReg();
|
return asBinaryArithIC()->output().scratchReg();
|
||||||
case CacheKind::Call:
|
|
||||||
case CacheKind::Compare:
|
case CacheKind::Compare:
|
||||||
|
return asCompareIC()->output().scratchReg();
|
||||||
|
case CacheKind::Call:
|
||||||
case CacheKind::TypeOf:
|
case CacheKind::TypeOf:
|
||||||
case CacheKind::ToBool:
|
case CacheKind::ToBool:
|
||||||
case CacheKind::GetIntrinsic:
|
case CacheKind::GetIntrinsic:
|
||||||
|
@ -629,6 +630,83 @@ IonBinaryArithIC::update(JSContext* cx, HandleScript outerScript, IonBinaryArith
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
IonCompareIC::update(JSContext* cx, HandleScript outerScript, IonCompareIC* ic,
|
||||||
|
HandleValue lhs, HandleValue rhs, MutableHandleValue res)
|
||||||
|
{
|
||||||
|
IonScript* ionScript = outerScript->ionScript();
|
||||||
|
RootedScript script(cx, ic->script());
|
||||||
|
jsbytecode* pc = ic->pc();
|
||||||
|
JSOp op = JSOp(*pc);
|
||||||
|
|
||||||
|
// Case operations in a CONDSWITCH are performing strict equality.
|
||||||
|
if (op == JSOP_CASE)
|
||||||
|
op = JSOP_STRICTEQ;
|
||||||
|
|
||||||
|
// Don't pass lhs/rhs directly, we need the original values when
|
||||||
|
// generating stubs.
|
||||||
|
RootedValue lhsCopy(cx, lhs);
|
||||||
|
RootedValue rhsCopy(cx, rhs);
|
||||||
|
|
||||||
|
// Perform the compare operation.
|
||||||
|
bool out;
|
||||||
|
switch (op) {
|
||||||
|
case JSOP_LT:
|
||||||
|
if (!LessThan(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case JSOP_LE:
|
||||||
|
if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case JSOP_GT:
|
||||||
|
if (!GreaterThan(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case JSOP_GE:
|
||||||
|
if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case JSOP_EQ:
|
||||||
|
if (!LooselyEqual<true>(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case JSOP_NE:
|
||||||
|
if (!LooselyEqual<false>(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case JSOP_STRICTEQ:
|
||||||
|
if (!StrictlyEqual<true>(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case JSOP_STRICTNE:
|
||||||
|
if (!StrictlyEqual<false>(cx, &lhsCopy, &rhsCopy, &out))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_ASSERT_UNREACHABLE("Unhandled ion compare op");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.setBoolean(out);
|
||||||
|
|
||||||
|
if (ic->state().maybeTransition())
|
||||||
|
ic->discardStubs(cx->zone());
|
||||||
|
|
||||||
|
if (ic->state().canAttachStub()) {
|
||||||
|
bool attached = false;
|
||||||
|
CompareIRGenerator gen(cx, script, pc, ic->state().mode(),
|
||||||
|
op, lhs, rhs);
|
||||||
|
if (gen.tryAttachStub()) {
|
||||||
|
ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
|
||||||
|
|
||||||
|
if (!attached)
|
||||||
|
ic->state().trackNotAttached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t*
|
uint8_t*
|
||||||
IonICStub::stubDataStart()
|
IonICStub::stubDataStart()
|
||||||
{
|
{
|
||||||
|
|
|
@ -65,6 +65,7 @@ class IonGetIteratorIC;
|
||||||
class IonHasOwnIC;
|
class IonHasOwnIC;
|
||||||
class IonInIC;
|
class IonInIC;
|
||||||
class IonInstanceOfIC;
|
class IonInstanceOfIC;
|
||||||
|
class IonCompareIC;
|
||||||
class IonUnaryArithIC;
|
class IonUnaryArithIC;
|
||||||
class IonBinaryArithIC;
|
class IonBinaryArithIC;
|
||||||
|
|
||||||
|
@ -174,6 +175,10 @@ class IonIC
|
||||||
MOZ_ASSERT(kind_ == CacheKind::InstanceOf);
|
MOZ_ASSERT(kind_ == CacheKind::InstanceOf);
|
||||||
return (IonInstanceOfIC*)this;
|
return (IonInstanceOfIC*)this;
|
||||||
}
|
}
|
||||||
|
IonCompareIC* asCompareIC() {
|
||||||
|
MOZ_ASSERT(kind_ == CacheKind::Compare);
|
||||||
|
return (IonCompareIC*)this;
|
||||||
|
}
|
||||||
IonUnaryArithIC* asUnaryArithIC() {
|
IonUnaryArithIC* asUnaryArithIC() {
|
||||||
MOZ_ASSERT(kind_ == CacheKind::UnaryArith);
|
MOZ_ASSERT(kind_ == CacheKind::UnaryArith);
|
||||||
return (IonUnaryArithIC*)this;
|
return (IonUnaryArithIC*)this;
|
||||||
|
@ -485,6 +490,33 @@ class IonInstanceOfIC : public IonIC
|
||||||
HandleValue lhs, HandleObject rhs, bool* attached);
|
HandleValue lhs, HandleObject rhs, bool* attached);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IonCompareIC : public IonIC
|
||||||
|
{
|
||||||
|
LiveRegisterSet liveRegs_;
|
||||||
|
|
||||||
|
TypedOrValueRegister lhs_;
|
||||||
|
TypedOrValueRegister rhs_;
|
||||||
|
ValueOperand output_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
IonCompareIC(LiveRegisterSet liveRegs, TypedOrValueRegister lhs, TypedOrValueRegister rhs, ValueOperand output)
|
||||||
|
: IonIC(CacheKind::Compare),
|
||||||
|
liveRegs_(liveRegs),
|
||||||
|
lhs_(lhs),
|
||||||
|
rhs_(rhs),
|
||||||
|
output_(output)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
LiveRegisterSet liveRegs() const { return liveRegs_; }
|
||||||
|
TypedOrValueRegister lhs() const { return lhs_; }
|
||||||
|
TypedOrValueRegister rhs() const { return rhs_; }
|
||||||
|
ValueOperand output() const { return output_; }
|
||||||
|
|
||||||
|
static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonCompareIC* stub,
|
||||||
|
HandleValue lhs, HandleValue rhs, MutableHandleValue res);
|
||||||
|
};
|
||||||
|
|
||||||
class IonUnaryArithIC : public IonIC
|
class IonUnaryArithIC : public IonIC
|
||||||
{
|
{
|
||||||
LiveRegisterSet liveRegs_;
|
LiveRegisterSet liveRegs_;
|
||||||
|
|
|
@ -1602,16 +1602,6 @@ MacroAssembler::loadDependentStringBase(Register str, Register dest)
|
||||||
loadPtr(Address(str, JSDependentString::offsetOfBase()), dest);
|
loadPtr(Address(str, JSDependentString::offsetOfBase()), dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
MacroAssembler::leaNewDependentStringBase(Register str, Register dest)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(str != dest);
|
|
||||||
|
|
||||||
// Spectre-safe because this is a newly allocated dependent string, thus we
|
|
||||||
// are certain of its type and the type of its base field.
|
|
||||||
computeEffectiveAddress(Address(str, JSDependentString::offsetOfBase()), dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::storeDependentStringBase(Register base, Register str)
|
MacroAssembler::storeDependentStringBase(Register base, Register str)
|
||||||
{
|
{
|
||||||
|
@ -1649,12 +1639,12 @@ MacroAssembler::loadStringChar(Register str, Register index, Register output, Re
|
||||||
// because a TwoByte rope might have a Latin1 child.
|
// because a TwoByte rope might have a Latin1 child.
|
||||||
branchLatin1String(output, &isLatin1);
|
branchLatin1String(output, &isLatin1);
|
||||||
loadStringChars(output, scratch, CharEncoding::TwoByte);
|
loadStringChars(output, scratch, CharEncoding::TwoByte);
|
||||||
load16ZeroExtend(BaseIndex(scratch, index, TimesTwo), output);
|
loadChar(scratch, index, output, CharEncoding::TwoByte);
|
||||||
jump(&done);
|
jump(&done);
|
||||||
|
|
||||||
bind(&isLatin1);
|
bind(&isLatin1);
|
||||||
loadStringChars(output, scratch, CharEncoding::Latin1);
|
loadStringChars(output, scratch, CharEncoding::Latin1);
|
||||||
load8ZeroExtend(BaseIndex(scratch, index, TimesOne), output);
|
loadChar(scratch, index, output, CharEncoding::Latin1);
|
||||||
|
|
||||||
bind(&done);
|
bind(&done);
|
||||||
}
|
}
|
||||||
|
@ -1673,6 +1663,27 @@ MacroAssembler::loadStringIndexValue(Register str, Register dest, Label* fail)
|
||||||
rshift32(Imm32(JSString::INDEX_VALUE_SHIFT), dest);
|
rshift32(Imm32(JSString::INDEX_VALUE_SHIFT), dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::loadChar(Register chars, Register index, Register dest, CharEncoding encoding,
|
||||||
|
int32_t offset/* = 0 */)
|
||||||
|
{
|
||||||
|
if (encoding == CharEncoding::Latin1)
|
||||||
|
loadChar(BaseIndex(chars, index, TimesOne, offset), dest, encoding);
|
||||||
|
else
|
||||||
|
loadChar(BaseIndex(chars, index, TimesTwo, offset), dest, encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::addToCharPtr(Register chars, Register index, CharEncoding encoding)
|
||||||
|
{
|
||||||
|
if (encoding == CharEncoding::Latin1) {
|
||||||
|
static_assert(sizeof(char) == 1, "Latin-1 string index shouldn't need scaling");
|
||||||
|
addPtr(index, chars);
|
||||||
|
} else {
|
||||||
|
computeEffectiveAddress(BaseIndex(chars, index, TimesTwo), chars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::typeOfObject(Register obj, Register scratch, Label* slow,
|
MacroAssembler::typeOfObject(Register obj, Register scratch, Label* slow,
|
||||||
Label* isObject, Label* isCallable, Label* isUndefined)
|
Label* isObject, Label* isCallable, Label* isUndefined)
|
||||||
|
|
|
@ -2121,10 +2121,43 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
|
|
||||||
void loadDependentStringBase(Register str, Register dest);
|
void loadDependentStringBase(Register str, Register dest);
|
||||||
void storeDependentStringBase(Register base, Register str);
|
void storeDependentStringBase(Register base, Register str);
|
||||||
void leaNewDependentStringBase(Register str, Register dest);
|
|
||||||
|
|
||||||
void loadStringIndexValue(Register str, Register dest, Label* fail);
|
void loadStringIndexValue(Register str, Register dest, Label* fail);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the character in |src| to |dest|.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void storeChar(const T& src, Address dest, CharEncoding encoding) {
|
||||||
|
if (encoding == CharEncoding::Latin1)
|
||||||
|
store8(src, dest);
|
||||||
|
else
|
||||||
|
store16(src, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the character at |src| into |dest|.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void loadChar(const T& src, Register dest, CharEncoding encoding) {
|
||||||
|
if (encoding == CharEncoding::Latin1)
|
||||||
|
load8ZeroExtend(src, dest);
|
||||||
|
else
|
||||||
|
load16ZeroExtend(src, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the character at |chars[index + offset]| into |dest|. The optional
|
||||||
|
* offset argument is not scaled to the character encoding.
|
||||||
|
*/
|
||||||
|
void loadChar(Register chars, Register index, Register dest, CharEncoding encoding,
|
||||||
|
int32_t offset = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add |index| to |chars| so that |chars| now points at |chars[index]|.
|
||||||
|
*/
|
||||||
|
void addToCharPtr(Register chars, Register index, CharEncoding encoding);
|
||||||
|
|
||||||
void loadJSContext(Register dest);
|
void loadJSContext(Register dest);
|
||||||
|
|
||||||
void switchToRealm(Register realm);
|
void switchToRealm(Register realm);
|
||||||
|
|
|
@ -671,542 +671,6 @@ SharedStubInfo::outerScript(JSContext* cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Compare_Fallback
|
|
||||||
//
|
|
||||||
|
|
||||||
static bool
|
|
||||||
DoCompareFallback(JSContext* cx, void* payload, ICCompare_Fallback* stub_, HandleValue lhs,
|
|
||||||
HandleValue rhs, MutableHandleValue ret)
|
|
||||||
{
|
|
||||||
SharedStubInfo info(cx, payload, stub_->icEntry());
|
|
||||||
ICStubCompiler::Engine engine = info.engine();
|
|
||||||
|
|
||||||
// This fallback stub may trigger debug mode toggling.
|
|
||||||
DebugModeOSRVolatileStub<ICCompare_Fallback*> stub(engine, info.maybeFrame(), stub_);
|
|
||||||
|
|
||||||
jsbytecode* pc = info.pc();
|
|
||||||
JSOp op = JSOp(*pc);
|
|
||||||
|
|
||||||
FallbackICSpew(cx, stub, "Compare(%s)", CodeName[op]);
|
|
||||||
|
|
||||||
// Case operations in a CONDSWITCH are performing strict equality.
|
|
||||||
if (op == JSOP_CASE)
|
|
||||||
op = JSOP_STRICTEQ;
|
|
||||||
|
|
||||||
// Don't pass lhs/rhs directly, we need the original values when
|
|
||||||
// generating stubs.
|
|
||||||
RootedValue lhsCopy(cx, lhs);
|
|
||||||
RootedValue rhsCopy(cx, rhs);
|
|
||||||
|
|
||||||
// Perform the compare operation.
|
|
||||||
bool out;
|
|
||||||
switch (op) {
|
|
||||||
case JSOP_LT:
|
|
||||||
if (!LessThan(cx, &lhsCopy, &rhsCopy, &out))
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
case JSOP_LE:
|
|
||||||
if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
case JSOP_GT:
|
|
||||||
if (!GreaterThan(cx, &lhsCopy, &rhsCopy, &out))
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
case JSOP_GE:
|
|
||||||
if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
case JSOP_EQ:
|
|
||||||
if (!LooselyEqual<true>(cx, &lhsCopy, &rhsCopy, &out))
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
case JSOP_NE:
|
|
||||||
if (!LooselyEqual<false>(cx, &lhsCopy, &rhsCopy, &out))
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
case JSOP_STRICTEQ:
|
|
||||||
if (!StrictlyEqual<true>(cx, &lhsCopy, &rhsCopy, &out))
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
case JSOP_STRICTNE:
|
|
||||||
if (!StrictlyEqual<false>(cx, &lhsCopy, &rhsCopy, &out))
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
MOZ_ASSERT_UNREACHABLE("Unhandled baseline compare op");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.setBoolean(out);
|
|
||||||
|
|
||||||
// Check if debug mode toggling made the stub invalid.
|
|
||||||
if (stub.invalid())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Check to see if a new stub should be generated.
|
|
||||||
if (stub->numOptimizedStubs() >= ICCompare_Fallback::MAX_OPTIMIZED_STUBS) {
|
|
||||||
// TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
|
|
||||||
// But for now we just bail.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (engine == ICStubEngine::Baseline && !JitOptions.disableCacheIR) {
|
|
||||||
RootedScript script(cx, info.outerScript(cx));
|
|
||||||
CompareIRGenerator gen(cx, script, pc, stub->state().mode(), op, lhs, rhs);
|
|
||||||
bool attached = false;
|
|
||||||
if (gen.tryAttachStub()) {
|
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
|
||||||
BaselineCacheIRStubKind::Regular,
|
|
||||||
engine, script, stub, &attached);
|
|
||||||
if (newStub)
|
|
||||||
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to generate new stubs.
|
|
||||||
if (lhs.isInt32() && rhs.isInt32()) {
|
|
||||||
JitSpew(JitSpew_BaselineIC, " Generating %s(Int32, Int32) stub", CodeName[op]);
|
|
||||||
ICCompare_Int32::Compiler compiler(cx, op, engine);
|
|
||||||
ICStub* int32Stub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
|
|
||||||
if (!int32Stub)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
stub->addNewStub(int32Stub);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cx->runtime()->jitSupportsFloatingPoint && (lhs.isNumber() || rhs.isNumber()))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (lhs.isNumber() && rhs.isNumber()) {
|
|
||||||
JitSpew(JitSpew_BaselineIC, " Generating %s(Number, Number) stub", CodeName[op]);
|
|
||||||
|
|
||||||
// Unlink int32 stubs, it's faster to always use the double stub.
|
|
||||||
stub->unlinkStubsWithKind(cx, ICStub::Compare_Int32);
|
|
||||||
|
|
||||||
ICCompare_Double::Compiler compiler(cx, op, engine);
|
|
||||||
ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
|
|
||||||
if (!doubleStub)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
stub->addNewStub(doubleStub);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((lhs.isNumber() && rhs.isUndefined()) ||
|
|
||||||
(lhs.isUndefined() && rhs.isNumber()))
|
|
||||||
{
|
|
||||||
JitSpew(JitSpew_BaselineIC, " Generating %s(%s, %s) stub", CodeName[op],
|
|
||||||
rhs.isUndefined() ? "Number" : "Undefined",
|
|
||||||
rhs.isUndefined() ? "Undefined" : "Number");
|
|
||||||
ICCompare_NumberWithUndefined::Compiler compiler(cx, op, engine, lhs.isUndefined());
|
|
||||||
ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
|
|
||||||
if (!doubleStub)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
stub->addNewStub(doubleStub);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lhs.isBoolean() && rhs.isBoolean()) {
|
|
||||||
JitSpew(JitSpew_BaselineIC, " Generating %s(Boolean, Boolean) stub", CodeName[op]);
|
|
||||||
ICCompare_Boolean::Compiler compiler(cx, op, engine);
|
|
||||||
ICStub* booleanStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
|
|
||||||
if (!booleanStub)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
stub->addNewStub(booleanStub);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((lhs.isBoolean() && rhs.isInt32()) || (lhs.isInt32() && rhs.isBoolean())) {
|
|
||||||
JitSpew(JitSpew_BaselineIC, " Generating %s(%s, %s) stub", CodeName[op],
|
|
||||||
rhs.isInt32() ? "Boolean" : "Int32",
|
|
||||||
rhs.isInt32() ? "Int32" : "Boolean");
|
|
||||||
ICCompare_Int32WithBoolean::Compiler compiler(cx, op, engine, lhs.isInt32());
|
|
||||||
ICStub* optStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
|
|
||||||
if (!optStub)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
stub->addNewStub(optStub);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsEqualityOp(op)) {
|
|
||||||
if (lhs.isString() && rhs.isString() && !stub->hasStub(ICStub::Compare_String)) {
|
|
||||||
JitSpew(JitSpew_BaselineIC, " Generating %s(String, String) stub", CodeName[op]);
|
|
||||||
ICCompare_String::Compiler compiler(cx, op, engine);
|
|
||||||
ICStub* stringStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
|
|
||||||
if (!stringStub)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
stub->addNewStub(stringStub);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lhs.isSymbol() && rhs.isSymbol() && !stub->hasStub(ICStub::Compare_Symbol)) {
|
|
||||||
JitSpew(JitSpew_BaselineIC, " Generating %s(Symbol, Symbol) stub", CodeName[op]);
|
|
||||||
ICCompare_Symbol::Compiler compiler(cx, op, engine);
|
|
||||||
ICStub* symbolStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
|
|
||||||
if (!symbolStub)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
stub->addNewStub(symbolStub);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lhs.isObject() && rhs.isObject()) {
|
|
||||||
MOZ_ASSERT(!stub->hasStub(ICStub::Compare_Object));
|
|
||||||
JitSpew(JitSpew_BaselineIC, " Generating %s(Object, Object) stub", CodeName[op]);
|
|
||||||
ICCompare_Object::Compiler compiler(cx, op, engine);
|
|
||||||
ICStub* objectStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
|
|
||||||
if (!objectStub)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
stub->addNewStub(objectStub);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lhs.isNullOrUndefined() || rhs.isNullOrUndefined()) {
|
|
||||||
JitSpew(JitSpew_BaselineIC, " Generating %s(Null/Undef or X, Null/Undef or X) stub",
|
|
||||||
CodeName[op]);
|
|
||||||
bool lhsIsUndefined = lhs.isNullOrUndefined();
|
|
||||||
bool compareWithNull = lhs.isNull() || rhs.isNull();
|
|
||||||
ICCompare_ObjectWithUndefined::Compiler compiler(cx, op, engine,
|
|
||||||
lhsIsUndefined, compareWithNull);
|
|
||||||
ICStub* objectStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
|
|
||||||
if (!objectStub)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
stub->addNewStub(objectStub);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stub->noteUnoptimizableAccess();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef bool (*DoCompareFallbackFn)(JSContext*, void*, ICCompare_Fallback*,
|
|
||||||
HandleValue, HandleValue, MutableHandleValue);
|
|
||||||
static const VMFunction DoCompareFallbackInfo =
|
|
||||||
FunctionInfo<DoCompareFallbackFn>(DoCompareFallback, "DoCompareFallback", TailCall,
|
|
||||||
PopValues(2));
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(R0 == JSReturnOperand);
|
|
||||||
|
|
||||||
// Restore the tail call register.
|
|
||||||
EmitRestoreTailCallReg(masm);
|
|
||||||
|
|
||||||
// Ensure stack is fully synced for the expression decompiler.
|
|
||||||
masm.pushValue(R0);
|
|
||||||
masm.pushValue(R1);
|
|
||||||
|
|
||||||
// Push arguments.
|
|
||||||
masm.pushValue(R1);
|
|
||||||
masm.pushValue(R0);
|
|
||||||
masm.push(ICStubReg);
|
|
||||||
pushStubPayload(masm, R0.scratchReg());
|
|
||||||
return tailCallVM(DoCompareFallbackInfo, masm);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Compare_String
|
|
||||||
//
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_String::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
Label failure, restore;
|
|
||||||
masm.branchTestString(Assembler::NotEqual, R0, &failure);
|
|
||||||
masm.branchTestString(Assembler::NotEqual, R1, &failure);
|
|
||||||
|
|
||||||
MOZ_ASSERT(IsEqualityOp(op));
|
|
||||||
|
|
||||||
// left/right are part of R0/R1. Restore R0 and R1 in the failure case.
|
|
||||||
Register left = R0.scratchReg();
|
|
||||||
Register right = R1.scratchReg();
|
|
||||||
masm.unboxString(R0, left);
|
|
||||||
masm.unboxString(R1, right);
|
|
||||||
|
|
||||||
AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
|
|
||||||
Register scratchReg = regs.takeAny();
|
|
||||||
|
|
||||||
masm.compareStrings(op, left, right, scratchReg, &restore);
|
|
||||||
masm.tagValue(JSVAL_TYPE_BOOLEAN, scratchReg, R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
masm.bind(&restore);
|
|
||||||
masm.tagValue(JSVAL_TYPE_STRING, left, R0);
|
|
||||||
masm.tagValue(JSVAL_TYPE_STRING, right, R1);
|
|
||||||
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Compare_Symbol
|
|
||||||
//
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_Symbol::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
Label failure;
|
|
||||||
masm.branchTestSymbol(Assembler::NotEqual, R0, &failure);
|
|
||||||
masm.branchTestSymbol(Assembler::NotEqual, R1, &failure);
|
|
||||||
|
|
||||||
MOZ_ASSERT(IsEqualityOp(op));
|
|
||||||
|
|
||||||
Register left = masm.extractSymbol(R0, ExtractTemp0);
|
|
||||||
Register right = masm.extractSymbol(R1, ExtractTemp1);
|
|
||||||
|
|
||||||
Label ifTrue;
|
|
||||||
masm.branchPtr(JSOpToCondition(op, /* signed = */true), left, right, &ifTrue);
|
|
||||||
|
|
||||||
masm.moveValue(BooleanValue(false), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
masm.bind(&ifTrue);
|
|
||||||
masm.moveValue(BooleanValue(true), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
// Failure case - jump to next stub
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Compare_Boolean
|
|
||||||
//
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_Boolean::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
Label failure;
|
|
||||||
masm.branchTestBoolean(Assembler::NotEqual, R0, &failure);
|
|
||||||
masm.branchTestBoolean(Assembler::NotEqual, R1, &failure);
|
|
||||||
|
|
||||||
Register left = masm.extractInt32(R0, ExtractTemp0);
|
|
||||||
Register right = masm.extractInt32(R1, ExtractTemp1);
|
|
||||||
|
|
||||||
// Compare payload regs of R0 and R1.
|
|
||||||
Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
|
|
||||||
masm.cmp32Set(cond, left, right, left);
|
|
||||||
|
|
||||||
// Box the result and return
|
|
||||||
masm.tagValue(JSVAL_TYPE_BOOLEAN, left, R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
// Failure case - jump to next stub
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Compare_NumberWithUndefined
|
|
||||||
//
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_NumberWithUndefined::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
ValueOperand numberOperand, undefinedOperand;
|
|
||||||
if (lhsIsUndefined) {
|
|
||||||
numberOperand = R1;
|
|
||||||
undefinedOperand = R0;
|
|
||||||
} else {
|
|
||||||
numberOperand = R0;
|
|
||||||
undefinedOperand = R1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Label failure;
|
|
||||||
masm.branchTestNumber(Assembler::NotEqual, numberOperand, &failure);
|
|
||||||
masm.branchTestUndefined(Assembler::NotEqual, undefinedOperand, &failure);
|
|
||||||
|
|
||||||
// Comparing a number with undefined will always be true for NE/STRICTNE,
|
|
||||||
// and always be false for other compare ops.
|
|
||||||
masm.moveValue(BooleanValue(op == JSOP_NE || op == JSOP_STRICTNE), R0);
|
|
||||||
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
// Failure case - jump to next stub
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Compare_Object
|
|
||||||
//
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_Object::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
Label failure;
|
|
||||||
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
|
|
||||||
masm.branchTestObject(Assembler::NotEqual, R1, &failure);
|
|
||||||
|
|
||||||
MOZ_ASSERT(IsEqualityOp(op));
|
|
||||||
|
|
||||||
Register left = masm.extractObject(R0, ExtractTemp0);
|
|
||||||
Register right = masm.extractObject(R1, ExtractTemp1);
|
|
||||||
|
|
||||||
Label ifTrue;
|
|
||||||
masm.branchPtr(JSOpToCondition(op, /* signed = */true), left, right, &ifTrue);
|
|
||||||
|
|
||||||
masm.moveValue(BooleanValue(false), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
masm.bind(&ifTrue);
|
|
||||||
masm.moveValue(BooleanValue(true), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
// Failure case - jump to next stub
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Compare_ObjectWithUndefined
|
|
||||||
//
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_ObjectWithUndefined::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(IsEqualityOp(op));
|
|
||||||
|
|
||||||
ValueOperand objectOperand, undefinedOperand;
|
|
||||||
if (lhsIsUndefined) {
|
|
||||||
objectOperand = R1;
|
|
||||||
undefinedOperand = R0;
|
|
||||||
} else {
|
|
||||||
objectOperand = R0;
|
|
||||||
undefinedOperand = R1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Label failure;
|
|
||||||
if (compareWithNull)
|
|
||||||
masm.branchTestNull(Assembler::NotEqual, undefinedOperand, &failure);
|
|
||||||
else
|
|
||||||
masm.branchTestUndefined(Assembler::NotEqual, undefinedOperand, &failure);
|
|
||||||
|
|
||||||
Label notObject;
|
|
||||||
masm.branchTestObject(Assembler::NotEqual, objectOperand, ¬Object);
|
|
||||||
|
|
||||||
if (op == JSOP_STRICTEQ || op == JSOP_STRICTNE) {
|
|
||||||
// obj !== undefined for all objects.
|
|
||||||
masm.moveValue(BooleanValue(op == JSOP_STRICTNE), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
} else {
|
|
||||||
// obj != undefined only where !obj->getClass()->emulatesUndefined()
|
|
||||||
Register obj = masm.extractObject(objectOperand, ExtractTemp0);
|
|
||||||
|
|
||||||
// We need a scratch register.
|
|
||||||
masm.push(obj);
|
|
||||||
Label slow, emulatesUndefined;
|
|
||||||
masm.branchIfObjectEmulatesUndefined(obj, obj, &slow, &emulatesUndefined);
|
|
||||||
|
|
||||||
masm.pop(obj);
|
|
||||||
masm.moveValue(BooleanValue(op == JSOP_NE), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
masm.bind(&emulatesUndefined);
|
|
||||||
masm.pop(obj);
|
|
||||||
masm.moveValue(BooleanValue(op == JSOP_EQ), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
masm.bind(&slow);
|
|
||||||
masm.pop(obj);
|
|
||||||
masm.jump(&failure);
|
|
||||||
}
|
|
||||||
|
|
||||||
masm.bind(¬Object);
|
|
||||||
|
|
||||||
// Also support null == null or undefined == undefined comparisons.
|
|
||||||
Label differentTypes;
|
|
||||||
if (compareWithNull)
|
|
||||||
masm.branchTestNull(Assembler::NotEqual, objectOperand, &differentTypes);
|
|
||||||
else
|
|
||||||
masm.branchTestUndefined(Assembler::NotEqual, objectOperand, &differentTypes);
|
|
||||||
|
|
||||||
masm.moveValue(BooleanValue(op == JSOP_STRICTEQ || op == JSOP_EQ), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
masm.bind(&differentTypes);
|
|
||||||
// Also support null == undefined or undefined == null.
|
|
||||||
Label neverEqual;
|
|
||||||
if (compareWithNull)
|
|
||||||
masm.branchTestUndefined(Assembler::NotEqual, objectOperand, &neverEqual);
|
|
||||||
else
|
|
||||||
masm.branchTestNull(Assembler::NotEqual, objectOperand, &neverEqual);
|
|
||||||
|
|
||||||
masm.moveValue(BooleanValue(op == JSOP_EQ || op == JSOP_STRICTNE), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
// null/undefined can only be equal to null/undefined or emulatesUndefined.
|
|
||||||
masm.bind(&neverEqual);
|
|
||||||
masm.moveValue(BooleanValue(op == JSOP_NE || op == JSOP_STRICTNE), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
// Failure case - jump to next stub
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Compare_Int32WithBoolean
|
|
||||||
//
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_Int32WithBoolean::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
Label failure;
|
|
||||||
ValueOperand int32Val;
|
|
||||||
ValueOperand boolVal;
|
|
||||||
if (lhsIsInt32_) {
|
|
||||||
int32Val = R0;
|
|
||||||
boolVal = R1;
|
|
||||||
} else {
|
|
||||||
boolVal = R0;
|
|
||||||
int32Val = R1;
|
|
||||||
}
|
|
||||||
masm.branchTestBoolean(Assembler::NotEqual, boolVal, &failure);
|
|
||||||
masm.branchTestInt32(Assembler::NotEqual, int32Val, &failure);
|
|
||||||
|
|
||||||
if (op_ == JSOP_STRICTEQ || op_ == JSOP_STRICTNE) {
|
|
||||||
// Ints and booleans are never strictly equal, always strictly not equal.
|
|
||||||
masm.moveValue(BooleanValue(op_ == JSOP_STRICTNE), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
} else {
|
|
||||||
Register boolReg = masm.extractBoolean(boolVal, ExtractTemp0);
|
|
||||||
Register int32Reg = masm.extractInt32(int32Val, ExtractTemp1);
|
|
||||||
|
|
||||||
// Compare payload regs of R0 and R1.
|
|
||||||
Assembler::Condition cond = JSOpToCondition(op_, /* signed = */true);
|
|
||||||
masm.cmp32Set(cond, (lhsIsInt32_ ? int32Reg : boolReg),
|
|
||||||
(lhsIsInt32_ ? boolReg : int32Reg), R0.scratchReg());
|
|
||||||
|
|
||||||
// Box the result and return
|
|
||||||
masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.scratchReg(), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Failure case - jump to next stub
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
LoadTypedThingData(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result)
|
LoadTypedThingData(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1684,269 +1684,6 @@ class ICCompare_Fallback : public ICFallbackStub
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
class ICCompare_Int32 : public ICStub
|
|
||||||
{
|
|
||||||
friend class ICStubSpace;
|
|
||||||
|
|
||||||
explicit ICCompare_Int32(JitCode* stubCode)
|
|
||||||
: ICStub(ICStub::Compare_Int32, stubCode) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Compiler for this stub kind.
|
|
||||||
class Compiler : public ICMultiStubCompiler {
|
|
||||||
protected:
|
|
||||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Compiler(JSContext* cx, JSOp op, Engine engine)
|
|
||||||
: ICMultiStubCompiler(cx, ICStub::Compare_Int32, op, engine) {}
|
|
||||||
|
|
||||||
ICStub* getStub(ICStubSpace* space) override {
|
|
||||||
return newStub<ICCompare_Int32>(space, getStubCode());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ICCompare_Double : public ICStub
|
|
||||||
{
|
|
||||||
friend class ICStubSpace;
|
|
||||||
|
|
||||||
explicit ICCompare_Double(JitCode* stubCode)
|
|
||||||
: ICStub(ICStub::Compare_Double, stubCode)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public:
|
|
||||||
class Compiler : public ICMultiStubCompiler {
|
|
||||||
protected:
|
|
||||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Compiler(JSContext* cx, JSOp op, Engine engine)
|
|
||||||
: ICMultiStubCompiler(cx, ICStub::Compare_Double, op, engine)
|
|
||||||
{}
|
|
||||||
|
|
||||||
ICStub* getStub(ICStubSpace* space) override {
|
|
||||||
return newStub<ICCompare_Double>(space, getStubCode());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ICCompare_NumberWithUndefined : public ICStub
|
|
||||||
{
|
|
||||||
friend class ICStubSpace;
|
|
||||||
|
|
||||||
ICCompare_NumberWithUndefined(JitCode* stubCode, bool lhsIsUndefined)
|
|
||||||
: ICStub(ICStub::Compare_NumberWithUndefined, stubCode)
|
|
||||||
{
|
|
||||||
extra_ = lhsIsUndefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool lhsIsUndefined() {
|
|
||||||
return extra_;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Compiler : public ICMultiStubCompiler {
|
|
||||||
protected:
|
|
||||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
|
|
||||||
|
|
||||||
bool lhsIsUndefined;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsUndefined)
|
|
||||||
: ICMultiStubCompiler(cx, ICStub::Compare_NumberWithUndefined, op, engine),
|
|
||||||
lhsIsUndefined(lhsIsUndefined)
|
|
||||||
{}
|
|
||||||
|
|
||||||
virtual int32_t getKey() const override {
|
|
||||||
return static_cast<int32_t>(engine_) |
|
|
||||||
(static_cast<int32_t>(kind) << 1) |
|
|
||||||
(static_cast<int32_t>(op) << 17) |
|
|
||||||
(static_cast<int32_t>(lhsIsUndefined) << 25);
|
|
||||||
}
|
|
||||||
|
|
||||||
ICStub* getStub(ICStubSpace* space) override {
|
|
||||||
return newStub<ICCompare_NumberWithUndefined>(space, getStubCode(),
|
|
||||||
lhsIsUndefined);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ICCompare_String : public ICStub
|
|
||||||
{
|
|
||||||
friend class ICStubSpace;
|
|
||||||
|
|
||||||
explicit ICCompare_String(JitCode* stubCode)
|
|
||||||
: ICStub(ICStub::Compare_String, stubCode)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public:
|
|
||||||
class Compiler : public ICMultiStubCompiler {
|
|
||||||
protected:
|
|
||||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Compiler(JSContext* cx, JSOp op, Engine engine)
|
|
||||||
: ICMultiStubCompiler(cx, ICStub::Compare_String, op, engine)
|
|
||||||
{}
|
|
||||||
|
|
||||||
ICStub* getStub(ICStubSpace* space) override {
|
|
||||||
return newStub<ICCompare_String>(space, getStubCode());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ICCompare_Symbol : public ICStub
|
|
||||||
{
|
|
||||||
friend class ICStubSpace;
|
|
||||||
|
|
||||||
explicit ICCompare_Symbol(JitCode* stubCode)
|
|
||||||
: ICStub(ICStub::Compare_Symbol, stubCode)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public:
|
|
||||||
class Compiler : public ICMultiStubCompiler {
|
|
||||||
protected:
|
|
||||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Compiler(JSContext* cx, JSOp op, Engine engine)
|
|
||||||
: ICMultiStubCompiler(cx, ICStub::Compare_Symbol, op, engine)
|
|
||||||
{}
|
|
||||||
|
|
||||||
ICStub* getStub(ICStubSpace* space) override {
|
|
||||||
return newStub<ICCompare_Symbol>(space, getStubCode());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ICCompare_Boolean : public ICStub
|
|
||||||
{
|
|
||||||
friend class ICStubSpace;
|
|
||||||
|
|
||||||
explicit ICCompare_Boolean(JitCode* stubCode)
|
|
||||||
: ICStub(ICStub::Compare_Boolean, stubCode)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public:
|
|
||||||
class Compiler : public ICMultiStubCompiler {
|
|
||||||
protected:
|
|
||||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Compiler(JSContext* cx, JSOp op, Engine engine)
|
|
||||||
: ICMultiStubCompiler(cx, ICStub::Compare_Boolean, op, engine)
|
|
||||||
{}
|
|
||||||
|
|
||||||
ICStub* getStub(ICStubSpace* space) override {
|
|
||||||
return newStub<ICCompare_Boolean>(space, getStubCode());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ICCompare_Object : public ICStub
|
|
||||||
{
|
|
||||||
friend class ICStubSpace;
|
|
||||||
|
|
||||||
explicit ICCompare_Object(JitCode* stubCode)
|
|
||||||
: ICStub(ICStub::Compare_Object, stubCode)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public:
|
|
||||||
class Compiler : public ICMultiStubCompiler {
|
|
||||||
protected:
|
|
||||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Compiler(JSContext* cx, JSOp op, Engine engine)
|
|
||||||
: ICMultiStubCompiler(cx, ICStub::Compare_Object, op, engine)
|
|
||||||
{}
|
|
||||||
|
|
||||||
ICStub* getStub(ICStubSpace* space) override {
|
|
||||||
return newStub<ICCompare_Object>(space, getStubCode());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ICCompare_ObjectWithUndefined : public ICStub
|
|
||||||
{
|
|
||||||
friend class ICStubSpace;
|
|
||||||
|
|
||||||
explicit ICCompare_ObjectWithUndefined(JitCode* stubCode)
|
|
||||||
: ICStub(ICStub::Compare_ObjectWithUndefined, stubCode)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public:
|
|
||||||
class Compiler : public ICMultiStubCompiler {
|
|
||||||
protected:
|
|
||||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
|
|
||||||
|
|
||||||
bool lhsIsUndefined;
|
|
||||||
bool compareWithNull;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsUndefined, bool compareWithNull)
|
|
||||||
: ICMultiStubCompiler(cx, ICStub::Compare_ObjectWithUndefined, op, engine),
|
|
||||||
lhsIsUndefined(lhsIsUndefined),
|
|
||||||
compareWithNull(compareWithNull)
|
|
||||||
{}
|
|
||||||
|
|
||||||
virtual int32_t getKey() const override {
|
|
||||||
return static_cast<int32_t>(engine_) |
|
|
||||||
(static_cast<int32_t>(kind) << 1) |
|
|
||||||
(static_cast<int32_t>(op) << 17) |
|
|
||||||
(static_cast<int32_t>(lhsIsUndefined) << 25) |
|
|
||||||
(static_cast<int32_t>(compareWithNull) << 26);
|
|
||||||
}
|
|
||||||
|
|
||||||
ICStub* getStub(ICStubSpace* space) override {
|
|
||||||
return newStub<ICCompare_ObjectWithUndefined>(space, getStubCode());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ICCompare_Int32WithBoolean : public ICStub
|
|
||||||
{
|
|
||||||
friend class ICStubSpace;
|
|
||||||
|
|
||||||
ICCompare_Int32WithBoolean(JitCode* stubCode, bool lhsIsInt32)
|
|
||||||
: ICStub(ICStub::Compare_Int32WithBoolean, stubCode)
|
|
||||||
{
|
|
||||||
extra_ = lhsIsInt32;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool lhsIsInt32() const {
|
|
||||||
return extra_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compiler for this stub kind.
|
|
||||||
class Compiler : public ICStubCompiler {
|
|
||||||
protected:
|
|
||||||
JSOp op_;
|
|
||||||
bool lhsIsInt32_;
|
|
||||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
|
|
||||||
|
|
||||||
virtual int32_t getKey() const override {
|
|
||||||
return static_cast<int32_t>(engine_) |
|
|
||||||
(static_cast<int32_t>(kind) << 1) |
|
|
||||||
(static_cast<int32_t>(op_) << 17) |
|
|
||||||
(static_cast<int32_t>(lhsIsInt32_) << 25);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsInt32)
|
|
||||||
: ICStubCompiler(cx, ICStub::Compare_Int32WithBoolean, engine),
|
|
||||||
op_(op),
|
|
||||||
lhsIsInt32_(lhsIsInt32)
|
|
||||||
{}
|
|
||||||
|
|
||||||
ICStub* getStub(ICStubSpace* space) override {
|
|
||||||
return newStub<ICCompare_Int32WithBoolean>(space, getStubCode(), lhsIsInt32_);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Enum for stubs handling a combination of typed arrays and typed objects.
|
// Enum for stubs handling a combination of typed arrays and typed objects.
|
||||||
enum TypedThingLayout {
|
enum TypedThingLayout {
|
||||||
Layout_TypedArray,
|
Layout_TypedArray,
|
||||||
|
|
|
@ -15,15 +15,6 @@ namespace jit {
|
||||||
_(BinaryArith_Fallback) \
|
_(BinaryArith_Fallback) \
|
||||||
\
|
\
|
||||||
_(Compare_Fallback) \
|
_(Compare_Fallback) \
|
||||||
_(Compare_Int32) \
|
|
||||||
_(Compare_Double) \
|
|
||||||
_(Compare_NumberWithUndefined) \
|
|
||||||
_(Compare_String) \
|
|
||||||
_(Compare_Symbol) \
|
|
||||||
_(Compare_Boolean) \
|
|
||||||
_(Compare_Object) \
|
|
||||||
_(Compare_ObjectWithUndefined) \
|
|
||||||
_(Compare_Int32WithBoolean) \
|
|
||||||
\
|
\
|
||||||
_(GetProp_Fallback) \
|
_(GetProp_Fallback) \
|
||||||
\
|
\
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
||||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#include "jit/BaselineCompiler.h"
|
|
||||||
#include "jit/BaselineIC.h"
|
|
||||||
#include "jit/BaselineJIT.h"
|
|
||||||
#include "jit/Linker.h"
|
|
||||||
#include "jit/SharedICHelpers.h"
|
|
||||||
|
|
||||||
#include "jit/MacroAssembler-inl.h"
|
|
||||||
|
|
||||||
using namespace js;
|
|
||||||
using namespace js::jit;
|
|
||||||
|
|
||||||
namespace js {
|
|
||||||
namespace jit {
|
|
||||||
|
|
||||||
// ICCompare_Int32
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
// Guard that R0 is an integer and R1 is an integer.
|
|
||||||
Label failure;
|
|
||||||
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
|
|
||||||
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
|
|
||||||
|
|
||||||
// Compare payload regs of R0 and R1.
|
|
||||||
Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
|
|
||||||
masm.cmp32(R0.payloadReg(), R1.payloadReg());
|
|
||||||
masm.ma_mov(Imm32(1), R0.payloadReg(), cond);
|
|
||||||
masm.ma_mov(Imm32(0), R0.payloadReg(), Assembler::InvertCondition(cond));
|
|
||||||
|
|
||||||
// Result is implicitly boxed already.
|
|
||||||
masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.payloadReg(), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
// Failure case - jump to next stub.
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_Double::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
Label failure, isNaN;
|
|
||||||
masm.ensureDouble(R0, FloatReg0, &failure);
|
|
||||||
masm.ensureDouble(R1, FloatReg1, &failure);
|
|
||||||
|
|
||||||
Register dest = R0.scratchReg();
|
|
||||||
|
|
||||||
Assembler::DoubleCondition doubleCond = JSOpToDoubleCondition(op);
|
|
||||||
Assembler::Condition cond = Assembler::ConditionFromDoubleCondition(doubleCond);
|
|
||||||
|
|
||||||
masm.compareDouble(FloatReg0, FloatReg1);
|
|
||||||
masm.ma_mov(Imm32(0), dest);
|
|
||||||
masm.ma_mov(Imm32(1), dest, cond);
|
|
||||||
|
|
||||||
masm.tagValue(JSVAL_TYPE_BOOLEAN, dest, R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
// Failure case - jump to next stub.
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace jit
|
|
||||||
} // namespace js
|
|
|
@ -1,75 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
||||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#include "jit/SharedIC.h"
|
|
||||||
#include "jit/SharedICHelpers.h"
|
|
||||||
|
|
||||||
#ifdef JS_SIMULATOR_ARM64
|
|
||||||
#include "jit/arm64/Assembler-arm64.h"
|
|
||||||
#include "jit/arm64/BaselineCompiler-arm64.h"
|
|
||||||
#include "jit/arm64/vixl/Debugger-vixl.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "jit/MacroAssembler-inl.h"
|
|
||||||
|
|
||||||
using namespace js;
|
|
||||||
using namespace js::jit;
|
|
||||||
|
|
||||||
namespace js {
|
|
||||||
namespace jit {
|
|
||||||
|
|
||||||
// ICCompare_Int32
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
// Guard that R0 is an integer and R1 is an integer.
|
|
||||||
Label failure;
|
|
||||||
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
|
|
||||||
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
|
|
||||||
|
|
||||||
// Compare payload regs of R0 and R1.
|
|
||||||
Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
|
|
||||||
masm.cmp32(R0.valueReg(), R1.valueReg());
|
|
||||||
masm.Cset(ARMRegister(R0.valueReg(), 32), cond);
|
|
||||||
|
|
||||||
// Result is implicitly boxed already.
|
|
||||||
masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.valueReg(), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
// Failure case - jump to next stub.
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_Double::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
Label failure, isNaN;
|
|
||||||
masm.ensureDouble(R0, FloatReg0, &failure);
|
|
||||||
masm.ensureDouble(R1, FloatReg1, &failure);
|
|
||||||
|
|
||||||
Register dest = R0.valueReg();
|
|
||||||
|
|
||||||
Assembler::DoubleCondition doubleCond = JSOpToDoubleCondition(op);
|
|
||||||
Assembler::Condition cond = Assembler::ConditionFromDoubleCondition(doubleCond);
|
|
||||||
|
|
||||||
masm.compareDouble(doubleCond, FloatReg0, FloatReg1);
|
|
||||||
masm.Cset(ARMRegister(dest, 32), cond);
|
|
||||||
|
|
||||||
masm.tagValue(JSVAL_TYPE_BOOLEAN, dest, R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
// Failure case - jump to next stub.
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace jit
|
|
||||||
} // namespace js
|
|
|
@ -1,45 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
||||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#include "jit/BaselineCompiler.h"
|
|
||||||
#include "jit/BaselineIC.h"
|
|
||||||
#include "jit/BaselineJIT.h"
|
|
||||||
#include "jit/Linker.h"
|
|
||||||
#include "jit/SharedICHelpers.h"
|
|
||||||
|
|
||||||
using namespace js;
|
|
||||||
using namespace js::jit;
|
|
||||||
|
|
||||||
namespace js {
|
|
||||||
namespace jit {
|
|
||||||
|
|
||||||
// ICCompare_Int32
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
// Guard that R0 is an integer and R1 is an integer.
|
|
||||||
Label failure;
|
|
||||||
Label conditionTrue;
|
|
||||||
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
|
|
||||||
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
|
|
||||||
|
|
||||||
// Compare payload regs of R0 and R1.
|
|
||||||
Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
|
|
||||||
masm.ma_cmp_set(R0.payloadReg(), R0.payloadReg(), R1.payloadReg(), cond);
|
|
||||||
|
|
||||||
masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.payloadReg(), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
// Failure case - jump to next stub
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace jit
|
|
||||||
} // namespace js
|
|
|
@ -1,47 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
||||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#include "jit/BaselineCompiler.h"
|
|
||||||
#include "jit/BaselineIC.h"
|
|
||||||
#include "jit/BaselineJIT.h"
|
|
||||||
#include "jit/Linker.h"
|
|
||||||
#include "jit/SharedICHelpers.h"
|
|
||||||
|
|
||||||
using namespace js;
|
|
||||||
using namespace js::jit;
|
|
||||||
|
|
||||||
namespace js {
|
|
||||||
namespace jit {
|
|
||||||
|
|
||||||
// ICCompare_Int32
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
// Guard that R0 is an integer and R1 is an integer.
|
|
||||||
Label failure;
|
|
||||||
Label conditionTrue;
|
|
||||||
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
|
|
||||||
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
|
|
||||||
|
|
||||||
// Compare payload regs of R0 and R1.
|
|
||||||
masm.unboxInt32(R0, ExtractTemp0);
|
|
||||||
masm.unboxInt32(R1, ExtractTemp1);
|
|
||||||
Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
|
|
||||||
masm.ma_cmp_set(R0.valueReg(), ExtractTemp0, ExtractTemp1, cond);
|
|
||||||
|
|
||||||
masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.valueReg(), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
// Failure case - jump to next stub
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace jit
|
|
||||||
} // namespace js
|
|
|
@ -42,5 +42,3 @@ BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& iter, Invalidati
|
||||||
MOZ_CRASH();
|
MOZ_CRASH();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ICCompare_Int32::Compiler::generateStubCode(MacroAssembler&) { MOZ_CRASH(); }
|
|
||||||
bool ICCompare_Double::Compiler::generateStubCode(MacroAssembler&) { MOZ_CRASH(); }
|
|
||||||
|
|
|
@ -885,10 +885,6 @@ class AssemblerShared
|
||||||
return embedsNurseryPointers_;
|
return embedsNurseryPointers_;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool canUseInSingleByteInstruction(Register reg) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addCodeLabel(CodeLabel label) {
|
void addCodeLabel(CodeLabel label) {
|
||||||
propagateOOM(codeLabels_.append(label));
|
propagateOOM(codeLabels_.append(label));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
||||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#include "jit/BaselineIC.h"
|
|
||||||
#include "jit/SharedICHelpers.h"
|
|
||||||
#include "jit/MacroAssembler-inl.h"
|
|
||||||
|
|
||||||
using namespace js;
|
|
||||||
using namespace js::jit;
|
|
||||||
|
|
||||||
namespace js {
|
|
||||||
namespace jit {
|
|
||||||
|
|
||||||
// ICCompare_Int32
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
// Guard that R0 is an integer and R1 is an integer.
|
|
||||||
Label failure;
|
|
||||||
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
|
|
||||||
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
|
|
||||||
|
|
||||||
// Directly compare the int32 payload of R0 and R1.
|
|
||||||
ScratchRegisterScope scratch(masm);
|
|
||||||
Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
|
|
||||||
masm.mov(ImmWord(0), scratch);
|
|
||||||
masm.cmp32(R0.valueReg(), R1.valueReg());
|
|
||||||
masm.setCC(cond, scratch);
|
|
||||||
|
|
||||||
// Box the result and return
|
|
||||||
masm.boxValue(JSVAL_TYPE_BOOLEAN, scratch, R0.valueReg());
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
// Failure case - jump to next stub
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace jit
|
|
||||||
} // namespace js
|
|
|
@ -1,44 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
||||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#include "jit/BaselineIC.h"
|
|
||||||
#include "jit/SharedICHelpers.h"
|
|
||||||
|
|
||||||
#include "jit/MacroAssembler-inl.h"
|
|
||||||
|
|
||||||
using namespace js;
|
|
||||||
using namespace js::jit;
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_Double::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
Label failure, notNaN;
|
|
||||||
masm.ensureDouble(R0, FloatReg0, &failure);
|
|
||||||
masm.ensureDouble(R1, FloatReg1, &failure);
|
|
||||||
|
|
||||||
Register dest = R0.scratchReg();
|
|
||||||
|
|
||||||
Assembler::DoubleCondition cond = JSOpToDoubleCondition(op);
|
|
||||||
masm.mov(ImmWord(0), dest);
|
|
||||||
masm.compareDouble(cond, FloatReg0, FloatReg1);
|
|
||||||
masm.setCC(Assembler::ConditionFromDoubleCondition(cond), dest);
|
|
||||||
|
|
||||||
// Check for NaN, if needed.
|
|
||||||
Assembler::NaNCond nanCond = Assembler::NaNCondFromDoubleCondition(cond);
|
|
||||||
if (nanCond != Assembler::NaN_HandledByCond) {
|
|
||||||
masm.j(Assembler::NoParity, ¬NaN);
|
|
||||||
masm.mov(ImmWord(nanCond == Assembler::NaN_IsTrue), dest);
|
|
||||||
masm.bind(¬NaN);
|
|
||||||
}
|
|
||||||
|
|
||||||
masm.tagValue(JSVAL_TYPE_BOOLEAN, dest, R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
// Failure case - jump to next stub
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1060,10 +1060,6 @@ class Assembler : public AssemblerX86Shared
|
||||||
masm.vmovups_rm(src.encoding(), dest.addr);
|
masm.vmovups_rm(src.encoding(), dest.addr);
|
||||||
return CodeOffset(masm.currentOffset());
|
return CodeOffset(masm.currentOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool canUseInSingleByteInstruction(Register reg) {
|
|
||||||
return X86Encoding::HasSubregL(reg.encoding());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get a register in which we plan to put a quantity that will be used as an
|
// Get a register in which we plan to put a quantity that will be used as an
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
||||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#include "jit/BaselineCompiler.h"
|
|
||||||
#include "jit/BaselineIC.h"
|
|
||||||
#include "jit/BaselineJIT.h"
|
|
||||||
#include "jit/Linker.h"
|
|
||||||
#include "jit/SharedICHelpers.h"
|
|
||||||
|
|
||||||
#include "jit/MacroAssembler-inl.h"
|
|
||||||
|
|
||||||
using namespace js;
|
|
||||||
using namespace js::jit;
|
|
||||||
|
|
||||||
namespace js {
|
|
||||||
namespace jit {
|
|
||||||
|
|
||||||
// ICCompare_Int32
|
|
||||||
|
|
||||||
bool
|
|
||||||
ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
// Guard that R0 is an integer and R1 is an integer.
|
|
||||||
Label failure;
|
|
||||||
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
|
|
||||||
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
|
|
||||||
|
|
||||||
// Compare payload regs of R0 and R1.
|
|
||||||
Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
|
|
||||||
masm.cmp32(R0.payloadReg(), R1.payloadReg());
|
|
||||||
masm.setCC(cond, R0.payloadReg());
|
|
||||||
masm.movzbl(R0.payloadReg(), R0.payloadReg());
|
|
||||||
|
|
||||||
// Box the result and return
|
|
||||||
masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.payloadReg(), R0);
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
// Failure case - jump to next stub
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace jit
|
|
||||||
} // namespace js
|
|
|
@ -259,7 +259,7 @@ BEGIN_TEST(testSavedStacks_selfHostedFrames)
|
||||||
result = JS::GetSavedFrameColumn(cx, principals, selfHostedFrame, &column,
|
result = JS::GetSavedFrameColumn(cx, principals, selfHostedFrame, &column,
|
||||||
JS::SavedFrameSelfHosted::Exclude);
|
JS::SavedFrameSelfHosted::Exclude);
|
||||||
CHECK(result == JS::SavedFrameResult::Ok);
|
CHECK(result == JS::SavedFrameResult::Ok);
|
||||||
CHECK_EQUAL(column, 5U);
|
CHECK_EQUAL(column, 9U);
|
||||||
|
|
||||||
// Function display name
|
// Function display name
|
||||||
result = JS::GetSavedFrameFunctionDisplayName(cx, principals, selfHostedFrame, &str,
|
result = JS::GetSavedFrameFunctionDisplayName(cx, principals, selfHostedFrame, &str,
|
||||||
|
|
|
@ -501,7 +501,6 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
|
||||||
'jit/x86-shared/Assembler-x86-shared.cpp',
|
'jit/x86-shared/Assembler-x86-shared.cpp',
|
||||||
'jit/x86-shared/AssemblerBuffer-x86-shared.cpp',
|
'jit/x86-shared/AssemblerBuffer-x86-shared.cpp',
|
||||||
'jit/x86-shared/BaselineCompiler-x86-shared.cpp',
|
'jit/x86-shared/BaselineCompiler-x86-shared.cpp',
|
||||||
'jit/x86-shared/BaselineIC-x86-shared.cpp',
|
|
||||||
'jit/x86-shared/CodeGenerator-x86-shared.cpp',
|
'jit/x86-shared/CodeGenerator-x86-shared.cpp',
|
||||||
'jit/x86-shared/Lowering-x86-shared.cpp',
|
'jit/x86-shared/Lowering-x86-shared.cpp',
|
||||||
'jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp',
|
'jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp',
|
||||||
|
@ -517,7 +516,6 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
|
||||||
'jit/x64/Assembler-x64.cpp',
|
'jit/x64/Assembler-x64.cpp',
|
||||||
'jit/x64/Bailouts-x64.cpp',
|
'jit/x64/Bailouts-x64.cpp',
|
||||||
'jit/x64/BaselineCompiler-x64.cpp',
|
'jit/x64/BaselineCompiler-x64.cpp',
|
||||||
'jit/x64/BaselineIC-x64.cpp',
|
|
||||||
'jit/x64/CodeGenerator-x64.cpp',
|
'jit/x64/CodeGenerator-x64.cpp',
|
||||||
'jit/x64/Lowering-x64.cpp',
|
'jit/x64/Lowering-x64.cpp',
|
||||||
'jit/x64/MacroAssembler-x64.cpp',
|
'jit/x64/MacroAssembler-x64.cpp',
|
||||||
|
@ -529,7 +527,6 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
|
||||||
'jit/x86/Assembler-x86.cpp',
|
'jit/x86/Assembler-x86.cpp',
|
||||||
'jit/x86/Bailouts-x86.cpp',
|
'jit/x86/Bailouts-x86.cpp',
|
||||||
'jit/x86/BaselineCompiler-x86.cpp',
|
'jit/x86/BaselineCompiler-x86.cpp',
|
||||||
'jit/x86/BaselineIC-x86.cpp',
|
|
||||||
'jit/x86/CodeGenerator-x86.cpp',
|
'jit/x86/CodeGenerator-x86.cpp',
|
||||||
'jit/x86/Lowering-x86.cpp',
|
'jit/x86/Lowering-x86.cpp',
|
||||||
'jit/x86/MacroAssembler-x86.cpp',
|
'jit/x86/MacroAssembler-x86.cpp',
|
||||||
|
@ -542,7 +539,6 @@ elif CONFIG['JS_CODEGEN_ARM']:
|
||||||
'jit/arm/Assembler-arm.cpp',
|
'jit/arm/Assembler-arm.cpp',
|
||||||
'jit/arm/Bailouts-arm.cpp',
|
'jit/arm/Bailouts-arm.cpp',
|
||||||
'jit/arm/BaselineCompiler-arm.cpp',
|
'jit/arm/BaselineCompiler-arm.cpp',
|
||||||
'jit/arm/BaselineIC-arm.cpp',
|
|
||||||
'jit/arm/CodeGenerator-arm.cpp',
|
'jit/arm/CodeGenerator-arm.cpp',
|
||||||
'jit/arm/disasm/Constants-arm.cpp',
|
'jit/arm/disasm/Constants-arm.cpp',
|
||||||
'jit/arm/disasm/Disasm-arm.cpp',
|
'jit/arm/disasm/Disasm-arm.cpp',
|
||||||
|
@ -566,7 +562,6 @@ elif CONFIG['JS_CODEGEN_ARM64']:
|
||||||
'jit/arm64/Architecture-arm64.cpp',
|
'jit/arm64/Architecture-arm64.cpp',
|
||||||
'jit/arm64/Assembler-arm64.cpp',
|
'jit/arm64/Assembler-arm64.cpp',
|
||||||
'jit/arm64/Bailouts-arm64.cpp',
|
'jit/arm64/Bailouts-arm64.cpp',
|
||||||
'jit/arm64/BaselineIC-arm64.cpp',
|
|
||||||
'jit/arm64/CodeGenerator-arm64.cpp',
|
'jit/arm64/CodeGenerator-arm64.cpp',
|
||||||
'jit/arm64/Disassembler-arm64.cpp',
|
'jit/arm64/Disassembler-arm64.cpp',
|
||||||
'jit/arm64/Lowering-arm64.cpp',
|
'jit/arm64/Lowering-arm64.cpp',
|
||||||
|
@ -598,7 +593,6 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
|
||||||
'jit/mips-shared/Assembler-mips-shared.cpp',
|
'jit/mips-shared/Assembler-mips-shared.cpp',
|
||||||
'jit/mips-shared/Bailouts-mips-shared.cpp',
|
'jit/mips-shared/Bailouts-mips-shared.cpp',
|
||||||
'jit/mips-shared/BaselineCompiler-mips-shared.cpp',
|
'jit/mips-shared/BaselineCompiler-mips-shared.cpp',
|
||||||
'jit/mips-shared/BaselineIC-mips-shared.cpp',
|
|
||||||
'jit/mips-shared/CodeGenerator-mips-shared.cpp',
|
'jit/mips-shared/CodeGenerator-mips-shared.cpp',
|
||||||
'jit/mips-shared/Lowering-mips-shared.cpp',
|
'jit/mips-shared/Lowering-mips-shared.cpp',
|
||||||
'jit/mips-shared/MacroAssembler-mips-shared.cpp',
|
'jit/mips-shared/MacroAssembler-mips-shared.cpp',
|
||||||
|
@ -611,7 +605,6 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
|
||||||
'jit/mips32/Assembler-mips32.cpp',
|
'jit/mips32/Assembler-mips32.cpp',
|
||||||
'jit/mips32/Bailouts-mips32.cpp',
|
'jit/mips32/Bailouts-mips32.cpp',
|
||||||
'jit/mips32/BaselineCompiler-mips32.cpp',
|
'jit/mips32/BaselineCompiler-mips32.cpp',
|
||||||
'jit/mips32/BaselineIC-mips32.cpp',
|
|
||||||
'jit/mips32/CodeGenerator-mips32.cpp',
|
'jit/mips32/CodeGenerator-mips32.cpp',
|
||||||
'jit/mips32/Lowering-mips32.cpp',
|
'jit/mips32/Lowering-mips32.cpp',
|
||||||
'jit/mips32/MacroAssembler-mips32.cpp',
|
'jit/mips32/MacroAssembler-mips32.cpp',
|
||||||
|
@ -629,7 +622,6 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
|
||||||
'jit/mips64/Assembler-mips64.cpp',
|
'jit/mips64/Assembler-mips64.cpp',
|
||||||
'jit/mips64/Bailouts-mips64.cpp',
|
'jit/mips64/Bailouts-mips64.cpp',
|
||||||
'jit/mips64/BaselineCompiler-mips64.cpp',
|
'jit/mips64/BaselineCompiler-mips64.cpp',
|
||||||
'jit/mips64/BaselineIC-mips64.cpp',
|
|
||||||
'jit/mips64/CodeGenerator-mips64.cpp',
|
'jit/mips64/CodeGenerator-mips64.cpp',
|
||||||
'jit/mips64/Lowering-mips64.cpp',
|
'jit/mips64/Lowering-mips64.cpp',
|
||||||
'jit/mips64/MacroAssembler-mips64.cpp',
|
'jit/mips64/MacroAssembler-mips64.cpp',
|
||||||
|
|
|
@ -4491,7 +4491,7 @@ JSScript::AutoDelazify::dropScript()
|
||||||
script_ = nullptr;
|
script_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
JS::ubi::Node::Size
|
JS::ubi::Base::Size
|
||||||
JS::ubi::Concrete<JSScript>::size(mozilla::MallocSizeOf mallocSizeOf) const
|
JS::ubi::Concrete<JSScript>::size(mozilla::MallocSizeOf mallocSizeOf) const
|
||||||
{
|
{
|
||||||
Size size = Arena::thingSize(get().asTenured().getAllocKind());
|
Size size = Arena::thingSize(get().asTenured().getAllocKind());
|
||||||
|
|
|
@ -21,13 +21,15 @@
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
struct MatchPair
|
struct MatchPair final
|
||||||
{
|
{
|
||||||
int32_t start;
|
int32_t start;
|
||||||
int32_t limit;
|
int32_t limit;
|
||||||
|
|
||||||
|
static constexpr int32_t NoMatch = -1;
|
||||||
|
|
||||||
MatchPair()
|
MatchPair()
|
||||||
: start(-1), limit(-1)
|
: start(NoMatch), limit(NoMatch)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
MatchPair(int32_t start, int32_t limit)
|
MatchPair(int32_t start, int32_t limit)
|
||||||
|
@ -39,8 +41,8 @@ struct MatchPair
|
||||||
|
|
||||||
inline bool check() const {
|
inline bool check() const {
|
||||||
MOZ_ASSERT(limit >= start);
|
MOZ_ASSERT(limit >= start);
|
||||||
MOZ_ASSERT_IF(start < 0, start == -1);
|
MOZ_ASSERT_IF(start < 0, start == NoMatch);
|
||||||
MOZ_ASSERT_IF(limit < 0, limit == -1);
|
MOZ_ASSERT_IF(limit < 0, limit == NoMatch);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1251,9 +1251,9 @@ RegExpRealm::createMatchResultTemplateObject(JSContext* cx)
|
||||||
|
|
||||||
// Make sure that the properties are in the right slots.
|
// Make sure that the properties are in the right slots.
|
||||||
DebugOnly<Shape*> shape = templateObject->lastProperty();
|
DebugOnly<Shape*> shape = templateObject->lastProperty();
|
||||||
MOZ_ASSERT(shape->previous()->slot() == 0 &&
|
MOZ_ASSERT(shape->previous()->slot() == MatchResultObjectIndexSlot &&
|
||||||
shape->previous()->propidRef() == NameToId(cx->names().index));
|
shape->previous()->propidRef() == NameToId(cx->names().index));
|
||||||
MOZ_ASSERT(shape->slot() == 1 &&
|
MOZ_ASSERT(shape->slot() == MatchResultObjectInputSlot &&
|
||||||
shape->propidRef() == NameToId(cx->names().input));
|
shape->propidRef() == NameToId(cx->names().input));
|
||||||
|
|
||||||
// Make sure type information reflects the indexed properties which might
|
// Make sure type information reflects the indexed properties which might
|
||||||
|
|
|
@ -325,6 +325,9 @@ class RegExpRealm
|
||||||
|
|
||||||
void sweep();
|
void sweep();
|
||||||
|
|
||||||
|
static const size_t MatchResultObjectIndexSlot = 0;
|
||||||
|
static const size_t MatchResultObjectInputSlot = 1;
|
||||||
|
|
||||||
/* Get or create template object used to base the result of .exec() on. */
|
/* Get or create template object used to base the result of .exec() on. */
|
||||||
ArrayObject* getOrCreateMatchResultTemplateObject(JSContext* cx) {
|
ArrayObject* getOrCreateMatchResultTemplateObject(JSContext* cx) {
|
||||||
if (matchResultTemplateObject_)
|
if (matchResultTemplateObject_)
|
||||||
|
|
|
@ -416,22 +416,21 @@ static inline ParseNode*
|
||||||
CallCallee(ParseNode* pn)
|
CallCallee(ParseNode* pn)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(pn->isKind(ParseNodeKind::Call));
|
MOZ_ASSERT(pn->isKind(ParseNodeKind::Call));
|
||||||
return ListHead(pn);
|
return BinaryLeft(pn);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned
|
static inline unsigned
|
||||||
CallArgListLength(ParseNode* pn)
|
CallArgListLength(ParseNode* pn)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(pn->isKind(ParseNodeKind::Call));
|
MOZ_ASSERT(pn->isKind(ParseNodeKind::Call));
|
||||||
MOZ_ASSERT(ListLength(pn) >= 1);
|
return ListLength(BinaryRight(pn));
|
||||||
return ListLength(pn) - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline ParseNode*
|
static inline ParseNode*
|
||||||
CallArgList(ParseNode* pn)
|
CallArgList(ParseNode* pn)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(pn->isKind(ParseNodeKind::Call));
|
MOZ_ASSERT(pn->isKind(ParseNodeKind::Call));
|
||||||
return NextNode(ListHead(pn));
|
return ListHead(BinaryRight(pn));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline ParseNode*
|
static inline ParseNode*
|
||||||
|
@ -594,16 +593,16 @@ static ParseNode*
|
||||||
DotBase(ParseNode* pn)
|
DotBase(ParseNode* pn)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
|
MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
|
||||||
MOZ_ASSERT(pn->isArity(PN_NAME));
|
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||||
return pn->expr();
|
return pn->pn_left;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PropertyName*
|
static PropertyName*
|
||||||
DotMember(ParseNode* pn)
|
DotMember(ParseNode* pn)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
|
MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
|
||||||
MOZ_ASSERT(pn->isArity(PN_NAME));
|
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||||
return pn->pn_atom->asPropertyName();
|
return pn->pn_right->pn_atom->asPropertyName();
|
||||||
}
|
}
|
||||||
|
|
||||||
static ParseNode*
|
static ParseNode*
|
||||||
|
@ -2800,9 +2799,11 @@ IsArrayViewCtorName(ModuleValidator& m, PropertyName* name, Scalar::Type* type)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* ctorExpr, PropertyName* bufferName)
|
CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* newExpr, PropertyName* bufferName)
|
||||||
{
|
{
|
||||||
ParseNode* bufArg = NextNode(ctorExpr);
|
ParseNode* ctorExpr = BinaryLeft(newExpr);
|
||||||
|
ParseNode* ctorArgs = BinaryRight(newExpr);
|
||||||
|
ParseNode* bufArg = ListHead(ctorArgs);
|
||||||
if (!bufArg || NextNode(bufArg) != nullptr)
|
if (!bufArg || NextNode(bufArg) != nullptr)
|
||||||
return m.fail(ctorExpr, "array view constructor takes exactly one argument");
|
return m.fail(ctorExpr, "array view constructor takes exactly one argument");
|
||||||
|
|
||||||
|
@ -2823,7 +2824,7 @@ CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr)
|
||||||
if (!bufferName)
|
if (!bufferName)
|
||||||
return m.fail(newExpr, "cannot create array view without an asm.js heap parameter");
|
return m.fail(newExpr, "cannot create array view without an asm.js heap parameter");
|
||||||
|
|
||||||
ParseNode* ctorExpr = ListHead(newExpr);
|
ParseNode* ctorExpr = BinaryLeft(newExpr);
|
||||||
|
|
||||||
PropertyName* field;
|
PropertyName* field;
|
||||||
Scalar::Type type;
|
Scalar::Type type;
|
||||||
|
@ -2852,7 +2853,7 @@ CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr)
|
||||||
type = global->viewType();
|
type = global->viewType();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CheckNewArrayViewArgs(m, ctorExpr, bufferName))
|
if (!CheckNewArrayViewArgs(m, newExpr, bufferName))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return m.addArrayView(varName, type, field);
|
return m.addArrayView(varName, type, field);
|
||||||
|
|
|
@ -1028,8 +1028,12 @@ ReadScriptOrFunction(nsIObjectInputStream* stream, JSContext* cx,
|
||||||
// We don't serialize mutedError-ness of scripts, which is fine as long as
|
// We don't serialize mutedError-ness of scripts, which is fine as long as
|
||||||
// we only serialize system and XUL-y things. We can detect this by checking
|
// we only serialize system and XUL-y things. We can detect this by checking
|
||||||
// where the caller wants us to deserialize.
|
// where the caller wants us to deserialize.
|
||||||
|
//
|
||||||
|
// CompilationScope() could theoretically GC, so get that out of the way
|
||||||
|
// before comparing to the cx global.
|
||||||
|
JSObject* loaderGlobal = xpc::CompilationScope();
|
||||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsSystemCaller(cx) ||
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsSystemCaller(cx) ||
|
||||||
CurrentGlobalOrNull(cx) == xpc::CompilationScope());
|
CurrentGlobalOrNull(cx) == loaderGlobal);
|
||||||
|
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
rv = stream->Read32(&size);
|
rv = stream->Read32(&size);
|
||||||
|
|
|
@ -39,11 +39,11 @@ add_task(async function() {
|
||||||
|
|
||||||
equal(frame.source, "thing.js", "Frame source");
|
equal(frame.source, "thing.js", "Frame source");
|
||||||
equal(frame.line, 5, "Frame line");
|
equal(frame.line, 5, "Frame line");
|
||||||
equal(frame.column, 14, "Frame column");
|
equal(frame.column, 18, "Frame column");
|
||||||
equal(frame.functionDisplayName, "it", "Frame function name");
|
equal(frame.functionDisplayName, "it", "Frame function name");
|
||||||
equal(frame.parent, null, "Frame parent");
|
equal(frame.parent, null, "Frame parent");
|
||||||
|
|
||||||
equal(String(frame), "it@thing.js:5:14\n", "Stringified frame");
|
equal(String(frame), "it@thing.js:5:18\n", "Stringified frame");
|
||||||
|
|
||||||
|
|
||||||
// reportError
|
// reportError
|
||||||
|
@ -55,7 +55,7 @@ add_task(async function() {
|
||||||
let [msg] = messages.filter(m => m.message.includes("Meh"));
|
let [msg] = messages.filter(m => m.message.includes("Meh"));
|
||||||
|
|
||||||
equal(msg.stack, frame, "reportError stack frame");
|
equal(msg.stack, frame, "reportError stack frame");
|
||||||
equal(msg.message, '[JavaScript Error: "Meh" {file: "thing.js" line: 5}]\nit@thing.js:5:14\n');
|
equal(msg.message, '[JavaScript Error: "Meh" {file: "thing.js" line: 5}]\nit@thing.js:5:18\n');
|
||||||
|
|
||||||
Assert.throws(() => { Cu.reportError("Meh", {}); },
|
Assert.throws(() => { Cu.reportError("Meh", {}); },
|
||||||
err => err.result == Cr.NS_ERROR_INVALID_ARG,
|
err => err.result == Cr.NS_ERROR_INVALID_ARG,
|
||||||
|
@ -82,5 +82,5 @@ add_task(async function() {
|
||||||
equal(error.message, "Meh", "Error message");
|
equal(error.message, "Meh", "Error message");
|
||||||
equal(error.fileName, "thing.js", "Error filename");
|
equal(error.fileName, "thing.js", "Error filename");
|
||||||
equal(error.lineNumber, 5, "Error line");
|
equal(error.lineNumber, 5, "Error line");
|
||||||
equal(error.columnNumber, 14, "Error column");
|
equal(error.columnNumber, 18, "Error column");
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
diff --git a/media/libvpx/libvpx/vp8/common/postproc.c b/media/libvpx/libvpx/vp8/common/postproc.c
|
||||||
|
--- a/media/libvpx/libvpx/vp8/common/postproc.c
|
||||||
|
+++ b/media/libvpx/libvpx/vp8/common/postproc.c
|
||||||
|
@@ -60,17 +60,17 @@ static void vp8_de_mblock(YV12_BUFFER_CO
|
||||||
|
}
|
||||||
|
|
||||||
|
void vp8_deblock(VP8_COMMON *cm, YV12_BUFFER_CONFIG *source,
|
||||||
|
YV12_BUFFER_CONFIG *post, int q, int low_var_thresh,
|
||||||
|
int flag) {
|
||||||
|
double level = 6.0e-05 * q * q * q - .0067 * q * q + .306 * q + .0065;
|
||||||
|
int ppl = (int)(level + .5);
|
||||||
|
|
||||||
|
- const MODE_INFO *mode_info_context = cm->show_frame_mi;
|
||||||
|
+ const MODE_INFO *mode_info_context = cm->mi;
|
||||||
|
int mbr, mbc;
|
||||||
|
|
||||||
|
/* The pixel thresholds are adjusted according to if or not the macroblock
|
||||||
|
* is a skipped block. */
|
||||||
|
unsigned char *ylimits = cm->pp_limits_buffer;
|
||||||
|
unsigned char *uvlimits = cm->pp_limits_buffer + 16 * cm->mb_cols;
|
||||||
|
(void)low_var_thresh;
|
||||||
|
(void)flag;
|
|
@ -65,7 +65,7 @@ void vp8_deblock(VP8_COMMON *cm, YV12_BUFFER_CONFIG *source,
|
||||||
double level = 6.0e-05 * q * q * q - .0067 * q * q + .306 * q + .0065;
|
double level = 6.0e-05 * q * q * q - .0067 * q * q + .306 * q + .0065;
|
||||||
int ppl = (int)(level + .5);
|
int ppl = (int)(level + .5);
|
||||||
|
|
||||||
const MODE_INFO *mode_info_context = cm->show_frame_mi;
|
const MODE_INFO *mode_info_context = cm->mi;
|
||||||
int mbr, mbc;
|
int mbr, mbc;
|
||||||
|
|
||||||
/* The pixel thresholds are adjusted according to if or not the macroblock
|
/* The pixel thresholds are adjusted according to if or not the macroblock
|
||||||
|
|
|
@ -41,6 +41,8 @@ def apply_patches():
|
||||||
# Avoid c/asm name collision for loopfilter_sse2
|
# Avoid c/asm name collision for loopfilter_sse2
|
||||||
os.system("patch -p1 < rename_duplicate_files.patch")
|
os.system("patch -p1 < rename_duplicate_files.patch")
|
||||||
os.system("mv libvpx/vpx_dsp/x86/loopfilter_sse2.c libvpx/vpx_dsp/x86/loopfilter_intrin_sse2.c")
|
os.system("mv libvpx/vpx_dsp/x86/loopfilter_sse2.c libvpx/vpx_dsp/x86/loopfilter_intrin_sse2.c")
|
||||||
|
# Cherrypick fix from upstream
|
||||||
|
os.system("patch -p3 < bug1480092.patch")
|
||||||
|
|
||||||
|
|
||||||
def update_readme(commit):
|
def update_readme(commit):
|
||||||
|
|
|
@ -145,36 +145,20 @@ class NavigationDelegateTest : BaseSessionTest() {
|
||||||
containsString(mobileSubStr))
|
containsString(mobileSubStr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun telemetryTest(process: String) {
|
@Test fun telemetrySnapshots() {
|
||||||
sessionRule.session.loadTestPath(HELLO_HTML_PATH)
|
sessionRule.session.loadTestPath(HELLO_HTML_PATH)
|
||||||
sessionRule.waitForPageStop()
|
sessionRule.waitForPageStop()
|
||||||
|
|
||||||
val telemetry = sessionRule.runtime.telemetry
|
val telemetry = sessionRule.runtime.telemetry
|
||||||
val result = sessionRule.waitForResult(telemetry.getSnapshots(true))
|
val result = sessionRule.waitForResult(telemetry.getSnapshots(false))
|
||||||
|
|
||||||
val snapshots = result?.get(process) as GeckoBundle
|
|
||||||
|
|
||||||
assertThat("Snapshots should not be null",
|
assertThat("Snapshots should not be null",
|
||||||
snapshots, notNullValue())
|
result?.get("parent"), notNullValue())
|
||||||
|
|
||||||
assertThat("Histograms should not be null",
|
if (sessionRule.env.isMultiprocess) {
|
||||||
snapshots.get("histograms"), notNullValue())
|
assertThat("Snapshots should not be null",
|
||||||
assertThat("Keyed histograms should not be null",
|
result?.get("content"), notNullValue())
|
||||||
snapshots.get("keyedHistograms"), notNullValue())
|
|
||||||
assertThat("Scalars should not be null",
|
|
||||||
snapshots.get("scalars"), notNullValue())
|
|
||||||
assertThat("Keyed scalars should not be null",
|
|
||||||
snapshots.get("keyedScalars"), notNullValue())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun telemetryParent() {
|
|
||||||
telemetryTest("parent")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test fun telemetryContent() {
|
|
||||||
assumeThat(sessionRule.env.isMultiprocess, equalTo(true));
|
|
||||||
telemetryTest("content")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun load() {
|
@Test fun load() {
|
||||||
|
|
|
@ -102,7 +102,7 @@ function run_analysis () {
|
||||||
|
|
||||||
(
|
(
|
||||||
cd "$analysis_dir"
|
cd "$analysis_dir"
|
||||||
$PYTHON "$ANALYSIS_SRCDIR/analyze.py" --buildcommand="$GECKO_DIR/taskcluster/scripts/builder/hazard-${build_type}.sh"
|
$PYTHON "$ANALYSIS_SRCDIR/analyze.py" -v --buildcommand="$GECKO_DIR/taskcluster/scripts/builder/hazard-${build_type}.sh"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
mkdir -p "$ANALYZED_OBJDIR"
|
mkdir -p "$ANALYZED_OBJDIR"
|
||||||
cd "$ANALYZED_OBJDIR"
|
cd "$ANALYZED_OBJDIR"
|
||||||
$SOURCE/js/src/configure --enable-debug --enable-optimize --enable-ctypes --enable-nspr-build
|
$SOURCE/js/src/configure --enable-debug --enable-optimize --enable-ctypes --enable-nspr-build
|
||||||
make -j12 -s
|
make -j8 -s
|
||||||
|
|
|
@ -140,7 +140,8 @@ class ADBCommand(object):
|
||||||
adb_port=None,
|
adb_port=None,
|
||||||
logger_name='adb',
|
logger_name='adb',
|
||||||
timeout=300,
|
timeout=300,
|
||||||
verbose=False):
|
verbose=False,
|
||||||
|
require_root=True):
|
||||||
"""Initializes the ADBCommand object.
|
"""Initializes the ADBCommand object.
|
||||||
|
|
||||||
:param str adb: path to adb executable. Defaults to 'adb'.
|
:param str adb: path to adb executable. Defaults to 'adb'.
|
||||||
|
@ -149,6 +150,8 @@ class ADBCommand(object):
|
||||||
:param adb_port: port of the adb server.
|
:param adb_port: port of the adb server.
|
||||||
:type adb_port: integer or None
|
:type adb_port: integer or None
|
||||||
:param str logger_name: logging logger name. Defaults to 'adb'.
|
:param str logger_name: logging logger name. Defaults to 'adb'.
|
||||||
|
:param bool verbose: provide verbose output
|
||||||
|
:param bool require_root: check that we have root permissions on device
|
||||||
|
|
||||||
:raises: * ADBError
|
:raises: * ADBError
|
||||||
* ADBTimeoutError
|
* ADBTimeoutError
|
||||||
|
@ -158,6 +161,7 @@ class ADBCommand(object):
|
||||||
|
|
||||||
self._logger = self._get_logger(logger_name)
|
self._logger = self._get_logger(logger_name)
|
||||||
self._verbose = verbose
|
self._verbose = verbose
|
||||||
|
self._require_root = require_root
|
||||||
self._adb_path = adb
|
self._adb_path = adb
|
||||||
self._adb_host = adb_host
|
self._adb_host = adb_host
|
||||||
self._adb_port = adb_port
|
self._adb_port = adb_port
|
||||||
|
@ -818,15 +822,15 @@ class ADBDevice(ADBCommand):
|
||||||
|
|
||||||
def _try_test_root(self, test_root):
|
def _try_test_root(self, test_root):
|
||||||
base_path, sub_path = posixpath.split(test_root)
|
base_path, sub_path = posixpath.split(test_root)
|
||||||
if not self.is_dir(base_path, root=True):
|
if not self.is_dir(base_path, root=self._require_root):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dummy_dir = posixpath.join(test_root, 'dummy')
|
dummy_dir = posixpath.join(test_root, 'dummy')
|
||||||
if self.is_dir(dummy_dir, root=True):
|
if self.is_dir(dummy_dir, root=self._require_root):
|
||||||
self.rm(dummy_dir, recursive=True, root=True)
|
self.rm(dummy_dir, recursive=True, root=self._require_root)
|
||||||
self.mkdir(dummy_dir, parents=True, root=True)
|
self.mkdir(dummy_dir, parents=True, root=self._require_root)
|
||||||
self.chmod(test_root, recursive=True, root=True)
|
self.chmod(test_root, recursive=True, root=self._require_root)
|
||||||
except ADBError:
|
except ADBError:
|
||||||
self._logger.debug("%s is not writable" % test_root)
|
self._logger.debug("%s is not writable" % test_root)
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -8,7 +8,7 @@ from __future__ import absolute_import
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
PACKAGE_NAME = 'mozdevice'
|
PACKAGE_NAME = 'mozdevice'
|
||||||
PACKAGE_VERSION = '1.0.0'
|
PACKAGE_VERSION = '1.0.1'
|
||||||
|
|
||||||
deps = ['mozfile >= 1.0',
|
deps = ['mozfile >= 1.0',
|
||||||
'mozlog >= 3.0',
|
'mozlog >= 3.0',
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
.then(t.step_func(e => {
|
.then(t.step_func(e => {
|
||||||
assert_equals(e.blockedURI, "inline");
|
assert_equals(e.blockedURI, "inline");
|
||||||
assert_equals(e.lineNumber, 131);
|
assert_equals(e.lineNumber, 131);
|
||||||
assert_equals(e.columnNumber, 4);
|
assert_in_array(e.columnNumber, [4, 59]);
|
||||||
assert_equals(e.target, document, "Elements created in this document, but pushed into a same-origin frame trigger on that frame's document, not on this frame's document.");
|
assert_equals(e.target, document, "Elements created in this document, but pushed into a same-origin frame trigger on that frame's document, not on this frame's document.");
|
||||||
return watcher.wait_for('securitypolicyviolation');
|
return watcher.wait_for('securitypolicyviolation');
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
// See also browser/base/content/test/newtab/.
|
// See also browser/base/content/test/newtab/.
|
||||||
|
|
||||||
|
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||||
|
|
||||||
// A small 1x1 test png
|
// A small 1x1 test png
|
||||||
const image1x1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==";
|
const image1x1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==";
|
||||||
|
|
||||||
|
@ -679,7 +681,8 @@ add_task(async function getTopFrecentSites() {
|
||||||
Assert.equal(links[0].url, testURI, "added visit corresponds to added url");
|
Assert.equal(links[0].url, testURI, "added visit corresponds to added url");
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(async function getTopFrecentSites_improveSearch() {
|
add_task({ skip_if: () => AppConstants.MOZ_APP_NAME == "thunderbird" },
|
||||||
|
async function getTopFrecentSites_improveSearch() {
|
||||||
await setUpActivityStreamTest();
|
await setUpActivityStreamTest();
|
||||||
const SEARCH_SHORTCUTS_EXPERIMENT_PREF = "browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts";
|
const SEARCH_SHORTCUTS_EXPERIMENT_PREF = "browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts";
|
||||||
Services.prefs.setBoolPref(SEARCH_SHORTCUTS_EXPERIMENT_PREF, true);
|
Services.prefs.setBoolPref(SEARCH_SHORTCUTS_EXPERIMENT_PREF, true);
|
||||||
|
|
|
@ -405,7 +405,9 @@ namespace recordreplay {
|
||||||
MACRO(SLDisplayCopyColorSpace) \
|
MACRO(SLDisplayCopyColorSpace) \
|
||||||
MACRO(SLDisplayIOServicePort) \
|
MACRO(SLDisplayIOServicePort) \
|
||||||
MACRO(SLEventSourceCounterForEventType) \
|
MACRO(SLEventSourceCounterForEventType) \
|
||||||
MACRO(SLMainDisplayID)
|
MACRO(SLMainDisplayID) \
|
||||||
|
MACRO(SLSSetDenyWindowServerConnections) \
|
||||||
|
MACRO(SLSShutdownServerConnections)
|
||||||
|
|
||||||
#define MAKE_CALL_EVENT(aName) CallEvent_ ##aName ,
|
#define MAKE_CALL_EVENT(aName) CallEvent_ ##aName ,
|
||||||
|
|
||||||
|
@ -2679,6 +2681,8 @@ RRFunction2(SLEventSourceCounterForEventType)
|
||||||
RRFunction1(SLDisplayCopyColorSpace)
|
RRFunction1(SLDisplayCopyColorSpace)
|
||||||
RRFunction1(SLDisplayIOServicePort)
|
RRFunction1(SLDisplayIOServicePort)
|
||||||
RRFunction0(SLMainDisplayID)
|
RRFunction0(SLMainDisplayID)
|
||||||
|
RRFunction1(SLSSetDenyWindowServerConnections)
|
||||||
|
RRFunctionVoid0(SLSShutdownServerConnections)
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Redirection generation
|
// Redirection generation
|
||||||
|
|
|
@ -16,10 +16,13 @@
|
||||||
#include "nsIObserverService.h"
|
#include "nsIObserverService.h"
|
||||||
#include "nsIURI.h"
|
#include "nsIURI.h"
|
||||||
#include "nsCRT.h"
|
#include "nsCRT.h"
|
||||||
|
#include "mozilla/XREAppData.h"
|
||||||
|
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <gdk/gdk.h>
|
#include <gdk/gdk.h>
|
||||||
|
|
||||||
|
extern const mozilla::StaticXREAppData* gAppData;
|
||||||
|
|
||||||
static bool gHasActions = false;
|
static bool gHasActions = false;
|
||||||
static bool gHasCaps = false;
|
static bool gHasCaps = false;
|
||||||
|
|
||||||
|
@ -33,6 +36,7 @@ nsAlertsIconListener::notify_notification_show_t nsAlertsIconListener::notify_no
|
||||||
nsAlertsIconListener::notify_notification_set_icon_from_pixbuf_t nsAlertsIconListener::notify_notification_set_icon_from_pixbuf = nullptr;
|
nsAlertsIconListener::notify_notification_set_icon_from_pixbuf_t nsAlertsIconListener::notify_notification_set_icon_from_pixbuf = nullptr;
|
||||||
nsAlertsIconListener::notify_notification_add_action_t nsAlertsIconListener::notify_notification_add_action = nullptr;
|
nsAlertsIconListener::notify_notification_add_action_t nsAlertsIconListener::notify_notification_add_action = nullptr;
|
||||||
nsAlertsIconListener::notify_notification_close_t nsAlertsIconListener::notify_notification_close = nullptr;
|
nsAlertsIconListener::notify_notification_close_t nsAlertsIconListener::notify_notification_close = nullptr;
|
||||||
|
nsAlertsIconListener::notify_notification_set_hint_t nsAlertsIconListener::notify_notification_set_hint = nullptr;
|
||||||
|
|
||||||
static void notify_action_cb(NotifyNotification *notification,
|
static void notify_action_cb(NotifyNotification *notification,
|
||||||
gchar *action, gpointer user_data)
|
gchar *action, gpointer user_data)
|
||||||
|
@ -111,6 +115,7 @@ nsAlertsIconListener::nsAlertsIconListener(nsSystemAlertsService* aBackend,
|
||||||
notify_notification_set_icon_from_pixbuf = (notify_notification_set_icon_from_pixbuf_t)dlsym(libNotifyHandle, "notify_notification_set_icon_from_pixbuf");
|
notify_notification_set_icon_from_pixbuf = (notify_notification_set_icon_from_pixbuf_t)dlsym(libNotifyHandle, "notify_notification_set_icon_from_pixbuf");
|
||||||
notify_notification_add_action = (notify_notification_add_action_t)dlsym(libNotifyHandle, "notify_notification_add_action");
|
notify_notification_add_action = (notify_notification_add_action_t)dlsym(libNotifyHandle, "notify_notification_add_action");
|
||||||
notify_notification_close = (notify_notification_close_t)dlsym(libNotifyHandle, "notify_notification_close");
|
notify_notification_close = (notify_notification_close_t)dlsym(libNotifyHandle, "notify_notification_close");
|
||||||
|
notify_notification_set_hint = (notify_notification_set_hint_t)dlsym(libNotifyHandle, "notify_notification_set_hint");
|
||||||
if (!notify_is_initted || !notify_init || !notify_get_server_caps || !notify_notification_new || !notify_notification_show || !notify_notification_set_icon_from_pixbuf || !notify_notification_add_action || !notify_notification_close) {
|
if (!notify_is_initted || !notify_init || !notify_get_server_caps || !notify_notification_new || !notify_notification_show || !notify_notification_set_icon_from_pixbuf || !notify_notification_add_action || !notify_notification_close) {
|
||||||
dlclose(libNotifyHandle);
|
dlclose(libNotifyHandle);
|
||||||
libNotifyHandle = nullptr;
|
libNotifyHandle = nullptr;
|
||||||
|
@ -175,6 +180,20 @@ nsAlertsIconListener::ShowAlert(GdkPixbuf* aPixbuf)
|
||||||
notify_action_cb, this, nullptr);
|
notify_action_cb, this, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (notify_notification_set_hint) {
|
||||||
|
// If MOZ_DESKTOP_FILE_NAME variable is set, use it as the application id,
|
||||||
|
// otherwise use gAppData->name
|
||||||
|
if (getenv("MOZ_DESKTOP_FILE_NAME")) {
|
||||||
|
// Send the desktop name to identify the application
|
||||||
|
// The desktop-entry is the part before the .desktop
|
||||||
|
notify_notification_set_hint(mNotification, "desktop-entry",
|
||||||
|
g_variant_new("s", getenv("MOZ_DESKTOP_FILE_NAME")));
|
||||||
|
} else {
|
||||||
|
notify_notification_set_hint(mNotification, "desktop-entry",
|
||||||
|
g_variant_new("s", gAppData->remotingName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fedora 10 calls NotifyNotification "closed" signal handlers with a
|
// Fedora 10 calls NotifyNotification "closed" signal handlers with a
|
||||||
// different signature, so a marshaller is used instead of a C callback to
|
// different signature, so a marshaller is used instead of a C callback to
|
||||||
// get the user_data (this) in a parseable format. |closure| is created
|
// get the user_data (this) in a parseable format. |closure| is created
|
||||||
|
|
|
@ -57,6 +57,7 @@ protected:
|
||||||
typedef void (*notify_notification_set_icon_from_pixbuf_t)(void*, GdkPixbuf*);
|
typedef void (*notify_notification_set_icon_from_pixbuf_t)(void*, GdkPixbuf*);
|
||||||
typedef void (*notify_notification_add_action_t)(void*, const char*, const char*, NotifyActionCallback, gpointer, GFreeFunc);
|
typedef void (*notify_notification_add_action_t)(void*, const char*, const char*, NotifyActionCallback, gpointer, GFreeFunc);
|
||||||
typedef bool (*notify_notification_close_t)(void*, GError**);
|
typedef bool (*notify_notification_close_t)(void*, GError**);
|
||||||
|
typedef void (*notify_notification_set_hint_t)(NotifyNotification*, const char*, GVariant*);
|
||||||
|
|
||||||
nsCOMPtr<nsICancelable> mIconRequest;
|
nsCOMPtr<nsICancelable> mIconRequest;
|
||||||
nsCString mAlertTitle;
|
nsCString mAlertTitle;
|
||||||
|
@ -80,6 +81,7 @@ protected:
|
||||||
static notify_notification_set_icon_from_pixbuf_t notify_notification_set_icon_from_pixbuf;
|
static notify_notification_set_icon_from_pixbuf_t notify_notification_set_icon_from_pixbuf;
|
||||||
static notify_notification_add_action_t notify_notification_add_action;
|
static notify_notification_add_action_t notify_notification_add_action;
|
||||||
static notify_notification_close_t notify_notification_close;
|
static notify_notification_close_t notify_notification_close;
|
||||||
|
static notify_notification_set_hint_t notify_notification_set_hint;
|
||||||
NotifyNotification* mNotification;
|
NotifyNotification* mNotification;
|
||||||
gulong mClosureHandler;
|
gulong mClosureHandler;
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче