author | Daniel Varga <dvarga@mozilla.com> |
Tue, 14 Aug 2018 00:59:16 +0300 | |
changeset 431306 | 8b39d1161075364a95bc2d1577b389411fe5c342 |
parent 431305 | bb02d7d281cb13805772a9875c4f31074c152c7b (current diff) |
parent 431267 | 6f69e808328401eba4d3984f5c755f82ebbcd631 (diff) |
child 431307 | 7ee37c654498bffa4168de734ed27f9296c70caf |
child 431366 | f4c0098a60888e14bbb168ea6c33ae30733ed3ea |
push id | 106412 |
push user | dvarga@mozilla.com |
push date | Mon, 13 Aug 2018 22:10:16 +0000 |
treeherder | mozilla-inbound@7ee37c654498 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 63.0a1 |
first release with | nightly linux32
8b39d1161075
/
63.0a1
/
20180813220525
/
files
nightly linux64
8b39d1161075
/
63.0a1
/
20180813220525
/
files
nightly mac
8b39d1161075
/
63.0a1
/
20180813220525
/
files
nightly win32
8b39d1161075
/
63.0a1
/
20180813220525
/
files
nightly win64
8b39d1161075
/
63.0a1
/
20180813220525
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
63.0a1
/
20180813220525
/
pushlog to previous
nightly linux64
63.0a1
/
20180813220525
/
pushlog to previous
nightly mac
63.0a1
/
20180813220525
/
pushlog to previous
nightly win32
63.0a1
/
20180813220525
/
pushlog to previous
nightly win64
63.0a1
/
20180813220525
/
pushlog to previous
|
js/src/jit/arm/BaselineIC-arm.cpp | file | annotate | diff | comparison | revisions | |
js/src/jit/arm64/BaselineIC-arm64.cpp | file | annotate | diff | comparison | revisions | |
js/src/jit/mips32/BaselineIC-mips32.cpp | file | annotate | diff | comparison | revisions | |
js/src/jit/mips64/BaselineIC-mips64.cpp | file | annotate | diff | comparison | revisions | |
js/src/jit/x64/BaselineIC-x64.cpp | file | annotate | diff | comparison | revisions | |
js/src/jit/x86-shared/BaselineIC-x86-shared.cpp | file | annotate | diff | comparison | revisions | |
js/src/jit/x86/BaselineIC-x86.cpp | file | annotate | diff | comparison | revisions |
--- 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
--- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -118,18 +118,18 @@ return /******/ (function(modules) { // /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports, __w_pdfjs_require__) { "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); var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(20); var pdfjsDisplayDOMUtils = __w_pdfjs_require__(8); var pdfjsDisplaySVG = __w_pdfjs_require__(21); let pdfjsDisplayWorkerOptions = __w_pdfjs_require__(13); let pdfjsDisplayAPICompatibility = __w_pdfjs_require__(10); @@ -4218,17 +4218,17 @@ function _fetchDocument(worker, source, return Promise.reject(new Error('Worker was destroyed')); } if (pdfDataRangeTransport) { source.length = pdfDataRangeTransport.length; source.initialData = pdfDataRangeTransport.initialData; } return worker.messageHandler.sendWithPromise('GetDocRequest', { docId, - apiVersion: '2.0.750', + apiVersion: '2.0.760', source: { data: source.data, url: source.url, password: source.password, disableAutoFetch: source.disableAutoFetch, rangeChunkSize: source.rangeChunkSize, length: source.length }, @@ -5309,16 +5309,19 @@ var WorkerTransport = function WorkerTra pageIndex, intent }); }, getDestinations: function WorkerTransport_getDestinations() { 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() { return this.messageHandler.sendWithPromise('GetPageLabels', null); }, getPageMode() { return this.messageHandler.sendWithPromise('GetPageMode', null); }, @@ -5558,18 +5561,18 @@ var InternalRenderTask = function Intern } }); } }; return InternalRenderTask; }(); 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; exports.PDFDataRangeTransport = PDFDataRangeTransport; exports.PDFWorker = PDFWorker; exports.PDFDocumentProxy = PDFDocumentProxy; exports.PDFPageProxy = PDFPageProxy; exports.setPDFNetworkStreamFactory = setPDFNetworkStreamFactory;
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -118,18 +118,18 @@ return /******/ (function(modules) { // /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports, __w_pdfjs_require__) { "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; /***/ }), /* 1 */ /***/ (function(module, exports, __w_pdfjs_require__) { "use strict"; @@ -322,17 +322,17 @@ var WorkerMessageHandler = { }); }, createDocumentHandler(docParams, port) { var pdfManager; var terminated = false; 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}".`); } var docId = docParams.docId; var docBaseUrl = docParams.docBaseUrl; var workerHandlerName = docParams.docId + '_worker'; var handler = new _message_handler.MessageHandler(workerHandlerName, docId, port); handler.postMessageTransfers = docParams.postMessageTransfers; @@ -576,19 +576,19 @@ var WorkerMessageHandler = { pdfManager.requestLoadedStream(); return pdfManager.onLoadedStream().then(function (stream) { return stream.bytes; }); }); 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) { var pageIndex = data.pageIndex; pdfManager.getPage(pageIndex).then(function (page) { var task = new WorkerTask('RenderPageRequest: page ' + pageIndex); startWorkerTask(task); var pageNum = pageIndex + 1; @@ -5388,18 +5388,17 @@ var Page = function PageClosure() { stream: contentStream, task, resources: this.resources, operatorList: opList }).then(function () { 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; } var i, ii, opListPromises = []; for (i = 0, ii = annotations.length; i < ii; i++) { @@ -5439,37 +5438,47 @@ var Page = function PageClosure() { task, resources: this.resources, normalizeWhitespace, combineTextItems, sink }); }); }, - 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); - } - } - return annotationsData; + 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; + }); }, 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', annotations); + 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 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; }(); var PDFDocument = function PDFDocumentClosure() { var FINGERPRINT_FIRST_BYTES = 1024; var EMPTY_FINGERPRINT = '\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00'; function PDFDocument(pdfManager, arg) { @@ -5754,16 +5763,19 @@ var _primitives = __w_pdfjs_require__(12 var _parser = __w_pdfjs_require__(13); var _chunked_stream = __w_pdfjs_require__(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; this.xref = xref; this.catDict = xref.getCatalogObj(); if (!(0, _primitives.isDict)(this.catDict)) { throw new _util.FormatError('catalog object is not a dictionary'); } @@ -5893,72 +5905,46 @@ var Catalog = function CatalogClosure() get numPages() { var obj = this.toplevelPagesDict.get('Count'); if (!Number.isInteger(obj)) { throw new _util.FormatError('page count in top level pages object is not an integer'); } 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; - } - var xref = this.xref; - var dest = null, - nameTreeRef, - nameDictionaryRef; - var obj = this.catDict.get('Names'); + getDestination(destinationId) { + const obj = this._readDests(); + if (obj instanceof NameTree || obj instanceof _primitives.Dict) { + return fetchDestination(obj.get(destinationId) || null); + } + 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'); - } - 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; + return this.catDict.get('Dests'); + } }, get pageLabels() { var obj = null; try { obj = this.readPageLabels(); } catch (ex) { if (ex instanceof _util.MissingDataException) { throw ex; @@ -17705,16 +17691,19 @@ var _primitives = __w_pdfjs_require__(12 var _colorspace = __w_pdfjs_require__(25); var _operator_list = __w_pdfjs_require__(27); 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; } let id = (0, _primitives.isRef)(ref) ? ref.toString() : 'annot_' + idFactory.createObjId(); let subtype = dict.get('Subtype'); subtype = (0, _primitives.isName)(subtype) ? subtype.name : null; let parameters = { @@ -22807,27 +22796,25 @@ var Font = function FontClosure() { if (composite) { fileType = 'CIDFontType2'; } else { fileType = 'OpenType'; } } else if (isType1File(file)) { if (composite) { fileType = 'CIDFontType0'; - } else if (type === 'MMType1') { - fileType = 'MMType1'; - } else { - fileType = 'Type1'; + } else { + 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 { (0, _util.warn)('getFontFileType: Unable to detect correct font file Type/Subtype.'); fileType = type; fileSubtype = subtype; } return [fileType, fileSubtype];
--- a/browser/extensions/pdfjs/moz.yaml +++ b/browser/extensions/pdfjs/moz.yaml @@ -15,15 +15,15 @@ origin: description: Portable Document Format (PDF) viewer that is built with HTML5 # Full URL for the package's homepage/etc # Usually different from repository url url: https://github.com/mozilla/pdf.js # 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/ # Multiple licenses can be specified (as a YAML list) # A "LICENSE" file must exist containing the full license text license: Apache-2.0
--- a/build/moz.configure/checks.configure +++ b/build/moz.configure/checks.configure @@ -85,26 +85,30 @@ def checking(what, callback=None): # The default is to create an option for the environment variable `var`. # This argument allows to use a different kind of option (possibly using a # configure flag), or doing some pre-processing with a @depends function. # - `allow_missing` indicates whether not finding the program is an error. # - `paths` is a list of paths or @depends function returning a list of paths # 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')) # This will look for 'a' or 'b' in $PATH, and set_config PROG to the one # it can find. If PROG is already set from the environment or command line, # use that value instead. @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) def input(value): if not value: return if isinstance(value, str): return (value,) @@ -126,21 +130,29 @@ def check_prog(var, progs, what=None, in @checking('for %s' % what, lambda x: quote(x) if x else 'not found') def check(value, progs, paths): if progs is None: progs = () 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) @depends_if(check, progs, when=when) def normalized_for_config(value, progs): return ':' if value is None else value
--- a/build/moz.configure/node.configure +++ b/build/moz.configure/node.configure @@ -30,17 +30,18 @@ def node_toolchain_search_path(host): updated_path = [mozbuild_node_path] + path return updated_path # "nodejs" is first in the tuple on the assumption that it's only likely to # 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) @checking('for node.js version') @imports('os') @imports('re') @imports('subprocess') def nodejs_version(node):
--- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -937,33 +937,29 @@ CopyingStructuredCloneReadCallback(JSCon result.set(&wrappedFile.toObject()); return result; } if (aTag == SCTAG_DOM_MUTABLEFILE) { MOZ_ASSERT(file.mType == StructuredCloneFile::eMutableFile); - RefPtr<IDBMutableFile> mutableFile = file.mMutableFile; - JS::Rooted<JS::Value> wrappedMutableFile(aCx); - if (NS_WARN_IF(!ToJSValue(aCx, mutableFile, &wrappedMutableFile))) { + if (NS_WARN_IF(!ToJSValue(aCx, file.mMutableFile, &wrappedMutableFile))) { return nullptr; } result.set(&wrappedMutableFile.toObject()); return result; } MOZ_ASSERT(file.mType == StructuredCloneFile::eWasmBytecode); - RefPtr<JS::WasmModule> module = file.mWasmModule; - - JS::Rooted<JSObject*> wrappedModule(aCx, module->createObject(aCx)); + JS::Rooted<JSObject*> wrappedModule(aCx, file.mWasmModule->createObject(aCx)); if (NS_WARN_IF(!wrappedModule)) { return nullptr; } result.set(wrappedModule); return result; }
--- a/dom/script/ScriptSettings.cpp +++ b/dom/script/ScriptSettings.cpp @@ -666,17 +666,18 @@ AutoEntryScript::AutoEntryScript(nsIGlob } } AutoEntryScript::AutoEntryScript(JSObject* aObject, const char* aReason, 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() { } AutoEntryScript::DocshellEntryMonitor::DocshellEntryMonitor(JSContext* aCx, const char* aReason)
--- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -2697,34 +2697,35 @@ ASTSerializer::expression(ParseNode* pn, builder.unaryExpression(op, expr, &pn->pn_pos, dst); } case ParseNodeKind::New: case ParseNodeKind::TaggedTemplate: 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); if (!expression(next, &arg)) return false; args.infallibleAppend(arg); } @@ -2735,27 +2736,27 @@ ASTSerializer::expression(ParseNode* pn, return pn->isKind(ParseNodeKind::New) ? builder.newExpression(callee, args, &pn->pn_pos, dst) : builder.callExpression(callee, args, &pn->pn_pos, 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<PropertyAccess>().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; } return identifier(pnAtom, nullptr, &propname) && builder.memberExpression(false, expr, propname, &pn->pn_pos, dst); } case ParseNodeKind::Elem:
--- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -74,33 +74,33 @@ js::CreateRegExpMatchResult(JSContext* c if (!str) return false; arr->setDenseInitializedLength(i + 1); arr->initDenseElement(i, StringValue(str)); } } /* 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. */ rval.setObject(*arr); return true; } static int32_t @@ -1020,26 +1020,28 @@ js::RegExpMatcher(JSContext* cx, unsigne } /* * Separate interface for use by IonMonkey. * This code cannot re-enter Ion code. */ 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); } /* * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2 * steps 3, 9-25, except 12.a.i, 12.c.i.1, 15. * This code is inlined in CodeGenerator.cpp generateRegExpSearcherStub, * changes to this code need to get reflected in there too. */ @@ -1101,17 +1103,17 @@ js::RegExpSearcher(JSContext* cx, unsign bool js::RegExpSearcherRaw(JSContext* cx, HandleObject regexp, HandleString input, int32_t lastIndex, MatchPairs* maybeMatches, int32_t* result) { 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) { *result = CreateRegExpSearchResult(*maybeMatches); return true; } return RegExpSearcherImpl(cx, regexp, input, lastIndex, result); } /* * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
--- a/js/src/builtin/RegExp.h +++ b/js/src/builtin/RegExp.h @@ -36,17 +36,17 @@ MOZ_MUST_USE bool CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& matches, MutableHandleValue rval); extern MOZ_MUST_USE bool 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); extern MOZ_MUST_USE bool RegExpSearcherRaw(JSContext* cx, HandleObject regexp, HandleString input, int32_t lastIndex, MatchPairs* maybeMatches, int32_t* result);
--- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -997,19 +997,17 @@ StructMetaTypeDescr::createFromArrays(JS descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj)); if (!LinkConstructorAndPrototype(cx, descr, prototypeObj)) return nullptr; 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; } return descr; } bool
--- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -389,16 +389,20 @@ function isOverridableField(initialCSU, if (field == 'GetNativeContext') return false; if (field == "GetGlobalJSObject") return false; if (field == "GetIsMainThread") 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') return false; // nsIScriptSecurityManager is not [builtinclass], but smaug says "the // interface definitely should be builtinclass", which is good enough. if (initialCSU == 'nsIScriptSecurityManager' && field == 'IsSystemPrincipal')
--- a/js/src/devtools/rootAnalysis/run_complete +++ b/js/src/devtools/rootAnalysis/run_complete @@ -21,17 +21,17 @@ # various run_monitor processes to be running in the background (maybe on other # machines) and watching a shared poll_file for jobs. if the output directory # for this script already exists then an incremental analysis will be performed # and the reports will only reflect the changes since the earlier run. use strict; use warnings; use IO::Handle; -use File::Basename qw(dirname); +use File::Basename qw(basename dirname); use Getopt::Long; use Cwd; ################################# # environment specific settings # ################################# my $WORKDIR; @@ -244,23 +244,27 @@ sub run_build push(@extra, "-fplugin-arg-xgill-annfile=$ann_file") if ($ann_file ne "" && -e $ann_file); print CONFIG join(" ", @extra) . "\n"; close(CONFIG); # 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; clean_project() if ($do_clean); my $exit_status = build_project(); # signal the manager that it's over. system("$xsource -remote=$address -end-manager");
--- a/js/src/frontend/BinSource-auto.cpp +++ b/js/src/frontend/BinSource-auto.cpp @@ -3457,19 +3457,18 @@ BinASTParser<Tok>::parseInterfaceCallExp && !parseContext_->innermostScope()->lookupDeclaredNameForAdd(callee->name())) { // This is a direct call to `eval`. if (!parseContext_->sc()->hasDirectEval()) return raiseMissingDirectEvalInAssertedScope(); 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; } /* interface CatchClause : Node { AssertedParameterScope? bindingScope; @@ -3737,17 +3736,17 @@ BinASTParser<Tok>::parseInterfaceCompute const BinField expected_fields[2] = { BinField::Object, BinField::Expression }; MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields)); #endif // defined(DEBUG) BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper()); 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; } /* interface ComputedMemberExpression : Node { (Expression or Super) object; Expression expression; @@ -3781,17 +3780,17 @@ BinASTParser<Tok>::parseInterfaceCompute const BinField expected_fields[2] = { BinField::Object, BinField::Expression }; MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields)); #endif // defined(DEBUG) BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper()); 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; } /* interface ComputedPropertyName : Node { Expression expression; } @@ -5672,20 +5671,17 @@ BinASTParser<Tok>::parseInterfaceNewExpr const BinField expected_fields[2] = { BinField::Callee, BinField::Arguments }; MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields)); #endif // defined(DEBUG) BINJS_MOZ_TRY_DECL(callee, parseExpression()); 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; } /* interface NewTargetExpression : Node { } */ @@ -6192,23 +6188,28 @@ BinASTParser<Tok>::parseInterfaceStaticM { MOZ_ASSERT(kind == BinKind::StaticMemberAssignmentTarget); BINJS_TRY(CheckRecursionLimit(cx_)); #if defined(DEBUG) 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()); - - BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start)); + { + nameStart = tokenizer_->offset(); + MOZ_TRY_VAR(property, tokenizer_->readAtom()); + + } + + BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart))); + BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name)); return result; } /* interface StaticMemberExpression : Node { (Expression or Super) object; IdentifierName property; @@ -6237,23 +6238,28 @@ BinASTParser<Tok>::parseInterfaceStaticM { MOZ_ASSERT(kind == BinKind::StaticMemberExpression); BINJS_TRY(CheckRecursionLimit(cx_)); #if defined(DEBUG) 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()); - - BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start)); + { + nameStart = tokenizer_->offset(); + MOZ_TRY_VAR(property, tokenizer_->readAtom()); + + } + + BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart))); + BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name)); return result; } /* interface Super : Node { } */ @@ -7376,17 +7382,17 @@ BinASTParser<Tok>::parseVariableDeclarat template<typename Tok> JS::Result<ParseNode*> BinASTParser<Tok>::parseArguments() { uint32_t length; AutoList guard(*tokenizer_); 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()); factory_.addList(/* list = */ result, /* kid = */ item); } MOZ_TRY(guard.done()); return result;
--- a/js/src/frontend/BinSource.yaml +++ b/js/src/frontend/BinSource.yaml @@ -202,17 +202,17 @@ hpp: } // namespace frontend } // namespace js #endif // frontend_BinToken_h 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); ArrayExpression: build: auto result = elements; AssertedBlockScope: @@ -423,19 +423,18 @@ CallExpression: && !parseContext_->innermostScope()->lookupDeclaredNameForAdd(callee->name())) { // This is a direct call to `eval`. if (!parseContext_->sc()->hasDirectEval()) return raiseMissingDirectEvalInAssertedScope(); 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); CatchClause: init: | ParseContext::Statement stmt(parseContext_, StatementKind::Catch); ParseContext::Scope currentScope(cx_, parseContext_, usedNames_); BINJS_TRY(currentScope.init(parseContext_)); @@ -484,21 +483,21 @@ CompoundAssignmentExpression: case CompoundAssignmentOperator::BitAndAssign: pnk = ParseNodeKind::BitAndAssign; break; } BINJS_TRY_DECL(result, factory_.newAssignment(pnk, binding, expression)); 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: | BINJS_TRY_DECL(result, factory_.newConditional(test, consequent, alternate)); ContinueStatement: fields: label: @@ -826,20 +825,17 @@ LiteralRegExpExpression: BINJS_TRY_DECL(result, factory_.newRegExp(reobj, tokenizer_->pos(start), *this)); LiteralStringExpression: build: BINJS_TRY_DECL(result, factory_.newStringLiteral(value, tokenizer_->pos(start))); 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: auto result = properties; OptionalAssertedBlockScope: type-ok: Ok @@ -917,22 +913,38 @@ SwitchStatementWithDefault: ParseNode* next = iter->pn_next; factory_.addList(cases, iter); iter = next; } BINJS_TRY_DECL(scope, factory_.newLexicalScope(nullptr, cases)); 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: | if (parseContext_->isFunctionBox()) parseContext_->functionBox()->usesThis = true; TokenPos pos = tokenizer_->pos(start); ParseNode* thisName(nullptr);
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1017,17 +1017,17 @@ BytecodeEmitter::checkSideEffects(ParseN case ParseNodeKind::Continue: case ParseNodeKind::Debugger: MOZ_ASSERT(pn->isArity(PN_NULLARY)); *answer = true; return true; // Watch out for getters! case ParseNodeKind::Dot: - MOZ_ASSERT(pn->isArity(PN_NAME)); + MOZ_ASSERT(pn->isArity(PN_BINARY)); *answer = true; return true; // Unary cases with side effects only if the child has them. case ParseNodeKind::TypeOfExpr: case ParseNodeKind::Void: case ParseNodeKind::Not: MOZ_ASSERT(pn->isArity(PN_UNARY)); @@ -1255,16 +1255,24 @@ BytecodeEmitter::checkSideEffects(ParseN goto restart; return true; // Function calls can invoke non-local code. case ParseNodeKind::New: 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; case ParseNodeKind::Pipeline: MOZ_ASSERT(pn->isArity(PN_LIST)); MOZ_ASSERT(pn->pn_count >= 2); *answer = true; @@ -1389,16 +1397,17 @@ BytecodeEmitter::checkSideEffects(ParseN case ParseNodeKind::ImportSpecList: // by ParseNodeKind::Import case ParseNodeKind::ImportSpec: // by ParseNodeKind::Import case ParseNodeKind::ExportBatchSpec:// by ParseNodeKind::Export case ParseNodeKind::ExportSpecList: // by ParseNodeKind::Export case ParseNodeKind::ExportSpec: // by ParseNodeKind::Export 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 MOZ_CRASH("invalid node kind"); } MOZ_CRASH("invalid, unenumerated ParseNodeKind value encountered in " "BytecodeEmitter::checkSideEffects"); @@ -1860,49 +1869,49 @@ BytecodeEmitter::emitTDZCheckIfNeeded(JS } bool BytecodeEmitter::emitPropLHS(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot)); MOZ_ASSERT(!pn->as<PropertyAccess>().isSuper()); - ParseNode* pn2 = pn->pn_expr; + ParseNode* pn2 = pn->pn_left; /* * If the object operand is also a dotted property reference, reverse the - * 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<PropertyAccess>().isSuper()) { ParseNode* pndot = pn2; 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<PropertyAccess>().isSuper()) break; pnup = pndot; pndot = pndown; } /* pndown is a primary expression, not a dotted property reference. */ if (!emitTree(pndown)) return false; do { /* Walk back up the list, emitting annotated name ops. */ - if (!emitAtomOp(pndot, JSOP_GETPROP)) - return false; - - /* Reverse the pn_expr link again. */ - pnup = pndot->pn_expr; - pndot->pn_expr = pndown; + if (!emitAtomOp(pndot->pn_right, JSOP_GETPROP)) + return false; + + /* Reverse the pn_left link again. */ + pnup = pndot->pn_left; + pndot->pn_left = pndown; pndown = pndot; } while ((pndot = pnup) != nullptr); return true; } // The non-optimized case. return emitTree(pn2); } @@ -1917,41 +1926,41 @@ BytecodeEmitter::emitSuperPropLHS(ParseN if (!emit1(JSOP_SUPERBASE)) return false; return true; } bool BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op) { - MOZ_ASSERT(pn->isArity(PN_NAME)); + MOZ_ASSERT(pn->isArity(PN_BINARY)); if (!emitPropLHS(pn)) return false; 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)) return false; return true; } bool BytecodeEmitter::emitSuperGetProp(ParseNode* pn, bool isCall) { ParseNode* base = &pn->as<PropertyAccess>().expression(); 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)) return false; return true; } @@ -1971,17 +1980,17 @@ BytecodeEmitter::emitPropIncDec(ParseNod if (!emit1(JSOP_DUP2)) // THIS OBJ THIS OBJ return false; } else { if (!emitPropLHS(pn->pn_kid)) // OBJ return false; 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; if (post && !emit1(JSOP_DUP)) // OBJ N? N return false; if (!emit1(JSOP_ONE)) // OBJ N? N 1 return false; if (!emit1(binop)) // OBJ N? N+1 @@ -1997,17 +2006,17 @@ BytecodeEmitter::emitPropIncDec(ParseNod return false; if (!emit1(JSOP_SWAP)) // N THIS OBJ N+1 return false; } } 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; return true; } bool @@ -2687,17 +2696,17 @@ BytecodeEmitter::emitDestructuringLHSRef switch (target->getKind()) { case ParseNodeKind::Dot: { if (target->as<PropertyAccess>().isSuper()) { if (!emitSuperPropLHS(&target->as<PropertyAccess>().expression())) return false; *emitted = 2; } else { - if (!emitTree(target->pn_expr)) + if (!emitTree(target->pn_left)) return false; *emitted = 1; } break; } case ParseNodeKind::Elem: { if (target->as<PropertyByValue>().isSuper()) { @@ -2805,17 +2814,17 @@ BytecodeEmitter::emitSetOrInitializeDest case ParseNodeKind::Dot: { // The reference is already pushed by emitDestructuringLHSRef. JSOp setOp; if (target->as<PropertyAccess>().isSuper()) 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; } case ParseNodeKind::Elem: { // The reference is already pushed by emitDestructuringLHSRef. if (target->as<PropertyByValue>().isSuper()) { JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER; @@ -3918,21 +3927,21 @@ BytecodeEmitter::emitAssignment(ParseNod switch (lhs->getKind()) { case ParseNodeKind::Dot: if (lhs->as<PropertyAccess>().isSuper()) { if (!emitSuperPropLHS(&lhs->as<PropertyAccess>().expression())) 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: { MOZ_ASSERT(lhs->isArity(PN_BINARY)); EmitElemOption opt = op == JSOP_NOP ? EmitElemOption::Get : EmitElemOption::CompoundAssign; if (lhs->as<PropertyByValue>().isSuper()) { if (!emitSuperElemOperands(lhs, opt)) return false; @@ -3971,17 +3980,17 @@ BytecodeEmitter::emitAssignment(ParseNod JSOp getOp; if (lhs->as<PropertyAccess>().isSuper()) { if (!emit1(JSOP_DUP2)) return false; getOp = JSOP_GETPROP_SUPER; } 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)) return false; break; } case ParseNodeKind::Elem: { JSOp elemOp; @@ -4348,22 +4357,20 @@ BytecodeEmitter::emitTry(ParseNode* pn) return true; } bool BytecodeEmitter::emitIf(ParseNode* pn) { IfEmitter ifThenElse(this); + if (!ifThenElse.emitIf(Some(pn->pn_pos.begin))) + return false; + 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)) - return false; - /* Emit code for the condition before pushing stmtInfo. */ if (!emitTree(pn->pn_kid1)) return false; ParseNode* elseNode = pn->pn_kid3; if (elseNode) { if (!ifThenElse.emitThenElse()) return false; @@ -4375,17 +4382,17 @@ BytecodeEmitter::emitIf(ParseNode* pn) /* Emit code for the then part. */ if (!emitTree(pn->pn_kid2)) return false; if (elseNode) { if (elseNode->isKind(ParseNodeKind::If)) { pn = elseNode; - if (!ifThenElse.emitElseIf()) + if (!ifThenElse.emitElseIf(Some(pn->pn_pos.begin))) return false; goto if_again; } if (!ifThenElse.emitElse()) return false; @@ -4816,17 +4823,17 @@ BytecodeEmitter::emitForOf(ParseNode* fo ParseNode* forHeadExpr = forOfHead->pn_kid3; // Certain builtins (e.g. Array.from) are implemented in self-hosting // as for-of loops. 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; } ForOfEmitter forOf(this, headLexicalEmitterScope, allowSelfHostedIter, iterKind); if (!forOf.emitIterated()) // return false; @@ -6137,45 +6144,47 @@ BytecodeEmitter::emitSelfHostedCallFunct // invokes the callee with the correct |this| object and arguments. // callFunction(fun, thisArg, arg0, arg1) thus becomes: // - emit lookup for fun // - emit lookup for thisArg // - emit lookups for arg0, arg1 // // 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); - - if (pn->pn_count < 3) { + ParseNode* pn_callee = pn->pn_left; + ParseNode* pn_args = pn->pn_right; + + 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; } JSOp callOp = pn->getOp(); if (callOp != JSOP_CALL) { reportError(pn, JSMSG_NOT_CONSTRUCTOR, errorName); 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 && funNode->name() == cx->names().std_Function_apply) { callOp = JSOP_FUNAPPLY; } if (!emitTree(funNode)) return false; #ifdef DEBUG if (emitterMode == BytecodeEmitter::SelfHosting && - pn2->name() == cx->names().callFunction) + pn_callee->name() == cx->names().callFunction) { if (!emit1(JSOP_DEBUGCHECKSELFHOSTED)) return false; } #endif ParseNode* thisOrNewTarget = funNode->pn_next; if (constructing) { @@ -6194,36 +6203,36 @@ BytecodeEmitter::emitSelfHostedCallFunct return false; } if (constructing) { if (!emitTree(thisOrNewTarget)) return false; } - uint32_t argc = pn->pn_count - 3; + uint32_t argc = pn_args->pn_count - 2; if (!emitCall(callOp, argc)) return false; checkTypeSet(callOp); return true; } 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; ParseNode* valNode = genNode->pn_next; if (!emitTree(valNode)) return false; ParseNode* kindNode = valNode->pn_next; @@ -6245,34 +6254,36 @@ BytecodeEmitter::emitSelfHostedForceInte if (!emit1(JSOP_UNDEFINED)) return false; return true; } 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* funNode = pn->pn_head; // The _DefineDataProperty node. - - ParseNode* objNode = funNode->pn_next; + ParseNode* pn_args = pn->pn_right; + + // Only optimize when 3 arguments are passed. + MOZ_ASSERT(pn_args->pn_count == 3); + + ParseNode* objNode = pn_args->pn_head; if (!emitTree(objNode)) return false; ParseNode* idNode = objNode->pn_next; if (!emitTree(idNode)) return false; ParseNode* valNode = idNode->pn_next; @@ -6283,45 +6294,45 @@ BytecodeEmitter::emitSelfHostedDefineDat // but that's fine because the self-hosted code doesn't use the return // value. return emit1(JSOP_INITELEM); } 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; ParseNode* objNode = idNode->pn_next; if (!emitTree(objNode)) return false; return emit1(JSOP_HASOWN); } 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; if (!emitTree(receiverNode)) return false; if (!emitTree(idNode)) return false; @@ -6340,21 +6351,21 @@ BytecodeEmitter::isRestParameter(ParseNo FunctionBox* funbox = sc->asFunctionBox(); RootedFunction fun(cx, funbox->function()); if (!funbox->hasRest()) return false; 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; } JSAtom* name = pn->name(); Maybe<NameLocation> paramLoc = locationOfNameBoundInFunctionScope(name); if (paramLoc && lookupName(name) == *paramLoc) { @@ -6474,111 +6485,32 @@ BytecodeEmitter::emitPipeline(ParseNode* checkTypeSet(JSOP_CALL); } while ((callee = callee->pn_next)); 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). - */ - uint32_t argc = pn->pn_count - 1; +BytecodeEmitter::emitArguments(ParseNode* pn, bool callop, bool spread) +{ + 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); if (emitOptCode) { // Emit a preparation code to optimize the spread call with a rest // parameter: // // function f(...args) { @@ -6608,39 +6540,169 @@ BytecodeEmitter::emitCallOrNew(ParseNode if (!emitArray(args, argc)) return false; if (emitOptCode) { 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()); } if (pn->isOp(JSOP_EVAL) || pn->isOp(JSOP_STRICTEVAL) || pn->isOp(JSOP_SPREADEVAL) || pn->isOp(JSOP_STRICTSPREADEVAL)) @@ -6841,36 +6903,38 @@ BytecodeEmitter::emitLabeledStatement(co return true; } 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()) - return false; - MOZ_ASSERT(ifThenElse.pushed() == 1); + if (!cond.emitEnd()) + return false; + MOZ_ASSERT(cond.pushed() == 1); return true; } bool BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, PropListType type) { for (ParseNode* propdef = pn->pn_head; propdef; propdef = propdef->pn_next) { @@ -7199,17 +7263,17 @@ BytecodeEmitter::emitArray(ParseNode* pn return false; } else { ParseNode* expr; if (pn2->isKind(ParseNodeKind::Spread)) { expr = pn2->pn_kid; if (emitterMode == BytecodeEmitter::SelfHosting && expr->isKind(ParseNodeKind::Call) && - expr->pn_head->name() == cx->names().allowContentIter) + expr->pn_left->name() == cx->names().allowContentIter) { allowSelfHostedIter = true; } } else { expr = pn2; } if (!emitTree(expr)) // ARRAY INDEX? VALUE return false; @@ -8247,18 +8311,19 @@ BytecodeEmitter::emitTree(ParseNode* pn, return false; break; case ParseNodeKind::SetThis: if (!emitSetThis(pn)) 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); } /* bce->emitLevel == 1 means we're last on the stack, so finish up. */ if (emitLevel == 1) { if (!updateSourceCoordNotes(pn->pn_pos.end))
--- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -803,16 +803,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_NEVER_INLINE MOZ_MUST_USE bool emitIncOrDec(ParseNode* pn); MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional, ValueUsage valueUsage = ValueUsage::WantValue); 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); MOZ_MUST_USE bool emitSelfHostedForceInterpreter(); MOZ_MUST_USE bool emitSelfHostedAllowContentIter(ParseNode* pn); MOZ_MUST_USE bool emitSelfHostedDefineDataProperty(ParseNode* pn); MOZ_MUST_USE bool emitSelfHostedGetPropertySuper(ParseNode* pn); MOZ_MUST_USE bool emitSelfHostedHasOwn(ParseNode* pn);
--- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -342,18 +342,20 @@ ContainsHoistedDeclaration(JSContext* cx case ParseNodeKind::UrshAssign: case ParseNodeKind::MulAssign: case ParseNodeKind::DivAssign: case ParseNodeKind::ModAssign: case ParseNodeKind::PowAssign: 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: case ParseNodeKind::TemplateStringList: case ParseNodeKind::TaggedTemplate: case ParseNodeKind::CallSiteObj: case ParseNodeKind::String: case ParseNodeKind::RegExp: @@ -1248,17 +1250,20 @@ FoldElement(JSContext* cx, ParseNode** n } // If we don't have a name, we can't optimize to getprop. if (!name) return true; // Optimization 3: We have expr["foo"] where foo is not an index. Convert // to a property access (like expr.foo) that optimizes better downstream. - ParseNode* dottedAccess = parser.newPropertyAccess(expr, name, node->pn_pos.end); + ParseNode* nameNode = parser.newPropertyName(name, key->pn_pos); + if (!nameNode) + return false; + ParseNode* dottedAccess = parser.newPropertyAccess(expr, nameNode); if (!dottedAccess) return false; dottedAccess->setInParens(node->isInParens()); ReplaceNode(nodePtr, dottedAccess); return true; } @@ -1404,34 +1409,51 @@ FoldAdd(JSContext* cx, ParseNode** nodeP return true; } static bool FoldCall(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser) { 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: // // var prop = "global"; // var obj = { prop: "obj", f: function() { return this.prop; } }; // assertEq((true ? obj.f : null)(), "global"); // assertEq(obj.f(), "obj"); // assertEq((true ? obj.f : null)``, "global"); // assertEq(obj.f``, "obj"); // // See bug 537673 and bug 1182373. + 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<FullParseHandler>& parser) +{ + MOZ_ASSERT(node->isKind(ParseNodeKind::Arguments)); + MOZ_ASSERT(node->isArity(PN_LIST)); + ParseNode** listp = &node->pn_head; - if ((*listp)->isInParens()) - listp = &(*listp)->pn_next; - for (; *listp; listp = &(*listp)->pn_next) { if (!Fold(cx, listp, parser)) return false; } // If the last node in the list was replaced, pn_tail points into the wrong node. node->pn_tail = listp; @@ -1477,24 +1499,24 @@ FoldForHead(JSContext* cx, ParseNode* no return true; } static bool FoldDottedProperty(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& 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); } static bool FoldName(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser) { @@ -1637,17 +1659,16 @@ Fold(JSContext* cx, ParseNode** pnp, Per case ParseNodeKind::Ne: case ParseNodeKind::Lt: case ParseNodeKind::Le: case ParseNodeKind::Gt: case ParseNodeKind::Ge: case ParseNodeKind::InstanceOf: case ParseNodeKind::In: case ParseNodeKind::Comma: - case ParseNodeKind::New: case ParseNodeKind::Array: case ParseNodeKind::Object: case ParseNodeKind::StatementList: case ParseNodeKind::ClassMethodList: case ParseNodeKind::TemplateStringList: case ParseNodeKind::Var: case ParseNodeKind::Const: case ParseNodeKind::Let: @@ -1689,20 +1710,24 @@ Fold(JSContext* cx, ParseNode** pnp, Per case ParseNodeKind::Elem: return FoldElement(cx, pnp, parser); case ParseNodeKind::Add: 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: case ParseNodeKind::AddAssign: case ParseNodeKind::SubAssign: case ParseNodeKind::BitOrAssign: case ParseNodeKind::BitAndAssign: case ParseNodeKind::BitXorAssign: @@ -1772,16 +1797,19 @@ Fold(JSContext* cx, ParseNode** pnp, Per case ParseNodeKind::ForHead: return FoldForHead(cx, pn, parser); case ParseNodeKind::Label: 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); case ParseNodeKind::LexicalScope: MOZ_ASSERT(pn->isArity(PN_SCOPE)); if (!pn->scopeBody()) return true; return Fold(cx, &pn->pn_u.scope.body, parser);
--- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -270,26 +270,30 @@ class FullParseHandler void addArrayElement(ParseNode* literal, ParseNode* element) { MOZ_ASSERT(literal->isArity(PN_LIST)); if (!element->isConstant()) literal->pn_xflags |= PNX_NONCONST; addList(/* list = */ literal, /* child = */ element); } - ParseNode* newCall(const TokenPos& pos) { - return new_<ListNode>(ParseNodeKind::Call, JSOP_CALL, pos); + ParseNode* newCall(ParseNode* callee, ParseNode* args) { + return new_<BinaryNode>(ParseNodeKind::Call, JSOP_CALL, callee, args); } - ParseNode* newSuperCall(ParseNode* callee) { - return new_<ListNode>(ParseNodeKind::SuperCall, JSOP_SUPERCALL, callee); + ParseNode* newArguments(const TokenPos& pos) { + return new_<ListNode>(ParseNodeKind::Arguments, JSOP_NOP, pos); } - ParseNode* newTaggedTemplate(const TokenPos& pos) { - return new_<ListNode>(ParseNodeKind::TaggedTemplate, JSOP_CALL, pos); + ParseNode* newSuperCall(ParseNode* callee, ParseNode* args) { + return new_<BinaryNode>(ParseNodeKind::SuperCall, JSOP_SUPERCALL, callee, args); + } + + ParseNode* newTaggedTemplate(ParseNode* tag, ParseNode* args) { + return new_<BinaryNode>(ParseNodeKind::TaggedTemplate, JSOP_CALL, tag, args); } ParseNode* newObjectLiteral(uint32_t begin) { return new_<ListNode>(ParseNodeKind::Object, TokenPos(begin, begin + 1)); } ParseNode* newClass(ParseNode* name, ParseNode* heritage, ParseNode* methodBlock, const TokenPos& pos) @@ -655,18 +659,22 @@ class FullParseHandler TokenPos pos(begin, (finallyBlock ? finallyBlock : catchScope)->pn_pos.end); return new_<TernaryNode>(ParseNodeKind::Try, body, catchScope, finallyBlock, pos); } ParseNode* newDebuggerStatement(const TokenPos& pos) { return new_<DebuggerStatement>(pos); } - ParseNode* newPropertyAccess(ParseNode* expr, PropertyName* key, uint32_t end) { - return new_<PropertyAccess>(expr, key, expr->pn_pos.begin, end); + ParseNode* newPropertyName(PropertyName* name, const TokenPos& pos) { + return new_<NameNode>(ParseNodeKind::PropertyName, JSOP_NOP, name, pos); + } + + ParseNode* newPropertyAccess(ParseNode* expr, ParseNode* key) { + return new_<PropertyAccess>(expr, key, expr->pn_pos.begin, key->pn_pos.end); } ParseNode* newPropertyByValue(ParseNode* lhs, ParseNode* index, uint32_t end) { return new_<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end); } bool setupCatchScope(ParseNode* lexicalScope, ParseNode* catchName, ParseNode* catchBody) { ParseNode* catchpn; @@ -730,23 +738,18 @@ class FullParseHandler ParseNode* newModule(const TokenPos& pos) { return new_<CodeNode>(ParseNodeKind::Module, JSOP_NOP, pos); } ParseNode* newLexicalScope(LexicalScope::Data* bindings, ParseNode* body) { return new_<LexicalScopeNode>(bindings, body); } - Node newNewExpression(uint32_t begin, ParseNode* ctor) { - ParseNode* newExpr = new_<ListNode>(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_<BinaryNode>(ParseNodeKind::New, JSOP_NEW, TokenPos(begin, args->pn_pos.end), ctor, args); } ParseNode* newAssignment(ParseNodeKind kind, ParseNode* lhs, ParseNode* rhs) { if (kind == ParseNodeKind::Assign && lhs->isKind(ParseNodeKind::Name) && !lhs->isInParens()) { checkAndSetIsDirectRHSAnonFunction(rhs); }
--- a/js/src/frontend/IfEmitter.cpp +++ b/js/src/frontend/IfEmitter.cpp @@ -8,37 +8,34 @@ #include "frontend/BytecodeEmitter.h" #include "frontend/SourceNotes.h" #include "vm/Opcodes.h" 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) : IfEmitter(bce, Kind::MayContainLexicalAccessInBranch) {} 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(); // Emit an annotated branch-if-false around the then part. if (!bce_->newSrcNote(type)) return false; if (!bce_->emitJump(JSOP_IFEQ, &jumpAroundThen_)) @@ -56,69 +53,30 @@ IfEmitter::emitIfInternal(SrcNoteType ty // Enclose then-branch with TDZCheckCache. if (kind_ == Kind::MayContainLexicalAccessInBranch) tdzCache_.emplace(bce_); return true; } void -IfEmitter::calculateOrCheckPushed() +BranchEmitterBase::calculateOrCheckPushed() { #ifdef DEBUG if (!calculatedPushed_) { pushed_ = bce_->stackDepth - thenDepth_; calculatedPushed_ = true; } else { MOZ_ASSERT(pushed_ == bce_->stackDepth - thenDepth_); } #endif } 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(); // The end of TDZCheckCache for then-clause. if (kind_ == Kind::MayContainLexicalAccessInBranch) { MOZ_ASSERT(tdzCache_.isSome()); tdzCache_.reset(); } @@ -133,67 +91,27 @@ IfEmitter::emitElseInternal() if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_)) return false; // Clear jumpAroundThen_ offset, to tell emitEnd there was an else part. jumpAroundThen_ = JumpList(); // 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()); tdzCache_.reset(); } calculateOrCheckPushed(); @@ -203,17 +121,178 @@ IfEmitter::emitEnd() if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_)) return false; } // Patch all the jumps around else parts. if (!bce_->emitJumpTargetAndPatch(jumpsAroundElse_)) return false; + return true; +} + +bool +IfEmitter::emitIf(const Maybe<uint32_t>& ifPos) +{ + MOZ_ASSERT(state_ == State::Start); + + if (ifPos) { + // Make sure this code is attributed to the "if" so that it gets a + // useful column number, instead of the default 0 value. + if (!bce_->updateSourceCoordNotes(*ifPos)) + return false; + } + +#ifdef DEBUG + state_ = State::If; +#endif + return true; +} + +bool +IfEmitter::emitThen() +{ + MOZ_ASSERT(state_ == State::If || state_ == State::ElseIf); + MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome()); + MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing()); + + if (!emitThenInternal(SRC_IF)) + return false; + +#ifdef DEBUG + state_ = State::Then; +#endif + return true; +} + +bool +IfEmitter::emitThenElse() +{ + MOZ_ASSERT(state_ == State::If || state_ == State::ElseIf); + MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome()); + MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing()); + + if (!emitThenInternal(SRC_IF_ELSE)) + return false; + +#ifdef DEBUG + state_ = State::ThenElse; +#endif + return true; +} + +bool +IfEmitter::emitElseIf(const Maybe<uint32_t>& ifPos) +{ + MOZ_ASSERT(state_ == State::ThenElse); + + if (!emitElseInternal()) + return false; + + if (ifPos) { + // Make sure this code is attributed to the "if" so that it gets a + // useful column number, instead of the default 0 value. + if (!bce_->updateSourceCoordNotes(*ifPos)) + return false; + } + +#ifdef DEBUG + state_ = State::ElseIf; +#endif + return true; +} + +bool +IfEmitter::emitElse() +{ + MOZ_ASSERT(state_ == State::ThenElse); + + if (!emitElseInternal()) + return false; + +#ifdef DEBUG + state_ = State::Else; +#endif + return true; +} + +bool +IfEmitter::emitEnd() +{ + MOZ_ASSERT(state_ == State::Then || state_ == State::Else); + // If there was an else part for the last branch, jumpAroundThen_ is + // already fixed up when emitting the else part. + MOZ_ASSERT_IF(state_ == State::Then, jumpAroundThen_.offset != -1); + MOZ_ASSERT_IF(state_ == State::Else, jumpAroundThen_.offset == -1); + + if (!emitEndInternal()) + return false; + #ifdef DEBUG state_ = State::End; #endif return true; } 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; +}
--- a/js/src/frontend/IfEmitter.h +++ b/js/src/frontend/IfEmitter.h @@ -16,68 +16,33 @@ #include "frontend/SourceNotes.h" #include "frontend/TDZCheckCache.h" namespace js { 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 // created for each basic block, which then-clause, else-clause, and // else-if condition are, but for internally used branches which are // known not to touch lexical variables we can skip creating TDZCheckCache // for them. // @@ -87,131 +52,253 @@ class MOZ_STACK_CLASS IfEmitter // which basically may contain declaration or accesses to lexical // variables inside then-clause, else-clause, and else-if condition. MayContainLexicalAccessInBranch, // For internally used branches which don't touch lexical variables // 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_; + Kind kind_; - // 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<TDZCheckCache> 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`. // Can be used in assertion after emitting if-then-else. int32_t pushed() const { return pushed_; } // Returns the number of values popped onto the value stack inside // `then_block` and `else_block`. // Can be used in assertion after emitting if-then-else. int32_t popped() const { 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<uint32_t>& ifPos); + + MOZ_MUST_USE bool emitThen(); + MOZ_MUST_USE bool emitThenElse(); + + MOZ_MUST_USE bool emitElseIf(const mozilla::Maybe<uint32_t>& ifPos); + MOZ_MUST_USE bool emitElse(); + + MOZ_MUST_USE bool emitEnd(); }; // Class for emitting bytecode for blocks like if-then-else which doesn't touch // lexical variables. // // 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 */ #endif /* frontend_IfEmitter_h */
--- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -70,21 +70,21 @@ class NameResolver * Walk over the given ParseNode, attempting to convert it to a stringified * name that respresents where the function is being assigned to. * * |*foundName| is set to true if a name is found for the expression. */ 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; return buf->append(n->pn_atom); case ParseNodeKind::This: *foundName = true; return buf->append("this"); @@ -310,27 +310,27 @@ class NameResolver element = element->pn_next; } } 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)); ParseNode* array = element->pn_head; MOZ_ASSERT(array->isKind(ParseNodeKind::Array)); for (ParseNode* kid = array->pn_head; kid; kid = kid->pn_next) MOZ_ASSERT(kid->isKind(ParseNodeKind::TemplateString)); for (ParseNode* next = array->pn_next; next; next = next->pn_next) { @@ -692,19 +692,16 @@ class NameResolver case ParseNodeKind::Add: case ParseNodeKind::Sub: case ParseNodeKind::Star: case ParseNodeKind::Div: case ParseNodeKind::Mod: 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: // Initializers for individual variables, and computed property names // within destructuring patterns, may contain unnamed functions. case ParseNodeKind::Var: case ParseNodeKind::Const: case ParseNodeKind::Let: @@ -728,21 +725,42 @@ class NameResolver // contents with expressions interpolated into the overall literal. case ParseNodeKind::TemplateStringList: MOZ_ASSERT(cur->isArity(PN_LIST)); if (!resolveTemplateLiteral(cur, prefix)) return false; 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. case ParseNodeKind::ExportSpecList: case ParseNodeKind::ImportSpecList: { MOZ_ASSERT(cur->isArity(PN_LIST)); #ifdef DEBUG bool isImport = cur->isKind(ParseNodeKind::ImportSpecList); @@ -761,22 +779,22 @@ class NameResolver MOZ_ASSERT(item->pn_right->isKind(ParseNodeKind::Name)); MOZ_ASSERT(!item->pn_right->expr()); } #endif break; } 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<PropertyAccess>().isSuper()) break; - if (!resolve(cur->expr(), prefix)) + if (!resolve(cur->pn_left, prefix)) return false; break; case ParseNodeKind::Label: MOZ_ASSERT(cur->isArity(PN_NAME)); if (!resolve(cur->expr(), prefix)) return false; break; @@ -805,16 +823,17 @@ class NameResolver break; // Kinds that should be handled by parent node resolution. case ParseNodeKind::ImportSpec: // by ParseNodeKind::ImportSpecList 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 MOZ_CRASH("invalid node kind"); } nparents--; MOZ_ASSERT(initialParents == nparents, "nparents imbalance detected");
--- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -211,16 +211,31 @@ UnaryNode::dump(GenericPrinter& out, int indent += strlen(name) + 2; DumpParseTree(pn_kid, out, indent); out.printf(")"); } void BinaryNode::dump(GenericPrinter& out, int indent) { + if (isKind(ParseNodeKind::Dot)) { + out.put("(."); + + DumpParseTree(pn_right, out, indent + 2); + + out.putChar(' '); + if (as<PropertyAccess>().isSuper()) + out.put("super"); + else + DumpParseTree(pn_left, out, indent + 2); + + out.printf(")"); + return; + } + const char* name = parseNodeNames[size_t(getKind())]; out.printf("(%s ", name); indent += strlen(name) + 2; DumpParseTree(pn_left, out, indent); IndentNewLine(out, indent); DumpParseTree(pn_right, out, indent); out.printf(")"); } @@ -283,43 +298,31 @@ DumpName(GenericPrinter& out, const Char else out.printf("\\u%04x", unsigned(c)); } } 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("#<null name>"); } else if (getOp() == JSOP_GETARG && pn_atom->length() == 0) { // Dump destructuring parameter. out.put("(#<zero-length name> "); DumpParseTree(expr(), out, indent + 21); out.printf(")"); } else { JS::AutoCheckCannotGC nogc; if (pn_atom->hasLatin1Chars()) DumpName(out, pn_atom->latin1Chars(nogc), pn_atom->length()); else DumpName(out, pn_atom->twoByteChars(nogc), pn_atom->length()); } - - if (isKind(ParseNodeKind::Dot)) { - out.putChar(' '); - if (as<PropertyAccess>().isSuper()) - out.put("super"); - else - DumpParseTree(expr(), out, indent + 2); - out.printf(")"); - } return; } const char* name = parseNodeNames[size_t(getKind())]; out.printf("(%s ", name); indent += strlen(name) + 2; DumpParseTree(expr(), out, indent); out.printf(")");
--- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -50,24 +50,26 @@ class ObjectBox; F(Colon) \ F(Shorthand) \ F(Pos) \ F(Neg) \ F(PreIncrement) \ F(PostIncrement) \ F(PreDecrement) \ F(PostDecrement) \ + F(PropertyName) \ F(Dot) \ F(Elem) \ F(Array) \ F(Elision) \ F(StatementList) \ F(Label) \ F(Object) \ F(Call) \ + F(Arguments) \ F(Name) \ F(ObjectPropertyName) \ F(ComputedName) \ F(Number) \ F(String) \ F(TemplateStringList) \ F(TemplateString) \ F(TaggedTemplate) \ @@ -366,34 +368,35 @@ IsTypeofKind(ParseNodeKind kind) * Not, * BitNot * TypeOfName, unary pn_kid: UNARY expr * TypeOfExpr * PreIncrement, unary pn_kid: MEMBER expr * 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 * DeleteExpr unary pn_kid: MEMBER expr that's evaluated, then the * overall delete evaluates to true; can't be a 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 * Object list pn_head: list of pn_count binary Colon nodes * Colon binary key-value pair in object initializer or * destructuring lhs * pn_left: property id, pn_right: value * Shorthand binary Same fields as Colon. This is used for object @@ -403,30 +406,31 @@ IsTypeofKind(ParseNodeKind kind) * Name, name pn_atom: name, string, or object atom * String pn_op: JSOP_GETNAME, JSOP_STRING, or JSOP_OBJECT * If JSOP_GETNAME, pn_op may be JSOP_*ARG or JSOP_*VAR * telling const-ness and static analysis results * TemplateStringList pn_head: list of alternating expr and template strings * 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 * Number dval pn_dval: double value of numeric literal * True, nullary pn_op: JSOp bytecode * False, * Null, * RawUndefined * * 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 * pn_u.scope.body: scope body * Generator nullary * InitialYield unary pn_kid: generator object * Yield, unary pn_kid: expr or null * YieldStar, @@ -566,18 +570,17 @@ class ParseNode } unary; struct { /* name, labeled statement, etc. */ union { JSAtom* atom; /* lexical name or label atom */ ObjectBox* objbox; /* regexp object */ 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; ParseNode* body; } scope; struct { double value; /* aligned numeric literal value */ DecimalPoint decimalPoint; /* Whether the number has a decimal point */ @@ -1171,40 +1174,43 @@ class RegExpLiteral : public NullaryNode static bool test(const ParseNode& node) { bool match = node.isKind(ParseNodeKind::RegExp); MOZ_ASSERT_IF(match, node.isArity(PN_NULLARY)); MOZ_ASSERT_IF(match, node.isOp(JSOP_REGEXP)); return match; } }; -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 { // ParseNodeKind::SuperBase cannot result from any expression syntax. return expression().isKind(ParseNodeKind::SuperBase); } };
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -3392,34 +3392,34 @@ GeneralParser<ParseHandler, CharT>::addE return false; } return tokenStream.getToken(ttp, TokenStream::TemplateTail); } template <class ParseHandler, typename CharT> bool -GeneralParser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling, Node nodeList, +GeneralParser<ParseHandler, CharT>::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)) return false; 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; } template <class ParseHandler, typename CharT> typename ParseHandler::Node GeneralParser<ParseHandler, CharT>::templateLiteral(YieldHandling yieldHandling) { Node pn = noSubstitutionUntaggedTemplate(); @@ -8638,68 +8638,71 @@ GeneralParser<ParseHandler, CharT>::assi errorAt(pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT); return null(); } } return res; } template <class ParseHandler, typename CharT> -bool -GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, Node listNode, - bool* isSpread, +typename ParseHandler::Node +GeneralParser<ParseHandler, CharT>::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; *isSpread = true; } Node argNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError); if (!argNode) - return false; + return null(); if (spread) { argNode = handler.newSpread(begin, argNode); if (!argNode) - return false; - } - - handler.addList(listNode, argNode); + return null(); + } + + handler.addList(argsList, argNode); bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::Operand)) - return false; + return null(); if (!matched) break; TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::Operand)) return null(); if (tt == TokenKind::Rp) break; } 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 ParserBase::checkAndMarkSuperScope() { if (!pc->sc()->allowSuperProperty()) return false; @@ -8735,30 +8738,37 @@ GeneralParser<ParseHandler, CharT>::memb // Gotten by tryNewTarget tt = anyChars.currentToken().type; Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited, tt, /* allowCallSyntax = */ false, /* possibleError = */ nullptr, PredictInvoked); 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(); if (!thisName) return null(); lhs = handler.newSuperBase(thisName, pos()); if (!lhs) return null(); @@ -8785,17 +8795,22 @@ GeneralParser<ParseHandler, CharT>::memb if (!tokenStream.getToken(&tt)) return null(); if (TokenKindIsPossibleIdentifierName(tt)) { PropertyName* field = anyChars.currentName(); if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) { 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 { error(JSMSG_NAME_AFTER_DOT); return null(); } } else if (tt == TokenKind::Lb) { Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited); @@ -8821,25 +8836,26 @@ GeneralParser<ParseHandler, CharT>::memb return null(); } if (tt != TokenKind::Lp) { error(JSMSG_BAD_SUPER); 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) handler.setOp(nextMember, JSOP_SPREADSUPERCALL); Node thisName = newThisName(); if (!thisName) return null(); @@ -8848,23 +8864,16 @@ GeneralParser<ParseHandler, CharT>::memb if (!nextMember) return null(); } else { if (options().selfHostingMode && handler.isPropertyAccess(lhs)) { error(JSMSG_SELFHOSTED_METHOD_CALL); 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)) { // Use the JSOP_FUN{APPLY,CALL} optimizations given the // right syntax. if (prop == context->names().apply) { op = JSOP_FUNAPPLY; if (pc->isFunctionBox()) @@ -8896,34 +8905,44 @@ GeneralParser<ParseHandler, CharT>::memb // If we're in a method, mark the method as requiring // support for 'super', since direct eval code can use // it. (If we're not in a method, that's fine, so // ignore the return value.) checkAndMarkSuperScope(); } } - 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) op = JSOP_SPREADEVAL; else if (op == JSOP_STRICTEVAL) op = JSOP_STRICTSPREADEVAL; 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); } } else { anyChars.ungetToken(); if (handler.isSuperBase(lhs)) break;
--- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -564,18 +564,22 @@ class MOZ_STACK_CLASS PerHandlerParser // If ParseHandler is FullParseHandler: // Do nothing. inline void clearAbortedSyntaxParse(); public: 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, Directives directives, GeneratorKind generatorKind, FunctionAsyncKind asyncKind); }; #define ABORTED_SYNTAX_PARSE_SENTINEL reinterpret_cast<void*>(0x1) @@ -1150,17 +1154,17 @@ class MOZ_STACK_CLASS GeneralParser enum FunctionBodyType { StatementListBody, ExpressionBody }; Node functionBody(InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, FunctionBodyType type); Node unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, uint32_t begin); 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); Node destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind, YieldHandling yieldHandling, TokenKind tt); inline bool checkExportedName(JSAtom* exportName); inline bool checkExportedNamesForArrayBinding(Node node);
--- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -243,19 +243,21 @@ class SyntaxParseHandler // Expressions Node newArrayLiteral(uint32_t begin) { return NodeUnparenthesizedArray; } MOZ_MUST_USE bool addElision(Node literal, const TokenPos& pos) { return true; } 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; } Node newClassNames(Node outer, Node inner, const TokenPos& pos) { return NodeGeneric; } Node newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; } Node newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; } Node newPosHolder(const TokenPos& pos) { return NodeGeneric; } @@ -327,18 +329,22 @@ class SyntaxParseHandler } Node newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; } Node newTryStatement(uint32_t begin, Node body, Node catchScope, Node finallyBlock) { return NodeGeneric; } 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; } Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeElement; } MOZ_MUST_USE bool setupCatchScope(Node letBlock, Node catchName, Node catchBody) { return true; } @@ -423,17 +429,17 @@ class SyntaxParseHandler MOZ_ASSERT(list == NodeGeneric || list == NodeUnparenthesizedArray || list == NodeUnparenthesizedObject || list == NodeVarDeclaration || list == NodeLexicalDeclaration || list == NodeFunctionCall); } - Node newNewExpression(uint32_t begin, Node ctor) { + Node newNewExpression(uint32_t begin, Node ctor, Node args) { return NodeGeneric; } Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs) { return kind == ParseNodeKind::Assign ? NodeUnparenthesizedAssignment : NodeGeneric; } bool isUnparenthesizedAssignment(Node node) {
--- a/js/src/gc/GC.cpp +++ b/js/src/gc/GC.cpp @@ -2773,19 +2773,31 @@ ForegroundUpdateKinds(AllocKinds kinds) result += kind; } return result; } 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<NativeObject>(); + UpdateCellPointers(trc, obj); + for (size_t i = 0; i < obj->slotSpan(); i++) { + Value value = obj->getSlot(i); + if (value.isObject()) + UpdateCellPointers(trc, &value.toObject()); + } + } } void GCRuntime::updateCellPointers(Zone* zone, AllocKinds kinds, size_t bgTaskCount) { AllocKinds fgKinds = bgTaskCount == 0 ? kinds : ForegroundUpdateKinds(kinds); AllocKinds bgKinds = kinds - fgKinds;
--- a/js/src/jit-test/lib/assert-offset-columns.js +++ b/js/src/jit-test/lib/assert-offset-columns.js @@ -28,17 +28,22 @@ function assertOffsetColumns(code, expec global.eval(execCode); // Allow some tests to append to a log that will show up in expected ordering. const hits = global.hits = []; const bpts = new Set(); // 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); bpts.add(offset.columnNumber); script.setBreakpoint(offset.offset, { hit(frame) { hits.push(offset.columnNumber);
--- 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
--- a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js +++ b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js @@ -78,8 +78,63 @@ assertOffsetColumns( " ^ ^ ^ ^", ); // getColumnOffsets correctly places the various parts of a DoWhileStatement. 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", +);
new file mode 100644 --- /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();
--- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -5010,16 +5010,137 @@ ICBinaryArith_Fallback::Compiler::genera masm.pushValue(R0); masm.push(ICStubReg); pushStubPayload(masm, R0.scratchReg()); return tailCallVM(DoBinaryArithFallbackInfo, masm); } // +// Compare_Fallback +// +static bool +DoCompareFallback(JSContext* cx, BaselineFrame* frame, ICCompare_Fallback* stub_, HandleValue lhs, + HandleValue rhs, MutableHandleValue ret) +{ + // This fallback stub may trigger debug mode toggling. + DebugModeOSRVolatileStub<ICCompare_Fallback*> stub(ICStubEngine::Baseline, frame, stub_); + + RootedScript script(cx, frame->script()); + jsbytecode* pc = stub->icEntry()->pc(script); + JSOp op = JSOp(*pc); + + FallbackICSpew(cx, stub, "Compare(%s)", CodeName[op]); + + // Case operations in a CONDSWITCH are performing strict equality. + if (op == JSOP_CASE) + op = JSOP_STRICTEQ; + + // Don't pass lhs/rhs directly, we need the original values when + // generating stubs. + RootedValue lhsCopy(cx, lhs); + RootedValue rhsCopy(cx, rhs); + + // Perform the compare operation. + bool out; + switch (op) { + case JSOP_LT: + if (!LessThan(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_LE: + if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_GT: + if (!GreaterThan(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_GE: + if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_EQ: + if (!LooselyEqual<true>(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_NE: + if (!LooselyEqual<false>(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_STRICTEQ: + if (!StrictlyEqual<true>(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_STRICTNE: + if (!StrictlyEqual<false>(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + default: + MOZ_ASSERT_UNREACHABLE("Unhandled baseline compare op"); + return false; + } + + ret.setBoolean(out); + + // Check if debug mode toggling made the stub invalid. + if (stub.invalid()) + return true; + + // Check to see if a new stub should be generated. + if (stub->numOptimizedStubs() >= ICCompare_Fallback::MAX_OPTIMIZED_STUBS) { + // TODO: Discard all stubs in this IC and replace with inert megamorphic stub. + // But for now we just bail. + return true; + } + + if (stub->state().canAttachStub()) { + CompareIRGenerator gen(cx, script, pc, stub->state().mode(), op, lhs, rhs); + bool attached = false; + if (gen.tryAttachStub()) { + ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), + BaselineCacheIRStubKind::Regular, + ICStubEngine::Baseline, script, stub, &attached); + if (newStub) + JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub"); + return true; + } + } + + stub->noteUnoptimizableAccess(); + return true; +} + +typedef bool (*DoCompareFallbackFn)(JSContext*, BaselineFrame*, ICCompare_Fallback*, + HandleValue, HandleValue, MutableHandleValue); +static const VMFunction DoCompareFallbackInfo = + FunctionInfo<DoCompareFallbackFn>(DoCompareFallback, "DoCompareFallback", TailCall, + PopValues(2)); + +bool +ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler& masm) +{ + MOZ_ASSERT(R0 == JSReturnOperand); + + // Restore the tail call register. + EmitRestoreTailCallReg(masm); + + // Ensure stack is fully synced for the expression decompiler. + masm.pushValue(R0); + masm.pushValue(R1); + + // Push arguments. + masm.pushValue(R1); + masm.pushValue(R0); + masm.push(ICStubReg); + pushStubPayload(masm, R0.scratchReg()); + return tailCallVM(DoCompareFallbackInfo, masm); +} + +// // NewArray_Fallback // static bool DoNewArray(JSContext* cx, BaselineFrame* frame, ICNewArray_Fallback* stub, uint32_t length, MutableHandleValue res) { FallbackICSpew(cx, stub, "NewArray");
--- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -1,16 +1,17 @@ /* -*- 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/BaselineInspector.h" +#include "mozilla/Array.h" #include "mozilla/DebugOnly.h" #include "jit/BaselineIC.h" #include "jit/CacheIRCompiler.h" #include "vm/EnvironmentObject-inl.h" #include "vm/JSScript-inl.h" #include "vm/ObjectGroup-inl.h" @@ -407,77 +408,183 @@ BaselineInspector::expectedResultType(js switch (stub->kind()) { case ICStub::CacheIR_Regular: return ParseCacheIRStub(stub); default: return MIRType::None; } } -// 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<MIRType,2>& guardType) { - return kind == ICStub::Compare_Double || kind == ICStub::Compare_NumberWithUndefined; + CacheOp op = reader.readOp(); + uint8_t guardOperand = reader.readByte(); + + // We only have two entries for guard types. + if (guardOperand > 1) + return false; + + // Already assigned this guard a type, fail. + if (guardType[guardOperand] != MIRType::None) + return false; + + switch (op) { + // 0 Skip cases + case CacheOp::GuardIsString: + guardType[guardOperand] = MIRType::String; + break; + case CacheOp::GuardIsSymbol: + guardType[guardOperand] = MIRType::Symbol; + break; + case CacheOp::GuardIsNumber: + guardType[guardOperand] = MIRType::Double; + break; + case CacheOp::GuardIsUndefined: + guardType[guardOperand] = MIRType::Undefined; + break; + // 1 skip + case CacheOp::GuardIsInt32: + guardType[guardOperand] = MIRType::Int32; + // Skip over result + reader.skip(); + break; + case CacheOp::GuardIsBoolean: + guardType[guardOperand] = MIRType::Boolean; + // Skip over result + reader.skip(); + break; + // Unknown op -- + default: + return false; + } + return true; } -// Whether a baseline stub kind is suitable for an int32 comparison that -// converts its operands to int32. +// This code works for all Compare ICs where the pattern is +// +// <Guard LHS/RHS> +// <Guard RHS/LHS> +// <CompareResult> +// +// in other cases (like StrictlyDifferentTypes) it will just +// return CompareUnknown +static MCompare::CompareType +ParseCacheIRStubForCompareType(ICCacheIR_Regular* stub) +{ + CacheIRReader reader(stub->stubInfo()); + + // Two element array to allow parsing the guards + // in whichever order they appear. + mozilla::Array<MIRType, 2> guards = { MIRType::None, MIRType::None }; + + // Parse out two guards + if (!GuardType(reader, guards)) + return MCompare::Compare_Unknown; + if (!GuardType(reader, guards)) + return MCompare::Compare_Unknown; + + // The lhs and rhs ids are asserted in + // CompareIRGenerator::tryAttachStub. + MIRType lhs_guard = guards[0]; + MIRType rhs_guard = guards[1]; + + if (lhs_guard == rhs_guard) + { + if (lhs_guard == MIRType::Int32) + return MCompare::Compare_Int32; + if (lhs_guard == MIRType::Double) + return MCompare::Compare_Double; + return MCompare::Compare_Unknown; + } + + if ((lhs_guard == MIRType::Int32 && rhs_guard == MIRType::Boolean) || + (lhs_guard == MIRType::Boolean && rhs_guard == MIRType::Int32)) + { + // RHS is converting + if (rhs_guard == MIRType::Boolean) + return MCompare::Compare_Int32MaybeCoerceRHS; + + return MCompare::Compare_Int32MaybeCoerceLHS; + } + + if ((lhs_guard == MIRType::Double && rhs_guard == MIRType::Undefined) || + (lhs_guard == MIRType::Undefined && rhs_guard == MIRType::Double)) + { + // RHS is converting + if (rhs_guard == MIRType::Undefined) + return MCompare::Compare_DoubleMaybeCoerceRHS; + + return MCompare::Compare_DoubleMaybeCoerceLHS; + } + + return MCompare::Compare_Unknown; +} + static bool -CanUseInt32Compare(ICStub::Kind kind) +CoercingCompare(MCompare::CompareType type) { - return kind == ICStub::Compare_Int32 || kind == ICStub::Compare_Int32WithBoolean; + //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 BaselineInspector::expectedCompareType(jsbytecode* pc) { ICStub* first = monomorphicStub(pc); ICStub* second = nullptr; if (!first && !dimorphicStub(pc, &first, &second)) return MCompare::Compare_Unknown; if (ICStub* fallback = second ? second->next() : first->next()) { MOZ_ASSERT(fallback->isFallback()); if (fallback->toCompare_Fallback()->hadUnoptimizableAccess()) 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; + + MCompare::CompareType second_type = ParseCacheIRStubForCompareType(second->toCacheIR_Regular()); - 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; - } + if (first_type == MCompare::Compare_Unknown || second_type == MCompare::Compare_Unknown) + return MCompare::Compare_Unknown; - return MCompare::Compare_Unknown; + if (first_type == second_type) + return first_type; + + return CompatibleType(first_type, second_type); } static bool TryToSpecializeBinaryArithOp(ICStub** stubs, uint32_t nstubs, MIRType* result) { DebugOnly<bool> sawInt32 = false;
--- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -32,16 +32,43 @@ const char* js::jit::CacheKindNames[] = #undef DEFINE_KIND }; void 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), cx_(cx), script_(script), pc_(pc), cacheKind_(cacheKind), mode_(mode) @@ -4809,53 +4836,193 @@ CompareIRGenerator::tryAttachStrictDiffe writer.loadBooleanResult(op_ == JSOP_STRICTNE ? true : false); writer.returnFromIC(); trackAttached("StrictDifferentTypes"); 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() { MOZ_ASSERT(cacheKind_ == CacheKind::Compare); MOZ_ASSERT(IsEqualityOp(op_) || op_ == JSOP_LE || op_ == JSOP_LT || op_ == JSOP_GE || op_ == JSOP_GT); 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)); if (IsEqualityOp(op_)) { if (tryAttachString(lhsId, rhsId)) return true; if (tryAttachObject(lhsId, rhsId)) 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; } void CompareIRGenerator::trackAttached(const char* name) { #ifdef JS_CACHEIR_SPEW if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) { sp.valueProperty("lhs", lhsVal_); sp.valueProperty("rhs", rhsVal_); + sp.opcodeProperty("op", op_); } #endif } ToBoolIRGenerator::ToBoolIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode, HandleValue val) : IRGenerator(cx, script, pc, CacheKind::ToBool, mode), val_(val)
--- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -177,16 +177,19 @@ enum class CacheKind : uint8_t }; extern const char* CacheKindNames[]; #define CACHE_IR_OPS(_) \ _(GuardIsObject) \ _(GuardIsObjectOrNull) \ _(GuardIsNullOrUndefined) \ + _(GuardIsNotNullOrUndefined) \ + _(GuardIsNull) \ + _(GuardIsUndefined) \ _(GuardIsBoolean) \ _(GuardIsString) \ _(GuardIsSymbol) \ _(GuardIsNumber) \ _(GuardIsInt32) \ _(GuardIsInt32Index) \ _(GuardType) \ _(GuardShape) \ @@ -319,16 +322,19 @@ extern const char* CacheKindNames[]; \ _(CallStringSplitResult) \ _(CallStringConcatResult) \ _(CallStringObjectConcatResult) \ \ _(CompareStringResult) \ _(CompareObjectResult) \ _(CompareSymbolResult) \ + _(CompareInt32Result) \ + _(CompareDoubleResult) \ + _(CompareObjectUndefinedNullResult) \ \ _(CallPrintString) \ _(Breakpoint) \ \ _(TypeMonitorResult) \ _(ReturnFromIC) \ _(WrapResult) @@ -433,16 +439,20 @@ class MOZ_RAII CacheIRWriter : public JS Vector<uint32_t, 8, SystemAllocPolicy> operandLastUsed_; // OperandId and stub offsets are stored in a single byte, so make sure // this doesn't overflow. We use a very conservative limit for now. static const size_t MaxOperandIds = 20; 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) { MOZ_ASSERT(uint32_t(op) <= UINT8_MAX); buffer_.writeByte(uint32_t(op)); nextInstructionId_++; } @@ -496,17 +506,19 @@ class MOZ_RAII CacheIRWriter : public JS public: explicit CacheIRWriter(JSContext* cx) : CustomAutoRooter(cx), cx_(cx), nextOperandId_(0), nextInstructionId_(0), numInputOperands_(0), stubDataSize_(0), - tooLarge_(false) + tooLarge_(false), + lastOffset_(0), + lastIndex_(0) {} bool failed() const { return buffer_.oom() || tooLarge_; } uint32_t numInputOperands() const { return numInputOperands_; } uint32_t numOperandIds() const { return nextOperandId_; } uint32_t numInstructions() const { return nextInstructionId_; } @@ -546,20 +558,17 @@ class MOZ_RAII CacheIRWriter : public JS } uint32_t codeLength() const { MOZ_ASSERT(!failed()); return buffer_.length(); } // 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); return ObjOperandId(val.id()); } Int32OperandId guardIsBoolean(ValOperandId val) { Int32OperandId res(nextOperandId_++); writeOpWithOperandId(CacheOp::GuardIsBoolean, val); @@ -595,16 +604,25 @@ class MOZ_RAII CacheIRWriter : public JS buffer_.writeByte(uint32_t(type)); } void guardIsObjectOrNull(ValOperandId val) { writeOpWithOperandId(CacheOp::GuardIsObjectOrNull, val); } 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); addStubField(uintptr_t(shape), StubField::Type::Shape); } void guardShapeForClass(ObjOperandId obj, Shape* shape) { // Guard shape to ensure that object class is unchanged. This is true // for all shapes. @@ -1252,21 +1270,35 @@ class MOZ_RAII CacheIRWriter : public JS writeOperandId(rhs); buffer_.writeByte(uint32_t(op)); } void compareObjectResult(uint32_t op, ObjOperandId lhs, ObjOperandId rhs) { writeOpWithOperandId(CacheOp::CompareObjectResult, lhs); 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); writePointer(const_cast<char*>(str)); } void breakpoint() { writeOp(CacheOp::Breakpoint); } @@ -1811,16 +1843,21 @@ class MOZ_RAII CompareIRGenerator : publ JSOp op_; HandleValue lhsVal_; HandleValue rhsVal_; bool tryAttachString(ValOperandId lhsId, ValOperandId rhsId); 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); public: CompareIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode mode, JSOp op, HandleValue lhsVal, HandleValue rhsVal); bool tryAttachStub();
--- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -88,17 +88,17 @@ CacheRegisterAllocator::useValueRegister } MOZ_CRASH(); } // 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()]; Label failure, done; switch (loc.kind()) { case OperandLocation::ValueReg: { masm.ensureDouble(loc.valueReg(), dest, &failure); break; @@ -120,22 +120,22 @@ CacheRegisterAllocator::loadDouble(Macro loc.setDoubleReg(dest); return; } case OperandLocation::Constant: 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); } ValueOperand CacheRegisterAllocator::useFixedValueRegister(MacroAssembler& masm, ValOperandId valId, ValueOperand reg) { @@ -1372,16 +1372,73 @@ CacheIRCompiler::emitGuardIsNullOrUndefi Label success; masm.branchTestNull(Assembler::Equal, input, &success); masm.branchTestUndefined(Assembler::NotEqual, input, failure->label()); masm.bind(&success); 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() { ValOperandId inputId = reader.valOperandId(); JSValueType knownType = allocator.knownType(inputId); if (knownType == JSVAL_TYPE_OBJECT || knownType == JSVAL_TYPE_NULL) return true; @@ -1989,71 +2046,71 @@ CacheIRCompiler::emitLoadInt32ArrayLengt bool CacheIRCompiler::emitDoubleAddResult() { AutoOutputRegister output(*this); // 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); return true; } bool 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); return true; } bool 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); return true; } bool 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); return true; } bool 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); masm.setupUnalignedABICall(scratch); masm.passABIArg(FloatReg0, MoveOp::DOUBLE); masm.passABIArg(FloatReg1, MoveOp::DOUBLE); @@ -3088,16 +3145,89 @@ CacheIRCompiler::emitCompareObjectResult bool 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() { const char* str = reinterpret_cast<char*>(reader.pointer()); masm.printf(str); return true; } bool
--- a/js/src/jit/CacheIRCompiler.h +++ b/js/src/jit/CacheIRCompiler.h @@ -14,16 +14,19 @@ namespace js { namespace jit { // The ops below are defined in CacheIRCompiler and codegen is shared between // BaselineCacheIRCompiler and IonCacheIRCompiler. #define CACHE_IR_SHARED_OPS(_) \ _(GuardIsObject) \ _(GuardIsNullOrUndefined) \ + _(GuardIsNotNullOrUndefined) \ + _(GuardIsNull) \ + _(GuardIsUndefined) \ _(GuardIsObjectOrNull) \ _(GuardIsBoolean) \ _(GuardIsString) \ _(GuardIsSymbol) \ _(GuardIsNumber) \ _(GuardIsInt32) \ _(GuardIsInt32Index) \ _(GuardType) \ @@ -88,16 +91,19 @@ namespace jit { _(LoadObjectResult) \ _(LoadTypeOfObjectResult) \ _(LoadInt32TruthyResult) \ _(LoadDoubleTruthyResult) \ _(LoadStringTruthyResult) \ _(LoadObjectTruthyResult) \ _(CompareObjectResult) \ _(CompareSymbolResult) \ + _(CompareInt32Result) \ + _(CompareDoubleResult) \ + _(CompareObjectUndefinedNullResult) \ _(ArrayJoinResult) \ _(CallPrintString) \ _(Breakpoint) \ _(MegamorphicLoadSlotResult) \ _(MegamorphicLoadSlotByValueResult) \ _(MegamorphicStoreSlot) \ _(MegamorphicHasPropResult) \ _(CallObjectHasSparseElementResult) \ @@ -483,18 +489,20 @@ class MOZ_RAII CacheRegisterAllocator Register useRegister(MacroAssembler& masm, TypedOperandId typedId); ConstantOrRegister useConstantOrRegister(MacroAssembler& masm, ValOperandId val); // Allocates an output register for the given operand. 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; // Emits code to restore registers and stack to the state at the start of // the stub. void restoreInputState(MacroAssembler& masm, bool discardStack = true); @@ -629,34 +637,31 @@ class MOZ_RAII CacheIRCompiler // Whether this IC may read double values from uint32 arrays. mozilla::Maybe<bool> allowDoubleResult_; // Distance from the IC to the stub data; mostly will be // sizeof(stubType) uint32_t stubDataOffset_; - uint32_t nextStubField_; - enum class StubFieldPolicy { Address, Constant }; StubFieldPolicy stubFieldPolicy_; CacheIRCompiler(JSContext* cx, const CacheIRWriter& writer, uint32_t stubDataOffset, Mode mode, StubFieldPolicy policy) : cx_(cx), reader(writer), writer_(writer), allocator(writer_), liveFloatRegs_(FloatRegisterSet::All()), mode_(mode), stubDataOffset_(stubDataOffset), - nextStubField_(0), stubFieldPolicy_(policy) { MOZ_ASSERT(!writer.failed()); } MOZ_MUST_USE bool addFailurePath(FailurePath** failure); MOZ_MUST_USE bool emitFailurePath(size_t i); @@ -711,25 +716,22 @@ class MOZ_RAII CacheIRCompiler #undef DEFINE_SHARED_OP void emitLoadStubField(StubFieldOffset val, Register dest); void emitLoadStubFieldConstant(StubFieldOffset val, Register dest); 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); return readStubWord(offset, StubField::Type::RawWord); } Shape* shapeStubField(uint32_t offset) { MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant); return (Shape*)readStubWord(offset, StubField::Type::Shape);
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -188,16 +188,21 @@ typedef bool (*IonUnaryArithICFn)(JSCont static const VMFunction IonUnaryArithICInfo = FunctionInfo<IonUnaryArithICFn>(IonUnaryArithIC::update, "IonUnaryArithIC::update"); typedef bool (*IonBinaryArithICFn)(JSContext* cx, HandleScript outerScript, IonBinaryArithIC* stub, HandleValue lhs, HandleValue rhs, MutableHandleValue res); static const VMFunction IonBinaryArithICInfo = FunctionInfo<IonBinaryArithICFn>(IonBinaryArithIC::update, "IonBinaryArithIC::update"); +typedef bool (*IonCompareICFn)(JSContext* cx, HandleScript outerScript, IonCompareIC* stub, + HandleValue lhs, HandleValue rhs, MutableHandleValue res); +static const VMFunction IonCompareICInfo = + FunctionInfo<IonCompareICFn>(IonCompareIC::update, "IonCompareIC::update"); + void CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) { LInstruction* lir = ool->lir(); size_t cacheIndex = ool->cacheIndex(); size_t cacheInfoIndex = ool->cacheInfoIndex(); DataPtr<IonIC> ic(this, cacheIndex); @@ -397,18 +402,34 @@ CodeGenerator::visitOutOfLineICFallback( callVM(IonBinaryArithICInfo, lir); StoreValueTo(binaryArithIC->output()).generate(this); restoreLiveIgnore(lir, StoreValueTo(binaryArithIC->output()).clobbered()); 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: MOZ_CRASH("Unsupported IC"); } MOZ_CRASH(); } @@ -1417,16 +1438,49 @@ PrepareAndExecuteRegExp(JSContext* cx, M Register temp1, Register temp2, Register temp3, size_t inputOutputDataStartOffset, RegExpShared::CompilationMode mode, bool stringsCanBeInNursery, Label* notFound, Label* failure) { 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); Address inputStartAddress(masm.getStackPointer(), inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, inputStart)); Address inputEndAddress(masm.getStackPointer(), inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, inputEnd)); Address matchesPointerAddress(masm.getStackPointer(), @@ -1437,32 +1491,40 @@ PrepareAndExecuteRegExp(JSContext* cx, M inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, endIndex)); Address matchResultAddress(masm.getStackPointer(), inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, result)); Address pairCountAddress = RegExpPairCountAddress(masm, inputOutputDataStartOffset); Address pairsPointerAddress(masm.getStackPointer(), matchPairsStartOffset + MatchPairs::offsetOfPairs()); - Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset); - RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global()); if (!res) return false; #ifdef JS_USE_LINK_REGISTER if (mode != RegExpShared::MatchOnly) masm.pushReturnAddress(); #endif if (mode == RegExpShared::Normal) { // First, fill in a skeletal MatchPairs instance on the stack. This will be // 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); } // Check for a linear input string. masm.branchIfRopeOrExternal(input, temp1, failure); // Get the RegExpShared for the RegExp. @@ -1502,30 +1564,29 @@ PrepareAndExecuteRegExp(JSContext* cx, M // <> floor(x / 1024) * 1024 = SurrogateMin // <> (x >>> 10) << 10 = SurrogateMin // <> x & ~(2^10 - 1) = SurrogateMin constexpr char16_t SurrogateMask = 0xFC00; // 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); } if (mode == RegExpShared::Normal) { // Don't handle RegExps with excessive parens. masm.load32(Address(temp1, RegExpShared::offsetOfParenCount()), temp2); masm.branch32(Assembler::AboveOrEqual, temp2, Imm32(RegExpObject::MaxPairCount), failure); @@ -1573,17 +1634,17 @@ PrepareAndExecuteRegExp(JSContext* cx, M masm.computeEffectiveAddress(Address(masm.getStackPointer(), matchPairsStartOffset), temp2); masm.storePtr(temp2, matchesPointerAddress); } else { // Use InputOutputData.endIndex itself for output. masm.computeEffectiveAddress(endIndexAddress, temp2); 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; if (lastIndex.volatile_()) volatileRegs.add(lastIndex); if (input.volatile_()) volatileRegs.add(input); if (regexp.volatile_()) @@ -1666,194 +1727,192 @@ PrepareAndExecuteRegExp(JSContext* cx, M masm.load32(endIndexAddress, temp3); } return true; } 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, NotInlineString, Count }; mozilla::EnumeratedArray<FallbackKind, FallbackKind::Count, Label> 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")); - - string_ = string; - temp_ = temp2; - failure_ = failure; + (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); + + 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. MOZ_ASSERT(startIndexAddress.base == masm.getStackPointer()); BaseIndex newStartIndexAddress = startIndexAddress; 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); - - CopyStringChars(masm, string, base, temp1, temp2, latin1 ? 1 : 2, latin1 ? 1 : 2); + masm.addToCharPtr(temp2_, base, encoding_); + + 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.jump(&done); + masm.pop(string_); + + 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())); - - 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); + newGCString(FallbackKind::NotInlineString); + + 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.setupUnalignedABICall(temp2); - masm.passABIArg(temp1); - masm.passABIArg(string); + masm.mov(ImmPtr(runtime), temp1_); + + masm.setupUnalignedABICall(temp2_); + masm.passABIArg(temp1_); + masm.passABIArg(string_); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier)); masm.PopRegsInMask(regsToSave); } masm.bind(&done); } @@ -1867,22 +1926,25 @@ AllocateString(JSContext* cx) static void* AllocateFatInlineString(JSContext* cx) { AutoUnsafeCallWithABI unsafe; return js::Allocate<JSFatInlineString, NoGC>(cx, js::gc::TenuredHeap); } void -CreateDependentString::generateFallback(MacroAssembler& masm, LiveRegisterSet regsToSave) -{ - JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString fallback"); - - regsToSave.take(string_); - regsToSave.take(temp_); +CreateDependentString::generateFallback(MacroAssembler& masm) +{ + 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_); + for (FallbackKind kind : mozilla::MakeEnumeratedRange(FallbackKind::Count)) { masm.bind(&fallbacks_[kind]); masm.PushRegsInMask(regsToSave); masm.setupUnalignedABICall(string_); masm.loadJSContext(string_); masm.passABIArg(string_); @@ -1903,46 +1965,46 @@ static void* CreateMatchResultFallbackFunc(JSContext* cx, gc::AllocKind kind, size_t nDynamicSlots) { AutoUnsafeCallWithABI unsafe; return js::Allocate<JSObject, NoGC>(cx, kind, nDynamicSlots, gc::DefaultHeap, &ArrayObject::class_); } 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_); - - regsToSave.take(object); - regsToSave.take(temp2); - regsToSave.take(temp5); + MOZ_ASSERT(templateObject.isArrayObject()); + + LiveRegisterSet regsToSave(RegisterSet::Volatile()); + regsToSave.takeUnchecked(object); + regsToSave.takeUnchecked(temp1); + regsToSave.takeUnchecked(temp2); + 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<NativeObject>().numDynamicSlots())), temp5); - masm.passABIArg(temp5); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, CreateMatchResultFallbackFunc)); masm.storeCallPointerResult(object); masm.PopRegsInMask(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* JitRealm::generateRegExpMatcherStub(JSContext* cx) { JitSpew(JitSpew_Codegen, "# Emitting RegExpMatcher stub"); Register regexp = RegExpMatcherRegExpReg; @@ -1951,224 +2013,214 @@ JitRealm::generateRegExpMatcherStub(JSCo ValueOperand result = JSReturnOperand; // We are free to clobber all registers, as LRegExpMatcher is a call instruction. AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); regs.take(input); 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); // The InputOutputData is placed above the return address on the stack. size_t inputOutputDataStartOffset = sizeof(void*); 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; } // 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))); - - size_t elementsOffset = NativeObject::offsetOfFixedElements(); - -#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); - - 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 - + masm.storeValue(nativeTemplateObj.getSlot(RegExpRealm::MatchResultObjectIndexSlot), + Address(temp2, 0)); + masm.storeValue(nativeTemplateObj.getSlot(RegExpRealm::MatchResultObjectInputSlot), + Address(temp2, sizeof(Value))); + + /* + * [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 | + * +---------------+ + */ + + static_assert(sizeof(MatchPair) == 2 * sizeof(int32_t), + "MatchPair consists of two int32 values representing the start" + "and the end offset of the match"); + + 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); - - BaseIndex stringAddress(object, matchIndex, TimesEight, elementsOffset); - - 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)); + // The element in which to store the result of the current match. + size_t elementsOffset = NativeObject::offsetOfFixedElements(); + BaseObjectElementIndex objectMatchElement(object, matchIndex, 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)); + + 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]; - - // depStr may refer to failureRestore during generateFallback below, - // so this variable must live outside of the block. - Label failureRestore; + // 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 }, + }; + { 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); - - 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); + masm.branch32(Assembler::LessThan, matchPairStart, Imm32(0), &isUndefined); + { + depStr.generate(masm, cx->names(), CompileRuntime::get(cx->runtime()), + input, matchPairStart, matchPairLimit, + stringsCanBeInNursery); + + // 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); masm.branch32(Assembler::LessThanOrEqual, pairCountAddress, matchIndex, &done); 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); masm.ret(); masm.bind(¬Found); 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. masm.bind(&oolEntry); masm.moveValue(UndefinedValue(), result); masm.ret(); Linker linker(masm); @@ -2307,24 +2359,54 @@ JitRealm::generateRegExpSearcherStub(JSC if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex, temp1, temp2, temp3, inputOutputDataStartOffset, RegExpShared::Normal, stringsCanBeInNursery, ¬Found, &oolEntry)) { return nullptr; } + /* + * [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 | + * +---------------+ + */ + size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset); - Address stringIndexAddress(masm.getStackPointer(), - pairsVectorStartOffset + offsetof(MatchPair, start)); - Address stringLimitAddress(masm.getStackPointer(), - pairsVectorStartOffset + offsetof(MatchPair, limit)); - - masm.load32(stringIndexAddress, result); - masm.load32(stringLimitAddress, input); + 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(); masm.bind(¬Found); masm.move32(Imm32(RegExpSearcherResultNotFound), result); masm.ret(); @@ -2683,29 +2765,37 @@ CodeGenerator::visitOutOfLineRegExpInsta masm.storeCallBoolResult(output); restoreVolatile(output); masm.jump(ool->rejoin()); } 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); masm.move32(Imm32(-1), output); masm.bind(&done); } @@ -2727,24 +2817,22 @@ CodeGenerator::visitGetFirstDollarIndex( StoreRegisterTo(output)); masm.branchIfRope(str, ool->entry()); masm.loadStringLength(str, len); 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()); } typedef JSString* (*StringReplaceFn)(JSContext*, HandleString, HandleString, HandleString); static const VMFunction StringFlatReplaceInfo = FunctionInfo<StringReplaceFn>(js::str_flat_replace_string, "str_flat_replace_string"); @@ -2815,18 +2903,44 @@ CodeGenerator::emitSharedStub(ICStub::Ki void CodeGenerator::visitBinaryCache(LBinaryCache* lir) { LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); TypedOrValueRegister lhs = TypedOrValueRegister(ToValue(lir, LBinaryCache::LhsInput)); 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 CodeGenerator::visitBinarySharedStub(LBinarySharedStub* lir) { JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc()); switch (jsop) { case JSOP_ADD: @@ -8292,149 +8406,144 @@ CodeGenerator::visitConcat(LConcat* lir) MOZ_ASSERT(ToRegister(lir->temp5()) == CallTempReg4); MOZ_ASSERT(output == CallTempReg5); emitConcat(lir, lhs, rhs, output); } 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 // next available char. #ifdef DEBUG Label ok; masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok); masm.assumeUnreachable("Length should be greater than 0."); 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) { // destChars is TwoByte and input is a Latin1 or TwoByte string, so we may // have to inflate. Label isLatin1, done; masm.loadStringLength(input, temp1); masm.branchLatin1String(input, &isLatin1); { 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); } 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. // Ensure both strings are linear. masm.branchIfRope(lhs, failure); masm.branchIfRope(rhs, failure); // Allocate a JSThinInlineString or JSFatInlineString. size_t maxThinInlineLength; - if (isTwoByte) + if (encoding == CharEncoding::Latin1) + maxThinInlineLength = JSThinInlineString::MAX_LENGTH_LATIN1; + else maxThinInlineLength = JSThinInlineString::MAX_LENGTH_TWO_BYTE; - else - maxThinInlineLength = JSThinInlineString::MAX_LENGTH_LATIN1; 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())); masm.jump(&allocDone); } 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())); } masm.bind(&allocDone); // Store length. masm.store32(temp2, Address(output, JSString::offsetOfLength())); // 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)); - } - - // 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)); - } - - // Null-terminate. - if (isTwoByte) - masm.store16(Imm32(0), Address(temp2, 0)); - else - masm.store8(Imm32(0), Address(temp2, 0)); - } + masm.loadStringLength(src, temp3); + masm.loadStringChars(src, temp1, CharEncoding::Latin1); + masm.movePtr(temp1, src); + CopyStringChars(masm, temp2, src, temp3, temp1, CharEncoding::Latin1); + } + }; + + // Copy lhs chars. Note that this advances temp2 to point to the next + // char. This also clobbers the lhs register. + copyChars(lhs); + + // Copy rhs chars. Clobbers the rhs register. + copyChars(rhs); + + // Null-terminate. + masm.storeChar(Imm32(0), Address(temp2, 0), encoding); masm.ret(); } typedef JSString* (*SubstringKernelFn)(JSContext* cx, HandleString str, int32_t begin, int32_t len); static const VMFunction SubstringKernelInfo = FunctionInfo<SubstringKernelFn>(SubstringKernel, "SubstringKernel"); @@ -8477,79 +8586,69 @@ CodeGenerator::visitSubstr(LSubstr* lir) masm.bind(&nonZero); masm.branchIfRopeOrExternal(string, temp, slowPath); // Handle inlined strings by creating a FatInlineString. masm.branchTest32(Assembler::Zero, stringFlags, Imm32(JSString::INLINE_CHARS_BIT), ¬Inline); 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. masm.bind(¬Inline); masm.newGCString(output, temp, slowPath, gen->stringsCanBeInNursery()); masm.store32(length, Address(output, JSString::offsetOfLength())); masm.storeDependentStringBase(string, output); + 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); { - 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); - masm.storeNonInlineStringChars(temp, output); - masm.jump(done); + 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); } JitCode* JitRealm::generateStringConcatStub(JSContext* cx) { @@ -8628,21 +8727,21 @@ JitRealm::generateStringConcatStub(JSCon masm.ret(); masm.bind(&rightEmpty); masm.mov(lhs, output); masm.ret(); 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); masm.bind(&failure); masm.movePtr(ImmPtr(nullptr), output); masm.ret();
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -5825,17 +5825,17 @@ IonBuilder::jsop_compare(JSOp op, MDefin MOZ_TRY(compareTryBitwise(&emitted, op, left, right)); if (emitted) return Ok(); MOZ_TRY(compareTrySpecializedOnBaselineInspector(&emitted, op, left, right)); if (emitted) return Ok(); } - MOZ_TRY(compareTrySharedStub(&emitted, left, right)); + MOZ_TRY(compareTryBinaryStub(&emitted, left, right)); if (emitted) return Ok(); trackOptimizationAttempt(TrackedStrategy::Compare_Call); // Not possible to optimize. Do a slow vm call. MCompare* ins = MCompare::New(alloc(), left, right, op); ins->cacheOperandMightEmulateUndefined(constraints()); @@ -6020,31 +6020,29 @@ IonBuilder::compareTrySpecializedOnBasel MOZ_ASSERT(!ins->isEffectful()); trackOptimizationSuccess(); *emitted = true; return Ok(); } AbortReasonOr<Ok> -IonBuilder::compareTrySharedStub(bool* emitted, MDefinition* left, MDefinition* right) +IonBuilder::compareTryBinaryStub(bool* emitted, MDefinition* left, MDefinition* right) { MOZ_ASSERT(*emitted == false); // Try to emit a shared stub cache. if (JitOptions.disableSharedStubs) return Ok(); 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)); MUnbox* unbox = MUnbox::New(alloc(), current->pop(), MIRType::Boolean, MUnbox::Infallible); current->add(unbox); current->push(unbox);
--- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -327,17 +327,17 @@ class IonBuilder // jsop_compare helpers. AbortReasonOr<Ok> compareTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right, bool canTrackOptimization); AbortReasonOr<Ok> compareTryBitwise(bool* emitted, JSOp op, MDefinition* left, MDefinition* right); AbortReasonOr<Ok> compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* left, MDefinition* right); - AbortReasonOr<Ok> compareTrySharedStub(bool* emitted, MDefinition* left, MDefinition* right); + AbortReasonOr<Ok> compareTryBinaryStub(bool* emitted, MDefinition* left, MDefinition* right); // jsop_newarray helpers. AbortReasonOr<Ok> newArrayTryTemplateObject(bool* emitted, JSObject* templateObject, uint32_t length); AbortReasonOr<Ok> newArrayTryVM(bool* emitted, JSObject* templateObject, uint32_t length); // jsop_newobject helpers. AbortReasonOr<Ok> newObjectTrySharedStub(bool* emitted);
--- a/js/src/jit/IonCacheIRCompiler.cpp +++ b/js/src/jit/IonCacheIRCompiler.cpp @@ -532,18 +532,31 @@ IonCacheIRCompiler::init() 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::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: MOZ_CRASH("Unsupported IC"); } if (liveRegs_) liveFloatRegs_ = LiveFloatRegisterSet(liveRegs_->fpus()); @@ -574,18 +587,16 @@ IonCacheIRCompiler::compile() default: MOZ_CRASH("Invalid op"); } 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. for (size_t i = 0; i < failurePaths.length(); i++) { if (!emitFailurePath(i)) return nullptr; Register scratch = ic_->scratchRegisterForEntryJump(); CodeOffset offset = masm.movWithPatch(ImmWord(-1), scratch);
--- a/js/src/jit/IonIC.cpp +++ b/js/src/jit/IonIC.cpp @@ -55,18 +55,19 @@ IonIC::scratchRegisterForEntryJump() case CacheKind::GetIterator: return asGetIteratorIC()->temp1(); case CacheKind::InstanceOf: return asInstanceOfIC()->output(); case CacheKind::UnaryArith: return asUnaryArithIC()->output().scratchReg(); case CacheKind::BinaryArith: return asBinaryArithIC()->output().scratchReg(); + case CacheKind::Compare: + return asCompareIC()->output().scratchReg(); case CacheKind::Call: - case CacheKind::Compare: case CacheKind::TypeOf: case CacheKind::ToBool: case CacheKind::GetIntrinsic: MOZ_CRASH("Unsupported IC"); } MOZ_CRASH("Invalid kind"); } @@ -624,16 +625,93 @@ IonBinaryArithIC::update(JSContext* cx, if (!attached) ic->state().trackNotAttached(); } } return true; } +/* static */ bool +IonCompareIC::update(JSContext* cx, HandleScript outerScript, IonCompareIC* ic, + HandleValue lhs, HandleValue rhs, MutableHandleValue res) +{ + IonScript* ionScript = outerScript->ionScript(); + RootedScript script(cx, ic->script()); + jsbytecode* pc = ic->pc(); + JSOp op = JSOp(*pc); + + // Case operations in a CONDSWITCH are performing strict equality. + if (op == JSOP_CASE) + op = JSOP_STRICTEQ; + + // Don't pass lhs/rhs directly, we need the original values when + // generating stubs. + RootedValue lhsCopy(cx, lhs); + RootedValue rhsCopy(cx, rhs); + + // Perform the compare operation. + bool out; + switch (op) { + case JSOP_LT: + if (!LessThan(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_LE: + if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_GT: + if (!GreaterThan(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_GE: + if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_EQ: + if (!LooselyEqual<true>(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_NE: + if (!LooselyEqual<false>(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_STRICTEQ: + if (!StrictlyEqual<true>(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + case JSOP_STRICTNE: + if (!StrictlyEqual<false>(cx, &lhsCopy, &rhsCopy, &out)) + return false; + break; + default: + MOZ_ASSERT_UNREACHABLE("Unhandled ion compare op"); + return false; + } + + res.setBoolean(out); + + if (ic->state().maybeTransition()) + ic->discardStubs(cx->zone()); + + if (ic->state().canAttachStub()) { + bool attached = false; + CompareIRGenerator gen(cx, script, pc, ic->state().mode(), + op, lhs, rhs); + if (gen.tryAttachStub()) { + ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached); + + if (!attached) + ic->state().trackNotAttached(); + } + } + return true; +} + uint8_t* IonICStub::stubDataStart() { return reinterpret_cast<uint8_t*>(this) + stubInfo_->stubDataOffset(); } void IonIC::attachStub(IonICStub* newStub, JitCode* code)
--- a/js/src/jit/IonIC.h +++ b/js/src/jit/IonIC.h @@ -60,16 +60,17 @@ class IonGetPropertyIC; class IonSetPropertyIC; class IonGetPropSuperIC; class IonGetNameIC; class IonBindNameIC; class IonGetIteratorIC; class IonHasOwnIC; class IonInIC; class IonInstanceOfIC; +class IonCompareIC; class IonUnaryArithIC; class IonBinaryArithIC; class IonIC { // This either points at the OOL path for the fallback path, or the code for // the first stub. uint8_t* codeRaw_; @@ -169,16 +170,20 @@ class IonIC IonInIC* asInIC() { MOZ_ASSERT(kind_ == CacheKind::In); return (IonInIC*)this; } IonInstanceOfIC* asInstanceOfIC() { 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; } IonBinaryArithIC* asBinaryArithIC() { MOZ_ASSERT(kind_ == CacheKind::BinaryArith); return (IonBinaryArithIC*)this; } @@ -480,16 +485,43 @@ class IonInstanceOfIC : public IonIC Register rhs() const { return rhs_; } Register output() const { return output_; } // This signature mimics that of TryAttachInstanceOfStub in baseline static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonInstanceOfIC* ic, 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_; TypedOrValueRegister input_; ValueOperand output_; public:
--- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -1598,26 +1598,16 @@ MacroAssembler::loadDependentStringBase( Address(str, JSString::offsetOfFlags()), Imm32(JSString::HAS_BASE_BIT), dest, str); } 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) { storePtr(base, Address(str, JSDependentString::offsetOfBase())); } void MacroAssembler::loadStringChar(Register str, Register index, Register output, Register scratch, Label* fail) @@ -1644,22 +1634,22 @@ MacroAssembler::loadStringChar(Register bind(¬Rope); Label isLatin1, done; // We have to check the left/right side for ropes, // 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); } void MacroAssembler::loadStringIndexValue(Register str, Register dest, Label* fail) { MOZ_ASSERT(str != dest); @@ -1669,16 +1659,37 @@ MacroAssembler::loadStringIndexValue(Reg // Does not have a cached index value. branchTest32(Assembler::Zero, dest, Imm32(JSString::INDEX_VALUE_BIT), fail); // Extract the index. 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) { loadObjClassUnsafe(obj, scratch); // Proxies can emulate undefined and have complex isCallable behavior. branchTestClassIsProxy(true, scratch, slow);
--- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -2116,20 +2116,53 @@ class MacroAssembler : public MacroAssem void loadStringChar(Register str, Register index, Register output, Register scratch, Label* fail); void loadRopeLeftChild(Register str, Register dest); void storeRopeChildren(Register left, Register right, Register str); 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 <typename T> + void storeChar(const T& src, Address dest, CharEncoding encoding) { + if (encoding == CharEncoding::Latin1) + store8(src, dest); + else + store16(src, dest); + } + + /** + * Load the character at |src| into |dest|. + */ + template <typename T> + void loadChar(const T& src, Register dest, CharEncoding encoding) { + if (encoding == CharEncoding::Latin1) + load8ZeroExtend(src, dest); + else + load16ZeroExtend(src, dest); + } + + /** + * Load the character at |chars[index + offset]| into |dest|. The optional + * offset argument is not scaled to the character encoding. + */ + void loadChar(Register chars, Register index, Register dest, CharEncoding encoding, + int32_t offset = 0); + + /** + * Add |index| to |chars| so that |chars| now points at |chars[index]|. + */ + void addToCharPtr(Register chars, Register index, CharEncoding encoding); + void loadJSContext(Register dest); void switchToRealm(Register realm); void switchToRealm(const void* realm, Register scratch); void switchToObjectRealm(Register obj, Register scratch); void switchToBaselineFrameRealm(Register scratch); void switchToWasmTlsRealm(Register scratch1, Register scratch2); void debugAssertContextRealm(const void* realm, Register scratch);
--- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -666,552 +666,16 @@ SharedStubInfo::outerScript(JSContext* c MOZ_ASSERT(it.isIonJS()); outerScript_ = it.script(); MOZ_ASSERT(!it.ionScript()->invalidated()); } return outerScript_; } // -// Compare_Fallback -// - -static bool -DoCompareFallback(JSContext* cx, void* payload, ICCompare_Fallback* stub_, HandleValue lhs, - HandleValue rhs, MutableHandleValue ret) -{ - SharedStubInfo info(cx, payload, stub_->icEntry()); - ICStubCompiler::Engine engine = info.engine(); - - // This fallback stub may trigger debug mode toggling. - DebugModeOSRVolatileStub<ICCompare_Fallback*> stub(engine, info.maybeFrame(), stub_); - - jsbytecode* pc = info.pc(); - JSOp op = JSOp(*pc); - - FallbackICSpew(cx, stub, "Compare(%s)", CodeName[op]); - - // Case operations in a CONDSWITCH are performing strict equality. - if (op == JSOP_CASE) - op = JSOP_STRICTEQ; - - // Don't pass lhs/rhs directly, we need the original values when - // generating stubs. - RootedValue lhsCopy(cx, lhs); - RootedValue rhsCopy(cx, rhs); - - // Perform the compare operation. - bool out; - switch (op) { - case JSOP_LT: - if (!LessThan(cx, &lhsCopy, &rhsCopy, &out)) - return false; - break; - case JSOP_LE: - if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, &out)) - return false; - break; - case JSOP_GT: - if (!GreaterThan(cx, &lhsCopy, &rhsCopy, &out)) - return false; - break; - case JSOP_GE: - if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, &out)) - return false; - break; - case JSOP_EQ: - if (!LooselyEqual<true>(cx, &lhsCopy, &rhsCopy, &out)) - return false; - break; - case JSOP_NE: - if (!LooselyEqual<false>(cx, &lhsCopy, &rhsCopy, &out)) - return false; - break; - case JSOP_STRICTEQ: - if (!StrictlyEqual<true>(cx, &lhsCopy, &rhsCopy, &out)) - return false; - break; - case JSOP_STRICTNE: - if (!StrictlyEqual<false>(cx, &lhsCopy, &rhsCopy, &out)) - return false; - break; - default: - MOZ_ASSERT_UNREACHABLE("Unhandled baseline compare op"); - return false; - } - - ret.setBoolean(out); - - // Check if debug mode toggling made the stub invalid. - if (stub.invalid()) - return true; - - // Check to see if a new stub should be generated. - if (stub->numOptimizedStubs() >= ICCompare_Fallback::MAX_OPTIMIZED_STUBS) { - // TODO: Discard all stubs in this IC and replace with inert megamorphic stub. - // But for now we just bail. - return true; - } - - if (engine == ICStubEngine::Baseline && !JitOptions.disableCacheIR) { - RootedScript script(cx, info.outerScript(cx)); - CompareIRGenerator gen(cx, script, pc, stub->state().mode(), op, lhs, rhs); - bool attached = false; - if (gen.tryAttachStub()) { - ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), - BaselineCacheIRStubKind::Regular, - engine, script, stub, &attached); - if (newStub) - JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub"); - return true; - } - } - - // Try to generate new stubs. - if (lhs.isInt32() && rhs.isInt32()) { - JitSpew(JitSpew_BaselineIC, " Generating %s(Int32, Int32) stub", CodeName[op]); - ICCompare_Int32::Compiler compiler(cx, op, engine); - ICStub* int32Stub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx))); - if (!int32Stub) - return false; - - stub->addNewStub(int32Stub); - return true; - } - - if (!cx->runtime()->jitSupportsFloatingPoint && (lhs.isNumber() || rhs.isNumber())) - return true; - - if (lhs.isNumber() && rhs.isNumber()) { - JitSpew(JitSpew_BaselineIC, " Generating %s(Number, Number) stub", CodeName[op]); - - // Unlink int32 stubs, it's faster to always use the double stub. - stub->unlinkStubsWithKind(cx, ICStub::Compare_Int32); - - ICCompare_Double::Compiler compiler(cx, op, engine); - ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx))); - if (!doubleStub) - return false; - - stub->addNewStub(doubleStub); - return true; - } - - if ((lhs.isNumber() && rhs.isUndefined()) || - (lhs.isUndefined() && rhs.isNumber())) - { - JitSpew(JitSpew_BaselineIC, " Generating %s(%s, %s) stub", CodeName[op], - rhs.isUndefined() ? "Number" : "Undefined", - rhs.isUndefined() ? "Undefined" : "Number"); - ICCompare_NumberWithUndefined::Compiler compiler(cx, op, engine, lhs.isUndefined()); - ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx))); - if (!doubleStub) - return false; - - stub->addNewStub(doubleStub); - return true; - } - - if (lhs.isBoolean() && rhs.isBoolean()) { - JitSpew(JitSpew_BaselineIC, " Generating %s(Boolean, Boolean) stub", CodeName[op]); - ICCompare_Boolean::Compiler compiler(cx, op, engine); - ICStub* booleanStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx))); - if (!booleanStub) - return false; - - stub->addNewStub(booleanStub); - return true; - } - - if ((lhs.isBoolean() && rhs.isInt32()) || (lhs.isInt32() && rhs.isBoolean())) { - JitSpew(JitSpew_BaselineIC, " Generating %s(%s, %s) stub", CodeName[op], - rhs.isInt32() ? "Boolean" : "Int32", - rhs.isInt32() ? "Int32" : "Boolean"); - ICCompare_Int32WithBoolean::Compiler compiler(cx, op, engine, lhs.isInt32()); - ICStub* optStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx))); - if (!optStub) - return false; - - stub->addNewStub(optStub); - return true; - } - - if (IsEqualityOp(op)) { - if (lhs.isString() && rhs.isString() && !stub->hasStub(ICStub::Compare_String)) { - JitSpew(JitSpew_BaselineIC, " Generating %s(String, String) stub", CodeName[op]); - ICCompare_String::Compiler compiler(cx, op, engine); - ICStub* stringStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx))); - if (!stringStub) - return false; - - stub->addNewStub(stringStub); - return true; - } - - if (lhs.isSymbol() && rhs.isSymbol() && !stub->hasStub(ICStub::Compare_Symbol)) { - JitSpew(JitSpew_BaselineIC, " Generating %s(Symbol, Symbol) stub", CodeName[op]); - ICCompare_Symbol::Compiler compiler(cx, op, engine); - ICStub* symbolStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx))); - if (!symbolStub) - return false; - - stub->addNewStub(symbolStub); - return true; - } - - if (lhs.isObject() && rhs.isObject()) { - MOZ_ASSERT(!stub->hasStub(ICStub::Compare_Object)); - JitSpew(JitSpew_BaselineIC, " Generating %s(Object, Object) stub", CodeName[op]); - ICCompare_Object::Compiler compiler(cx, op, engine); - ICStub* objectStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx))); - if (!objectStub) - return false; - - stub->addNewStub(objectStub); - return true; - } - - if (lhs.isNullOrUndefined() || rhs.isNullOrUndefined()) { - JitSpew(JitSpew_BaselineIC, " Generating %s(Null/Undef or X, Null/Undef or X) stub", - CodeName[op]); - bool lhsIsUndefined = lhs.isNullOrUndefined(); - bool compareWithNull = lhs.isNull() || rhs.isNull(); - ICCompare_ObjectWithUndefined::Compiler compiler(cx, op, engine, - lhsIsUndefined, compareWithNull); - ICStub* objectStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx))); - if (!objectStub) - return false; - - stub->addNewStub(objectStub); - return true; - } - } - - stub->noteUnoptimizableAccess(); - - return true; -} - -typedef bool (*DoCompareFallbackFn)(JSContext*, void*, ICCompare_Fallback*, - HandleValue, HandleValue, MutableHandleValue); -static const VMFunction DoCompareFallbackInfo = - FunctionInfo<DoCompareFallbackFn>(DoCompareFallback, "DoCompareFallback", TailCall, - PopValues(2)); - -bool -ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(R0 == JSReturnOperand); - - // Restore the tail call register. - EmitRestoreTailCallReg(masm); - - // Ensure stack is fully synced for the expression decompiler. - masm.pushValue(R0); - masm.pushValue(R1); - - // Push arguments. - masm.pushValue(R1); - masm.pushValue(R0); - masm.push(ICStubReg); - pushStubPayload(masm, R0.scratchReg()); - return tailCallVM(DoCompareFallbackInfo, masm); -} - -// -// Compare_String -// - -bool -ICCompare_String::Compiler::generateStubCode(MacroAssembler& masm) -{ - Label failure, restore; - masm.branchTestString(Assembler::NotEqual, R0, &failure); - masm.branchTestString(Assembler::NotEqual, R1, &failure); - - MOZ_ASSERT(IsEqualityOp(op)); - - // left/right are part of R0/R1. Restore R0 and R1 in the failure case. - Register left = R0.scratchReg(); - Register right = R1.scratchReg(); - masm.unboxString(R0, left); - masm.unboxString(R1, right); - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); - Register scratchReg = regs.takeAny(); - - masm.compareStrings(op, left, right, scratchReg, &restore); - masm.tagValue(JSVAL_TYPE_BOOLEAN, scratchReg, R0); - EmitReturnFromIC(masm); - - masm.bind(&restore); - masm.tagValue(JSVAL_TYPE_STRING, left, R0); - masm.tagValue(JSVAL_TYPE_STRING, right, R1); - - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -// -// Compare_Symbol -// - -bool -ICCompare_Symbol::Compiler::generateStubCode(MacroAssembler& masm) -{ - Label failure; - masm.branchTestSymbol(Assembler::NotEqual, R0, &failure); - masm.branchTestSymbol(Assembler::NotEqual, R1, &failure); - - MOZ_ASSERT(IsEqualityOp(op)); - - Register left = masm.extractSymbol(R0, ExtractTemp0); - Register right = masm.extractSymbol(R1, ExtractTemp1); - - Label ifTrue; - masm.branchPtr(JSOpToCondition(op, /* signed = */true), left, right, &ifTrue); - - masm.moveValue(BooleanValue(false), R0); - EmitReturnFromIC(masm); - - masm.bind(&ifTrue); - masm.moveValue(BooleanValue(true), R0); - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -// -// Compare_Boolean -// - -bool -ICCompare_Boolean::Compiler::generateStubCode(MacroAssembler& masm) -{ - Label failure; - masm.branchTestBoolean(Assembler::NotEqual, R0, &failure); - masm.branchTestBoolean(Assembler::NotEqual, R1, &failure); - - Register left = masm.extractInt32(R0, ExtractTemp0); - Register right = masm.extractInt32(R1, ExtractTemp1); - - // Compare payload regs of R0 and R1. - Assembler::Condition cond = JSOpToCondition(op, /* signed = */true); - masm.cmp32Set(cond, left, right, left); - - // Box the result and return - masm.tagValue(JSVAL_TYPE_BOOLEAN, left, R0); - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -// -// Compare_NumberWithUndefined -// - -bool -ICCompare_NumberWithUndefined::Compiler::generateStubCode(MacroAssembler& masm) -{ - ValueOperand numberOperand, undefinedOperand; - if (lhsIsUndefined) { - numberOperand = R1; - undefinedOperand = R0; - } else { - numberOperand = R0; - undefinedOperand = R1; - } - - Label failure; - masm.branchTestNumber(Assembler::NotEqual, numberOperand, &failure); - masm.branchTestUndefined(Assembler::NotEqual, undefinedOperand, &failure); - - // Comparing a number with undefined will always be true for NE/STRICTNE, - // and always be false for other compare ops. - masm.moveValue(BooleanValue(op == JSOP_NE || op == JSOP_STRICTNE), R0); - - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -// -// Compare_Object -// - -bool -ICCompare_Object::Compiler::generateStubCode(MacroAssembler& masm) -{ - Label failure; - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - masm.branchTestObject(Assembler::NotEqual, R1, &failure); - - MOZ_ASSERT(IsEqualityOp(op)); - - Register left = masm.extractObject(R0, ExtractTemp0); - Register right = masm.extractObject(R1, ExtractTemp1); - - Label ifTrue; - masm.branchPtr(JSOpToCondition(op, /* signed = */true), left, right, &ifTrue); - - masm.moveValue(BooleanValue(false), R0); - EmitReturnFromIC(masm); - - masm.bind(&ifTrue); - masm.moveValue(BooleanValue(true), R0); - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -// -// Compare_ObjectWithUndefined -// - -bool -ICCompare_ObjectWithUndefined::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(IsEqualityOp(op)); - - ValueOperand objectOperand, undefinedOperand; - if (lhsIsUndefined) { - objectOperand = R1; - undefinedOperand = R0; - } else { - objectOperand = R0; - undefinedOperand = R1; - } - - Label failure; - if (compareWithNull) - masm.branchTestNull(Assembler::NotEqual, undefinedOperand, &failure); - else - masm.branchTestUndefined(Assembler::NotEqual, undefinedOperand, &failure); - - Label notObject; - masm.branchTestObject(Assembler::NotEqual, objectOperand, ¬Object); - - if (op == JSOP_STRICTEQ || op == JSOP_STRICTNE) { - // obj !== undefined for all objects. - masm.moveValue(BooleanValue(op == JSOP_STRICTNE), R0); - EmitReturnFromIC(masm); - } else { - // obj != undefined only where !obj->getClass()->emulatesUndefined() - Register obj = masm.extractObject(objectOperand, ExtractTemp0); - - // We need a scratch register. - masm.push(obj); - Label slow, emulatesUndefined; - masm.branchIfObjectEmulatesUndefined(obj, obj, &slow, &emulatesUndefined); - - masm.pop(obj); - masm.moveValue(BooleanValue(op == JSOP_NE), R0); - EmitReturnFromIC(masm); - - masm.bind(&emulatesUndefined); - masm.pop(obj); - masm.moveValue(BooleanValue(op == JSOP_EQ), R0); - EmitReturnFromIC(masm); - - masm.bind(&slow); - masm.pop(obj); - masm.jump(&failure); - } - - masm.bind(¬Object); - - // Also support null == null or undefined == undefined comparisons. - Label differentTypes; - if (compareWithNull) - masm.branchTestNull(Assembler::NotEqual, objectOperand, &differentTypes); - else - masm.branchTestUndefined(Assembler::NotEqual, objectOperand, &differentTypes); - - masm.moveValue(BooleanValue(op == JSOP_STRICTEQ || op == JSOP_EQ), R0); - EmitReturnFromIC(masm); - - masm.bind(&differentTypes); - // Also support null == undefined or undefined == null. - Label neverEqual; - if (compareWithNull) - masm.branchTestUndefined(Assembler::NotEqual, objectOperand, &neverEqual); - else - masm.branchTestNull(Assembler::NotEqual, objectOperand, &neverEqual); - - masm.moveValue(BooleanValue(op == JSOP_EQ || op == JSOP_STRICTNE), R0); - EmitReturnFromIC(masm); - - // null/undefined can only be equal to null/undefined or emulatesUndefined. - masm.bind(&neverEqual); - masm.moveValue(BooleanValue(op == JSOP_NE || op == JSOP_STRICTNE), R0); - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -// -// Compare_Int32WithBoolean -// - -bool -ICCompare_Int32WithBoolean::Compiler::generateStubCode(MacroAssembler& masm) -{ - Label failure; - ValueOperand int32Val; - ValueOperand boolVal; - if (lhsIsInt32_) { - int32Val = R0; - boolVal = R1; - } else { - boolVal = R0; - int32Val = R1; - } - masm.branchTestBoolean(Assembler::NotEqual, boolVal, &failure); - masm.branchTestInt32(Assembler::NotEqual, int32Val, &failure); - - if (op_ == JSOP_STRICTEQ || op_ == JSOP_STRICTNE) { - // Ints and booleans are never strictly equal, always strictly not equal. - masm.moveValue(BooleanValue(op_ == JSOP_STRICTNE), R0); - EmitReturnFromIC(masm); - } else { - Register boolReg = masm.extractBoolean(boolVal, ExtractTemp0); - Register int32Reg = masm.extractInt32(int32Val, ExtractTemp1); - - // Compare payload regs of R0 and R1. - Assembler::Condition cond = JSOpToCondition(op_, /* signed = */true); - masm.cmp32Set(cond, (lhsIsInt32_ ? int32Reg : boolReg), - (lhsIsInt32_ ? boolReg : int32Reg), R0.scratchReg()); - - // Box the result and return - masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.scratchReg(), R0); - EmitReturnFromIC(masm); - } - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - void LoadTypedThingData(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result) { switch (layout) { case Layout_TypedArray: masm.loadPtr(Address(obj, TypedArrayObject::dataOffset()), result); break; case Layout_OutlineTypedObject:
--- a/js/src/jit/SharedIC.h +++ b/js/src/jit/SharedIC.h @@ -1679,279 +1679,16 @@ class ICCompare_Fallback : public ICFall : ICStubCompiler(cx, ICStub::Compare_Fallback, engine) {} ICStub* getStub(ICStubSpace* space) override { return newStub<ICCompare_Fallback>(space, getStubCode()); } }; }; -class ICCompare_Int32 : public ICStub -{ - friend class ICStubSpace; - - explicit ICCompare_Int32(JitCode* stubCode) - : ICStub(ICStub::Compare_Int32, stubCode) {} - - public: - // Compiler for this stub kind. - class Compiler : public ICMultiStubCompiler { - protected: - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override; - - public: - Compiler(JSContext* cx, JSOp op, Engine engine) - : ICMultiStubCompiler(cx, ICStub::Compare_Int32, op, engine) {} - - ICStub* getStub(ICStubSpace* space) override { - return newStub<ICCompare_Int32>(space, getStubCode()); - } - }; -}; - -class ICCompare_Double : public ICStub -{ - friend class ICStubSpace; - - explicit ICCompare_Double(JitCode* stubCode) - : ICStub(ICStub::Compare_Double, stubCode) - {} - - public: - class Compiler : public ICMultiStubCompiler { - protected: - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override; - - public: - Compiler(JSContext* cx, JSOp op, Engine engine) - : ICMultiStubCompiler(cx, ICStub::Compare_Double, op, engine) - {} - - ICStub* getStub(ICStubSpace* space) override { - return newStub<ICCompare_Double>(space, getStubCode()); - } - }; -}; - -class ICCompare_NumberWithUndefined : public ICStub -{ - friend class ICStubSpace; - - ICCompare_NumberWithUndefined(JitCode* stubCode, bool lhsIsUndefined) - : ICStub(ICStub::Compare_NumberWithUndefined, stubCode) - { - extra_ = lhsIsUndefined; - } - - public: - bool lhsIsUndefined() { - return extra_; - } - - class Compiler : public ICMultiStubCompiler { - protected: - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override; - - bool lhsIsUndefined; - - public: - Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsUndefined) - : ICMultiStubCompiler(cx, ICStub::Compare_NumberWithUndefined, op, engine), - lhsIsUndefined(lhsIsUndefined) - {} - - virtual int32_t getKey() const override { - return static_cast<int32_t>(engine_) | - (static_cast<int32_t>(kind) << 1) | - (static_cast<int32_t>(op) << 17) | - (static_cast<int32_t>(lhsIsUndefined) << 25); - } - - ICStub* getStub(ICStubSpace* space) override { - return newStub<ICCompare_NumberWithUndefined>(space, getStubCode(), - lhsIsUndefined); - } - }; -}; - -class ICCompare_String : public ICStub -{ - friend class ICStubSpace; - - explicit ICCompare_String(JitCode* stubCode) - : ICStub(ICStub::Compare_String, stubCode) - {} - - public: - class Compiler : public ICMultiStubCompiler { - protected: - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override; - - public: - Compiler(JSContext* cx, JSOp op, Engine engine) - : ICMultiStubCompiler(cx, ICStub::Compare_String, op, engine) - {} - - ICStub* getStub(ICStubSpace* space) override { - return newStub<ICCompare_String>(space, getStubCode()); - } - }; -}; - -class ICCompare_Symbol : public ICStub -{ - friend class ICStubSpace; - - explicit ICCompare_Symbol(JitCode* stubCode) - : ICStub(ICStub::Compare_Symbol, stubCode) - {} - - public: - class Compiler : public ICMultiStubCompiler { - protected: - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override; - - public: - Compiler(JSContext* cx, JSOp op, Engine engine) - : ICMultiStubCompiler(cx, ICStub::Compare_Symbol, op, engine) - {} - - ICStub* getStub(ICStubSpace* space) override { - return newStub<ICCompare_Symbol>(space, getStubCode()); - } - }; -}; - -class ICCompare_Boolean : public ICStub -{ - friend class ICStubSpace; - - explicit ICCompare_Boolean(JitCode* stubCode) - : ICStub(ICStub::Compare_Boolean, stubCode) - {} - - public: - class Compiler : public ICMultiStubCompiler { - protected: - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override; - - public: - Compiler(JSContext* cx, JSOp op, Engine engine) - : ICMultiStubCompiler(cx, ICStub::Compare_Boolean, op, engine) - {} - - ICStub* getStub(ICStubSpace* space) override { - return newStub<ICCompare_Boolean>(space, getStubCode()); - } - }; -}; - -class ICCompare_Object : public ICStub -{ - friend class ICStubSpace; - - explicit ICCompare_Object(JitCode* stubCode) - : ICStub(ICStub::Compare_Object, stubCode) - {} - - public: - class Compiler : public ICMultiStubCompiler { - protected: - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override; - - public: - Compiler(JSContext* cx, JSOp op, Engine engine) - : ICMultiStubCompiler(cx, ICStub::Compare_Object, op, engine) - {} - - ICStub* getStub(ICStubSpace* space) override { - return newStub<ICCompare_Object>(space, getStubCode()); - } - }; -}; - -class ICCompare_ObjectWithUndefined : public ICStub -{ - friend class ICStubSpace; - - explicit ICCompare_ObjectWithUndefined(JitCode* stubCode) - : ICStub(ICStub::Compare_ObjectWithUndefined, stubCode) - {} - - public: - class Compiler : public ICMultiStubCompiler { - protected: - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override; - - bool lhsIsUndefined; - bool compareWithNull; - - public: - Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsUndefined, bool compareWithNull) - : ICMultiStubCompiler(cx, ICStub::Compare_ObjectWithUndefined, op, engine), - lhsIsUndefined(lhsIsUndefined), - compareWithNull(compareWithNull) - {} - - virtual int32_t getKey() const override { - return static_cast<int32_t>(engine_) | - (static_cast<int32_t>(kind) << 1) | - (static_cast<int32_t>(op) << 17) | - (static_cast<int32_t>(lhsIsUndefined) << 25) | - (static_cast<int32_t>(compareWithNull) << 26); - } - - ICStub* getStub(ICStubSpace* space) override { - return newStub<ICCompare_ObjectWithUndefined>(space, getStubCode()); - } - }; -}; - -class ICCompare_Int32WithBoolean : public ICStub -{ - friend class ICStubSpace; - - ICCompare_Int32WithBoolean(JitCode* stubCode, bool lhsIsInt32) - : ICStub(ICStub::Compare_Int32WithBoolean, stubCode) - { - extra_ = lhsIsInt32; - } - - public: - bool lhsIsInt32() const { - return extra_; - } - - // Compiler for this stub kind. - class Compiler : public ICStubCompiler { - protected: - JSOp op_; - bool lhsIsInt32_; - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override; - - virtual int32_t getKey() const override { - return static_cast<int32_t>(engine_) | - (static_cast<int32_t>(kind) << 1) | - (static_cast<int32_t>(op_) << 17) | - (static_cast<int32_t>(lhsIsInt32_) << 25); - } - - public: - Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsInt32) - : ICStubCompiler(cx, ICStub::Compare_Int32WithBoolean, engine), - op_(op), - lhsIsInt32_(lhsIsInt32) - {} - - ICStub* getStub(ICStubSpace* space) override { - return newStub<ICCompare_Int32WithBoolean>(space, getStubCode(), lhsIsInt32_); - } - }; -}; - // Enum for stubs handling a combination of typed arrays and typed objects. enum TypedThingLayout { Layout_TypedArray, Layout_OutlineTypedObject, Layout_InlineTypedObject }; void
--- a/js/src/jit/SharedICList.h +++ b/js/src/jit/SharedICList.h @@ -10,25 +10,16 @@ namespace js { namespace jit { // List of IC stub kinds that can run in Baseline and in IonMonkey #define IC_SHARED_STUB_KIND_LIST(_) \ _(BinaryArith_Fallback) \ \ _(Compare_Fallback) \ - _(Compare_Int32) \ - _(Compare_Double) \ - _(Compare_NumberWithUndefined) \ - _(Compare_String) \ - _(Compare_Symbol) \ - _(Compare_Boolean) \ - _(Compare_Object) \ - _(Compare_ObjectWithUndefined) \ - _(Compare_Int32WithBoolean) \ \ _(GetProp_Fallback) \ \ _(CacheIR_Regular) \ _(CacheIR_Monitored) \ _(CacheIR_Updated) \ \
deleted file mode 100644 --- 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
deleted file mode 100644 --- 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
deleted file mode 100644 --- 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
deleted file mode 100644 --- 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
--- a/js/src/jit/none/Trampoline-none.cpp +++ b/js/src/jit/none/Trampoline-none.cpp @@ -37,10 +37,8 @@ BailoutFrameInfo::BailoutFrameInfo(const MOZ_CRASH(); } BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& iter, InvalidationBailoutStack* bailout) { MOZ_CRASH(); } -bool ICCompare_Int32::Compiler::generateStubCode(MacroAssembler&) { MOZ_CRASH(); } -bool ICCompare_Double::Compiler::generateStubCode(MacroAssembler&) { MOZ_CRASH(); }
--- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -880,20 +880,16 @@ class AssemblerShared bool oom() const { return !enoughMemory_; } bool embedsNurseryPointers() const { return embedsNurseryPointers_; } - static bool canUseInSingleByteInstruction(Register reg) { - return true; - } - void addCodeLabel(CodeLabel label) { propagateOOM(codeLabels_.append(label)); } size_t numCodeLabels() const { return codeLabels_.length(); } CodeLabel codeLabel(size_t i) { return codeLabels_[i];
deleted file mode 100644 --- 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
deleted file mode 100644 --- 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; -}
--- a/js/src/jit/x86/Assembler-x86.h +++ b/js/src/jit/x86/Assembler-x86.h @@ -1055,20 +1055,16 @@ class Assembler : public AssemblerX86Sha masm.vmovdqu_rm(src.encoding(), dest.addr); return CodeOffset(masm.currentOffset()); } CodeOffset vmovupsWithPatch(FloatRegister src, PatchedAbsoluteAddress dest) { MOZ_ASSERT(HasSSE2()); 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 // integer argument. This differs from GetIntArgReg in that if we have no more // actual argument registers to use we will fall back on using whatever // CallTempReg* don't overlap the argument registers, and only fail once those // run out too. static inline bool
deleted file mode 100644 --- 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
--- a/js/src/jsapi-tests/testSavedStacks.cpp +++ b/js/src/jsapi-tests/testSavedStacks.cpp @@ -254,17 +254,17 @@ BEGIN_TEST(testSavedStacks_selfHostedFra CHECK(result == JS::SavedFrameResult::Ok); CHECK_EQUAL(line, 3U); // Column uint32_t column = 123; 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, JS::SavedFrameSelfHosted::Exclude); CHECK(result == JS::SavedFrameResult::Ok); lin = str->ensureLinear(cx); CHECK(lin); CHECK(js::StringEqualsAscii(lin, "one"));
--- a/js/src/moz.build +++ b/js/src/moz.build @@ -496,58 +496,54 @@ if not CONFIG['ENABLE_ION']: ] elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']: LOpcodesGenerated.inputs += ['jit/x86-shared/LIR-x86-shared.h'] UNIFIED_SOURCES += [ 'jit/x86-shared/Architecture-x86-shared.cpp', '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', 'jit/x86-shared/MacroAssembler-x86-shared.cpp', 'jit/x86-shared/MoveEmitter-x86-shared.cpp', ] SOURCES += [ 'jit/x86-shared/Disassembler-x86-shared.cpp', # using namespace js::jit::X86Encoding; ] if CONFIG['JS_CODEGEN_X64']: LOpcodesGenerated.inputs += ['jit/x64/LIR-x64.h'] UNIFIED_SOURCES += [ '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', 'jit/x64/Trampoline-x64.cpp', ] else: LOpcodesGenerated.inputs += ['jit/x86/LIR-x86.h'] UNIFIED_SOURCES += [ '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', 'jit/x86/Trampoline-x86.cpp', ] elif CONFIG['JS_CODEGEN_ARM']: LOpcodesGenerated.inputs += ['jit/arm/LIR-arm.h'] UNIFIED_SOURCES += [ 'jit/arm/Architecture-arm.cpp', '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', 'jit/arm/Lowering-arm.cpp', 'jit/arm/MacroAssembler-arm.cpp', 'jit/arm/MoveEmitter-arm.cpp', 'jit/arm/Trampoline-arm.cpp', ] @@ -561,17 +557,16 @@ elif CONFIG['JS_CODEGEN_ARM']: 'jit/arm/llvm-compiler-rt/arm/aeabi_uidivmod.S', ] elif CONFIG['JS_CODEGEN_ARM64']: LOpcodesGenerated.inputs += ['jit/arm64/LIR-arm64.h'] UNIFIED_SOURCES += [ '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', 'jit/arm64/MacroAssembler-arm64.cpp', 'jit/arm64/MoveEmitter-arm64.cpp', 'jit/arm64/Trampoline-arm64.cpp', 'jit/arm64/vixl/Assembler-vixl.cpp', 'jit/arm64/vixl/Cpu-vixl.cpp', @@ -593,30 +588,28 @@ elif CONFIG['JS_CODEGEN_ARM64']: ] elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']: LOpcodesGenerated.inputs += ['jit/mips-shared/LIR-mips-shared.h'] UNIFIED_SOURCES += [ 'jit/mips-shared/Architecture-mips-shared.cpp', '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', 'jit/mips-shared/MoveEmitter-mips-shared.cpp', ] if CONFIG['JS_CODEGEN_MIPS32']: LOpcodesGenerated.inputs += ['jit/mips32/LIR-mips32.h'] UNIFIED_SOURCES += [ 'jit/mips32/Architecture-mips32.cpp', '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', 'jit/mips32/MoveEmitter-mips32.cpp', 'jit/mips32/Trampoline-mips32.cpp', ] if CONFIG['JS_SIMULATOR_MIPS32']: UNIFIED_SOURCES += [ @@ -624,17 +617,16 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONF ] elif CONFIG['JS_CODEGEN_MIPS64']: LOpcodesGenerated.inputs += ['jit/mips64/LIR-mips64.h'] UNIFIED_SOURCES += [ 'jit/mips64/Architecture-mips64.cpp', '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', 'jit/mips64/MoveEmitter-mips64.cpp', 'jit/mips64/Trampoline-mips64.cpp', ] if CONFIG['JS_SIMULATOR_MIPS64']: UNIFIED_SOURCES += [
--- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -4486,17 +4486,17 @@ JSScript::AutoDelazify::dropScript() { // Don't touch script_ if it's in the self-hosting realm, see the comment // in holdScript. if (script_ && !script_->realm()->isSelfHostingRealm()) script_->setDoNotRelazify(oldDoNotRelazify_); script_ = nullptr; } -JS::ubi::Node::Size +JS::ubi::Base::Size JS::ubi::Concrete<JSScript>::size(mozilla::MallocSizeOf mallocSizeOf) const { Size size = Arena::thingSize(get().asTenured().getAllocKind()); size += get().sizeOfData(mallocSizeOf); size += get().sizeOfTypeScript(mallocSizeOf); size_t baselineSize = 0;
--- a/js/src/vm/MatchPairs.h +++ b/js/src/vm/MatchPairs.h @@ -16,36 +16,38 @@ * indices delimiting (start, limit] segments of the input string. * * The pair count for a given RegExp match is the capturing parentheses * count plus one for the "0 capturing paren" whole text match. */ 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) : start(start), limit(limit) { } size_t length() const { MOZ_ASSERT(!isUndefined()); return limit - start; } bool isUndefined() const { return start < 0; } 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; } }; // MachPairs is used as base class for VectorMatchPairs but can also be // stack-allocated (without a Vector) in JIT code. class MatchPairs {
--- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -1246,19 +1246,19 @@ RegExpRealm::createMatchResultTemplateOb if (!NativeDefineDataProperty(cx, templateObject, cx->names().input, inputVal, JSPROP_ENUMERATE)) { return matchResultTemplateObject_; // = nullptr } // Make sure that the properties are in the right slots. DebugOnly<Shape*> 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 // be added. AddTypePropertyId(cx, templateObject, JSID_VOID, TypeSet::StringType()); AddTypePropertyId(cx, templateObject, JSID_VOID, TypeSet::UndefinedType()); matchResultTemplateObject_.set(templateObject);
--- a/js/src/vm/RegExpShared.h +++ b/js/src/vm/RegExpShared.h @@ -320,16 +320,19 @@ class RegExpRealm ArrayObject* createMatchResultTemplateObject(JSContext* cx); public: explicit 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_) return matchResultTemplateObject_; return createMatchResultTemplateObject(cx); } Shape* getOptimizableRegExpPrototypeShape() {
--- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -411,32 +411,31 @@ ListLength(ParseNode* pn) MOZ_ASSERT(pn->isArity(PN_LIST)); return pn->pn_count; } 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* VarListHead(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Var) || pn->isKind(ParseNodeKind::Const)); return ListHead(pn); } @@ -589,26 +588,26 @@ NumberNodeHasFrac(ParseNode* pn) MOZ_ASSERT(pn->isKind(ParseNodeKind::Number)); return pn->pn_u.number.decimalPoint == HasDecimal; } 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* ElemBase(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Elem)); return BinaryLeft(pn); } @@ -2795,19 +2794,21 @@ IsArrayViewCtorName(ModuleValidator& m, *type = Scalar::Float64; } else { return false; } return true; } static bool -CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* ctorExpr, PropertyName* bufferName) -{ - ParseNode* bufArg = NextNode(ctorExpr); +CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* newExpr, PropertyName* bufferName) +{ + 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"); if (!IsUseOfName(bufArg, bufferName)) return m.failName(bufArg, "argument to array view constructor must be '%s'", bufferName); return true; } @@ -2818,17 +2819,17 @@ CheckNewArrayView(ModuleValidator& m, Pr PropertyName* globalName = m.globalArgumentName(); if (!globalName) return m.fail(newExpr, "cannot create array view without an asm.js global parameter"); PropertyName* bufferName = m.bufferArgumentName(); 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; if (ctorExpr->isKind(ParseNodeKind::Dot)) { ParseNode* base = DotBase(ctorExpr); if (!IsUseOfName(base, globalName)) return m.failName(base, "expecting '%s.*Array", globalName); @@ -2847,17 +2848,17 @@ CheckNewArrayView(ModuleValidator& m, Pr if (global->which() != ModuleValidator::Global::ArrayViewCtor) return m.failName(ctorExpr, "%s must be an imported array view constructor", globalName); field = nullptr; type = global->viewType(); } - if (!CheckNewArrayViewArgs(m, ctorExpr, bufferName)) + if (!CheckNewArrayViewArgs(m, newExpr, bufferName)) return false; return m.addArrayView(varName, type, field); } static bool CheckGlobalMathImport(ModuleValidator& m, ParseNode* initNode, PropertyName* varName, PropertyName* field)
--- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -1023,18 +1023,22 @@ ReadScriptOrFunction(nsIObjectInputStrea uint8_t flags; nsresult rv = stream->Read8(&flags); if (NS_FAILED(rv)) return rv; // 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); if (NS_FAILED(rv)) return rv; char* data; rv = stream->ReadBytes(size, &data);
--- a/js/xpconnect/tests/unit/test_getCallerLocation.js +++ b/js/xpconnect/tests/unit/test_getCallerLocation.js @@ -34,33 +34,33 @@ add_task(async function() { `, sandbox, undefined, "thing.js"); Cu.exportFunction(foo, sandbox, {defineAs: "foo"}); let frame = sandbox.thing(); 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 let {messages} = await AddonTestUtils.promiseConsoleOutput(() => { Cu.reportError("Meh", frame); }); 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, "reportError should throw when passed a non-SavedFrame object"); // createError @@ -77,10 +77,10 @@ add_task(async function() { equal(Cu.getGlobalForObject(error), sandbox, "createError creates errors in the global of the SavedFrame"); equal(error.stack, String(cloned), "createError creates errors with the correct stack"); 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"); });
new file mode 100644 --- /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;
--- 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;
--- a/media/libvpx/update.py +++ b/media/libvpx/update.py @@ -36,16 +36,18 @@ def apply_patches(): os.system("patch -p3 < bug1137614.patch") # Bug 1263384 - Check input frame resolution os.system("patch -p3 < input_frame_validation.patch") # Bug 1315288 - Check input frame resolution for vp9 os.system("patch -p3 < input_frame_validation_vp9.patch") # 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): with open('README_MOZILLA') as f: readme = f.read() if 'The git commit ID used was' in readme: new_readme = re.sub('The git commit ID used was [v\.a-f0-9]+',
--- 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 @@ -140,46 +140,30 @@ class NavigationDelegateTest : BaseSessi sessionRule.session.reload() sessionRule.session.waitForPageStop() assertThat("User agent should be set to mobile", sessionRule.session.evaluateJS(userAgentJs) as String, 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() { sessionRule.session.loadTestPath(HELLO_HTML_PATH) sessionRule.waitForPageStop() sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate { @AssertCalled(count = 1, order = [1])
--- a/taskcluster/scripts/builder/hazard-analysis.sh +++ b/taskcluster/scripts/builder/hazard-analysis.sh @@ -97,17 +97,17 @@ function run_analysis () { build_type="$2" if [[ -z "$HAZ_DEP" ]]; then [ -d $MOZ_OBJDIR ] && rm -rf $MOZ_OBJDIR fi ( 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" ) } function grab_artifacts () { local analysis_dir analysis_dir="$1" local artifacts artifacts="$2"
--- a/taskcluster/scripts/builder/hazard-shell.sh +++ b/taskcluster/scripts/builder/hazard-shell.sh @@ -1,6 +1,6 @@ #!/bin/bash -ex 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
--- a/testing/mozbase/mozdevice/mozdevice/adb.py +++ b/testing/mozbase/mozdevice/mozdevice/adb.py @@ -135,34 +135,38 @@ class ADBCommand(object): """ def __init__(self, adb='adb', adb_host=None, 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'. :param adb_host: host of the adb server. :type adb_host: str or None :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 """ if self.__class__ == ADBCommand: raise NotImplementedError 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 self._timeout = timeout self._polling_interval = 0.1 self._adb_version = '' self._logger.debug("%s: %s" % (self.__class__.__name__, @@ -813,25 +817,25 @@ class ADBDevice(ADBCommand): if attempt != max_attempts: time.sleep(20) raise ADBError("Unable to set up test root using paths: [%s]" % ", ".join(paths)) 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 return True # Host Command methods
--- a/testing/mozbase/mozdevice/setup.py +++ b/testing/mozbase/mozdevice/setup.py @@ -3,17 +3,17 @@ # 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/. 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', 'moznetwork >= 0.24', 'mozprocess >= 0.19', ] setup(name=PACKAGE_NAME,
--- a/testing/web-platform/tests/content-security-policy/securitypolicyviolation/targeting.html +++ b/testing/web-platform/tests/content-security-policy/securitypolicyviolation/targeting.html @@ -38,17 +38,17 @@ assert_equals(e.lineNumber, 118); assert_equals(e.columnNumber, 4); 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'); })) .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'); })) .then(t.step_func(e => { assert_equals(e.blockedURI, "inline"); assert_equals(e.lineNumber, 139); assert_equals(e.columnNumber, 4); assert_equals(e.target, document, "Inline event handlers for disconnected elements target the document.");
--- a/toolkit/modules/tests/xpcshell/test_NewTabUtils.js +++ b/toolkit/modules/tests/xpcshell/test_NewTabUtils.js @@ -1,13 +1,15 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ // 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=="; function getBookmarksSize() { return NewTabUtils.activityStreamProvider.executePlacesQuery( "SELECT count(*) FROM moz_bookmarks WHERE type = :type", {params: {type: PlacesUtils.bookmarks.TYPE_BOOKMARK}}); } @@ -674,17 +676,18 @@ add_task(async function getTopFrecentSit links = await provider.getTopSites(); Assert.equal(links.length, 0, "adding a single visit doesn't exceed default threshold"); links = await provider.getTopSites({topsiteFrecency: 100}); Assert.equal(links.length, 1, "adding a visit yields a link"); 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); let testURI = "https://www.amazon.com?search=tv"; await PlacesTestUtils.addVisits(testURI); let provider = NewTabUtils.activityStreamLinks;
--- a/toolkit/recordreplay/ProcessRedirectDarwin.cpp +++ b/toolkit/recordreplay/ProcessRedirectDarwin.cpp @@ -400,17 +400,19 @@ namespace recordreplay { MACRO(OSSpinLockLock) \ MACRO(ReleaseEvent) \ MACRO(RemoveEventFromQueue) \ MACRO(RetainEvent) \ MACRO(SendEventToEventTarget) \ MACRO(SLDisplayCopyColorSpace) \ MACRO(SLDisplayIOServicePort) \ MACRO(SLEventSourceCounterForEventType) \ - MACRO(SLMainDisplayID) + MACRO(SLMainDisplayID) \ + MACRO(SLSSetDenyWindowServerConnections) \ + MACRO(SLSShutdownServerConnections) #define MAKE_CALL_EVENT(aName) CallEvent_ ##aName , enum CallEvent { \ FOR_EACH_REDIRECTION(MAKE_CALL_EVENT) \ CallEvent_Count \ }; @@ -2674,16 +2676,18 @@ RRFunction2(SendEventToEventTarget) // These are not public APIs, but other redirected functions may be aliases for // these which are dynamically installed on the first call in a way that our // redirection mechanism doesn't completely account for. RRFunction2(SLEventSourceCounterForEventType) RRFunction1(SLDisplayCopyColorSpace) RRFunction1(SLDisplayIOServicePort) RRFunction0(SLMainDisplayID) +RRFunction1(SLSSetDenyWindowServerConnections) +RRFunctionVoid0(SLSShutdownServerConnections) /////////////////////////////////////////////////////////////////////////////// // Redirection generation /////////////////////////////////////////////////////////////////////////////// #define MAKE_REDIRECTION_ENTRY(aName) \ { #aName, nullptr, (uint8_t*) RR_ ##aName },
--- a/toolkit/system/gnome/nsAlertsIconListener.cpp +++ b/toolkit/system/gnome/nsAlertsIconListener.cpp @@ -11,33 +11,37 @@ #include "nsSystemAlertsService.h" #include "nsIAlertsService.h" #include "nsICancelable.h" #include "nsIImageToPixbuf.h" #include "nsIStringBundle.h" #include "nsIObserverService.h" #include "nsIURI.h" #include "nsCRT.h" +#include "mozilla/XREAppData.h" #include <dlfcn.h> #include <gdk/gdk.h> +extern const mozilla::StaticXREAppData* gAppData; + static bool gHasActions = false; static bool gHasCaps = false; void* nsAlertsIconListener::libNotifyHandle = nullptr; bool nsAlertsIconListener::libNotifyNotAvail = false; nsAlertsIconListener::notify_is_initted_t nsAlertsIconListener::notify_is_initted = nullptr; nsAlertsIconListener::notify_init_t nsAlertsIconListener::notify_init = nullptr; nsAlertsIconListener::notify_get_server_caps_t nsAlertsIconListener::notify_get_server_caps = nullptr; nsAlertsIconListener::notify_notification_new_t nsAlertsIconListener::notify_notification_new = nullptr; nsAlertsIconListener::notify_notification_show_t nsAlertsIconListener::notify_notification_show = nullptr; nsAlertsIconListener::notify_notification_set_icon_from_pixbuf_t nsAlertsIconListener::notify_notification_set_icon_from_pixbuf = nullptr; nsAlertsIconListener::notify_notification_add_action_t nsAlertsIconListener::notify_notification_add_action = nullptr; nsAlertsIconListener::notify_notification_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) { nsAlertsIconListener* alert = static_cast<nsAlertsIconListener*> (user_data); alert->SendCallback(); } @@ -106,16 +110,17 @@ nsAlertsIconListener::nsAlertsIconListen notify_is_initted = (notify_is_initted_t)dlsym(libNotifyHandle, "notify_is_initted"); notify_init = (notify_init_t)dlsym(libNotifyHandle, "notify_init"); notify_get_server_caps = (notify_get_server_caps_t)dlsym(libNotifyHandle, "notify_get_server_caps"); notify_notification_new = (notify_notification_new_t)dlsym(libNotifyHandle, "notify_notification_new"); notify_notification_show = (notify_notification_show_t)dlsym(libNotifyHandle, "notify_notification_show"); 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; } } } nsAlertsIconListener::~nsAlertsIconListener() @@ -170,16 +175,30 @@ nsAlertsIconListener::ShowAlert(GdkPixbu if (mAlertHasAction) { // What we put as the label doesn't matter here, if the action // string is "default" then that makes the entire bubble clickable // rather than creating a button. notify_notification_add_action(mNotification, "default", "Activate", 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 // with a floating reference, which gets sunk by g_signal_connect_closure(). GClosure* closure = g_closure_new_simple(sizeof(GClosure), this); g_closure_set_marshal(closure, notify_closed_marshal); mClosureHandler = g_signal_connect_closure(mNotification, "closed", closure, FALSE); GError* error = nullptr;
--- a/toolkit/system/gnome/nsAlertsIconListener.h +++ b/toolkit/system/gnome/nsAlertsIconListener.h @@ -52,16 +52,17 @@ protected: typedef bool (*notify_is_initted_t)(void); typedef bool (*notify_init_t)(const char*); typedef GList* (*notify_get_server_caps_t)(void); typedef NotifyNotification* (*notify_notification_new_t)(const char*, const char*, const char*, const char*); typedef bool (*notify_notification_show_t)(void*, GError**); 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<nsICancelable> mIconRequest; nsCString mAlertTitle; nsCString mAlertText; nsCOMPtr<nsIObserver> mAlertListener; nsString mAlertCookie; nsString mAlertName; @@ -75,16 +76,17 @@ protected: static notify_is_initted_t notify_is_initted; static notify_init_t notify_init; static notify_get_server_caps_t notify_get_server_caps; static notify_notification_new_t notify_notification_new; static notify_notification_show_t notify_notification_show; 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; nsresult ShowAlert(GdkPixbuf* aPixbuf); void NotifyFinished(); };