diff --git a/browser/extensions/pdfjs/README.mozilla b/browser/extensions/pdfjs/README.mozilla index 66de0ce7a641..454a0354808d 100644 --- a/browser/extensions/pdfjs/README.mozilla +++ b/browser/extensions/pdfjs/README.mozilla @@ -1,5 +1,5 @@ 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 diff --git a/browser/extensions/pdfjs/content/build/pdf.js b/browser/extensions/pdfjs/content/build/pdf.js index 27332e281aa7..a30c21519274 100644 --- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -123,8 +123,8 @@ return /******/ (function(modules) { // webpackBootstrap "use strict"; -var pdfjsVersion = '2.0.750'; -var pdfjsBuild = 'c8ee6331'; +var pdfjsVersion = '2.0.760'; +var pdfjsBuild = '1268aea2'; var pdfjsSharedUtil = __w_pdfjs_require__(1); var pdfjsDisplayAPI = __w_pdfjs_require__(7); var pdfjsDisplayTextLayer = __w_pdfjs_require__(19); @@ -4223,7 +4223,7 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) { } return worker.messageHandler.sendWithPromise('GetDocRequest', { docId, - apiVersion: '2.0.750', + apiVersion: '2.0.760', source: { data: source.data, url: source.url, @@ -5314,6 +5314,9 @@ var WorkerTransport = function WorkerTransportClosure() { return this.messageHandler.sendWithPromise('GetDestinations', null); }, getDestination: function WorkerTransport_getDestination(id) { + if (typeof id !== 'string') { + return Promise.reject(new Error('Invalid destination request.')); + } return this.messageHandler.sendWithPromise('GetDestination', { id }); }, getPageLabels: function WorkerTransport_getPageLabels() { @@ -5563,8 +5566,8 @@ var InternalRenderTask = function InternalRenderTaskClosure() { }(); var version, build; { - exports.version = version = '2.0.750'; - exports.build = build = 'c8ee6331'; + exports.version = version = '2.0.760'; + exports.build = build = '1268aea2'; } exports.getDocument = getDocument; exports.LoopbackPort = LoopbackPort; diff --git a/browser/extensions/pdfjs/content/build/pdf.worker.js b/browser/extensions/pdfjs/content/build/pdf.worker.js index 593584c5b68a..dd05147dc01c 100644 --- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -123,8 +123,8 @@ return /******/ (function(modules) { // webpackBootstrap "use strict"; -var pdfjsVersion = '2.0.750'; -var pdfjsBuild = 'c8ee6331'; +var pdfjsVersion = '2.0.760'; +var pdfjsBuild = '1268aea2'; var pdfjsCoreWorker = __w_pdfjs_require__(1); exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler; @@ -327,7 +327,7 @@ var WorkerMessageHandler = { var cancelXHRs = null; var WorkerTasks = []; let apiVersion = docParams.apiVersion; - let workerVersion = '2.0.750'; + let workerVersion = '2.0.760'; if (apiVersion !== 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) { return pdfManager.pdfDocument.xref.stats; }); - handler.on('GetAnnotations', function wphSetupGetAnnotations(data) { - return pdfManager.getPage(data.pageIndex).then(function (page) { - return pdfManager.ensure(page, 'getAnnotationsData', [data.intent]); + handler.on('GetAnnotations', function ({ pageIndex, intent }) { + return pdfManager.getPage(pageIndex).then(function (page) { + return page.getAnnotationsData(intent); }); }); handler.on('RenderPageRequest', function wphSetupRenderPage(data) { @@ -5393,8 +5393,7 @@ var Page = function PageClosure() { return opList; }); }); - var annotationsPromise = this.pdfManager.ensure(this, 'annotations'); - return Promise.all([pageListPromise, annotationsPromise]).then(function ([pageOpList, annotations]) { + return Promise.all([pageListPromise, this._parsedAnnotations]).then(function ([pageOpList, annotations]) { if (annotations.length === 0) { pageOpList.flush(true); return pageOpList; @@ -5444,27 +5443,37 @@ var Page = function PageClosure() { }); }); }, - getAnnotationsData: function Page_getAnnotationsData(intent) { - var annotations = this.annotations; - var annotationsData = []; - for (var i = 0, n = annotations.length; i < n; ++i) { - if (!intent || isAnnotationRenderable(annotations[i], intent)) { - annotationsData.push(annotations[i].data); + getAnnotationsData(intent) { + return this._parsedAnnotations.then(function (annotations) { + let annotationsData = []; + for (let i = 0, ii = annotations.length; i < ii; i++) { + if (!intent || isAnnotationRenderable(annotations[i], intent)) { + annotationsData.push(annotations[i].data); + } } - } - return annotationsData; + return annotationsData; + }); }, get annotations() { - var annotations = []; - var annotationRefs = this._getInheritableProperty('Annots') || []; - for (var i = 0, n = annotationRefs.length; i < n; ++i) { - var annotationRef = annotationRefs[i]; - var annotation = _annotation.AnnotationFactory.create(this.xref, annotationRef, this.pdfManager, this.idFactory); - if (annotation) { - annotations.push(annotation); + return (0, _util.shadow)(this, 'annotations', this._getInheritableProperty('Annots') || []); + }, + get _parsedAnnotations() { + const parsedAnnotations = this.pdfManager.ensure(this, 'annotations').then(() => { + const annotationRefs = this.annotations; + const annotationPromises = []; + for (let i = 0, ii = annotationRefs.length; i < ii; i++) { + annotationPromises.push(_annotation.AnnotationFactory.create(this.xref, annotationRefs[i], this.pdfManager, this.idFactory)); } - } - return (0, _util.shadow)(this, 'annotations', annotations); + return Promise.all(annotationPromises).then(function (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; @@ -5759,6 +5768,9 @@ var _crypto = __w_pdfjs_require__(24); var _colorspace = __w_pdfjs_require__(25); +function fetchDestination(dest) { + return (0, _primitives.isDict)(dest) ? dest.get('D') : dest; +} var Catalog = function CatalogClosure() { function Catalog(pdfManager, xref) { this.pdfManager = pdfManager; @@ -5898,62 +5910,36 @@ var Catalog = function CatalogClosure() { return (0, _util.shadow)(this, 'numPages', obj); }, get destinations() { - function fetchDestination(dest) { - return (0, _primitives.isDict)(dest) ? dest.get('D') : dest; - } - var xref = this.xref; - var dests = {}, - 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) { + const obj = this._readDests(), + dests = Object.create(null); + if (obj instanceof NameTree) { + const names = obj.getAll(); + for (let name in names) { 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); }, - getDestination: function Catalog_getDestination(destinationId) { - function fetchDestination(dest) { - return (0, _primitives.isDict)(dest) ? dest.get('D') : dest; + getDestination(destinationId) { + const obj = this._readDests(); + if (obj instanceof NameTree || obj instanceof _primitives.Dict) { + return fetchDestination(obj.get(destinationId) || null); } - var xref = this.xref; - var dest = null, - nameTreeRef, - nameDictionaryRef; - var obj = this.catDict.get('Names'); + return null; + }, + _readDests() { + const obj = this.catDict.get('Names'); if (obj && obj.has('Dests')) { - nameTreeRef = obj.getRaw('Dests'); + return new NameTree(obj.getRaw('Dests'), this.xref); } 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() { var obj = null; @@ -17710,6 +17696,9 @@ var _stream = __w_pdfjs_require__(14); class AnnotationFactory { 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); if (!(0, _primitives.isDict)(dict)) { return; @@ -22812,17 +22801,15 @@ var Font = function FontClosure() { } else if (isType1File(file)) { if (composite) { fileType = 'CIDFontType0'; - } else if (type === 'MMType1') { - fileType = 'MMType1'; } else { - fileType = 'Type1'; + fileType = type === 'MMType1' ? 'MMType1' : 'Type1'; } } else if (isCFFFile(file)) { if (composite) { fileType = 'CIDFontType0'; fileSubtype = 'CIDFontType0C'; } else { - fileType = 'Type1'; + fileType = type === 'MMType1' ? 'MMType1' : 'Type1'; fileSubtype = 'Type1C'; } } else { diff --git a/browser/extensions/pdfjs/moz.yaml b/browser/extensions/pdfjs/moz.yaml index 65a6c3fe0aba..12111c3cd382 100644 --- a/browser/extensions/pdfjs/moz.yaml +++ b/browser/extensions/pdfjs/moz.yaml @@ -20,7 +20,7 @@ origin: # Human-readable identifier for this version/release # 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 # https://spdx.org/licenses/ diff --git a/build/moz.configure/checks.configure b/build/moz.configure/checks.configure index dc140a42058b..3fd3e146c50d 100644 --- a/build/moz.configure/checks.configure +++ b/build/moz.configure/checks.configure @@ -90,6 +90,10 @@ def checking(what, callback=None): # 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 # 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: # check_prog('PROG', ('a', 'b')) @@ -99,7 +103,7 @@ def checking(what, callback=None): @template @imports(_from='mozbuild.shellutil', _import='quote') 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: # Wrap input with type checking and normalization. @depends(input, when=when) @@ -131,11 +135,19 @@ def check_prog(var, progs, what=None, input=None, allow_missing=False, if not isinstance(progs, (tuple, list)): configure_error('progs must resolve to a list or tuple!') - for prog in value or progs: - log.debug('%s: Trying %s', var.lower(), quote(prog)) - result = find_program(prog, paths) - if result: - return result + 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: + log.debug('%s: Trying %s', var.lower(), quote(prog)) + result = find_program(prog, paths) + if result: + return result if not allow_missing or value: raise FatalCheckError('Cannot find %s' % what) diff --git a/build/moz.configure/node.configure b/build/moz.configure/node.configure index 3c3d6be4da32..4b09e30105d6 100644 --- a/build/moz.configure/node.configure +++ b/build/moz.configure/node.configure @@ -35,7 +35,8 @@ def node_toolchain_search_path(host): # exist on systems (probably linux distros) where there is a program in the path # called "node" that does something else. 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) diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index 6f32a570c014..6800f3e732ae 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -942,10 +942,8 @@ CopyingStructuredCloneReadCallback(JSContext* aCx, if (aTag == SCTAG_DOM_MUTABLEFILE) { MOZ_ASSERT(file.mType == StructuredCloneFile::eMutableFile); - RefPtr mutableFile = file.mMutableFile; - JS::Rooted wrappedMutableFile(aCx); - if (NS_WARN_IF(!ToJSValue(aCx, mutableFile, &wrappedMutableFile))) { + if (NS_WARN_IF(!ToJSValue(aCx, file.mMutableFile, &wrappedMutableFile))) { return nullptr; } @@ -956,9 +954,7 @@ CopyingStructuredCloneReadCallback(JSContext* aCx, MOZ_ASSERT(file.mType == StructuredCloneFile::eWasmBytecode); - RefPtr module = file.mWasmModule; - - JS::Rooted wrappedModule(aCx, module->createObject(aCx)); + JS::Rooted wrappedModule(aCx, file.mWasmModule->createObject(aCx)); if (NS_WARN_IF(!wrappedModule)) { return nullptr; } diff --git a/dom/script/ScriptSettings.cpp b/dom/script/ScriptSettings.cpp index 5169b911f027..a262507a9681 100644 --- a/dom/script/ScriptSettings.cpp +++ b/dom/script/ScriptSettings.cpp @@ -671,7 +671,8 @@ AutoEntryScript::AutoEntryScript(JSObject* aObject, bool 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() diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index afe16e8b86f5..fb300811b87b 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -2702,24 +2702,25 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case ParseNodeKind::Call: case ParseNodeKind::SuperCall: { - ParseNode* next = pn->pn_head; - MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos)); + ParseNode* pn_callee = pn->pn_left; + ParseNode* pn_args = pn->pn_right; + MOZ_ASSERT(pn->pn_pos.encloses(pn_callee->pn_pos)); RootedValue callee(cx); if (pn->isKind(ParseNodeKind::SuperCall)) { - MOZ_ASSERT(next->isKind(ParseNodeKind::SuperBase)); - if (!builder.super(&next->pn_pos, &callee)) + MOZ_ASSERT(pn_callee->isKind(ParseNodeKind::SuperBase)); + if (!builder.super(&pn_callee->pn_pos, &callee)) return false; } else { - if (!expression(next, &callee)) + if (!expression(pn_callee, &callee)) return false; } NodeVector args(cx); - if (!args.reserve(pn->pn_count - 1)) + if (!args.reserve(pn_args->pn_count)) 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)); RootedValue arg(cx); @@ -2740,17 +2741,17 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) 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 propname(cx); - RootedAtom pnAtom(cx, pn->pn_atom); + RootedAtom pnAtom(cx, pn->pn_right->pn_atom); if (pn->as().isSuper()) { - if (!builder.super(&pn->pn_expr->pn_pos, &expr)) + if (!builder.super(&pn->pn_left->pn_pos, &expr)) return false; } else { - if (!expression(pn->pn_expr, &expr)) + if (!expression(pn->pn_left, &expr)) return false; } diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp index 02e810f800e9..e6a44c4af1f4 100644 --- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -79,23 +79,23 @@ js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& } /* Step 20 (reordered). - * Set the |index| property. (TemplateObject positions it in slot 0) */ - arr->setSlot(0, Int32Value(matches[0].start)); + * Set the |index| property. */ + arr->setSlot(RegExpRealm::MatchResultObjectIndexSlot, Int32Value(matches[0].start)); /* Step 21 (reordered). - * Set the |input| property. (TemplateObject positions it in slot 1) */ - arr->setSlot(1, StringValue(input)); + * Set the |input| property. */ + arr->setSlot(RegExpRealm::MatchResultObjectInputSlot, StringValue(input)); #ifdef DEBUG RootedValue test(cx); RootedId id(cx, NameToId(cx->names().index)); if (!NativeGetProperty(cx, arr, id, &test)) return false; - MOZ_ASSERT(test == arr->getSlot(0)); + MOZ_ASSERT(test == arr->getSlot(RegExpRealm::MatchResultObjectIndexSlot)); id = NameToId(cx->names().input); if (!NativeGetProperty(cx, arr, id, &test)) return false; - MOZ_ASSERT(test == arr->getSlot(1)); + MOZ_ASSERT(test == arr->getSlot(RegExpRealm::MatchResultObjectInputSlot)); #endif /* Step 25. */ @@ -1025,16 +1025,18 @@ js::RegExpMatcher(JSContext* cx, unsigned argc, Value* vp) */ bool js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input, - int32_t lastIndex, + int32_t maybeLastIndex, MatchPairs* maybeMatches, MutableHandleValue output) { - MOZ_ASSERT(lastIndex >= 0); - // The MatchPairs will always be passed in, but RegExp execution was // 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 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 // 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); return true; } diff --git a/js/src/builtin/RegExp.h b/js/src/builtin/RegExp.h index 72c0fbc69abf..91d472dea26b 100644 --- a/js/src/builtin/RegExp.h +++ b/js/src/builtin/RegExp.h @@ -41,7 +41,7 @@ RegExpMatcher(JSContext* cx, unsigned argc, Value* vp); extern MOZ_MUST_USE bool 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 RegExpSearcher(JSContext* cx, unsigned argc, Value* vp); diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 0b3506af52ac..8c0db8823f48 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -1002,9 +1002,7 @@ StructMetaTypeDescr::createFromArrays(JSContext* cx, if (!CreateTraceList(cx, descr)) return nullptr; - if (!cx->zone()->addTypeDescrObject(cx, descr) || - !cx->zone()->addTypeDescrObject(cx, fieldTypeVec)) - { + if (!cx->zone()->addTypeDescrObject(cx, descr)) { ReportOutOfMemory(cx); return nullptr; } diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index 443bf9f454b5..7f9e82b5bd56 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -394,6 +394,10 @@ function isOverridableField(initialCSU, csu, field) return false; if (field == "GetThreadFromPRThread") return false; + if (field == "ConstructUbiNode") + return false; + if (initialCSU == 'nsIXPCScriptable' && field == "GetScriptableFlags") + return false; if (initialCSU == 'nsIXPConnectJSObjectHolder' && field == 'GetJSObject') return false; if (initialCSU == 'nsIXPConnect' && field == 'GetSafeJSContext') diff --git a/js/src/devtools/rootAnalysis/run_complete b/js/src/devtools/rootAnalysis/run_complete index b1fbadb81c58..c9355267dbdc 100755 --- a/js/src/devtools/rootAnalysis/run_complete +++ b/js/src/devtools/rootAnalysis/run_complete @@ -26,7 +26,7 @@ use strict; use warnings; use IO::Handle; -use File::Basename qw(dirname); +use File::Basename qw(basename dirname); use Getopt::Long; use Cwd; @@ -249,13 +249,17 @@ sub run_build # Tell the wrapper where to find the config $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}) { - $ENV{PATH} = dirname($ENV{CC}) . ":$ENV{PATH}"; - delete $ENV{CC}; - delete $ENV{CXX}; + $ENV{GCCDIR} = dirname($ENV{CC}); } - $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. chdir $build_dir; diff --git a/js/src/frontend/BinSource-auto.cpp b/js/src/frontend/BinSource-auto.cpp index a31d0f1563a9..d8f32867ab59 100644 --- a/js/src/frontend/BinSource-auto.cpp +++ b/js/src/frontend/BinSource-auto.cpp @@ -3462,9 +3462,8 @@ BinASTParser::parseInterfaceCallExpression(const size_t start, const BinKin op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL; } } - auto result = arguments; - result->setKind(ParseNodeKind::Call); - result->prepend(callee); + + BINJS_TRY_DECL(result, factory_.newCall(callee, arguments)); result->setOp(op); return result; } @@ -3742,7 +3741,7 @@ BinASTParser::parseInterfaceComputedMemberAssignmentTarget(const size_t sta 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; } @@ -3786,7 +3785,7 @@ BinASTParser::parseInterfaceComputedMemberExpression(const size_t start, co 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; } @@ -5677,10 +5676,7 @@ BinASTParser::parseInterfaceNewExpression(const size_t start, const BinKind BINJS_MOZ_TRY_DECL(arguments, parseArguments()); - auto result = arguments; - result->setKind(ParseNodeKind::New); - result->prepend(callee); - result->setOp(JSOP_NEW); + BINJS_TRY_DECL(result, factory_.newNewExpression(tokenizer_->pos(start).begin, callee, arguments)); return result; } @@ -6197,13 +6193,18 @@ BinASTParser::parseInterfaceStaticMemberAssignmentTarget(const size_t start const BinField expected_fields[2] = { BinField::Object, BinField::Property }; MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields)); #endif // defined(DEBUG) + size_t nameStart; BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper()); - RootedAtom property(cx_); - MOZ_TRY_VAR(property, tokenizer_->readAtom()); + { + nameStart = tokenizer_->offset(); + 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; } @@ -6242,13 +6243,18 @@ BinASTParser::parseInterfaceStaticMemberExpression(const size_t start, cons const BinField expected_fields[2] = { BinField::Object, BinField::Property }; MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields)); #endif // defined(DEBUG) + size_t nameStart; BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper()); - RootedAtom property(cx_); - MOZ_TRY_VAR(property, tokenizer_->readAtom()); + { + nameStart = tokenizer_->offset(); + 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; } @@ -7381,7 +7387,7 @@ BinASTParser::parseArguments() const auto start = tokenizer_->offset(); 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) { BINJS_MOZ_TRY_DECL(item, parseSpreadElementOrExpression()); diff --git a/js/src/frontend/BinSource.yaml b/js/src/frontend/BinSource.yaml index 597b97fa480d..01af4eed1a20 100644 --- a/js/src/frontend/BinSource.yaml +++ b/js/src/frontend/BinSource.yaml @@ -207,7 +207,7 @@ hpp: Arguments: init: - BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::ParamsBody, tokenizer_->pos(start))); + BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::Arguments, tokenizer_->pos(start))); append: factory_.addList(/* list = */ result, /* kid = */ item); @@ -428,9 +428,8 @@ CallExpression: op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL; } } - auto result = arguments; - result->setKind(ParseNodeKind::Call); - result->prepend(callee); + + BINJS_TRY_DECL(result, factory_.newCall(callee, arguments)); result->setOp(op); @@ -489,11 +488,11 @@ CompoundAssignmentExpression: ComputedMemberAssignmentTarget: build: | - BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start)); + BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, tokenizer_->offset())); ComputedMemberExpression: build: | - BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start)); + BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, tokenizer_->offset())); ConditionalExpression: build: | @@ -831,10 +830,7 @@ LiteralStringExpression: NewExpression: build: | - auto result = arguments; - result->setKind(ParseNodeKind::New); - result->prepend(callee); - result->setOp(JSOP_NEW); + BINJS_TRY_DECL(result, factory_.newNewExpression(tokenizer_->pos(start).begin, callee, arguments)); ObjectExpression: build: @@ -922,12 +918,28 @@ SwitchStatementWithDefault: BINJS_TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope, true)); StaticMemberAssignmentTarget: + init: + size_t nameStart; + fields: + property: + block: + before: | + nameStart = tokenizer_->offset(); 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: + init: + size_t nameStart; + fields: + property: + block: + before: | + nameStart = tokenizer_->offset(); 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: build: | diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 993807a953ab..9dd0d44d8a01 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1022,7 +1022,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) // Watch out for getters! case ParseNodeKind::Dot: - MOZ_ASSERT(pn->isArity(PN_NAME)); + MOZ_ASSERT(pn->isArity(PN_BINARY)); *answer = true; return true; @@ -1260,6 +1260,14 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case ParseNodeKind::Call: case ParseNodeKind::TaggedTemplate: 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)); *answer = true; return true; @@ -1394,6 +1402,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate case ParseNodeKind::PosHolder: // by ParseNodeKind::NewTarget case ParseNodeKind::SuperBase: // by ParseNodeKind::Elem and others + case ParseNodeKind::PropertyName: // by ParseNodeKind::Dot MOZ_CRASH("handled by parent nodes"); case ParseNodeKind::Limit: // invalid sentinel value @@ -1865,11 +1874,11 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn) MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot)); MOZ_ASSERT(!pn->as().isSuper()); - ParseNode* pn2 = pn->pn_expr; + ParseNode* pn2 = pn->pn_left; /* * 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. */ if (pn2->isKind(ParseNodeKind::Dot) && !pn2->as().isSuper()) { @@ -1877,9 +1886,9 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn) ParseNode* pnup = nullptr; ParseNode* pndown; for (;;) { - /* Reverse pndot->pn_expr to point up, not down. */ - pndown = pndot->pn_expr; - pndot->pn_expr = pnup; + /* Reverse pndot->pn_left to point up, not down. */ + pndown = pndot->pn_left; + pndot->pn_left = pnup; if (!pndown->isKind(ParseNodeKind::Dot) || pndown->as().isSuper()) break; pnup = pndot; @@ -1892,12 +1901,12 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn) do { /* Walk back up the list, emitting annotated name ops. */ - if (!emitAtomOp(pndot, JSOP_GETPROP)) + if (!emitAtomOp(pndot->pn_right, JSOP_GETPROP)) return false; - /* Reverse the pn_expr link again. */ - pnup = pndot->pn_expr; - pndot->pn_expr = pndown; + /* Reverse the pn_left link again. */ + pnup = pndot->pn_left; + pndot->pn_left = pndown; pndown = pndot; } while ((pndot = pnup) != nullptr); return true; @@ -1922,7 +1931,7 @@ BytecodeEmitter::emitSuperPropLHS(ParseNode* superBase, bool isCall) bool BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op) { - MOZ_ASSERT(pn->isArity(PN_NAME)); + MOZ_ASSERT(pn->isArity(PN_BINARY)); if (!emitPropLHS(pn)) return false; @@ -1930,7 +1939,7 @@ BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op) if (op == JSOP_CALLPROP && !emit1(JSOP_DUP)) return false; - if (!emitAtomOp(pn, op)) + if (!emitAtomOp(pn->pn_right, op)) return false; if (op == JSOP_CALLPROP && !emit1(JSOP_SWAP)) @@ -1946,7 +1955,7 @@ BytecodeEmitter::emitSuperGetProp(ParseNode* pn, bool isCall) if (!emitSuperPropLHS(base, isCall)) return false; - if (!emitAtomOp(pn, JSOP_GETPROP_SUPER)) + if (!emitAtomOp(pn->pn_right, JSOP_GETPROP_SUPER)) return false; if (isCall && !emit1(JSOP_SWAP)) @@ -1976,7 +1985,7 @@ BytecodeEmitter::emitPropIncDec(ParseNode* pn) if (!emit1(JSOP_DUP)) // OBJ OBJ 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; if (!emit1(JSOP_POS)) // OBJ N return false; @@ -2002,7 +2011,7 @@ BytecodeEmitter::emitPropIncDec(ParseNode* pn) JSOp setOp = isSuper ? sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER : 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; if (post && !emit1(JSOP_POP)) // RESULT return false; @@ -2692,7 +2701,7 @@ BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, size_t* emitted) return false; *emitted = 2; } else { - if (!emitTree(target->pn_expr)) + if (!emitTree(target->pn_left)) return false; *emitted = 1; } @@ -2810,7 +2819,7 @@ BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, Destructuri setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER; else setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP; - if (!emitAtomOp(target, setOp)) + if (!emitAtomOp(target->pn_right, setOp)) return false; break; } @@ -3923,11 +3932,11 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, ParseNodeKind pnk, ParseNode* rh return false; offset += 2; } else { - if (!emitTree(lhs->expr())) + if (!emitTree(lhs->pn_left)) return false; offset += 1; } - if (!makeAtomIndex(lhs->pn_atom, &atomIndex)) + if (!makeAtomIndex(lhs->pn_right->pn_atom, &atomIndex)) return false; break; case ParseNodeKind::Elem: { @@ -3976,7 +3985,7 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, ParseNodeKind pnk, ParseNode* rh } else { if (!emit1(JSOP_DUP)) 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; } if (!emitIndex32(getOp, atomIndex)) @@ -4353,12 +4362,10 @@ BytecodeEmitter::emitIf(ParseNode* pn) { IfEmitter ifThenElse(this); - if_again: - // 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)) + if (!ifThenElse.emitIf(Some(pn->pn_pos.begin))) return false; + if_again: /* Emit code for the condition before pushing stmtInfo. */ if (!emitTree(pn->pn_kid1)) return false; @@ -4380,7 +4387,7 @@ BytecodeEmitter::emitIf(ParseNode* pn) if (elseNode->isKind(ParseNodeKind::If)) { pn = elseNode; - if (!ifThenElse.emitElseIf()) + if (!ifThenElse.emitElseIf(Some(pn->pn_pos.begin))) return false; goto if_again; @@ -4821,7 +4828,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, const EmitterScope* headLexical bool allowSelfHostedIter = false; if (emitterMode == BytecodeEmitter::SelfHosting && forHeadExpr->isKind(ParseNodeKind::Call) && - forHeadExpr->pn_head->name() == cx->names().allowContentIter) + forHeadExpr->pn_left->name() == cx->names().allowContentIter) { allowSelfHostedIter = true; } @@ -6142,10 +6149,12 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) // // argc is set to the amount of actually emitted args and the // emitting of args below is disabled by setting emitArgs to false. - ParseNode* pn2 = pn->pn_head; - const char* errorName = SelfHostedCallFunctionName(pn2->name(), cx); + ParseNode* pn_callee = pn->pn_left; + 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"); return false; } @@ -6156,8 +6165,8 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) return false; } - bool constructing = pn2->name() == cx->names().constructContentFunction; - ParseNode* funNode = pn2->pn_next; + bool constructing = pn_callee->name() == cx->names().constructContentFunction; + ParseNode* funNode = pn_args->pn_head; if (constructing) { callOp = JSOP_NEW; } else if (funNode->getKind() == ParseNodeKind::Name && @@ -6170,7 +6179,7 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) #ifdef DEBUG if (emitterMode == BytecodeEmitter::SelfHosting && - pn2->name() == cx->names().callFunction) + pn_callee->name() == cx->names().callFunction) { if (!emit1(JSOP_DEBUGCHECKSELFHOSTED)) return false; @@ -6199,7 +6208,7 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) return false; } - uint32_t argc = pn->pn_count - 3; + uint32_t argc = pn_args->pn_count - 2; if (!emitCall(callOp, argc)) return false; @@ -6210,15 +6219,15 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) bool BytecodeEmitter::emitSelfHostedResumeGenerator(ParseNode* pn) { + ParseNode* pn_args = pn->pn_right; + // 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"); return false; } - ParseNode* funNode = pn->pn_head; // The resumeGenerator node. - - ParseNode* genNode = funNode->pn_next; + ParseNode* genNode = pn_args->pn_head; if (!emitTree(genNode)) return false; @@ -6250,24 +6259,26 @@ BytecodeEmitter::emitSelfHostedForceInterpreter() bool 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", ""); return false; } // 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 BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn) { - // Only optimize when 3 arguments are passed (we use 4 to include |this|). - MOZ_ASSERT(pn->pn_count == 4); + ParseNode* pn_args = pn->pn_right; - 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)) return false; @@ -6288,14 +6299,14 @@ BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn) bool 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", ""); return false; } - ParseNode* funNode = pn->pn_head; // The hasOwn node. - - ParseNode* idNode = funNode->pn_next; + ParseNode* idNode = pn_args->pn_head; if (!emitTree(idNode)) return false; @@ -6309,14 +6320,14 @@ BytecodeEmitter::emitSelfHostedHasOwn(ParseNode* pn) bool 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", ""); return false; } - ParseNode* funNode = pn->pn_head; // The getPropertySuper node. - - ParseNode* objNode = funNode->pn_next; + ParseNode* objNode = pn_args->pn_head; ParseNode* idNode = objNode->pn_next; ParseNode* receiverNode = idNode->pn_next; @@ -6345,11 +6356,11 @@ BytecodeEmitter::isRestParameter(ParseNode* pn) if (!pn->isKind(ParseNodeKind::Name)) { if (emitterMode == BytecodeEmitter::SelfHosting && pn->isKind(ParseNodeKind::Call)) { - ParseNode* pn2 = pn->pn_head; - if (pn2->getKind() == ParseNodeKind::Name && - pn2->name() == cx->names().allowContentIter) + ParseNode* pn_callee = pn->pn_left; + if (pn_callee->getKind() == ParseNodeKind::Name && + pn_callee->name() == cx->names().allowContentIter) { - return isRestParameter(pn2->pn_next); + return isRestParameter(pn->pn_right->pn_head); } } return false; @@ -6479,101 +6490,22 @@ BytecodeEmitter::emitPipeline(ParseNode* pn) } bool -BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */) +BytecodeEmitter::emitArguments(ParseNode* pn, bool callop, bool spread) { - 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). - */ - uint32_t argc = pn->pn_count - 1; + uint32_t argc = pn->pn_count; if (argc >= ARGC_LIMIT) { reportError(pn, callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS); 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) { - 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)) return false; } - - if (isNewOp) { - if (pn->isKind(ParseNodeKind::SuperCall)) { - if (!emit1(JSOP_NEWTARGET)) - return false; - } else { - // Repush the callee as new.target - if (!emitDupAt(argc + 1)) - return false; - } - } } else { - ParseNode* args = pn2->pn_next; + ParseNode* args = pn->pn_head; bool emitOptCode = (argc == 1) && isRestParameter(args->pn_kid); InternalIfEmitter ifNotOptimizable(this); @@ -6613,29 +6545,159 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUs if (!ifNotOptimizable.emitEnd()) 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 (pn->isKind(ParseNodeKind::SuperCall)) { - if (!emit1(JSOP_NEWTARGET)) - return false; - } else { - if (!emitDupAt(2)) - return false; + 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 (pn->isKind(ParseNodeKind::SuperCall)) { + if (!emit1(JSOP_NEWTARGET)) + return false; + } else if (!spread) { + // Repush the callee as new.target + if (!emitDupAt(argc + 1)) + return false; + } else { + if (!emitDupAt(2)) + 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 (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; checkTypeSet(JSOP_CALL_IGNORES_RV); } else { - if (!emitCall(pn->getOp(), argc, pn)) + if (!emitCall(pn->getOp(), argc, coordNode)) return false; checkTypeSet(pn->getOp()); } } else { + if (coordNode) { + if (!updateSourceCoordNotes(coordNode->pn_pos.begin)) + return false; + } + if (!emit1(pn->getOp())) return false; checkTypeSet(pn->getOp()); @@ -6846,26 +6908,28 @@ bool BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional, 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())) return false; - IfEmitter ifThenElse(this); - if (!ifThenElse.emitCond()) + if (!cond.emitThenElse()) return false; if (!emitTree(&conditional.thenExpression(), valueUsage)) return false; - if (!ifThenElse.emitElse()) + if (!cond.emitElse()) return false; if (!emitTree(&conditional.elseExpression(), valueUsage)) return false; - if (!ifThenElse.emitEnd()) + if (!cond.emitEnd()) return false; - MOZ_ASSERT(ifThenElse.pushed() == 1); + MOZ_ASSERT(cond.pushed() == 1); return true; } @@ -7204,7 +7268,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count) if (emitterMode == BytecodeEmitter::SelfHosting && expr->isKind(ParseNodeKind::Call) && - expr->pn_head->name() == cx->names().allowContentIter) + expr->pn_left->name() == cx->names().allowContentIter) { allowSelfHostedIter = true; } @@ -8252,8 +8316,9 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: return false; break; + case ParseNodeKind::PropertyName: 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: MOZ_ASSERT(0); diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 2fad16509c01..b975ceaad324 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -808,6 +808,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter 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 emitSelfHostedCallFunction(ParseNode* pn); MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn); diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 079194d5d607..0d8e3005740b 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -347,8 +347,10 @@ ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result) case ParseNodeKind::Comma: case ParseNodeKind::Array: case ParseNodeKind::Object: + case ParseNodeKind::PropertyName: case ParseNodeKind::Dot: case ParseNodeKind::Elem: + case ParseNodeKind::Arguments: case ParseNodeKind::Call: case ParseNodeKind::Name: case ParseNodeKind::TemplateString: @@ -1253,7 +1255,10 @@ FoldElement(JSContext* cx, ParseNode** nodePtr, PerHandlerParserpn_pos.end); + ParseNode* nameNode = parser.newPropertyName(name, key->pn_pos); + if (!nameNode) + return false; + ParseNode* dottedAccess = parser.newPropertyAccess(expr, nameNode); if (!dottedAccess) return false; dottedAccess->setInParens(node->isInParens()); @@ -1409,8 +1414,9 @@ FoldCall(JSContext* cx, ParseNode* node, PerHandlerParser& par { MOZ_ASSERT(node->isKind(ParseNodeKind::Call) || node->isKind(ParseNodeKind::SuperCall) || + node->isKind(ParseNodeKind::New) || 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 // might cause a different |this| value to be used, changing semantics: @@ -1423,10 +1429,26 @@ FoldCall(JSContext* cx, ParseNode* node, PerHandlerParser& par // assertEq(obj.f``, "obj"); // // See bug 537673 and bug 1182373. - ParseNode** listp = &node->pn_head; - if ((*listp)->isInParens()) - listp = &(*listp)->pn_next; + ParseNode** pn_callee = &node->pn_left; + if (node->isKind(ParseNodeKind::New) || !(*pn_callee)->isInParens()) { + 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& parser) +{ + MOZ_ASSERT(node->isKind(ParseNodeKind::Arguments)); + MOZ_ASSERT(node->isArity(PN_LIST)); + + ParseNode** listp = &node->pn_head; for (; *listp; listp = &(*listp)->pn_next) { if (!Fold(cx, listp, parser)) return false; @@ -1482,14 +1504,14 @@ static bool FoldDottedProperty(JSContext* cx, ParseNode* node, PerHandlerParser& parser) { 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 // most-nested non-dotted property node, then fold that. - ParseNode** nested = &node->pn_expr; + ParseNode** nested = &node->pn_left; while ((*nested)->isKind(ParseNodeKind::Dot)) { - MOZ_ASSERT((*nested)->isArity(PN_NAME)); - nested = &(*nested)->pn_expr; + MOZ_ASSERT((*nested)->isArity(PN_BINARY)); + nested = &(*nested)->pn_left; } return Fold(cx, nested, parser); @@ -1642,7 +1664,6 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser& parser) case ParseNodeKind::InstanceOf: case ParseNodeKind::In: case ParseNodeKind::Comma: - case ParseNodeKind::New: case ParseNodeKind::Array: case ParseNodeKind::Object: case ParseNodeKind::StatementList: @@ -1694,10 +1715,14 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser& parser) return FoldAdd(cx, pnp, parser); case ParseNodeKind::Call: + case ParseNodeKind::New: case ParseNodeKind::SuperCall: case ParseNodeKind::TaggedTemplate: return FoldCall(cx, pn, parser); + case ParseNodeKind::Arguments: + return FoldArguments(cx, pn, parser); + case ParseNodeKind::Switch: case ParseNodeKind::Colon: case ParseNodeKind::Assign: @@ -1777,6 +1802,9 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser& parser) MOZ_ASSERT(pn->isArity(PN_NAME)); return Fold(cx, &pn->pn_expr, parser); + case ParseNodeKind::PropertyName: + MOZ_CRASH("unreachable, handled by ::Dot"); + case ParseNodeKind::Dot: return FoldDottedProperty(cx, pn, parser); diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 60c5b5147944..8a1ee11d2f1d 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -275,16 +275,20 @@ class FullParseHandler addList(/* list = */ literal, /* child = */ element); } - ParseNode* newCall(const TokenPos& pos) { - return new_(ParseNodeKind::Call, JSOP_CALL, pos); + ParseNode* newCall(ParseNode* callee, ParseNode* args) { + return new_(ParseNodeKind::Call, JSOP_CALL, callee, args); } - ParseNode* newSuperCall(ParseNode* callee) { - return new_(ParseNodeKind::SuperCall, JSOP_SUPERCALL, callee); + ParseNode* newArguments(const TokenPos& pos) { + return new_(ParseNodeKind::Arguments, JSOP_NOP, pos); } - ParseNode* newTaggedTemplate(const TokenPos& pos) { - return new_(ParseNodeKind::TaggedTemplate, JSOP_CALL, pos); + ParseNode* newSuperCall(ParseNode* callee, ParseNode* args) { + return new_(ParseNodeKind::SuperCall, JSOP_SUPERCALL, callee, args); + } + + ParseNode* newTaggedTemplate(ParseNode* tag, ParseNode* args) { + return new_(ParseNodeKind::TaggedTemplate, JSOP_CALL, tag, args); } ParseNode* newObjectLiteral(uint32_t begin) { @@ -660,8 +664,12 @@ class FullParseHandler return new_(pos); } - ParseNode* newPropertyAccess(ParseNode* expr, PropertyName* key, uint32_t end) { - return new_(expr, key, expr->pn_pos.begin, end); + ParseNode* newPropertyName(PropertyName* name, const TokenPos& pos) { + return new_(ParseNodeKind::PropertyName, JSOP_NOP, name, pos); + } + + ParseNode* newPropertyAccess(ParseNode* expr, ParseNode* key) { + return new_(expr, key, expr->pn_pos.begin, key->pn_pos.end); } ParseNode* newPropertyByValue(ParseNode* lhs, ParseNode* index, uint32_t end) { @@ -735,13 +743,8 @@ class FullParseHandler return new_(bindings, body); } - Node newNewExpression(uint32_t begin, ParseNode* ctor) { - ParseNode* newExpr = new_(ParseNodeKind::New, JSOP_NEW, TokenPos(begin, begin + 1)); - if (!newExpr) - return nullptr; - - addList(/* list = */ newExpr, /* child = */ ctor); - return newExpr; + Node newNewExpression(uint32_t begin, ParseNode* ctor, ParseNode* args) { + return new_(ParseNodeKind::New, JSOP_NEW, TokenPos(begin, args->pn_pos.end), ctor, args); } ParseNode* newAssignment(ParseNodeKind kind, ParseNode* lhs, ParseNode* rhs) { diff --git a/js/src/frontend/IfEmitter.cpp b/js/src/frontend/IfEmitter.cpp index b85cb8e77a84..2aa708121c4a 100644 --- a/js/src/frontend/IfEmitter.cpp +++ b/js/src/frontend/IfEmitter.cpp @@ -13,15 +13,15 @@ using namespace js; using namespace js::frontend; -IfEmitter::IfEmitter(BytecodeEmitter* bce, Kind kind) +using mozilla::Maybe; + +BranchEmitterBase::BranchEmitterBase(BytecodeEmitter* bce, Kind kind) : bce_(bce), - thenDepth_(0), kind_(kind) -#ifdef DEBUG - , pushed_(0), - calculatedPushed_(false), - state_(State::Start) -#endif +{} + +IfEmitter::IfEmitter(BytecodeEmitter* bce, Kind kind) + : BranchEmitterBase(bce, kind) {} IfEmitter::IfEmitter(BytecodeEmitter* bce) @@ -29,11 +29,8 @@ IfEmitter::IfEmitter(BytecodeEmitter* bce) {} 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. if (kind_ == Kind::MayContainLexicalAccessInBranch) tdzCache_.reset(); @@ -61,7 +58,7 @@ IfEmitter::emitIfInternal(SrcNoteType type) } void -IfEmitter::calculateOrCheckPushed() +BranchEmitterBase::calculateOrCheckPushed() { #ifdef DEBUG if (!calculatedPushed_) { @@ -74,46 +71,7 @@ IfEmitter::calculateOrCheckPushed() } bool -IfEmitter::emitThen() -{ - 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() +BranchEmitterBase::emitElseInternal() { calculateOrCheckPushed(); @@ -138,57 +96,17 @@ IfEmitter::emitElseInternal() // Restore stack depth of the then part. 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. if (kind_ == Kind::MayContainLexicalAccessInBranch) tdzCache_.emplace(bce_); -#ifdef DEBUG - state_ = State::Else; -#endif return true; } 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. if (kind_ == Kind::MayContainLexicalAccessInBranch) { MOZ_ASSERT(tdzCache_.isSome()); @@ -208,6 +126,106 @@ IfEmitter::emitEnd() if (!bce_->emitJumpTargetAndPatch(jumpsAroundElse_)) return false; + return true; +} + +bool +IfEmitter::emitIf(const Maybe& 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& 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 state_ = State::End; #endif @@ -216,4 +234,65 @@ IfEmitter::emitEnd() InternalIfEmitter::InternalIfEmitter(BytecodeEmitter* bce) : 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; +} diff --git a/js/src/frontend/IfEmitter.h b/js/src/frontend/IfEmitter.h index b12d6be1e729..56281e40957f 100644 --- a/js/src/frontend/IfEmitter.h +++ b/js/src/frontend/IfEmitter.h @@ -21,58 +21,23 @@ namespace frontend { struct BytecodeEmitter; -// Class for emitting bytecode for blocks like if-then-else. -// -// 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 +class MOZ_STACK_CLASS BranchEmitterBase { - 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 // contain declaration or access to lexical variables, which means they // 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. 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_; + mozilla::Maybe tdzCache_; #ifdef DEBUG // The number of values pushed in the then and else blocks. - int32_t pushed_; - bool calculatedPushed_; - - // 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_; + int32_t pushed_ = 0; + bool calculatedPushed_ = false; #endif protected: - // For InternalIfEmitter. - IfEmitter(BytecodeEmitter* bce, Kind kind); + BranchEmitterBase(BytecodeEmitter* bce, Kind kind); + + MOZ_MUST_USE bool emitThenInternal(SrcNoteType type); + void calculateOrCheckPushed(); + MOZ_MUST_USE bool emitElseInternal(); + MOZ_MUST_USE bool emitEndInternal(); 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 // Returns the number of values pushed onto the value stack inside // `then_block` and `else_block`. @@ -193,11 +91,129 @@ class MOZ_STACK_CLASS IfEmitter return -pushed_; } #endif +}; - private: - MOZ_MUST_USE bool emitIfInternal(SrcNoteType type); - void calculateOrCheckPushed(); - MOZ_MUST_USE bool emitElseInternal(); +// Class for emitting bytecode for blocks like if-then-else. +// +// 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); +// 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& ifPos); + + MOZ_MUST_USE bool emitThen(); + MOZ_MUST_USE bool emitThenElse(); + + MOZ_MUST_USE bool emitElseIf(const mozilla::Maybe& 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 @@ -205,12 +221,83 @@ class MOZ_STACK_CLASS IfEmitter // // See the comments above NoLexicalAccessInBranch for more details when to use // 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 { public: 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 js */ diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index 0528ebcadac2..426b8a6bff62 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -75,11 +75,11 @@ class NameResolver bool nameExpression(ParseNode* n, bool* foundName) { switch (n->getKind()) { case ParseNodeKind::Dot: - if (!nameExpression(n->expr(), foundName)) + if (!nameExpression(n->pn_left, foundName)) return false; if (!*foundName) return true; - return appendPropertyReference(n->pn_atom); + return appendPropertyReference(n->pn_right->pn_atom); case ParseNodeKind::Name: *foundName = true; @@ -315,17 +315,17 @@ class NameResolver bool resolveTaggedTemplate(ParseNode* node, HandleAtom prefix) { 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. - if (!resolve(element, prefix)) + if (!resolve(tag, prefix)) 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 // expressions. - element = element->pn_next; + ParseNode* element = node->pn_right->pn_head; #ifdef DEBUG { MOZ_ASSERT(element->isKind(ParseNodeKind::CallSiteObj)); @@ -697,9 +697,6 @@ class NameResolver case ParseNodeKind::Pow: case ParseNodeKind::Pipeline: case ParseNodeKind::Comma: - case ParseNodeKind::New: - case ParseNodeKind::Call: - case ParseNodeKind::SuperCall: case ParseNodeKind::Array: case ParseNodeKind::StatementList: case ParseNodeKind::ParamsBody: @@ -733,11 +730,32 @@ class NameResolver break; case ParseNodeKind::TaggedTemplate: - MOZ_ASSERT(cur->isArity(PN_LIST)); + MOZ_ASSERT(cur->isArity(PN_BINARY)); if (!resolveTaggedTemplate(cur, prefix)) return false; 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 // only pairs of names. Alternatively, an export spec lists may // contain a single export batch specifier. @@ -766,12 +784,12 @@ class NameResolver } case ParseNodeKind::Dot: - MOZ_ASSERT(cur->isArity(PN_NAME)); + MOZ_ASSERT(cur->isArity(PN_BINARY)); // Super prop nodes do not have a meaningful LHS if (cur->as().isSuper()) break; - if (!resolve(cur->expr(), prefix)) + if (!resolve(cur->pn_left, prefix)) return false; break; @@ -810,6 +828,7 @@ class NameResolver case ParseNodeKind::ExportSpec: // by ParseNodeKind::ExportSpecList case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate case ParseNodeKind::ClassNames: // by ParseNodeKind::Class + case ParseNodeKind::PropertyName: // by ParseNodeKind::Dot MOZ_CRASH("should have been handled by a parent node"); case ParseNodeKind::Limit: // invalid sentinel value diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index b54653bbb7b5..3325d2f2579e 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -216,6 +216,21 @@ UnaryNode::dump(GenericPrinter& out, int indent) void BinaryNode::dump(GenericPrinter& out, int indent) { + if (isKind(ParseNodeKind::Dot)) { + out.put("(."); + + DumpParseTree(pn_right, out, indent + 2); + + out.putChar(' '); + if (as().isSuper()) + out.put("super"); + else + DumpParseTree(pn_left, out, indent + 2); + + out.printf(")"); + return; + } + const char* name = parseNodeNames[size_t(getKind())]; out.printf("(%s ", name); indent += strlen(name) + 2; @@ -288,10 +303,7 @@ DumpName(GenericPrinter& out, const CharT* s, size_t len) void NameNode::dump(GenericPrinter& out, int indent) { - if (isKind(ParseNodeKind::Name) || isKind(ParseNodeKind::Dot)) { - if (isKind(ParseNodeKind::Dot)) - out.put("(."); - + if (isKind(ParseNodeKind::Name) || isKind(ParseNodeKind::PropertyName)) { if (!pn_atom) { out.put("#"); } else if (getOp() == JSOP_GETARG && pn_atom->length() == 0) { @@ -306,15 +318,6 @@ NameNode::dump(GenericPrinter& out, int indent) else DumpName(out, pn_atom->twoByteChars(nogc), pn_atom->length()); } - - if (isKind(ParseNodeKind::Dot)) { - out.putChar(' '); - if (as().isSuper()) - out.put("super"); - else - DumpParseTree(expr(), out, indent + 2); - out.printf(")"); - } return; } diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 780195aadcd9..ed5b8d72ae2e 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -55,6 +55,7 @@ class ObjectBox; F(PostIncrement) \ F(PreDecrement) \ F(PostDecrement) \ + F(PropertyName) \ F(Dot) \ F(Elem) \ F(Array) \ @@ -63,6 +64,7 @@ class ObjectBox; F(Label) \ F(Object) \ F(Call) \ + F(Arguments) \ F(Name) \ F(ObjectPropertyName) \ F(ComputedName) \ @@ -371,9 +373,8 @@ IsTypeofKind(ParseNodeKind kind) * PostIncrement, * PreDecrement, * PostDecrement - * New list pn_head: list of ctor, arg1, arg2, ... argN - * pn_count: 1 + N (where N is number of args) - * ctor is a MEMBER expr + * New binary pn_left: ctor expression on the left of the ( + * pn_right: Arguments * DeleteName unary pn_kid: Name expr * DeleteProp unary pn_kid: Dot expr * DeleteElem unary pn_kid: Elem expr @@ -382,13 +383,15 @@ IsTypeofKind(ParseNodeKind kind) * for a more-specific PNK_DELETE* unless constant * folding (or a similar parse tree manipulation) has * occurred - * Dot name pn_expr: MEMBER expr to left of . - * pn_atom: name to right of . + * PropertyName name pn_atom: property name being accessed + * Dot binary pn_left: MEMBER expr to left of . + * pn_right: PropertyName to right of . * Elem binary pn_left: MEMBER expr to left of [ * pn_right: expr between [ and ] - * Call list pn_head: list of call, arg1, arg2, ... argN - * pn_count: 1 + N (where N is number of args) - * call is a MEMBER expr naming a callable object + * Call binary pn_left: callee expression on the left of the ( + * pn_right: Arguments + * 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 * [,,] holes are represented by Elision nodes * pn_xflags: PN_ENDCOMMA if extra comma at end @@ -408,8 +411,9 @@ IsTypeofKind(ParseNodeKind kind) * list * TemplateString pn_atom: template string atom nullary pn_op: JSOP_NOP - * TaggedTemplate pn_head: list of call, call site object, arg1, arg2, ... argN - * list pn_count: 2 + N (N is the number of substitutions) + * TaggedTemplate pn_left: tag expression + * 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 * list of pn_count - 1 TemplateString nodes * 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 * SuperBase unary pn_kid: '.this' Name - * + * SuperCall binary pn_left: SuperBase pn_right: Arguments * SetThis binary pn_left: '.this' Name, pn_right: SuperCall * * LexicalScope scope pn_u.scope.bindings: scope bindings @@ -571,8 +575,7 @@ class ParseNode FunctionBox* funbox; /* function object */ }; ParseNode* expr; /* module or function body, var - initializer, argument default, or - base object of ParseNodeKind::Dot */ + initializer, or argument default */ } name; struct { LexicalScope::Data* bindings; @@ -1176,30 +1179,33 @@ class RegExpLiteral : public NullaryNode } }; -class PropertyAccess : public ParseNode +class PropertyAccess : public BinaryNode { 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(name != nullptr); - pn_u.name.expr = lhs; - pn_u.name.atom = name; } static bool test(const ParseNode& node) { 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; } ParseNode& expression() const { - return *pn_u.name.expr; + return *pn_u.binary.left; } PropertyName& name() const { - return *pn_u.name.atom->asPropertyName(); + return *pn_u.binary.right->pn_atom->asPropertyName(); } bool isSuper() const { diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index bca0d93dac58..ad2841ebefd3 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -3397,13 +3397,13 @@ GeneralParser::addExprAndGetNextTemplStrToken(YieldHandling template bool -GeneralParser::taggedTemplate(YieldHandling yieldHandling, Node nodeList, +GeneralParser::taggedTemplate(YieldHandling yieldHandling, Node tagArgsList, TokenKind tt) { Node callSiteObjNode = handler.newCallSiteObject(pos().begin); if (!callSiteObjNode) return false; - handler.addList(nodeList, callSiteObjNode); + handler.addList(tagArgsList, callSiteObjNode); while (true) { if (!appendToCallSiteObj(callSiteObjNode)) @@ -3411,10 +3411,10 @@ GeneralParser::taggedTemplate(YieldHandling yieldHandling, if (tt != TokenKind::TemplateHead) break; - if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt)) + if (!addExprAndGetNextTemplStrToken(yieldHandling, tagArgsList, &tt)) return false; } - handler.setEndPosition(nodeList, callSiteObjNode); + handler.setEndPosition(tagArgsList, callSiteObjNode); return true; } @@ -8643,24 +8643,27 @@ GeneralParser::assignExprWithoutYieldOrAwait(YieldHandling } template -bool -GeneralParser::argumentList(YieldHandling yieldHandling, Node listNode, - bool* isSpread, +typename ParseHandler::Node +GeneralParser::argumentList(YieldHandling yieldHandling, bool* isSpread, PossibleError* possibleError /* = nullptr */) { + Node argsList = handler.newArguments(pos()); + if (!argsList) + return null(); + bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Rp, TokenStream::Operand)) - return false; + return null(); if (matched) { - handler.setEndPosition(listNode, pos().end); - return true; + handler.setEndPosition(argsList, pos().end); + return argsList; } while (true) { bool spread = false; uint32_t begin = 0; if (!tokenStream.matchToken(&matched, TokenKind::TripleDot, TokenStream::Operand)) - return false; + return null(); if (matched) { spread = true; begin = pos().begin; @@ -8669,18 +8672,18 @@ GeneralParser::argumentList(YieldHandling yieldHandling, No Node argNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError); if (!argNode) - return false; + return null(); if (spread) { argNode = handler.newSpread(begin, argNode); if (!argNode) - return false; + return null(); } - handler.addList(listNode, argNode); + handler.addList(argsList, argNode); bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::Operand)) - return false; + return null(); if (!matched) break; @@ -8693,8 +8696,8 @@ GeneralParser::argumentList(YieldHandling yieldHandling, No MUST_MATCH_TOKEN_MOD(TokenKind::Rp, TokenStream::Operand, JSMSG_PAREN_AFTER_ARGS); - handler.setEndPosition(listNode, pos().end); - return true; + handler.setEndPosition(argsList, pos().end); + return argsList; } bool @@ -8740,20 +8743,27 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, if (!ctorExpr) return null(); - lhs = handler.newNewExpression(newBegin, ctorExpr); - if (!lhs) - return null(); - bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Lp)) return null(); + + bool isSpread = false; + Node args; if (matched) { - bool isSpread = false; - if (!argumentList(yieldHandling, lhs, &isSpread)) - return null(); - if (isSpread) - handler.setOp(lhs, JSOP_SPREADNEW); + args = argumentList(yieldHandling, &isSpread); + } else { + args = handler.newArguments(pos()); } + + if (!args) + return null(); + + lhs = handler.newNewExpression(newBegin, ctorExpr, args); + if (!lhs) + return null(); + + if (isSpread) + handler.setOp(lhs, JSOP_SPREADNEW); } } else if (tt == TokenKind::Super) { Node thisName = newThisName(); @@ -8790,7 +8800,12 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, error(JSMSG_BAD_SUPERPROP, "property"); 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) return null(); } else { @@ -8826,15 +8841,16 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, return null(); } - nextMember = handler.newSuperCall(lhs); - if (!nextMember) - return null(); - // Despite the fact that it's impossible to have |super()| in a // generator, we still inherit the yieldHandling of the // memberExpression, per spec. Curious. 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(); if (isSpread) @@ -8853,13 +8869,6 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, return null(); } - TokenPos nextMemberPos = pos(); - nextMember = tt == TokenKind::Lp - ? handler.newCall(nextMemberPos) - : handler.newTaggedTemplate(nextMemberPos); - if (!nextMember) - return null(); - JSOp op = JSOP_CALL; bool maybeAsyncArrow = false; if (PropertyName* prop = handler.maybeDottedProperty(lhs)) { @@ -8901,13 +8910,11 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, } } - handler.setBeginPosition(nextMember, lhs); - handler.addList(nextMember, lhs); - if (tt == TokenKind::Lp) { bool isSpread = false; PossibleError* asyncPossibleError = maybeAsyncArrow ? possibleError : nullptr; - if (!argumentList(yieldHandling, nextMember, &isSpread, asyncPossibleError)) + Node args = argumentList(yieldHandling, &isSpread, asyncPossibleError); + if (!args) return null(); if (isSpread) { if (op == JSOP_EVAL) @@ -8917,8 +8924,20 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, else op = JSOP_SPREADCALL; } + + nextMember = handler.newCall(lhs, args); + if (!nextMember) + return null(); } 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(); } handler.setOp(nextMember, op); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 000dfb465395..b27b6bde405e 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -569,8 +569,12 @@ class MOZ_STACK_CLASS PerHandlerParser bool isValidSimpleAssignmentTarget(Node node, FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls); - Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) { - return handler.newPropertyAccess(expr, key, end); + Node newPropertyName(PropertyName* key, const TokenPos& pos) { + 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, @@ -1155,7 +1159,7 @@ class MOZ_STACK_CLASS GeneralParser Node condition(InHandling inHandling, YieldHandling yieldHandling); - bool argumentList(YieldHandling yieldHandling, Node listNode, bool* isSpread, + Node argumentList(YieldHandling yieldHandling, bool* isSpread, PossibleError* possibleError = nullptr); Node destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling, TokenKind tt); diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 320720ce42af..6286c54a6380 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -248,9 +248,11 @@ class SyntaxParseHandler MOZ_MUST_USE bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; } void addArrayElement(Node literal, Node element) { } - Node newCall(const TokenPos& pos) { return NodeFunctionCall; } - Node newSuperCall(Node callee) { return NodeGeneric; } - Node newTaggedTemplate(const TokenPos& pos) { return NodeGeneric; } + Node newArguments(const TokenPos& pos) { return NodeGeneric; } + Node newCall(Node callee, Node args) { return NodeFunctionCall; } + + Node newSuperCall(Node callee, Node args) { return NodeGeneric; } + Node newTaggedTemplate(Node callee, Node args) { return NodeGeneric; } Node newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; } Node newClassMethodList(uint32_t begin) { return NodeGeneric; } @@ -332,8 +334,12 @@ class SyntaxParseHandler } Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; } - Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) { - lastAtom = key; + Node newPropertyName(PropertyName* name, const TokenPos& pos) { + lastAtom = name; + return NodeGeneric; + } + + Node newPropertyAccess(Node expr, Node key) { return NodeDottedProperty; } @@ -428,7 +434,7 @@ class SyntaxParseHandler list == NodeFunctionCall); } - Node newNewExpression(uint32_t begin, Node ctor) { + Node newNewExpression(uint32_t begin, Node ctor, Node args) { return NodeGeneric; } diff --git a/js/src/gc/GC.cpp b/js/src/gc/GC.cpp index 82a89f6c2287..ffe8b80380fc 100644 --- a/js/src/gc/GC.cpp +++ b/js/src/gc/GC.cpp @@ -2778,9 +2778,21 @@ ForegroundUpdateKinds(AllocKinds kinds) void 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(); - 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(); + 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 diff --git a/js/src/jit-test/lib/assert-offset-columns.js b/js/src/jit-test/lib/assert-offset-columns.js index e69d07a6dde3..0377f870dc53 100644 --- a/js/src/jit-test/lib/assert-offset-columns.js +++ b/js/src/jit-test/lib/assert-offset-columns.js @@ -33,7 +33,12 @@ function assertOffsetColumns(code, expectedBpts, expectedOrdering = null) { // Set breakpoints everywhere and call the function. 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()) { assertEq(offset.lineNumber, 1); assertEq(offset.columnNumber < execCode.length, true); diff --git a/js/src/jit-test/tests/cacheir/compare.js b/js/src/jit-test/tests/cacheir/compare.js index e92c1336d10f..dc0d311734d9 100644 --- a/js/src/jit-test/tests/cacheir/compare.js +++ b/js/src/jit-test/tests/cacheir/compare.js @@ -1,16 +1,193 @@ -function warmup(fun, input, expected) { - assertEq(input.length, expected.length); - for (var i = 0; i < 30; i++) { - for (var j = 0; j < input.length; j++) { - lhs = input[j][0]; - rhs = input[j][1]; - assertEq(fun(lhs,rhs), expected[j]); +setJitCompilerOption('ion.forceinlineCaches', 1); + +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++) { + var str = ""; + var y = fun(input_lhs, input_rhs); + if (y != output) { + 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], - [0, -0]], - [true, true, false, false, false, - true]); \ No newline at end of file + +// Int32 + Int32 +var Int32Int32Fun_GT1 = (a, b) => { return a > b; } +warmup(Int32Int32Fun_GT1, [[1,2, false], [1,1, false], [3,4, false], [4294967295, 2, 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]]); \ No newline at end of file diff --git a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js index f12cf81fdf72..d8333155330d 100644 --- a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js +++ b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js @@ -83,3 +83,58 @@ assertOffsetColumns( "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", +); diff --git a/js/src/jit-test/tests/gc/bug-1481093.js b/js/src/jit-test/tests/gc/bug-1481093.js new file mode 100644 index 000000000000..1b0a70172916 --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-1481093.js @@ -0,0 +1,5 @@ +v = new new TypedObject.StructType({ + f: TypedObject.Any +}) +gczeal(14); +var lfOffThreadGlobal = newGlobal(); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index b091882857da..928193a6e82a 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -5014,6 +5014,127 @@ ICBinaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& 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 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(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_NE: + if (!LooselyEqual(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_STRICTEQ: + if (!StrictlyEqual(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_STRICTNE: + if (!StrictlyEqual(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(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 // diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index cf31c42e9794..7adc581d53ba 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -6,6 +6,7 @@ #include "jit/BaselineInspector.h" +#include "mozilla/Array.h" #include "mozilla/DebugOnly.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 -// converts its operands to doubles. +// Return the MIRtype corresponding to the guard the reader is pointing +// 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 -CanUseDoubleCompare(ICStub::Kind kind) +GuardType(CacheIRReader& reader, mozilla::Array& 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 -// converts its operands to int32. -static bool -CanUseInt32Compare(ICStub::Kind kind) +// This code works for all Compare ICs where the pattern is +// +// +// +// +// +// 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 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 @@ -442,37 +567,19 @@ BaselineInspector::expectedCompareType(jsbytecode* pc) return MCompare::Compare_Unknown; } - if (CanUseInt32Compare(first->kind()) && (!second || CanUseInt32Compare(second->kind()))) { - ICCompare_Int32WithBoolean* coerce = - first->isCompare_Int32WithBoolean() - ? 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; - } + MCompare::CompareType first_type = ParseCacheIRStubForCompareType(first->toCacheIR_Regular()); + if (!second) + return first_type; - if (CanUseDoubleCompare(first->kind()) && (!second || CanUseDoubleCompare(second->kind()))) { - 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; - } + MCompare::CompareType second_type = ParseCacheIRStubForCompareType(second->toCacheIR_Regular()); - return MCompare::Compare_Unknown; + if (first_type == MCompare::Compare_Unknown || second_type == MCompare::Compare_Unknown) + return MCompare::Compare_Unknown; + + if (first_type == second_type) + return first_type; + + return CompatibleType(first_type, second_type); } static bool diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index 2dd4aed2463b..d29eea2dd0cb 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -37,6 +37,33 @@ CacheIRWriter::assertSameCompartment(JSObject* 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, ICState::Mode mode) : writer(cx), @@ -4813,6 +4840,122 @@ CompareIRGenerator::tryAttachStrictDifferentTypes(ValOperandId lhsId, ValOperand 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 CompareIRGenerator::tryAttachStub() { @@ -4823,6 +4966,12 @@ CompareIRGenerator::tryAttachStub() 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 rhsId(writer.setInputOperandId(1)); @@ -4833,13 +4982,30 @@ CompareIRGenerator::tryAttachStub() return true; if (tryAttachSymbol(lhsId, rhsId)) return true; + if (tryAttachObjectUndefined(lhsId, rhsId)) + return true; if (tryAttachStrictDifferentTypes(lhsId, rhsId)) return true; - trackAttached(IRGenerator::NotAttached); - return false; + // This should come after strictDifferent types to + // 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); return false; } @@ -4851,6 +5017,7 @@ CompareIRGenerator::trackAttached(const char* name) if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) { sp.valueProperty("lhs", lhsVal_); sp.valueProperty("rhs", rhsVal_); + sp.opcodeProperty("op", op_); } #endif } diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index 2636b7050f10..993b19f8486c 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -182,6 +182,9 @@ extern const char* CacheKindNames[]; _(GuardIsObject) \ _(GuardIsObjectOrNull) \ _(GuardIsNullOrUndefined) \ + _(GuardIsNotNullOrUndefined) \ + _(GuardIsNull) \ + _(GuardIsUndefined) \ _(GuardIsBoolean) \ _(GuardIsString) \ _(GuardIsSymbol) \ @@ -324,6 +327,9 @@ extern const char* CacheKindNames[]; _(CompareStringResult) \ _(CompareObjectResult) \ _(CompareSymbolResult) \ + _(CompareInt32Result) \ + _(CompareDoubleResult) \ + _(CompareObjectUndefinedNullResult) \ \ _(CallPrintString) \ _(Breakpoint) \ @@ -438,6 +444,10 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter static const size_t MaxStubDataSizeInBytes = 20 * sizeof(uintptr_t); bool tooLarge_; + // Basic caching to avoid quadatic lookup behaviour in readStubFieldForIon. + mutable uint32_t lastOffset_; + mutable uint32_t lastIndex_; + void assertSameCompartment(JSObject*); void writeOp(CacheOp op) { @@ -501,7 +511,9 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter nextInstructionId_(0), numInputOperands_(0), stubDataSize_(0), - tooLarge_(false) + tooLarge_(false), + lastOffset_(0), + lastIndex_(0) {} 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 // shouldn't bake in stub values. - StubField readStubFieldForIon(size_t i, StubField::Type type) const { - MOZ_ASSERT(stubFields_[i].type() == type); - return stubFields_[i]; - } + StubField readStubFieldForIon(uint32_t offset, StubField::Type type) const; ObjOperandId guardIsObject(ValOperandId val) { writeOpWithOperandId(CacheOp::GuardIsObject, val); @@ -600,6 +609,15 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter void guardIsNullOrUndefined(ValOperandId 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) { MOZ_ASSERT(shape); writeOpWithOperandId(CacheOp::GuardShape, obj); @@ -1257,11 +1275,25 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter writeOperandId(rhs); 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) { writeOpWithOperandId(CacheOp::CompareSymbolResult, lhs); writeOperandId(rhs); 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) { writeOp(CacheOp::CallPrintString); @@ -1816,6 +1848,11 @@ class MOZ_RAII CompareIRGenerator : public IRGenerator bool tryAttachObject(ValOperandId lhsId, ValOperandId rhsId); bool tryAttachSymbol(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); diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp index e901aae4520e..b7f7524fcaa7 100644 --- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -93,7 +93,7 @@ CacheRegisterAllocator::useValueRegister(MacroAssembler& masm, ValOperandId op) // Load a value operand directly into a float register. Caller must have // guarded isNumber on the provided val. void -CacheRegisterAllocator::loadDouble(MacroAssembler& masm, ValOperandId op, FloatRegister dest) +CacheRegisterAllocator::ensureDoubleRegister(MacroAssembler& masm, ValOperandId op, FloatRegister dest) { OperandLocation& loc = operandLocations_[op.id()]; @@ -125,12 +125,12 @@ CacheRegisterAllocator::loadDouble(MacroAssembler& masm, ValOperandId op, FloatR case OperandLocation::PayloadStack: case OperandLocation::PayloadReg: case OperandLocation::Uninitialized: - MOZ_CRASH("Unhandled operand type in loadDouble"); + MOZ_CRASH("Unhandled operand type in ensureDoubleRegister"); return; } masm.jump(&done); 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); } @@ -1377,6 +1377,63 @@ CacheIRCompiler::emitGuardIsNullOrUndefined() 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 CacheIRCompiler::emitGuardIsObjectOrNull() { @@ -1994,8 +2051,8 @@ CacheIRCompiler::emitDoubleAddResult() // Float register must be preserved. The BinaryArith ICs use // the fact that baseline has them available, as well as fixed temps on // LBinaryCache. - allocator.loadDouble(masm, reader.valOperandId(), FloatReg0); - allocator.loadDouble(masm, reader.valOperandId(), FloatReg1); + allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0); + allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1); masm.addDouble(FloatReg1, FloatReg0); masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0); @@ -2007,8 +2064,8 @@ CacheIRCompiler::emitDoubleSubResult() { AutoOutputRegister output(*this); - allocator.loadDouble(masm, reader.valOperandId(), FloatReg0); - allocator.loadDouble(masm, reader.valOperandId(), FloatReg1); + allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0); + allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1); masm.subDouble(FloatReg1, FloatReg0); masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0); @@ -2020,8 +2077,8 @@ CacheIRCompiler::emitDoubleMulResult() { AutoOutputRegister output(*this); - allocator.loadDouble(masm, reader.valOperandId(), FloatReg0); - allocator.loadDouble(masm, reader.valOperandId(), FloatReg1); + allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0); + allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1); masm.mulDouble(FloatReg1, FloatReg0); masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0); @@ -2033,8 +2090,8 @@ CacheIRCompiler::emitDoubleDivResult() { AutoOutputRegister output(*this); - allocator.loadDouble(masm, reader.valOperandId(), FloatReg0); - allocator.loadDouble(masm, reader.valOperandId(), FloatReg1); + allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0); + allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1); masm.divDouble(FloatReg1, FloatReg0); masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0); @@ -2047,8 +2104,8 @@ CacheIRCompiler::emitDoubleModResult() AutoOutputRegister output(*this); AutoScratchRegisterMaybeOutput scratch(allocator, masm, output); - allocator.loadDouble(masm, reader.valOperandId(), FloatReg0); - allocator.loadDouble(masm, reader.valOperandId(), FloatReg1); + allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0); + allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1); LiveRegisterSet save(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs()); masm.PushRegsInMask(save); @@ -3092,6 +3149,79 @@ CacheIRCompiler::emitCompareSymbolResult() 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 CacheIRCompiler::emitCallPrintString() { diff --git a/js/src/jit/CacheIRCompiler.h b/js/src/jit/CacheIRCompiler.h index 41566fb533af..1af08a4c172c 100644 --- a/js/src/jit/CacheIRCompiler.h +++ b/js/src/jit/CacheIRCompiler.h @@ -19,6 +19,9 @@ namespace jit { #define CACHE_IR_SHARED_OPS(_) \ _(GuardIsObject) \ _(GuardIsNullOrUndefined) \ + _(GuardIsNotNullOrUndefined) \ + _(GuardIsNull) \ + _(GuardIsUndefined) \ _(GuardIsObjectOrNull) \ _(GuardIsBoolean) \ _(GuardIsString) \ @@ -93,6 +96,9 @@ namespace jit { _(LoadObjectTruthyResult) \ _(CompareObjectResult) \ _(CompareSymbolResult) \ + _(CompareInt32Result) \ + _(CompareDoubleResult) \ + _(CompareObjectUndefinedNullResult) \ _(ArrayJoinResult) \ _(CallPrintString) \ _(Breakpoint) \ @@ -488,8 +494,10 @@ class MOZ_RAII CacheRegisterAllocator Register defineRegister(MacroAssembler& masm, TypedOperandId typedId); ValueOperand defineValueRegister(MacroAssembler& masm, ValOperandId val); - // Loads (and unboxes) a value into a float register (caller guarded) - void loadDouble(MacroAssembler&, ValOperandId, FloatRegister); + // Loads (potentially coercing) and unboxes a value into a float register + // 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. JSValueType knownType(ValOperandId val) const; @@ -634,8 +642,6 @@ class MOZ_RAII CacheIRCompiler // sizeof(stubType) uint32_t stubDataOffset_; - uint32_t nextStubField_; - enum class StubFieldPolicy { Address, Constant @@ -651,7 +657,6 @@ class MOZ_RAII CacheIRCompiler liveFloatRegs_(FloatRegisterSet::All()), mode_(mode), stubDataOffset_(stubDataOffset), - nextStubField_(0), stubFieldPolicy_(policy) { MOZ_ASSERT(!writer.failed()); @@ -716,15 +721,12 @@ class MOZ_RAII CacheIRCompiler uintptr_t readStubWord(uint32_t offset, StubField::Type type) { MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant); MOZ_ASSERT((offset % sizeof(uintptr_t)) == 0); - // We use nextStubField_ to access the data as it's stored in an as-of-yet - // 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(); + return writer_.readStubFieldForIon(offset, type).asWord(); } uint64_t readStubInt64(uint32_t offset, StubField::Type type) { MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant); 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) { MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 18f21d18d372..5376240992c0 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -193,6 +193,11 @@ typedef bool (*IonBinaryArithICFn)(JSContext* cx, HandleScript outerScript, IonB static const VMFunction IonBinaryArithICInfo = FunctionInfo(IonBinaryArithIC::update, "IonBinaryArithIC::update"); +typedef bool (*IonCompareICFn)(JSContext* cx, HandleScript outerScript, IonCompareIC* stub, + HandleValue lhs, HandleValue rhs, MutableHandleValue res); +static const VMFunction IonCompareICInfo = + FunctionInfo(IonCompareIC::update, "IonCompareIC::update"); + void CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) { @@ -402,8 +407,24 @@ CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) masm.jump(ool->rejoin()); return; } + case CacheKind::Compare: { + IonCompareIC* compareIC = ic->asCompareIC(); + + saveLive(lir); + + pushArg(compareIC->rhs()); + pushArg(compareIC->lhs()); + icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); + pushArg(ImmGCPtr(gen->info().script())); + callVM(IonCompareICInfo, lir); + + StoreValueTo(compareIC->output()).generate(this); + restoreLiveIgnore(lir, StoreValueTo(compareIC->output()).clobbered()); + + masm.jump(ool->rejoin()); + return; + } case CacheKind::Call: - case CacheKind::Compare: case CacheKind::TypeOf: case CacheKind::ToBool: case CacheKind::GetIntrinsic: @@ -1422,6 +1443,39 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re { JitSpew(JitSpew_Codegen, "# Emitting PrepareAndExecuteRegExp"); + /* + * [SMDOC] Stack layout for PrepareAndExecuteRegExp + * + * inputOutputDataStartOffset +-----> +---------------+ + * |InputOutputData| + * inputStartAddress +----------> inputStart| + * inputEndAddress +----------> inputEnd| + * startIndexAddress +----------> startIndex| + * endIndexAddress +----------> endIndex| + * matchesPointerAddress +----------> matches| + * matchResultAddress +----------> result| + * +---------------+ + * matchPairsStartOffset +-----> +---------------+ + * | MatchPairs | + * pairCountAddress +-----------> count | + * pairsPointerAddress +-----------> pairs | + * | | + * +---------------+ + * pairsVectorStartOffset +-----> +---------------+ + * | MatchPair | + * | start | <-------+ + * | limit | | Reserved space for + * +---------------+ | `RegExpObject::MaxPairCount` + * . | MatchPair objects. + * . | + * . | + * +---------------+ | + * | MatchPair | | + * | start | <-------+ + * | limit | + * +---------------+ + */ + size_t matchPairsStartOffset = inputOutputDataStartOffset + sizeof(irregexp::InputOutputData); size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset); @@ -1442,8 +1496,6 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re Address pairsPointerAddress(masm.getStackPointer(), matchPairsStartOffset + MatchPairs::offsetOfPairs()); - Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset); - RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global()); if (!res) return false; @@ -1456,8 +1508,18 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re // passed to the OOL stub in the caller if we aren't able to execute the // RegExp inline, and that stub needs to be able to determine whether the // execution finished successfully. + + // Initialize MatchPairs::pairCount to 1, the correct value can only + // be determined after loading the RegExpShared. masm.store32(Imm32(1), pairCountAddress); - masm.store32(Imm32(-1), pairsVectorAddress); + + // Initialize MatchPairs::pairs[0]::start to MatchPair::NoMatch. + Address firstMatchPairStartAddress(masm.getStackPointer(), + pairsVectorStartOffset + offsetof(MatchPair, start)); + masm.store32(Imm32(MatchPair::NoMatch), firstMatchPairStartAddress); + + // Assign the MatchPairs::pairs pointer to the first MatchPair object. + Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset); masm.computeEffectiveAddress(pairsVectorAddress, temp1); masm.storePtr(temp1, pairsPointerAddress); } @@ -1507,20 +1569,19 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re // Check if input[lastIndex] is trail surrogate. masm.loadStringChars(input, temp2, CharEncoding::TwoByte); - masm.load16ZeroExtend(BaseIndex(temp2, lastIndex, TimesTwo), temp3); + masm.loadChar(temp2, lastIndex, temp3, CharEncoding::TwoByte); masm.and32(Imm32(SurrogateMask), temp3); masm.branch32(Assembler::NotEqual, temp3, Imm32(unicode::TrailSurrogateMin), &done); // Check if input[lastIndex-1] is lead surrogate. - masm.load16ZeroExtend(BaseIndex(temp2, lastIndex, TimesTwo, -int32_t(sizeof(char16_t))), - temp3); + masm.loadChar(temp2, lastIndex, temp3, CharEncoding::TwoByte, -int32_t(sizeof(char16_t))); masm.and32(Imm32(SurrogateMask), temp3); masm.branch32(Assembler::NotEqual, temp3, Imm32(unicode::LeadSurrogateMin), &done); // Move lastIndex to lead surrogate. - masm.subPtr(Imm32(1), lastIndex); + masm.sub32(Imm32(1), lastIndex); masm.bind(&done); } @@ -1578,7 +1639,7 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re masm.storePtr(temp2, endIndexAddress); } masm.storePtr(lastIndex, startIndexAddress); - masm.store32(Imm32(0), matchResultAddress); + masm.store32(Imm32(RegExpRunStatus_Error), matchResultAddress); // Save any volatile inputs. LiveGeneralRegisterSet volatileRegs; @@ -1671,13 +1732,16 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re static void CopyStringChars(MacroAssembler& masm, Register to, Register from, Register len, - Register byteOpScratch, size_t fromWidth, size_t toWidth); + Register byteOpScratch, CharEncoding encoding); class CreateDependentString { + CharEncoding encoding_; Register string_; - Register temp_; + Register temp1_; + Register temp2_; Label* failure_; + enum class FallbackKind : uint8_t { InlineString, FatInlineString, @@ -1686,85 +1750,94 @@ class CreateDependentString }; mozilla::EnumeratedArray fallbacks_, joins_; -public: + public: + CreateDependentString(CharEncoding encoding, Register string, Register temp1, Register temp2, + Label* failure) + : encoding_(encoding), string_(string), temp1_(temp1), temp2_(temp2), failure_(failure) + { } + + Register string() const { return string_; } + CharEncoding encoding() const { return encoding_; } + // Generate code that creates DependentString. // Caller should call generateFallback after masm.ret(), to generate // fallback path. void generate(MacroAssembler& masm, const JSAtomState& names, - CompileRuntime* runtime, - bool latin1, Register string, - Register base, Register temp1, Register temp2, + CompileRuntime* runtime, Register base, BaseIndex startIndexAddress, BaseIndex limitIndexAddress, - bool stringsCanBeInNursery, - Label* failure); + bool stringsCanBeInNursery); // Generate fallback path for creating DependentString. - void generateFallback(MacroAssembler& masm, LiveRegisterSet regsToSave); + void generateFallback(MacroAssembler& masm); }; void CreateDependentString::generate(MacroAssembler& masm, const JSAtomState& names, - CompileRuntime* runtime, - bool latin1, Register string, - Register base, Register temp1, Register temp2, + CompileRuntime* runtime, Register base, BaseIndex startIndexAddress, BaseIndex limitIndexAddress, - bool stringsCanBeInNursery, - Label* failure) + bool stringsCanBeInNursery) { JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString (encoding=%s)", - (latin1 ? "Latin-1" : "Two-Byte")); + (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); - string_ = string; - temp_ = temp2; - failure_ = failure; + auto newGCString = [&](FallbackKind kind) { + uint32_t flags = kind == FallbackKind::InlineString + ? JSString::INIT_THIN_INLINE_FLAGS + : kind == FallbackKind::FatInlineString + ? JSString::INIT_FAT_INLINE_FLAGS + : JSString::DEPENDENT_FLAGS; + if (encoding_ == CharEncoding::Latin1) + flags |= JSString::LATIN1_CHARS_BIT; + + if (kind != FallbackKind::FatInlineString) + masm.newGCString(string_, temp2_, &fallbacks_[kind], stringsCanBeInNursery); + else + masm.newGCFatInlineString(string_, temp2_, &fallbacks_[kind], stringsCanBeInNursery); + masm.bind(&joins_[kind]); + masm.store32(Imm32(flags), Address(string_, JSString::offsetOfFlags())); + }; // Compute the string length. - masm.load32(startIndexAddress, temp2); - masm.load32(limitIndexAddress, temp1); - masm.sub32(temp2, temp1); + masm.load32(startIndexAddress, temp2_); + masm.load32(limitIndexAddress, temp1_); + masm.sub32(temp2_, temp1_); Label done, nonEmpty; // Zero length matches use the empty string. - masm.branchTest32(Assembler::NonZero, temp1, temp1, &nonEmpty); - masm.movePtr(ImmGCPtr(names.empty), string); + masm.branchTest32(Assembler::NonZero, temp1_, temp1_, &nonEmpty); + masm.movePtr(ImmGCPtr(names.empty), string_); masm.jump(&done); masm.bind(&nonEmpty); Label notInline; - int32_t maxInlineLength = latin1 - ? (int32_t) JSFatInlineString::MAX_LENGTH_LATIN1 - : (int32_t) JSFatInlineString::MAX_LENGTH_TWO_BYTE; - masm.branch32(Assembler::Above, temp1, Imm32(maxInlineLength), ¬Inline); - + int32_t maxInlineLength = encoding_ == CharEncoding::Latin1 + ? JSFatInlineString::MAX_LENGTH_LATIN1 + : JSFatInlineString::MAX_LENGTH_TWO_BYTE; + masm.branch32(Assembler::Above, temp1_, Imm32(maxInlineLength), ¬Inline); { // Make a thin or fat inline string. Label stringAllocated, fatInline; - int32_t maxThinInlineLength = latin1 - ? (int32_t) JSThinInlineString::MAX_LENGTH_LATIN1 - : (int32_t) JSThinInlineString::MAX_LENGTH_TWO_BYTE; - masm.branch32(Assembler::Above, temp1, Imm32(maxThinInlineLength), &fatInline); - - int32_t thinFlags = (latin1 ? JSString::LATIN1_CHARS_BIT : 0) | JSString::INIT_THIN_INLINE_FLAGS; - masm.newGCString(string, temp2, &fallbacks_[FallbackKind::InlineString], stringsCanBeInNursery); - masm.bind(&joins_[FallbackKind::InlineString]); - masm.store32(Imm32(thinFlags), Address(string, JSString::offsetOfFlags())); - masm.jump(&stringAllocated); - + int32_t maxThinInlineLength = encoding_ == CharEncoding::Latin1 + ? JSThinInlineString::MAX_LENGTH_LATIN1 + : JSThinInlineString::MAX_LENGTH_TWO_BYTE; + masm.branch32(Assembler::Above, temp1_, Imm32(maxThinInlineLength), &fatInline); + { + newGCString(FallbackKind::InlineString); + masm.jump(&stringAllocated); + } masm.bind(&fatInline); - - int32_t fatFlags = (latin1 ? JSString::LATIN1_CHARS_BIT : 0) | JSString::INIT_FAT_INLINE_FLAGS; - masm.newGCFatInlineString(string, temp2, &fallbacks_[FallbackKind::FatInlineString], stringsCanBeInNursery); - masm.bind(&joins_[FallbackKind::FatInlineString]); - masm.store32(Imm32(fatFlags), Address(string, JSString::offsetOfFlags())); - + { + newGCString(FallbackKind::FatInlineString); + } masm.bind(&stringAllocated); - masm.store32(temp1, Address(string, JSString::offsetOfLength())); - masm.push(string); + masm.store32(temp1_, Address(string_, JSString::offsetOfLength())); + + masm.push(string_); masm.push(base); // Adjust the start index address for the above pushes. @@ -1773,82 +1846,68 @@ CreateDependentString::generate(MacroAssembler& masm, const JSAtomState& names, newStartIndexAddress.offset += 2 * sizeof(void*); // Load chars pointer for the new string. - masm.loadInlineStringCharsForStore(string, string); + masm.loadInlineStringCharsForStore(string_, string_); // Load the source characters pointer. - masm.loadStringChars(base, temp2, - latin1 ? CharEncoding::Latin1 : CharEncoding::TwoByte); + masm.loadStringChars(base, temp2_, encoding_); masm.load32(newStartIndexAddress, base); - if (latin1) - masm.addPtr(temp2, base); - else - masm.computeEffectiveAddress(BaseIndex(temp2, base, TimesTwo), base); + masm.addToCharPtr(temp2_, base, encoding_); - CopyStringChars(masm, string, base, temp1, temp2, latin1 ? 1 : 2, latin1 ? 1 : 2); + CopyStringChars(masm, string_, temp2_, temp1_, base, encoding_); // Null-terminate. - if (latin1) - masm.store8(Imm32(0), Address(string, 0)); - else - masm.store16(Imm32(0), Address(string, 0)); + masm.storeChar(Imm32(0), Address(string_, 0), encoding_); masm.pop(base); - masm.pop(string); + masm.pop(string_); + + masm.jump(&done); } - masm.jump(&done); masm.bind(¬Inline); { // Make a dependent string. - int32_t flags = (latin1 ? JSString::LATIN1_CHARS_BIT : 0) | JSString::DEPENDENT_FLAGS; - - masm.newGCString(string, temp2, &fallbacks_[FallbackKind::NotInlineString], stringsCanBeInNursery); // Warning: string may be tenured (if the fallback case is hit), so // stores into it must be post barriered. - masm.bind(&joins_[FallbackKind::NotInlineString]); - masm.store32(Imm32(flags), Address(string, JSString::offsetOfFlags())); - masm.store32(temp1, Address(string, JSString::offsetOfLength())); + newGCString(FallbackKind::NotInlineString); - masm.loadNonInlineStringChars(base, temp1, - latin1 ? CharEncoding::Latin1 : CharEncoding::TwoByte); - masm.load32(startIndexAddress, temp2); - if (latin1) - masm.addPtr(temp2, temp1); - else - masm.computeEffectiveAddress(BaseIndex(temp1, temp2, TimesTwo), temp1); - masm.storeNonInlineStringChars(temp1, string); - masm.storeDependentStringBase(base, string); - masm.movePtr(base, temp1); + masm.store32(temp1_, Address(string_, JSString::offsetOfLength())); + + masm.loadNonInlineStringChars(base, temp1_, encoding_); + masm.load32(startIndexAddress, temp2_); + masm.addToCharPtr(temp1_, temp2_, encoding_); + masm.storeNonInlineStringChars(temp1_, string_); + masm.storeDependentStringBase(base, string_); + masm.movePtr(base, temp1_); // Follow any base pointer if the input is itself a dependent string. // Watch for undepended strings, which have a base pointer but don't // actually share their characters with it. Label noBase; - masm.load32(Address(base, JSString::offsetOfFlags()), temp2); - masm.and32(Imm32(JSString::TYPE_FLAGS_MASK), temp2); - masm.branch32(Assembler::NotEqual, temp2, Imm32(JSString::DEPENDENT_FLAGS), &noBase); - masm.loadDependentStringBase(base, temp1); - masm.storeDependentStringBase(temp1, string); + masm.load32(Address(base, JSString::offsetOfFlags()), temp2_); + masm.and32(Imm32(JSString::TYPE_FLAGS_MASK), temp2_); + masm.branch32(Assembler::NotEqual, temp2_, Imm32(JSString::DEPENDENT_FLAGS), &noBase); + masm.loadDependentStringBase(base, temp1_); + masm.storeDependentStringBase(temp1_, string_); masm.bind(&noBase); // Post-barrier the base store, whether it was the direct or indirect // base (both will end up in temp1 here). - masm.branchPtrInNurseryChunk(Assembler::Equal, string, temp2, &done); - masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp1, temp2, &done); + masm.branchPtrInNurseryChunk(Assembler::Equal, string_, temp2_, &done); + masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp1_, temp2_, &done); LiveRegisterSet regsToSave(RegisterSet::Volatile()); - regsToSave.takeUnchecked(temp1); - regsToSave.takeUnchecked(temp2); - regsToSave.addUnchecked(string); + regsToSave.takeUnchecked(temp1_); + regsToSave.takeUnchecked(temp2_); masm.PushRegsInMask(regsToSave); - masm.mov(ImmPtr(runtime), temp1); + masm.mov(ImmPtr(runtime), temp1_); - masm.setupUnalignedABICall(temp2); - masm.passABIArg(temp1); - masm.passABIArg(string); + masm.setupUnalignedABICall(temp2_); + masm.passABIArg(temp1_); + masm.passABIArg(string_); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier)); masm.PopRegsInMask(regsToSave); @@ -1872,12 +1931,15 @@ AllocateFatInlineString(JSContext* cx) } void -CreateDependentString::generateFallback(MacroAssembler& masm, LiveRegisterSet regsToSave) +CreateDependentString::generateFallback(MacroAssembler& masm) { - JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString fallback"); + JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString fallback (encoding=%s)", + (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); + + LiveRegisterSet regsToSave(RegisterSet::Volatile()); + regsToSave.takeUnchecked(string_); + regsToSave.takeUnchecked(temp2_); - regsToSave.take(string_); - regsToSave.take(temp_); for (FallbackKind kind : mozilla::MakeEnumeratedRange(FallbackKind::Count)) { masm.bind(&fallbacks_[kind]); @@ -1908,27 +1970,28 @@ CreateMatchResultFallbackFunc(JSContext* cx, gc::AllocKind kind, size_t nDynamic } static void -CreateMatchResultFallback(MacroAssembler& masm, LiveRegisterSet regsToSave, - Register object, Register temp2, Register temp5, - ArrayObject* templateObj, Label* fail) +CreateMatchResultFallback(MacroAssembler& masm, Register object, Register temp1, Register temp2, + const TemplateObject& templateObject, Label* fail) { JitSpew(JitSpew_Codegen, "# Emitting CreateMatchResult fallback"); - MOZ_ASSERT(templateObj->group()->clasp() == &ArrayObject::class_); + MOZ_ASSERT(templateObject.isArrayObject()); + + LiveRegisterSet regsToSave(RegisterSet::Volatile()); + regsToSave.takeUnchecked(object); + regsToSave.takeUnchecked(temp1); + regsToSave.takeUnchecked(temp2); - regsToSave.take(object); - regsToSave.take(temp2); - regsToSave.take(temp5); masm.PushRegsInMask(regsToSave); masm.setupUnalignedABICall(object); masm.loadJSContext(object); masm.passABIArg(object); - masm.move32(Imm32(int32_t(templateObj->asTenured().getAllocKind())), temp2); + masm.move32(Imm32(int32_t(templateObject.getAllocKind())), temp1); + masm.passABIArg(temp1); + masm.move32(Imm32(int32_t(templateObject.asNativeTemplateObject().numDynamicSlots())), temp2); masm.passABIArg(temp2); - masm.move32(Imm32(int32_t(templateObj->as().numDynamicSlots())), temp5); - masm.passABIArg(temp5); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, CreateMatchResultFallbackFunc)); masm.storeCallPointerResult(object); @@ -1936,8 +1999,7 @@ CreateMatchResultFallback(MacroAssembler& masm, LiveRegisterSet regsToSave, masm.branchPtr(Assembler::Equal, object, ImmWord(0), fail); - TemplateObject templateObject(templateObj); - masm.initGCThing(object, temp2, templateObject, true); + masm.initGCThing(object, temp1, templateObject, true); } JitCode* @@ -1956,36 +2018,26 @@ JitRealm::generateRegExpMatcherStub(JSContext* cx) regs.take(regexp); regs.take(lastIndex); - // temp5 is used in single byte instructions when creating dependent - // strings, and has restrictions on which register it can be on some - // platforms. - Register temp5; - { - AllocatableGeneralRegisterSet oregs = regs; - do { - temp5 = oregs.takeAny(); - } while (!MacroAssembler::canUseInSingleByteInstruction(temp5)); - regs.take(temp5); - } - Register temp1 = regs.takeAny(); Register temp2 = regs.takeAny(); Register temp3 = regs.takeAny(); - - Register maybeTemp4 = InvalidReg; + Register temp4 = regs.takeAny(); + Register maybeTemp5 = InvalidReg; if (!regs.empty()) { // There are not enough registers on x86. - maybeTemp4 = regs.takeAny(); + maybeTemp5 = regs.takeAny(); } ArrayObject* templateObject = cx->realm()->regExps.getOrCreateMatchResultTemplateObject(cx); if (!templateObject) return nullptr; + TemplateObject templateObj(templateObject); + const NativeTemplateObject& nativeTemplateObj = templateObj.asNativeTemplateObject(); // The template object should have enough space for the maximum number of // pairs this stub can handle. MOZ_ASSERT(ObjectElements::VALUES_PER_HEADER + RegExpObject::MaxPairCount == - gc::GetGCKindSlots(templateObject->asTenured().getAllocKind())); + gc::GetGCKindSlots(templateObj.getAllocKind())); StackMacroAssembler masm(cx); @@ -1994,7 +2046,7 @@ JitRealm::generateRegExpMatcherStub(JSContext* cx) Label notFound, oolEntry; if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex, - temp1, temp2, temp5, inputOutputDataStartOffset, + temp1, temp2, temp3, inputOutputDataStartOffset, RegExpShared::Normal, stringsCanBeInNursery, ¬Found, &oolEntry)) { return nullptr; @@ -2003,97 +2055,124 @@ JitRealm::generateRegExpMatcherStub(JSContext* cx) // Construct the result. Register object = temp1; Label matchResultFallback, matchResultJoin; - TemplateObject templateObj(templateObject); masm.createGCObject(object, temp2, templateObj, gc::DefaultHeap, &matchResultFallback); masm.bind(&matchResultJoin); // Initialize slots of result object. + MOZ_ASSERT(nativeTemplateObj.numFixedSlots() == 0); + MOZ_ASSERT(nativeTemplateObj.numDynamicSlots() == 2); + static_assert(RegExpRealm::MatchResultObjectIndexSlot == 0, + "First slot holds the 'index' property"); + static_assert(RegExpRealm::MatchResultObjectInputSlot == 1, + "Second slot holds the 'input' property"); + masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2); - masm.storeValue(templateObject->getSlot(0), Address(temp2, 0)); - masm.storeValue(templateObject->getSlot(1), Address(temp2, sizeof(Value))); + masm.storeValue(nativeTemplateObj.getSlot(RegExpRealm::MatchResultObjectIndexSlot), + Address(temp2, 0)); + masm.storeValue(nativeTemplateObj.getSlot(RegExpRealm::MatchResultObjectInputSlot), + Address(temp2, sizeof(Value))); - size_t elementsOffset = NativeObject::offsetOfFixedElements(); + /* + * [SMDOC] Stack layout for the RegExpMatcher stub + * + * +---------------+ + * |Return-Address | + * +---------------+ + * inputOutputDataStartOffset +-----> +---------------+ + * |InputOutputData| + * +---------------+ + * +---------------+ + * | MatchPairs | + * pairsCountAddress +-----------> count | + * | pairs | + * | | + * +---------------+ + * pairsVectorStartOffset +-----> +---------------+ + * | MatchPair | + * matchPairStart +------------> start | <-------+ + * matchPairLimit +------------> limit | | Reserved space for + * +---------------+ | `RegExpObject::MaxPairCount` + * . | MatchPair objects. + * . | + * . | `count` objects will be + * +---------------+ | initialized and can be + * | MatchPair | | accessed below. + * | start | <-------+ + * | limit | + * +---------------+ + */ -#ifdef DEBUG - // Assert the initial value of initializedLength and length to make sure - // restoration on failure case works. - { - Label initLengthOK, lengthOK; - masm.branch32(Assembler::Equal, - Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()), - Imm32(templateObject->getDenseInitializedLength()), - &initLengthOK); - masm.assumeUnreachable("Initial value of the match object's initializedLength does not match to restoration."); - masm.bind(&initLengthOK); + static_assert(sizeof(MatchPair) == 2 * sizeof(int32_t), + "MatchPair consists of two int32 values representing the start" + "and the end offset of the match"); - masm.branch32(Assembler::Equal, - Address(object, elementsOffset + ObjectElements::offsetOfLength()), - Imm32(templateObject->length()), - &lengthOK); - masm.assumeUnreachable("Initial value of The match object's length does not match to restoration."); - masm.bind(&lengthOK); - } -#endif + Address pairCountAddress = RegExpPairCountAddress(masm, inputOutputDataStartOffset); + size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset); + Address firstMatchPairStartAddress(masm.getStackPointer(), + pairsVectorStartOffset + offsetof(MatchPair, start)); + + // Incremented by one below for each match pair. Register matchIndex = temp2; masm.move32(Imm32(0), matchIndex); - size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset); - Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset); - Address pairCountAddress = RegExpPairCountAddress(masm, inputOutputDataStartOffset); + // The element in which to store the result of the current match. + size_t elementsOffset = NativeObject::offsetOfFixedElements(); + BaseObjectElementIndex objectMatchElement(object, matchIndex, elementsOffset); - BaseIndex stringAddress(object, matchIndex, TimesEight, elementsOffset); + // The current match pair's "start" and "limit" member. + BaseIndex matchPairStart(masm.getStackPointer(), matchIndex, TimesEight, + pairsVectorStartOffset + offsetof(MatchPair, start)); + BaseIndex matchPairLimit(masm.getStackPointer(), matchIndex, TimesEight, + pairsVectorStartOffset + offsetof(MatchPair, limit)); - JS_STATIC_ASSERT(sizeof(MatchPair) == 8); - BaseIndex stringIndexAddress(masm.getStackPointer(), matchIndex, TimesEight, - pairsVectorStartOffset + offsetof(MatchPair, start)); - BaseIndex stringLimitAddress(masm.getStackPointer(), matchIndex, TimesEight, - pairsVectorStartOffset + offsetof(MatchPair, limit)); + Register temp5; + if (maybeTemp5 == InvalidReg) { + // We don't have enough registers for a fifth temporary. Reuse + // |lastIndex| as a temporary. We don't need to restore its value, + // because |lastIndex| is no longer used after a successful match. + // (Neither here nor in the OOL path, cf. js::RegExpMatcherRaw.) + temp5 = lastIndex; + } else { + temp5 = maybeTemp5; + } // Loop to construct the match strings. There are two different loops, - // depending on whether the input is latin1. - CreateDependentString depStr[2]; + // depending on whether the input is a Two-Byte or a Latin-1 string. + CreateDependentString depStrs[] { + { CharEncoding::TwoByte, temp3, temp4, temp5, &oolEntry }, + { CharEncoding::Latin1, temp3, temp4, temp5, &oolEntry }, + }; - // depStr may refer to failureRestore during generateFallback below, - // so this variable must live outside of the block. - Label failureRestore; { Label isLatin1, done; masm.branchLatin1String(input, &isLatin1); - Label* failure = &oolEntry; - Register temp4 = (maybeTemp4 == InvalidReg) ? lastIndex : maybeTemp4; - - if (maybeTemp4 == InvalidReg) { - failure = &failureRestore; - - // Save lastIndex value to temporary space. - masm.store32(lastIndex, Address(object, elementsOffset + ObjectElements::offsetOfLength())); - } - - for (int isLatin = 0; isLatin <= 1; isLatin++) { - if (isLatin) + for (auto& depStr : depStrs) { + if (depStr.encoding() == CharEncoding::Latin1) masm.bind(&isLatin1); Label matchLoop; masm.bind(&matchLoop); + static_assert(MatchPair::NoMatch == -1, + "MatchPair::start is negative if no match was found"); + Label isUndefined, storeDone; - masm.branch32(Assembler::LessThan, stringIndexAddress, Imm32(0), &isUndefined); + masm.branch32(Assembler::LessThan, matchPairStart, Imm32(0), &isUndefined); + { + depStr.generate(masm, cx->names(), CompileRuntime::get(cx->runtime()), + input, matchPairStart, matchPairLimit, + stringsCanBeInNursery); - depStr[isLatin].generate(masm, cx->names(), - CompileRuntime::get(cx->runtime()), - isLatin, temp3, input, temp4, temp5, - stringIndexAddress, stringLimitAddress, - stringsCanBeInNursery, - failure); - - masm.storeValue(JSVAL_TYPE_STRING, temp3, stringAddress); - // Storing into nursery-allocated results object's elements; no post barrier. - masm.jump(&storeDone); + // Storing into nursery-allocated results object's elements; no post barrier. + masm.storeValue(JSVAL_TYPE_STRING, depStr.string(), objectMatchElement); + masm.jump(&storeDone); + } masm.bind(&isUndefined); - - masm.storeValue(UndefinedValue(), stringAddress); + { + masm.storeValue(UndefinedValue(), objectMatchElement); + } masm.bind(&storeDone); masm.add32(Imm32(1), matchIndex); @@ -2101,42 +2180,26 @@ JitRealm::generateRegExpMatcherStub(JSContext* cx) masm.jump(&matchLoop); } - if (maybeTemp4 == InvalidReg) { - // Restore lastIndex value from temporary space, both for success - // and failure cases. - - masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfLength()), lastIndex); - masm.jump(&done); - - masm.bind(&failureRestore); - masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfLength()), lastIndex); - - // Restore the match object for failure case. - masm.store32(Imm32(templateObject->getDenseInitializedLength()), - Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength())); - masm.store32(Imm32(templateObject->length()), - Address(object, elementsOffset + ObjectElements::offsetOfLength())); - masm.jump(&oolEntry); - } +#ifdef DEBUG + masm.assumeUnreachable("The match string loop doesn't fall through."); +#endif masm.bind(&done); } // Fill in the rest of the output object. - masm.store32(matchIndex, Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength())); - masm.store32(matchIndex, Address(object, elementsOffset + ObjectElements::offsetOfLength())); + masm.store32(matchIndex, + Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength())); + masm.store32(matchIndex, + Address(object, elementsOffset + ObjectElements::offsetOfLength())); masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2); - MOZ_ASSERT(templateObject->numFixedSlots() == 0); - MOZ_ASSERT(templateObject->lookupPure(cx->names().index)->slot() == 0); - MOZ_ASSERT(templateObject->lookupPure(cx->names().input)->slot() == 1); - - masm.load32(pairsVectorAddress, temp3); + masm.load32(firstMatchPairStartAddress, temp3); masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0)); - Address inputSlotAddress(temp2, sizeof(Value)); - masm.storeValue(JSVAL_TYPE_STRING, input, inputSlotAddress); - // No post barrier needed (inputSlotAddress is within nursery object.) + + // No post barrier needed (address is within nursery object.) + masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value))); // All done! masm.tagValue(JSVAL_TYPE_OBJECT, object, result); @@ -2146,24 +2209,13 @@ JitRealm::generateRegExpMatcherStub(JSContext* cx) masm.moveValue(NullValue(), result); masm.ret(); - // Fallback paths for CreateDependentString and createGCObject. - // Need to save all registers in use when they were called. - LiveRegisterSet regsToSave(RegisterSet::Volatile()); - regsToSave.addUnchecked(regexp); - regsToSave.addUnchecked(input); - regsToSave.addUnchecked(lastIndex); - regsToSave.addUnchecked(temp1); - regsToSave.addUnchecked(temp2); - regsToSave.addUnchecked(temp3); - if (maybeTemp4 != InvalidReg) - regsToSave.addUnchecked(maybeTemp4); - regsToSave.addUnchecked(temp5); - - for (int isLatin = 0; isLatin <= 1; isLatin++) - depStr[isLatin].generateFallback(masm, regsToSave); + // Fallback paths for CreateDependentString. + for (auto& depStr : depStrs) + depStr.generateFallback(masm); + // Fallback path for createGCObject. masm.bind(&matchResultFallback); - CreateMatchResultFallback(masm, regsToSave, object, temp2, temp5, templateObject, &oolEntry); + CreateMatchResultFallback(masm, object, temp2, temp3, templateObj, &oolEntry); masm.jump(&matchResultJoin); // Use an undefined value to signal to the caller that the OOL stub needs to be called. @@ -2312,14 +2364,44 @@ JitRealm::generateRegExpSearcherStub(JSContext* cx) return nullptr; } - size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset); - Address stringIndexAddress(masm.getStackPointer(), - pairsVectorStartOffset + offsetof(MatchPair, start)); - Address stringLimitAddress(masm.getStackPointer(), - pairsVectorStartOffset + offsetof(MatchPair, limit)); + /* + * [SMDOC] Stack layout for the RegExpSearcher stub + * + * +---------------+ + * |Return-Address | + * +---------------+ + * inputOutputDataStartOffset +-----> +---------------+ + * |InputOutputData| + * +---------------+ + * +---------------+ + * | MatchPairs | + * | count | + * | pairs | + * | | + * +---------------+ + * pairsVectorStartOffset +-----> +---------------+ + * | MatchPair | + * matchPairStart +------------> start | <-------+ + * matchPairLimit +------------> limit | | Reserved space for + * +---------------+ | `RegExpObject::MaxPairCount` + * . | MatchPair objects. + * . | + * . | Only a single object will + * +---------------+ | be initialized and can be + * | MatchPair | | accessed below. + * | start | <-------+ + * | limit | + * +---------------+ + */ - masm.load32(stringIndexAddress, result); - masm.load32(stringLimitAddress, input); + size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset); + Address matchPairStart(masm.getStackPointer(), + pairsVectorStartOffset + offsetof(MatchPair, start)); + Address matchPairLimit(masm.getStackPointer(), + pairsVectorStartOffset + offsetof(MatchPair, limit)); + + masm.load32(matchPairStart, result); + masm.load32(matchPairLimit, input); masm.lshiftPtr(Imm32(15), input); masm.or32(input, result); masm.ret(); @@ -2688,19 +2770,27 @@ CodeGenerator::visitOutOfLineRegExpInstanceOptimizable(OutOfLineRegExpInstanceOp } static void -FindFirstDollarIndex(MacroAssembler& masm, Register len, Register chars, - Register temp, Register output, bool isLatin1) +FindFirstDollarIndex(MacroAssembler& masm, Register str, Register len, Register temp0, + Register temp1, Register output, CharEncoding encoding) { +#ifdef DEBUG + Label ok; + masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok); + masm.assumeUnreachable("Length should be greater than 0."); + masm.bind(&ok); +#endif + + Register chars = temp0; + masm.loadStringChars(str, chars, encoding); + masm.move32(Imm32(0), output); Label start, done; masm.bind(&start); - if (isLatin1) - masm.load8ZeroExtend(BaseIndex(chars, output, TimesOne), temp); - else - masm.load16ZeroExtend(BaseIndex(chars, output, TimesTwo), temp); - masm.branch32(Assembler::Equal, temp, Imm32('$'), &done); + Register currentChar = temp1; + masm.loadChar(chars, output, currentChar, encoding); + masm.branch32(Assembler::Equal, currentChar, Imm32('$'), &done); masm.add32(Imm32(1), output); masm.branch32(Assembler::NotEqual, output, len, &start); @@ -2732,14 +2822,12 @@ CodeGenerator::visitGetFirstDollarIndex(LGetFirstDollarIndex* ins) Label isLatin1, done; masm.branchLatin1String(str, &isLatin1); { - masm.loadStringChars(str, temp0, CharEncoding::TwoByte); - FindFirstDollarIndex(masm, len, temp0, temp1, output, /* isLatin1 = */ false); + FindFirstDollarIndex(masm, str, len, temp0, temp1, output, CharEncoding::TwoByte); masm.jump(&done); } masm.bind(&isLatin1); { - masm.loadStringChars(str, temp0, CharEncoding::Latin1); - FindFirstDollarIndex(masm, len, temp0, temp1, output, /* isLatin1 = */ true); + FindFirstDollarIndex(masm, str, len, temp0, temp1, output, CharEncoding::Latin1); } masm.bind(&done); masm.bind(ool->rejoin()); @@ -2820,8 +2908,34 @@ CodeGenerator::visitBinaryCache(LBinaryCache* lir) TypedOrValueRegister rhs = TypedOrValueRegister(ToValue(lir, LBinaryCache::RhsInput)); ValueOperand output = ToOutValue(lir); - IonBinaryArithIC ic(liveRegs, lhs, rhs, output); - addIC(lir, allocateIC(ic)); + JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc()); + + switch (jsop) { + case JSOP_ADD: + case JSOP_SUB: + case JSOP_MUL: + case JSOP_DIV: + case JSOP_MOD: + case JSOP_POW: { + IonBinaryArithIC ic(liveRegs, lhs, rhs, output); + addIC(lir, allocateIC(ic)); + return; + } + case JSOP_LT: + case JSOP_LE: + case JSOP_GT: + case JSOP_GE: + case JSOP_EQ: + case JSOP_NE: + case JSOP_STRICTEQ: + case JSOP_STRICTNE: { + IonCompareIC ic(liveRegs, lhs, rhs, output); + addIC(lir, allocateIC(ic)); + return; + } + default: + MOZ_CRASH("Unsupported jsop in MBinaryCache"); + } } void @@ -8297,7 +8411,7 @@ CodeGenerator::visitConcat(LConcat* lir) static void CopyStringChars(MacroAssembler& masm, Register to, Register from, Register len, - Register byteOpScratch, size_t fromWidth, size_t toWidth) + Register byteOpScratch, CharEncoding fromEncoding, CharEncoding toEncoding) { // Copy |len| char16_t code units from |from| to |to|. Assumes len > 0 // (checked below in debug builds), and when done |to| must point to the @@ -8310,25 +8424,27 @@ CopyStringChars(MacroAssembler& masm, Register to, Register from, Register len, masm.bind(&ok); #endif - MOZ_ASSERT(fromWidth == 1 || fromWidth == 2); - MOZ_ASSERT(toWidth == 1 || toWidth == 2); - MOZ_ASSERT_IF(toWidth == 1, fromWidth == 1); + MOZ_ASSERT_IF(toEncoding == CharEncoding::Latin1, fromEncoding == CharEncoding::Latin1); + + size_t fromWidth = fromEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t); + size_t toWidth = toEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t); Label start; masm.bind(&start); - if (fromWidth == 2) - masm.load16ZeroExtend(Address(from, 0), byteOpScratch); - else - masm.load8ZeroExtend(Address(from, 0), byteOpScratch); - if (toWidth == 2) - masm.store16(byteOpScratch, Address(to, 0)); - else - masm.store8(byteOpScratch, Address(to, 0)); + masm.loadChar(Address(from, 0), byteOpScratch, fromEncoding); + masm.storeChar(byteOpScratch, Address(to, 0), toEncoding); masm.addPtr(Imm32(fromWidth), from); masm.addPtr(Imm32(toWidth), to); masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start); } +static void +CopyStringChars(MacroAssembler& masm, Register to, Register from, Register len, + Register byteOpScratch, CharEncoding encoding) +{ + CopyStringChars(masm, to, from, len, byteOpScratch, encoding, encoding); +} + static void CopyStringCharsMaybeInflate(MacroAssembler& masm, Register input, Register destChars, Register temp1, Register temp2) @@ -8342,14 +8458,15 @@ CopyStringCharsMaybeInflate(MacroAssembler& masm, Register input, Register destC { masm.loadStringChars(input, temp2, CharEncoding::TwoByte); masm.movePtr(temp2, input); - CopyStringChars(masm, destChars, input, temp1, temp2, sizeof(char16_t), sizeof(char16_t)); + CopyStringChars(masm, destChars, input, temp1, temp2, CharEncoding::TwoByte); masm.jump(&done); } masm.bind(&isLatin1); { masm.loadStringChars(input, temp2, CharEncoding::Latin1); masm.movePtr(temp2, input); - CopyStringChars(masm, destChars, input, temp1, temp2, sizeof(char), sizeof(char16_t)); + CopyStringChars(masm, destChars, input, temp1, temp2, CharEncoding::Latin1, + CharEncoding::TwoByte); } masm.bind(&done); } @@ -8358,10 +8475,10 @@ static void ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, Register output, Register temp1, Register temp2, Register temp3, bool stringsCanBeInNursery, - Label* failure, bool isTwoByte) + Label* failure, CharEncoding encoding) { JitSpew(JitSpew_Codegen, "# Emitting ConcatInlineString (encoding=%s)", - (isTwoByte ? "Two-Byte" : "Latin-1")); + (encoding == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); // State: result length in temp2. @@ -8371,16 +8488,16 @@ ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, Register ou // Allocate a JSThinInlineString or JSFatInlineString. size_t maxThinInlineLength; - if (isTwoByte) - maxThinInlineLength = JSThinInlineString::MAX_LENGTH_TWO_BYTE; - else + if (encoding == CharEncoding::Latin1) maxThinInlineLength = JSThinInlineString::MAX_LENGTH_LATIN1; + else + maxThinInlineLength = JSThinInlineString::MAX_LENGTH_TWO_BYTE; Label isFat, allocDone; masm.branch32(Assembler::Above, temp2, Imm32(maxThinInlineLength), &isFat); { uint32_t flags = JSString::INIT_THIN_INLINE_FLAGS; - if (!isTwoByte) + if (encoding == CharEncoding::Latin1) flags |= JSString::LATIN1_CHARS_BIT; masm.newGCString(output, temp1, failure, stringsCanBeInNursery); masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); @@ -8389,7 +8506,7 @@ ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, Register ou masm.bind(&isFat); { uint32_t flags = JSString::INIT_FAT_INLINE_FLAGS; - if (!isTwoByte) + if (encoding == CharEncoding::Latin1) flags |= JSString::LATIN1_CHARS_BIT; masm.newGCFatInlineString(output, temp1, failure, stringsCanBeInNursery); masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); @@ -8402,34 +8519,26 @@ ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, Register ou // Load chars pointer in temp2. masm.loadInlineStringCharsForStore(output, temp2); - { - // Copy lhs chars. Note that this advances temp2 to point to the next - // char. This also clobbers the lhs register. - if (isTwoByte) { - CopyStringCharsMaybeInflate(masm, lhs, temp2, temp1, temp3); + auto copyChars = [&](Register src) { + if (encoding == CharEncoding::TwoByte) { + CopyStringCharsMaybeInflate(masm, src, temp2, temp1, temp3); } else { - masm.loadStringLength(lhs, temp3); - masm.loadStringChars(lhs, temp1, CharEncoding::Latin1); - masm.movePtr(temp1, lhs); - CopyStringChars(masm, temp2, lhs, temp3, temp1, sizeof(char), sizeof(char)); + masm.loadStringLength(src, temp3); + masm.loadStringChars(src, temp1, CharEncoding::Latin1); + masm.movePtr(temp1, src); + CopyStringChars(masm, temp2, src, temp3, temp1, CharEncoding::Latin1); } + }; - // Copy rhs chars. Clobbers the rhs register. - if (isTwoByte) { - CopyStringCharsMaybeInflate(masm, rhs, temp2, temp1, temp3); - } else { - masm.loadStringLength(rhs, temp3); - masm.loadStringChars(rhs, temp1, CharEncoding::Latin1); - masm.movePtr(temp1, rhs); - CopyStringChars(masm, temp2, rhs, temp3, temp1, sizeof(char), sizeof(char)); - } + // Copy lhs chars. Note that this advances temp2 to point to the next + // char. This also clobbers the lhs register. + copyChars(lhs); - // Null-terminate. - if (isTwoByte) - masm.store16(Imm32(0), Address(temp2, 0)); - else - masm.store8(Imm32(0), Address(temp2, 0)); - } + // Copy rhs chars. Clobbers the rhs register. + copyChars(rhs); + + // Null-terminate. + masm.storeChar(Imm32(0), Address(temp2, 0), encoding); masm.ret(); } @@ -8482,43 +8591,32 @@ CodeGenerator::visitSubstr(LSubstr* lir) masm.newGCFatInlineString(output, temp, slowPath, stringsCanBeInNursery()); masm.store32(length, Address(output, JSString::offsetOfLength())); - masm.branchLatin1String(string, &isInlinedLatin1); - { - masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS), - Address(output, JSString::offsetOfFlags())); - masm.loadInlineStringChars(string, temp, CharEncoding::TwoByte); + auto initializeFatInlineString = [&](CharEncoding encoding) { + uint32_t flags = JSString::INIT_FAT_INLINE_FLAGS; + if (encoding == CharEncoding::Latin1) + flags |= JSString::LATIN1_CHARS_BIT; + + masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); + masm.loadInlineStringChars(string, temp, encoding); + masm.addToCharPtr(temp, begin, encoding); if (temp2 == string) masm.push(string); - BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t))); - masm.computeEffectiveAddress(chars, temp2); - masm.loadInlineStringCharsForStore(output, temp); - CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char16_t), sizeof(char16_t)); - masm.load32(Address(output, JSString::offsetOfLength()), length); - masm.store16(Imm32(0), Address(temp, 0)); + masm.loadInlineStringCharsForStore(output, temp2); + CopyStringChars(masm, temp2, temp, length, temp3, encoding); + masm.loadStringLength(output, length); + masm.storeChar(Imm32(0), Address(temp2, 0), encoding); if (temp2 == string) masm.pop(string); masm.jump(done); + }; + + masm.branchLatin1String(string, &isInlinedLatin1); + { + initializeFatInlineString(CharEncoding::TwoByte); } masm.bind(&isInlinedLatin1); { - masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS | JSString::LATIN1_CHARS_BIT), - Address(output, JSString::offsetOfFlags())); - if (temp2 == string) { - masm.push(string); - masm.loadInlineStringChars(string, temp, CharEncoding::Latin1); - masm.movePtr(temp, temp2); - } else { - masm.loadInlineStringChars(string, temp2, CharEncoding::Latin1); - } - static_assert(sizeof(char) == 1, "begin index shouldn't need scaling"); - masm.addPtr(begin, temp2); - masm.loadInlineStringCharsForStore(output, temp); - CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char), sizeof(char)); - masm.load32(Address(output, JSString::offsetOfLength()), length); - masm.store8(Imm32(0), Address(temp, 0)); - if (temp2 == string) - masm.pop(string); - masm.jump(done); + initializeFatInlineString(CharEncoding::Latin1); } // Handle other cases with a DependentString. @@ -8527,24 +8625,25 @@ CodeGenerator::visitSubstr(LSubstr* lir) masm.store32(length, Address(output, JSString::offsetOfLength())); masm.storeDependentStringBase(string, output); - masm.branchLatin1String(string, &isLatin1); - { - masm.store32(Imm32(JSString::DEPENDENT_FLAGS), Address(output, JSString::offsetOfFlags())); - masm.loadNonInlineStringChars(string, temp, CharEncoding::TwoByte); - BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t))); - masm.computeEffectiveAddress(chars, temp); + auto initializeDependentString = [&](CharEncoding encoding) { + uint32_t flags = JSString::DEPENDENT_FLAGS; + if (encoding == CharEncoding::Latin1) + flags |= JSString::LATIN1_CHARS_BIT; + + masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); + masm.loadNonInlineStringChars(string, temp, encoding); + masm.addToCharPtr(temp, begin, encoding); masm.storeNonInlineStringChars(temp, output); masm.jump(done); + }; + + masm.branchLatin1String(string, &isLatin1); + { + initializeDependentString(CharEncoding::TwoByte); } masm.bind(&isLatin1); { - masm.store32(Imm32(JSString::DEPENDENT_FLAGS | JSString::LATIN1_CHARS_BIT), - Address(output, JSString::offsetOfFlags())); - masm.loadNonInlineStringChars(string, temp, CharEncoding::Latin1); - static_assert(sizeof(char) == 1, "begin index shouldn't need scaling"); - masm.addPtr(begin, temp); - masm.storeNonInlineStringChars(temp, output); - masm.jump(done); + initializeDependentString(CharEncoding::Latin1); } masm.bind(done); @@ -8633,11 +8732,11 @@ JitRealm::generateStringConcatStub(JSContext* cx) masm.bind(&isFatInlineTwoByte); ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, - stringsCanBeInNursery, &failure, true); + stringsCanBeInNursery, &failure, CharEncoding::TwoByte); masm.bind(&isFatInlineLatin1); ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, - stringsCanBeInNursery, &failure, false); + stringsCanBeInNursery, &failure, CharEncoding::Latin1); masm.pop(temp2); masm.pop(temp1); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 801a56f3769d..ed8fe224fc7a 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -5830,7 +5830,7 @@ IonBuilder::jsop_compare(JSOp op, MDefinition* left, MDefinition* right) return Ok(); } - MOZ_TRY(compareTrySharedStub(&emitted, left, right)); + MOZ_TRY(compareTryBinaryStub(&emitted, left, right)); if (emitted) return Ok(); @@ -6025,7 +6025,7 @@ IonBuilder::compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDe } AbortReasonOr -IonBuilder::compareTrySharedStub(bool* emitted, MDefinition* left, MDefinition* right) +IonBuilder::compareTryBinaryStub(bool* emitted, MDefinition* left, MDefinition* right) { MOZ_ASSERT(*emitted == false); @@ -6037,9 +6037,7 @@ IonBuilder::compareTrySharedStub(bool* emitted, MDefinition* left, MDefinition* if (JSOp(*pc) == JSOP_CASE) return Ok(); - trackOptimizationAttempt(TrackedStrategy::Compare_SharedCache); - - MBinarySharedStub* stub = MBinarySharedStub::New(alloc(), left, right); + MBinaryCache* stub = MBinaryCache::New(alloc(), left, right); current->add(stub); current->push(stub); MOZ_TRY(resumeAfter(stub)); diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 4ab88e36b4d9..2dfd98ba1121 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -332,7 +332,7 @@ class IonBuilder AbortReasonOr compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* left, MDefinition* right); - AbortReasonOr compareTrySharedStub(bool* emitted, MDefinition* left, MDefinition* right); + AbortReasonOr compareTryBinaryStub(bool* emitted, MDefinition* left, MDefinition* right); // jsop_newarray helpers. AbortReasonOr newArrayTryTemplateObject(bool* emitted, JSObject* templateObject, diff --git a/js/src/jit/IonCacheIRCompiler.cpp b/js/src/jit/IonCacheIRCompiler.cpp index 043e15110cb4..e8853175373a 100644 --- a/js/src/jit/IonCacheIRCompiler.cpp +++ b/js/src/jit/IonCacheIRCompiler.cpp @@ -537,8 +537,21 @@ IonCacheIRCompiler::init() allocator.initInputLocation(1, ic->rhs()); 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::Compare: case CacheKind::TypeOf: case CacheKind::ToBool: case CacheKind::GetIntrinsic: @@ -579,8 +592,6 @@ IonCacheIRCompiler::compile() allocator.nextOp(); } while (reader.more()); - MOZ_RELEASE_ASSERT(nextStubField_ == writer_.numStubFields()); - masm.assumeUnreachable("Should have returned from IC"); // Done emitting the main IC code. Now emit the failure paths. diff --git a/js/src/jit/IonIC.cpp b/js/src/jit/IonIC.cpp index 09720498afcb..4a30897789aa 100644 --- a/js/src/jit/IonIC.cpp +++ b/js/src/jit/IonIC.cpp @@ -60,8 +60,9 @@ IonIC::scratchRegisterForEntryJump() return asUnaryArithIC()->output().scratchReg(); case CacheKind::BinaryArith: return asBinaryArithIC()->output().scratchReg(); - case CacheKind::Call: case CacheKind::Compare: + return asCompareIC()->output().scratchReg(); + case CacheKind::Call: case CacheKind::TypeOf: case CacheKind::ToBool: case CacheKind::GetIntrinsic: @@ -629,6 +630,83 @@ IonBinaryArithIC::update(JSContext* cx, HandleScript outerScript, IonBinaryArith 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(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_NE: + if (!LooselyEqual(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_STRICTEQ: + if (!StrictlyEqual(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_STRICTNE: + if (!StrictlyEqual(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* IonICStub::stubDataStart() { diff --git a/js/src/jit/IonIC.h b/js/src/jit/IonIC.h index ec7548fa2637..8eaefe45414a 100644 --- a/js/src/jit/IonIC.h +++ b/js/src/jit/IonIC.h @@ -65,6 +65,7 @@ class IonGetIteratorIC; class IonHasOwnIC; class IonInIC; class IonInstanceOfIC; +class IonCompareIC; class IonUnaryArithIC; class IonBinaryArithIC; @@ -174,6 +175,10 @@ class IonIC MOZ_ASSERT(kind_ == CacheKind::InstanceOf); return (IonInstanceOfIC*)this; } + IonCompareIC* asCompareIC() { + MOZ_ASSERT(kind_ == CacheKind::Compare); + return (IonCompareIC*)this; + } IonUnaryArithIC* asUnaryArithIC() { MOZ_ASSERT(kind_ == CacheKind::UnaryArith); return (IonUnaryArithIC*)this; @@ -485,6 +490,33 @@ class IonInstanceOfIC : public IonIC 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 { LiveRegisterSet liveRegs_; diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index ce0e6c82d7e2..1ef1f090db5a 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -1602,16 +1602,6 @@ MacroAssembler::loadDependentStringBase(Register str, Register 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 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. branchLatin1String(output, &isLatin1); loadStringChars(output, scratch, CharEncoding::TwoByte); - load16ZeroExtend(BaseIndex(scratch, index, TimesTwo), output); + loadChar(scratch, index, output, CharEncoding::TwoByte); jump(&done); bind(&isLatin1); loadStringChars(output, scratch, CharEncoding::Latin1); - load8ZeroExtend(BaseIndex(scratch, index, TimesOne), output); + loadChar(scratch, index, output, CharEncoding::Latin1); bind(&done); } @@ -1673,6 +1663,27 @@ MacroAssembler::loadStringIndexValue(Register str, Register dest, Label* fail) 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 MacroAssembler::typeOfObject(Register obj, Register scratch, Label* slow, Label* isObject, Label* isCallable, Label* isUndefined) diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index 5ecda8fb4e66..9ebfe158ee5e 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -2121,10 +2121,43 @@ class MacroAssembler : public MacroAssemblerSpecific void loadDependentStringBase(Register str, Register dest); void storeDependentStringBase(Register base, Register str); - void leaNewDependentStringBase(Register str, Register dest); void loadStringIndexValue(Register str, Register dest, Label* fail); + /** + * Store the character in |src| to |dest|. + */ + template + 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 + 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 switchToRealm(Register realm); diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index 5e5ba09a01e7..f3c6bc607a04 100644 --- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -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 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(cx, &lhsCopy, &rhsCopy, &out)) - return false; - break; - case JSOP_NE: - if (!LooselyEqual(cx, &lhsCopy, &rhsCopy, &out)) - return false; - break; - case JSOP_STRICTEQ: - if (!StrictlyEqual(cx, &lhsCopy, &rhsCopy, &out)) - return false; - break; - case JSOP_STRICTNE: - if (!StrictlyEqual(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(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 LoadTypedThingData(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result) { diff --git a/js/src/jit/SharedIC.h b/js/src/jit/SharedIC.h index 4f3daf732533..4e5f9ffafc0c 100644 --- a/js/src/jit/SharedIC.h +++ b/js/src/jit/SharedIC.h @@ -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(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(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(engine_) | - (static_cast(kind) << 1) | - (static_cast(op) << 17) | - (static_cast(lhsIsUndefined) << 25); - } - - ICStub* getStub(ICStubSpace* space) override { - return newStub(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(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(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(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(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(engine_) | - (static_cast(kind) << 1) | - (static_cast(op) << 17) | - (static_cast(lhsIsUndefined) << 25) | - (static_cast(compareWithNull) << 26); - } - - ICStub* getStub(ICStubSpace* space) override { - return newStub(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(engine_) | - (static_cast(kind) << 1) | - (static_cast(op_) << 17) | - (static_cast(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(space, getStubCode(), lhsIsInt32_); - } - }; -}; - // Enum for stubs handling a combination of typed arrays and typed objects. enum TypedThingLayout { Layout_TypedArray, diff --git a/js/src/jit/SharedICList.h b/js/src/jit/SharedICList.h index bbfeb86aa4c5..b442b5f549f2 100644 --- a/js/src/jit/SharedICList.h +++ b/js/src/jit/SharedICList.h @@ -15,15 +15,6 @@ namespace jit { _(BinaryArith_Fallback) \ \ _(Compare_Fallback) \ - _(Compare_Int32) \ - _(Compare_Double) \ - _(Compare_NumberWithUndefined) \ - _(Compare_String) \ - _(Compare_Symbol) \ - _(Compare_Boolean) \ - _(Compare_Object) \ - _(Compare_ObjectWithUndefined) \ - _(Compare_Int32WithBoolean) \ \ _(GetProp_Fallback) \ \ diff --git a/js/src/jit/arm/BaselineIC-arm.cpp b/js/src/jit/arm/BaselineIC-arm.cpp deleted file mode 100644 index 8534638882e8..000000000000 --- a/js/src/jit/arm/BaselineIC-arm.cpp +++ /dev/null @@ -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 diff --git a/js/src/jit/arm64/BaselineIC-arm64.cpp b/js/src/jit/arm64/BaselineIC-arm64.cpp deleted file mode 100644 index 54ac47d5bbdf..000000000000 --- a/js/src/jit/arm64/BaselineIC-arm64.cpp +++ /dev/null @@ -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 diff --git a/js/src/jit/mips32/BaselineIC-mips32.cpp b/js/src/jit/mips32/BaselineIC-mips32.cpp deleted file mode 100644 index e41ecf774e4e..000000000000 --- a/js/src/jit/mips32/BaselineIC-mips32.cpp +++ /dev/null @@ -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 diff --git a/js/src/jit/mips64/BaselineIC-mips64.cpp b/js/src/jit/mips64/BaselineIC-mips64.cpp deleted file mode 100644 index 5c0e6d0b7104..000000000000 --- a/js/src/jit/mips64/BaselineIC-mips64.cpp +++ /dev/null @@ -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 diff --git a/js/src/jit/none/Trampoline-none.cpp b/js/src/jit/none/Trampoline-none.cpp index b71ff83003e0..5f4bc13b10b7 100644 --- a/js/src/jit/none/Trampoline-none.cpp +++ b/js/src/jit/none/Trampoline-none.cpp @@ -42,5 +42,3 @@ BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& iter, Invalidati MOZ_CRASH(); } -bool ICCompare_Int32::Compiler::generateStubCode(MacroAssembler&) { MOZ_CRASH(); } -bool ICCompare_Double::Compiler::generateStubCode(MacroAssembler&) { MOZ_CRASH(); } diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index 9f4c028cf234..90a33a91a411 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -885,10 +885,6 @@ class AssemblerShared return embedsNurseryPointers_; } - static bool canUseInSingleByteInstruction(Register reg) { - return true; - } - void addCodeLabel(CodeLabel label) { propagateOOM(codeLabels_.append(label)); } diff --git a/js/src/jit/x64/BaselineIC-x64.cpp b/js/src/jit/x64/BaselineIC-x64.cpp deleted file mode 100644 index f04052b0dd46..000000000000 --- a/js/src/jit/x64/BaselineIC-x64.cpp +++ /dev/null @@ -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 diff --git a/js/src/jit/x86-shared/BaselineIC-x86-shared.cpp b/js/src/jit/x86-shared/BaselineIC-x86-shared.cpp deleted file mode 100644 index 4e25f87bfbf0..000000000000 --- a/js/src/jit/x86-shared/BaselineIC-x86-shared.cpp +++ /dev/null @@ -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; -} diff --git a/js/src/jit/x86/Assembler-x86.h b/js/src/jit/x86/Assembler-x86.h index 5c46d01de380..8682941682ea 100644 --- a/js/src/jit/x86/Assembler-x86.h +++ b/js/src/jit/x86/Assembler-x86.h @@ -1060,10 +1060,6 @@ class Assembler : public AssemblerX86Shared masm.vmovups_rm(src.encoding(), dest.addr); 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 diff --git a/js/src/jit/x86/BaselineIC-x86.cpp b/js/src/jit/x86/BaselineIC-x86.cpp deleted file mode 100644 index a2227ab0ac6c..000000000000 --- a/js/src/jit/x86/BaselineIC-x86.cpp +++ /dev/null @@ -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 diff --git a/js/src/jsapi-tests/testSavedStacks.cpp b/js/src/jsapi-tests/testSavedStacks.cpp index b7c437af0551..6051539d0b4a 100644 --- a/js/src/jsapi-tests/testSavedStacks.cpp +++ b/js/src/jsapi-tests/testSavedStacks.cpp @@ -259,7 +259,7 @@ BEGIN_TEST(testSavedStacks_selfHostedFrames) result = JS::GetSavedFrameColumn(cx, principals, selfHostedFrame, &column, JS::SavedFrameSelfHosted::Exclude); CHECK(result == JS::SavedFrameResult::Ok); - CHECK_EQUAL(column, 5U); + CHECK_EQUAL(column, 9U); // Function display name result = JS::GetSavedFrameFunctionDisplayName(cx, principals, selfHostedFrame, &str, diff --git a/js/src/moz.build b/js/src/moz.build index 31dcb34a2b21..23e5f8060b37 100755 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -501,7 +501,6 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']: 'jit/x86-shared/Assembler-x86-shared.cpp', 'jit/x86-shared/AssemblerBuffer-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/Lowering-x86-shared.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/Bailouts-x64.cpp', 'jit/x64/BaselineCompiler-x64.cpp', - 'jit/x64/BaselineIC-x64.cpp', 'jit/x64/CodeGenerator-x64.cpp', 'jit/x64/Lowering-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/Bailouts-x86.cpp', 'jit/x86/BaselineCompiler-x86.cpp', - 'jit/x86/BaselineIC-x86.cpp', 'jit/x86/CodeGenerator-x86.cpp', 'jit/x86/Lowering-x86.cpp', 'jit/x86/MacroAssembler-x86.cpp', @@ -542,7 +539,6 @@ elif CONFIG['JS_CODEGEN_ARM']: 'jit/arm/Assembler-arm.cpp', 'jit/arm/Bailouts-arm.cpp', 'jit/arm/BaselineCompiler-arm.cpp', - 'jit/arm/BaselineIC-arm.cpp', 'jit/arm/CodeGenerator-arm.cpp', 'jit/arm/disasm/Constants-arm.cpp', 'jit/arm/disasm/Disasm-arm.cpp', @@ -566,7 +562,6 @@ elif CONFIG['JS_CODEGEN_ARM64']: 'jit/arm64/Architecture-arm64.cpp', 'jit/arm64/Assembler-arm64.cpp', 'jit/arm64/Bailouts-arm64.cpp', - 'jit/arm64/BaselineIC-arm64.cpp', 'jit/arm64/CodeGenerator-arm64.cpp', 'jit/arm64/Disassembler-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/Bailouts-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/Lowering-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/Bailouts-mips32.cpp', 'jit/mips32/BaselineCompiler-mips32.cpp', - 'jit/mips32/BaselineIC-mips32.cpp', 'jit/mips32/CodeGenerator-mips32.cpp', 'jit/mips32/Lowering-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/Bailouts-mips64.cpp', 'jit/mips64/BaselineCompiler-mips64.cpp', - 'jit/mips64/BaselineIC-mips64.cpp', 'jit/mips64/CodeGenerator-mips64.cpp', 'jit/mips64/Lowering-mips64.cpp', 'jit/mips64/MacroAssembler-mips64.cpp', diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp index c9232f5987e3..50df0273601c 100644 --- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -4491,7 +4491,7 @@ JSScript::AutoDelazify::dropScript() script_ = nullptr; } -JS::ubi::Node::Size +JS::ubi::Base::Size JS::ubi::Concrete::size(mozilla::MallocSizeOf mallocSizeOf) const { Size size = Arena::thingSize(get().asTenured().getAllocKind()); diff --git a/js/src/vm/MatchPairs.h b/js/src/vm/MatchPairs.h index 288814c09d58..06c69e774daa 100644 --- a/js/src/vm/MatchPairs.h +++ b/js/src/vm/MatchPairs.h @@ -21,13 +21,15 @@ namespace js { -struct MatchPair +struct MatchPair final { int32_t start; int32_t limit; + static constexpr int32_t NoMatch = -1; + MatchPair() - : start(-1), limit(-1) + : start(NoMatch), limit(NoMatch) { } MatchPair(int32_t start, int32_t limit) @@ -39,8 +41,8 @@ struct MatchPair inline bool check() const { MOZ_ASSERT(limit >= start); - MOZ_ASSERT_IF(start < 0, start == -1); - MOZ_ASSERT_IF(limit < 0, limit == -1); + MOZ_ASSERT_IF(start < 0, start == NoMatch); + MOZ_ASSERT_IF(limit < 0, limit == NoMatch); return true; } }; diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 300536c172f4..e71b22d26406 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -1251,9 +1251,9 @@ RegExpRealm::createMatchResultTemplateObject(JSContext* cx) // Make sure that the properties are in the right slots. DebugOnly shape = templateObject->lastProperty(); - MOZ_ASSERT(shape->previous()->slot() == 0 && + MOZ_ASSERT(shape->previous()->slot() == MatchResultObjectIndexSlot && shape->previous()->propidRef() == NameToId(cx->names().index)); - MOZ_ASSERT(shape->slot() == 1 && + MOZ_ASSERT(shape->slot() == MatchResultObjectInputSlot && shape->propidRef() == NameToId(cx->names().input)); // Make sure type information reflects the indexed properties which might diff --git a/js/src/vm/RegExpShared.h b/js/src/vm/RegExpShared.h index e077ed3f9bd3..6348221deda6 100644 --- a/js/src/vm/RegExpShared.h +++ b/js/src/vm/RegExpShared.h @@ -325,6 +325,9 @@ class RegExpRealm 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. */ ArrayObject* getOrCreateMatchResultTemplateObject(JSContext* cx) { if (matchResultTemplateObject_) diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index 5cd884a44f19..f1875b9bd24d 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -416,22 +416,21 @@ static inline ParseNode* CallCallee(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Call)); - return ListHead(pn); + return BinaryLeft(pn); } static inline unsigned CallArgListLength(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Call)); - MOZ_ASSERT(ListLength(pn) >= 1); - return ListLength(pn) - 1; + return ListLength(BinaryRight(pn)); } static inline ParseNode* CallArgList(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Call)); - return NextNode(ListHead(pn)); + return ListHead(BinaryRight(pn)); } static inline ParseNode* @@ -594,16 +593,16 @@ static ParseNode* DotBase(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot)); - MOZ_ASSERT(pn->isArity(PN_NAME)); - return pn->expr(); + MOZ_ASSERT(pn->isArity(PN_BINARY)); + return pn->pn_left; } static PropertyName* DotMember(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot)); - MOZ_ASSERT(pn->isArity(PN_NAME)); - return pn->pn_atom->asPropertyName(); + MOZ_ASSERT(pn->isArity(PN_BINARY)); + return pn->pn_right->pn_atom->asPropertyName(); } static ParseNode* @@ -2800,9 +2799,11 @@ IsArrayViewCtorName(ModuleValidator& m, PropertyName* name, Scalar::Type* type) } 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) return m.fail(ctorExpr, "array view constructor takes exactly one argument"); @@ -2823,7 +2824,7 @@ CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr) if (!bufferName) return m.fail(newExpr, "cannot create array view without an asm.js heap parameter"); - ParseNode* ctorExpr = ListHead(newExpr); + ParseNode* ctorExpr = BinaryLeft(newExpr); PropertyName* field; Scalar::Type type; @@ -2852,7 +2853,7 @@ CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr) type = global->viewType(); } - if (!CheckNewArrayViewArgs(m, ctorExpr, bufferName)) + if (!CheckNewArrayViewArgs(m, newExpr, bufferName)) return false; return m.addArrayView(varName, type, field); diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 72499b9f5d74..680a8f50fa07 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -1028,8 +1028,12 @@ ReadScriptOrFunction(nsIObjectInputStream* stream, JSContext* cx, // 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 // 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) || - CurrentGlobalOrNull(cx) == xpc::CompilationScope()); + CurrentGlobalOrNull(cx) == loaderGlobal); uint32_t size; rv = stream->Read32(&size); diff --git a/js/xpconnect/tests/unit/test_getCallerLocation.js b/js/xpconnect/tests/unit/test_getCallerLocation.js index e1b12f34c304..c47ab775f261 100644 --- a/js/xpconnect/tests/unit/test_getCallerLocation.js +++ b/js/xpconnect/tests/unit/test_getCallerLocation.js @@ -39,11 +39,11 @@ add_task(async function() { equal(frame.source, "thing.js", "Frame source"); 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.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 @@ -55,7 +55,7 @@ add_task(async function() { let [msg] = messages.filter(m => m.message.includes("Meh")); 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", {}); }, err => err.result == Cr.NS_ERROR_INVALID_ARG, @@ -82,5 +82,5 @@ add_task(async function() { equal(error.message, "Meh", "Error message"); equal(error.fileName, "thing.js", "Error filename"); equal(error.lineNumber, 5, "Error line"); - equal(error.columnNumber, 14, "Error column"); + equal(error.columnNumber, 18, "Error column"); }); diff --git a/media/libvpx/bug1480092.patch b/media/libvpx/bug1480092.patch new file mode 100644 index 000000000000..db290331007e --- /dev/null +++ b/media/libvpx/bug1480092.patch @@ -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; diff --git a/media/libvpx/libvpx/vp8/common/postproc.c b/media/libvpx/libvpx/vp8/common/postproc.c index d67ee8a57d82..8c292d6161dd 100644 --- a/media/libvpx/libvpx/vp8/common/postproc.c +++ b/media/libvpx/libvpx/vp8/common/postproc.c @@ -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; 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 diff --git a/media/libvpx/update.py b/media/libvpx/update.py index 0ac5f237ed35..45a5a9092ef8 100755 --- a/media/libvpx/update.py +++ b/media/libvpx/update.py @@ -41,6 +41,8 @@ def apply_patches(): # Avoid c/asm name collision for loopfilter_sse2 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") + # Cherrypick fix from upstream + os.system("patch -p3 < bug1480092.patch") def update_readme(commit): diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt index df3d677d6172..f33690903c3f 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt @@ -145,36 +145,20 @@ class NavigationDelegateTest : BaseSessionTest() { containsString(mobileSubStr)) } - fun telemetryTest(process: String) { + @Test fun telemetrySnapshots() { sessionRule.session.loadTestPath(HELLO_HTML_PATH) sessionRule.waitForPageStop() val telemetry = sessionRule.runtime.telemetry - val result = sessionRule.waitForResult(telemetry.getSnapshots(true)) - - val snapshots = result?.get(process) as GeckoBundle + val result = sessionRule.waitForResult(telemetry.getSnapshots(false)) assertThat("Snapshots should not be null", - snapshots, notNullValue()) + result?.get("parent"), notNullValue()) - assertThat("Histograms should not be null", - snapshots.get("histograms"), notNullValue()) - assertThat("Keyed histograms should not be null", - 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") + if (sessionRule.env.isMultiprocess) { + assertThat("Snapshots should not be null", + result?.get("content"), notNullValue()) + } } @Test fun load() { diff --git a/taskcluster/scripts/builder/hazard-analysis.sh b/taskcluster/scripts/builder/hazard-analysis.sh index 7c9d5a160b9d..33031200c03a 100755 --- a/taskcluster/scripts/builder/hazard-analysis.sh +++ b/taskcluster/scripts/builder/hazard-analysis.sh @@ -102,7 +102,7 @@ function run_analysis () { ( 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" ) } diff --git a/taskcluster/scripts/builder/hazard-shell.sh b/taskcluster/scripts/builder/hazard-shell.sh index d053a13b9872..c4cc5a9c7ae3 100755 --- a/taskcluster/scripts/builder/hazard-shell.sh +++ b/taskcluster/scripts/builder/hazard-shell.sh @@ -3,4 +3,4 @@ mkdir -p "$ANALYZED_OBJDIR" cd "$ANALYZED_OBJDIR" $SOURCE/js/src/configure --enable-debug --enable-optimize --enable-ctypes --enable-nspr-build -make -j12 -s +make -j8 -s diff --git a/testing/mozbase/mozdevice/mozdevice/adb.py b/testing/mozbase/mozdevice/mozdevice/adb.py index cc988002dd1d..9d41cb9801b4 100644 --- a/testing/mozbase/mozdevice/mozdevice/adb.py +++ b/testing/mozbase/mozdevice/mozdevice/adb.py @@ -140,7 +140,8 @@ class ADBCommand(object): adb_port=None, logger_name='adb', timeout=300, - verbose=False): + verbose=False, + require_root=True): """Initializes the ADBCommand object. :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. :type adb_port: integer or None :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 * ADBTimeoutError @@ -158,6 +161,7 @@ class ADBCommand(object): self._logger = self._get_logger(logger_name) self._verbose = verbose + self._require_root = require_root self._adb_path = adb self._adb_host = adb_host self._adb_port = adb_port @@ -818,15 +822,15 @@ class ADBDevice(ADBCommand): def _try_test_root(self, 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 try: dummy_dir = posixpath.join(test_root, 'dummy') - if self.is_dir(dummy_dir, root=True): - self.rm(dummy_dir, recursive=True, root=True) - self.mkdir(dummy_dir, parents=True, root=True) - self.chmod(test_root, recursive=True, root=True) + if self.is_dir(dummy_dir, root=self._require_root): + self.rm(dummy_dir, recursive=True, root=self._require_root) + self.mkdir(dummy_dir, parents=True, root=self._require_root) + self.chmod(test_root, recursive=True, root=self._require_root) except ADBError: self._logger.debug("%s is not writable" % test_root) return False diff --git a/testing/mozbase/mozdevice/setup.py b/testing/mozbase/mozdevice/setup.py index cf3f23754e76..dd6598b404ef 100644 --- a/testing/mozbase/mozdevice/setup.py +++ b/testing/mozbase/mozdevice/setup.py @@ -8,7 +8,7 @@ from __future__ import absolute_import from setuptools import setup PACKAGE_NAME = 'mozdevice' -PACKAGE_VERSION = '1.0.0' +PACKAGE_VERSION = '1.0.1' deps = ['mozfile >= 1.0', 'mozlog >= 3.0', diff --git a/testing/web-platform/tests/content-security-policy/securitypolicyviolation/targeting.html b/testing/web-platform/tests/content-security-policy/securitypolicyviolation/targeting.html index 10b96e9e61da..28ca32e3dcc3 100644 --- a/testing/web-platform/tests/content-security-policy/securitypolicyviolation/targeting.html +++ b/testing/web-platform/tests/content-security-policy/securitypolicyviolation/targeting.html @@ -43,7 +43,7 @@ .then(t.step_func(e => { assert_equals(e.blockedURI, "inline"); 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."); return watcher.wait_for('securitypolicyviolation'); })) diff --git a/toolkit/modules/tests/xpcshell/test_NewTabUtils.js b/toolkit/modules/tests/xpcshell/test_NewTabUtils.js index 99b5c571d485..1cb5a4b34d7b 100644 --- a/toolkit/modules/tests/xpcshell/test_NewTabUtils.js +++ b/toolkit/modules/tests/xpcshell/test_NewTabUtils.js @@ -3,6 +3,8 @@ // See also browser/base/content/test/newtab/. +ChromeUtils.import("resource://gre/modules/AppConstants.jsm"); + // A small 1x1 test png 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"); }); -add_task(async function getTopFrecentSites_improveSearch() { +add_task({ skip_if: () => AppConstants.MOZ_APP_NAME == "thunderbird" }, + async function getTopFrecentSites_improveSearch() { await setUpActivityStreamTest(); const SEARCH_SHORTCUTS_EXPERIMENT_PREF = "browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts"; Services.prefs.setBoolPref(SEARCH_SHORTCUTS_EXPERIMENT_PREF, true); diff --git a/toolkit/recordreplay/ProcessRedirectDarwin.cpp b/toolkit/recordreplay/ProcessRedirectDarwin.cpp index b0d241f834c4..ecdc0d275a59 100644 --- a/toolkit/recordreplay/ProcessRedirectDarwin.cpp +++ b/toolkit/recordreplay/ProcessRedirectDarwin.cpp @@ -405,7 +405,9 @@ namespace recordreplay { MACRO(SLDisplayCopyColorSpace) \ MACRO(SLDisplayIOServicePort) \ MACRO(SLEventSourceCounterForEventType) \ - MACRO(SLMainDisplayID) + MACRO(SLMainDisplayID) \ + MACRO(SLSSetDenyWindowServerConnections) \ + MACRO(SLSShutdownServerConnections) #define MAKE_CALL_EVENT(aName) CallEvent_ ##aName , @@ -2679,6 +2681,8 @@ RRFunction2(SLEventSourceCounterForEventType) RRFunction1(SLDisplayCopyColorSpace) RRFunction1(SLDisplayIOServicePort) RRFunction0(SLMainDisplayID) +RRFunction1(SLSSetDenyWindowServerConnections) +RRFunctionVoid0(SLSShutdownServerConnections) /////////////////////////////////////////////////////////////////////////////// // Redirection generation diff --git a/toolkit/system/gnome/nsAlertsIconListener.cpp b/toolkit/system/gnome/nsAlertsIconListener.cpp index 9b1c62a560ee..9b9a479e9cd5 100644 --- a/toolkit/system/gnome/nsAlertsIconListener.cpp +++ b/toolkit/system/gnome/nsAlertsIconListener.cpp @@ -16,10 +16,13 @@ #include "nsIObserverService.h" #include "nsIURI.h" #include "nsCRT.h" +#include "mozilla/XREAppData.h" #include #include +extern const mozilla::StaticXREAppData* gAppData; + static bool gHasActions = 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_add_action_t nsAlertsIconListener::notify_notification_add_action = 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, 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_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_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) { dlclose(libNotifyHandle); libNotifyHandle = nullptr; @@ -175,6 +180,20 @@ nsAlertsIconListener::ShowAlert(GdkPixbuf* aPixbuf) 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 // 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 diff --git a/toolkit/system/gnome/nsAlertsIconListener.h b/toolkit/system/gnome/nsAlertsIconListener.h index f3a745d33061..4aa921accdb4 100644 --- a/toolkit/system/gnome/nsAlertsIconListener.h +++ b/toolkit/system/gnome/nsAlertsIconListener.h @@ -57,6 +57,7 @@ protected: 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 bool (*notify_notification_close_t)(void*, GError**); + typedef void (*notify_notification_set_hint_t)(NotifyNotification*, const char*, GVariant*); nsCOMPtr mIconRequest; nsCString mAlertTitle; @@ -80,6 +81,7 @@ protected: 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_close_t notify_notification_close; + static notify_notification_set_hint_t notify_notification_set_hint; NotifyNotification* mNotification; gulong mClosureHandler;