Merge mozilla-central to autoland

This commit is contained in:
Daniel Varga 2018-08-14 01:04:58 +03:00
Родитель f059140c0d eeaa371bd2
Коммит 5a1596f8f2
82 изменённых файлов: 2653 добавлений и 2381 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -123,8 +123,8 @@ return /******/ (function(modules) { // webpackBootstrap
"use strict";
var pdfjsVersion = '2.0.750';
var pdfjsBuild = 'c8ee6331';
var pdfjsVersion = '2.0.760';
var pdfjsBuild = '1268aea2';
var pdfjsSharedUtil = __w_pdfjs_require__(1);
var pdfjsDisplayAPI = __w_pdfjs_require__(7);
var pdfjsDisplayTextLayer = __w_pdfjs_require__(19);
@ -4223,7 +4223,7 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
}
return worker.messageHandler.sendWithPromise('GetDocRequest', {
docId,
apiVersion: '2.0.750',
apiVersion: '2.0.760',
source: {
data: source.data,
url: source.url,
@ -5314,6 +5314,9 @@ var WorkerTransport = function WorkerTransportClosure() {
return this.messageHandler.sendWithPromise('GetDestinations', null);
},
getDestination: function WorkerTransport_getDestination(id) {
if (typeof id !== 'string') {
return Promise.reject(new Error('Invalid destination request.'));
}
return this.messageHandler.sendWithPromise('GetDestination', { id });
},
getPageLabels: function WorkerTransport_getPageLabels() {
@ -5563,8 +5566,8 @@ var InternalRenderTask = function InternalRenderTaskClosure() {
}();
var version, build;
{
exports.version = version = '2.0.750';
exports.build = build = 'c8ee6331';
exports.version = version = '2.0.760';
exports.build = build = '1268aea2';
}
exports.getDocument = getDocument;
exports.LoopbackPort = LoopbackPort;

Просмотреть файл

@ -123,8 +123,8 @@ return /******/ (function(modules) { // webpackBootstrap
"use strict";
var pdfjsVersion = '2.0.750';
var pdfjsBuild = 'c8ee6331';
var pdfjsVersion = '2.0.760';
var pdfjsBuild = '1268aea2';
var pdfjsCoreWorker = __w_pdfjs_require__(1);
exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
@ -327,7 +327,7 @@ var WorkerMessageHandler = {
var cancelXHRs = null;
var WorkerTasks = [];
let apiVersion = docParams.apiVersion;
let workerVersion = '2.0.750';
let workerVersion = '2.0.760';
if (apiVersion !== workerVersion) {
throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
}
@ -581,9 +581,9 @@ var WorkerMessageHandler = {
handler.on('GetStats', function wphSetupGetStats(data) {
return pdfManager.pdfDocument.xref.stats;
});
handler.on('GetAnnotations', function wphSetupGetAnnotations(data) {
return pdfManager.getPage(data.pageIndex).then(function (page) {
return pdfManager.ensure(page, 'getAnnotationsData', [data.intent]);
handler.on('GetAnnotations', function ({ pageIndex, intent }) {
return pdfManager.getPage(pageIndex).then(function (page) {
return page.getAnnotationsData(intent);
});
});
handler.on('RenderPageRequest', function wphSetupRenderPage(data) {
@ -5393,8 +5393,7 @@ var Page = function PageClosure() {
return opList;
});
});
var annotationsPromise = this.pdfManager.ensure(this, 'annotations');
return Promise.all([pageListPromise, annotationsPromise]).then(function ([pageOpList, annotations]) {
return Promise.all([pageListPromise, this._parsedAnnotations]).then(function ([pageOpList, annotations]) {
if (annotations.length === 0) {
pageOpList.flush(true);
return pageOpList;
@ -5444,27 +5443,37 @@ var Page = function PageClosure() {
});
});
},
getAnnotationsData: function Page_getAnnotationsData(intent) {
var annotations = this.annotations;
var annotationsData = [];
for (var i = 0, n = annotations.length; i < n; ++i) {
if (!intent || isAnnotationRenderable(annotations[i], intent)) {
annotationsData.push(annotations[i].data);
getAnnotationsData(intent) {
return this._parsedAnnotations.then(function (annotations) {
let annotationsData = [];
for (let i = 0, ii = annotations.length; i < ii; i++) {
if (!intent || isAnnotationRenderable(annotations[i], intent)) {
annotationsData.push(annotations[i].data);
}
}
}
return annotationsData;
return annotationsData;
});
},
get annotations() {
var annotations = [];
var annotationRefs = this._getInheritableProperty('Annots') || [];
for (var i = 0, n = annotationRefs.length; i < n; ++i) {
var annotationRef = annotationRefs[i];
var annotation = _annotation.AnnotationFactory.create(this.xref, annotationRef, this.pdfManager, this.idFactory);
if (annotation) {
annotations.push(annotation);
return (0, _util.shadow)(this, 'annotations', this._getInheritableProperty('Annots') || []);
},
get _parsedAnnotations() {
const parsedAnnotations = this.pdfManager.ensure(this, 'annotations').then(() => {
const annotationRefs = this.annotations;
const annotationPromises = [];
for (let i = 0, ii = annotationRefs.length; i < ii; i++) {
annotationPromises.push(_annotation.AnnotationFactory.create(this.xref, annotationRefs[i], this.pdfManager, this.idFactory));
}
}
return (0, _util.shadow)(this, 'annotations', annotations);
return Promise.all(annotationPromises).then(function (annotations) {
return annotations.filter(function isDefined(annotation) {
return !!annotation;
});
}, function (reason) {
(0, _util.warn)(`_parsedAnnotations: "${reason}".`);
return [];
});
});
return (0, _util.shadow)(this, '_parsedAnnotations', parsedAnnotations);
}
};
return Page;
@ -5759,6 +5768,9 @@ var _crypto = __w_pdfjs_require__(24);
var _colorspace = __w_pdfjs_require__(25);
function fetchDestination(dest) {
return (0, _primitives.isDict)(dest) ? dest.get('D') : dest;
}
var Catalog = function CatalogClosure() {
function Catalog(pdfManager, xref) {
this.pdfManager = pdfManager;
@ -5898,62 +5910,36 @@ var Catalog = function CatalogClosure() {
return (0, _util.shadow)(this, 'numPages', obj);
},
get destinations() {
function fetchDestination(dest) {
return (0, _primitives.isDict)(dest) ? dest.get('D') : dest;
}
var xref = this.xref;
var dests = {},
nameTreeRef,
nameDictionaryRef;
var obj = this.catDict.get('Names');
if (obj && obj.has('Dests')) {
nameTreeRef = obj.getRaw('Dests');
} else if (this.catDict.has('Dests')) {
nameDictionaryRef = this.catDict.get('Dests');
}
if (nameDictionaryRef) {
obj = nameDictionaryRef;
obj.forEach(function catalogForEach(key, value) {
if (!value) {
return;
}
dests[key] = fetchDestination(value);
});
}
if (nameTreeRef) {
var nameTree = new NameTree(nameTreeRef, xref);
var names = nameTree.getAll();
for (var name in names) {
const obj = this._readDests(),
dests = Object.create(null);
if (obj instanceof NameTree) {
const names = obj.getAll();
for (let name in names) {
dests[name] = fetchDestination(names[name]);
}
} else if (obj instanceof _primitives.Dict) {
obj.forEach(function (key, value) {
if (value) {
dests[key] = fetchDestination(value);
}
});
}
return (0, _util.shadow)(this, 'destinations', dests);
},
getDestination: function Catalog_getDestination(destinationId) {
function fetchDestination(dest) {
return (0, _primitives.isDict)(dest) ? dest.get('D') : dest;
getDestination(destinationId) {
const obj = this._readDests();
if (obj instanceof NameTree || obj instanceof _primitives.Dict) {
return fetchDestination(obj.get(destinationId) || null);
}
var xref = this.xref;
var dest = null,
nameTreeRef,
nameDictionaryRef;
var obj = this.catDict.get('Names');
return null;
},
_readDests() {
const obj = this.catDict.get('Names');
if (obj && obj.has('Dests')) {
nameTreeRef = obj.getRaw('Dests');
return new NameTree(obj.getRaw('Dests'), this.xref);
} else if (this.catDict.has('Dests')) {
nameDictionaryRef = this.catDict.get('Dests');
return this.catDict.get('Dests');
}
if (nameDictionaryRef) {
var value = nameDictionaryRef.get(destinationId);
if (value) {
dest = fetchDestination(value);
}
}
if (nameTreeRef) {
var nameTree = new NameTree(nameTreeRef, xref);
dest = fetchDestination(nameTree.get(destinationId));
}
return dest;
},
get pageLabels() {
var obj = null;
@ -17710,6 +17696,9 @@ var _stream = __w_pdfjs_require__(14);
class AnnotationFactory {
static create(xref, ref, pdfManager, idFactory) {
return pdfManager.ensure(this, '_create', [xref, ref, pdfManager, idFactory]);
}
static _create(xref, ref, pdfManager, idFactory) {
let dict = xref.fetchIfRef(ref);
if (!(0, _primitives.isDict)(dict)) {
return;
@ -22812,17 +22801,15 @@ var Font = function FontClosure() {
} else if (isType1File(file)) {
if (composite) {
fileType = 'CIDFontType0';
} else if (type === 'MMType1') {
fileType = 'MMType1';
} else {
fileType = 'Type1';
fileType = type === 'MMType1' ? 'MMType1' : 'Type1';
}
} else if (isCFFFile(file)) {
if (composite) {
fileType = 'CIDFontType0';
fileSubtype = 'CIDFontType0C';
} else {
fileType = 'Type1';
fileType = type === 'MMType1' ? 'MMType1' : 'Type1';
fileSubtype = 'Type1C';
}
} else {

Просмотреть файл

@ -20,7 +20,7 @@ origin:
# Human-readable identifier for this version/release
# Generally "version NNN", "tag SSS", "bookmark SSS"
release: version 2.0.719
release: version 2.0.760
# The package's license, where possible using the mnemonic from
# https://spdx.org/licenses/

Просмотреть файл

@ -90,6 +90,10 @@ def checking(what, callback=None):
# that will cause the given path(s) to be searched rather than $PATH. Input
# paths may either be individual paths or delimited by os.pathsep, to allow
# passing $PATH (for example) as an element.
# - `paths_have_priority` means that any programs found early in the PATH
# will be prioritized over programs found later in the PATH. The default is
# False, meaning that any of the programs earlier in the program list will be
# given priority, no matter where in the PATH they are found.
#
# The simplest form is:
# check_prog('PROG', ('a', 'b'))
@ -99,7 +103,7 @@ def checking(what, callback=None):
@template
@imports(_from='mozbuild.shellutil', _import='quote')
def check_prog(var, progs, what=None, input=None, allow_missing=False,
paths=None, when=None):
paths=None, paths_have_priority=False, when=None):
if input is not None:
# Wrap input with type checking and normalization.
@depends(input, when=when)
@ -131,11 +135,19 @@ def check_prog(var, progs, what=None, input=None, allow_missing=False,
if not isinstance(progs, (tuple, list)):
configure_error('progs must resolve to a list or tuple!')
for prog in value or progs:
log.debug('%s: Trying %s', var.lower(), quote(prog))
result = find_program(prog, paths)
if result:
return result
if paths_have_priority:
for path in paths:
for prog in value or progs:
log.debug('%s: Trying %s', var.lower(), quote(prog))
result = find_program(prog, [path])
if result:
return result
else:
for prog in value or progs:
log.debug('%s: Trying %s', var.lower(), quote(prog))
result = find_program(prog, paths)
if result:
return result
if not allow_missing or value:
raise FatalCheckError('Cannot find %s' % what)

Просмотреть файл

@ -35,7 +35,8 @@ def node_toolchain_search_path(host):
# exist on systems (probably linux distros) where there is a program in the path
# called "node" that does something else.
nodejs = check_prog('NODEJS', ('nodejs', 'node',),
allow_missing=True, paths=node_toolchain_search_path)
allow_missing=True, paths=node_toolchain_search_path,
paths_have_priority=True)
@depends_if(nodejs)

Просмотреть файл

@ -942,10 +942,8 @@ CopyingStructuredCloneReadCallback(JSContext* aCx,
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;
}
@ -956,9 +954,7 @@ CopyingStructuredCloneReadCallback(JSContext* aCx,
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;
}

Просмотреть файл

@ -671,7 +671,8 @@ AutoEntryScript::AutoEntryScript(JSObject* aObject,
bool aIsMainThread)
: AutoEntryScript(xpc::NativeGlobal(aObject), aReason, aIsMainThread)
{
MOZ_ASSERT(!js::IsCrossCompartmentWrapper(aObject));
// xpc::NativeGlobal uses JS::GetNonCCWObjectGlobal, which asserts that
// aObject is not a CCW.
}
AutoEntryScript::~AutoEntryScript()

Просмотреть файл

@ -2702,24 +2702,25 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
case ParseNodeKind::Call:
case ParseNodeKind::SuperCall:
{
ParseNode* next = pn->pn_head;
MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
ParseNode* pn_callee = pn->pn_left;
ParseNode* pn_args = pn->pn_right;
MOZ_ASSERT(pn->pn_pos.encloses(pn_callee->pn_pos));
RootedValue callee(cx);
if (pn->isKind(ParseNodeKind::SuperCall)) {
MOZ_ASSERT(next->isKind(ParseNodeKind::SuperBase));
if (!builder.super(&next->pn_pos, &callee))
MOZ_ASSERT(pn_callee->isKind(ParseNodeKind::SuperBase));
if (!builder.super(&pn_callee->pn_pos, &callee))
return false;
} else {
if (!expression(next, &callee))
if (!expression(pn_callee, &callee))
return false;
}
NodeVector args(cx);
if (!args.reserve(pn->pn_count - 1))
if (!args.reserve(pn_args->pn_count))
return false;
for (next = next->pn_next; next; next = next->pn_next) {
for (ParseNode* next = pn_args->pn_head; next; next = next->pn_next) {
MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
RootedValue arg(cx);
@ -2740,17 +2741,17 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
case ParseNodeKind::Dot:
{
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos));
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
RootedValue expr(cx);
RootedValue propname(cx);
RootedAtom pnAtom(cx, pn->pn_atom);
RootedAtom pnAtom(cx, pn->pn_right->pn_atom);
if (pn->as<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;
}

Просмотреть файл

@ -79,23 +79,23 @@ js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs&
}
/* Step 20 (reordered).
* Set the |index| property. (TemplateObject positions it in slot 0) */
arr->setSlot(0, Int32Value(matches[0].start));
* Set the |index| property. */
arr->setSlot(RegExpRealm::MatchResultObjectIndexSlot, Int32Value(matches[0].start));
/* Step 21 (reordered).
* Set the |input| property. (TemplateObject positions it in slot 1) */
arr->setSlot(1, StringValue(input));
* Set the |input| property. */
arr->setSlot(RegExpRealm::MatchResultObjectInputSlot, StringValue(input));
#ifdef DEBUG
RootedValue test(cx);
RootedId id(cx, NameToId(cx->names().index));
if (!NativeGetProperty(cx, arr, id, &test))
return false;
MOZ_ASSERT(test == arr->getSlot(0));
MOZ_ASSERT(test == arr->getSlot(RegExpRealm::MatchResultObjectIndexSlot));
id = NameToId(cx->names().input);
if (!NativeGetProperty(cx, arr, id, &test))
return false;
MOZ_ASSERT(test == arr->getSlot(1));
MOZ_ASSERT(test == arr->getSlot(RegExpRealm::MatchResultObjectInputSlot));
#endif
/* Step 25. */
@ -1025,16 +1025,18 @@ js::RegExpMatcher(JSContext* cx, unsigned argc, Value* vp)
*/
bool
js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
int32_t lastIndex,
int32_t maybeLastIndex,
MatchPairs* maybeMatches, MutableHandleValue output)
{
MOZ_ASSERT(lastIndex >= 0);
// The MatchPairs will always be passed in, but RegExp execution was
// successful only if the pairs have actually been filled in.
if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0)
if (maybeMatches && maybeMatches->pairsRaw()[0] > MatchPair::NoMatch)
return CreateRegExpMatchResult(cx, input, *maybeMatches, output);
return RegExpMatcherImpl(cx, regexp, input, lastIndex, output);
// |maybeLastIndex| only contains a valid value when the RegExp execution
// was not successful.
MOZ_ASSERT(maybeLastIndex >= 0);
return RegExpMatcherImpl(cx, regexp, input, maybeLastIndex, output);
}
/*
@ -1106,7 +1108,7 @@ js::RegExpSearcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
// The MatchPairs will always be passed in, but RegExp execution was
// successful only if the pairs have actually been filled in.
if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0) {
if (maybeMatches && maybeMatches->pairsRaw()[0] > MatchPair::NoMatch) {
*result = CreateRegExpSearchResult(*maybeMatches);
return true;
}

Просмотреть файл

@ -41,7 +41,7 @@ RegExpMatcher(JSContext* cx, unsigned argc, Value* vp);
extern MOZ_MUST_USE bool
RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
int32_t lastIndex, MatchPairs* maybeMatches, MutableHandleValue output);
int32_t maybeLastIndex, MatchPairs* maybeMatches, MutableHandleValue output);
extern MOZ_MUST_USE bool
RegExpSearcher(JSContext* cx, unsigned argc, Value* vp);

Просмотреть файл

@ -1002,9 +1002,7 @@ StructMetaTypeDescr::createFromArrays(JSContext* cx,
if (!CreateTraceList(cx, descr))
return nullptr;
if (!cx->zone()->addTypeDescrObject(cx, descr) ||
!cx->zone()->addTypeDescrObject(cx, fieldTypeVec))
{
if (!cx->zone()->addTypeDescrObject(cx, descr)) {
ReportOutOfMemory(cx);
return nullptr;
}

Просмотреть файл

@ -394,6 +394,10 @@ function isOverridableField(initialCSU, csu, field)
return false;
if (field == "GetThreadFromPRThread")
return false;
if (field == "ConstructUbiNode")
return false;
if (initialCSU == 'nsIXPCScriptable' && field == "GetScriptableFlags")
return false;
if (initialCSU == 'nsIXPConnectJSObjectHolder' && field == 'GetJSObject')
return false;
if (initialCSU == 'nsIXPConnect' && field == 'GetSafeJSContext')

Просмотреть файл

@ -26,7 +26,7 @@
use strict;
use warnings;
use IO::Handle;
use File::Basename qw(dirname);
use File::Basename qw(basename dirname);
use Getopt::Long;
use Cwd;
@ -249,13 +249,17 @@ sub run_build
# Tell the wrapper where to find the config
$ENV{"XGILL_CONFIG"} = Cwd::abs_path($config_file);
# update the PATH so that the build will see the wrappers.
# If overriding $CC, use GCCDIR to tell the wrapper scripts where the
# real compiler is. If $CC is not set, then the wrapper script will
# search $PATH anyway.
if (exists $ENV{CC}) {
$ENV{PATH} = dirname($ENV{CC}) . ":$ENV{PATH}";
delete $ENV{CC};
delete $ENV{CXX};
$ENV{GCCDIR} = dirname($ENV{CC});
}
$ENV{"PATH"} = "$wrap_dir:" . $ENV{"PATH"};
# Force the wrapper scripts to be run in place of the compiler during
# whatever build process we use.
$ENV{CC} = "$wrap_dir/" . basename($ENV{CC} // "gcc");
$ENV{CXX} = "$wrap_dir/" . basename($ENV{CXX} // "g++");
# do the build, cleaning if necessary.
chdir $build_dir;

Просмотреть файл

@ -3462,9 +3462,8 @@ BinASTParser<Tok>::parseInterfaceCallExpression(const size_t start, const BinKin
op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
}
}
auto result = arguments;
result->setKind(ParseNodeKind::Call);
result->prepend(callee);
BINJS_TRY_DECL(result, factory_.newCall(callee, arguments));
result->setOp(op);
return result;
}
@ -3742,7 +3741,7 @@ BinASTParser<Tok>::parseInterfaceComputedMemberAssignmentTarget(const size_t sta
BINJS_MOZ_TRY_DECL(expression, parseExpression());
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, tokenizer_->offset()));
return result;
}
@ -3786,7 +3785,7 @@ BinASTParser<Tok>::parseInterfaceComputedMemberExpression(const size_t start, co
BINJS_MOZ_TRY_DECL(expression, parseExpression());
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, tokenizer_->offset()));
return result;
}
@ -5677,10 +5676,7 @@ BinASTParser<Tok>::parseInterfaceNewExpression(const size_t start, const BinKind
BINJS_MOZ_TRY_DECL(arguments, parseArguments());
auto result = arguments;
result->setKind(ParseNodeKind::New);
result->prepend(callee);
result->setOp(JSOP_NEW);
BINJS_TRY_DECL(result, factory_.newNewExpression(tokenizer_->pos(start).begin, callee, arguments));
return result;
}
@ -6197,13 +6193,18 @@ BinASTParser<Tok>::parseInterfaceStaticMemberAssignmentTarget(const size_t start
const BinField expected_fields[2] = { BinField::Object, BinField::Property };
MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
#endif // defined(DEBUG)
size_t nameStart;
BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
RootedAtom property(cx_);
MOZ_TRY_VAR(property, tokenizer_->readAtom());
{
nameStart = tokenizer_->offset();
MOZ_TRY_VAR(property, tokenizer_->readAtom());
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
}
BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
return result;
}
@ -6242,13 +6243,18 @@ BinASTParser<Tok>::parseInterfaceStaticMemberExpression(const size_t start, cons
const BinField expected_fields[2] = { BinField::Object, BinField::Property };
MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
#endif // defined(DEBUG)
size_t nameStart;
BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
RootedAtom property(cx_);
MOZ_TRY_VAR(property, tokenizer_->readAtom());
{
nameStart = tokenizer_->offset();
MOZ_TRY_VAR(property, tokenizer_->readAtom());
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
}
BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
return result;
}
@ -7381,7 +7387,7 @@ BinASTParser<Tok>::parseArguments()
const auto start = tokenizer_->offset();
MOZ_TRY(tokenizer_->enterList(length, guard));
BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::Arguments, tokenizer_->pos(start)));
for (uint32_t i = 0; i < length; ++i) {
BINJS_MOZ_TRY_DECL(item, parseSpreadElementOrExpression());

Просмотреть файл

@ -207,7 +207,7 @@ hpp:
Arguments:
init:
BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::Arguments, tokenizer_->pos(start)));
append:
factory_.addList(/* list = */ result, /* kid = */ item);
@ -428,9 +428,8 @@ CallExpression:
op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
}
}
auto result = arguments;
result->setKind(ParseNodeKind::Call);
result->prepend(callee);
BINJS_TRY_DECL(result, factory_.newCall(callee, arguments));
result->setOp(op);
@ -489,11 +488,11 @@ CompoundAssignmentExpression:
ComputedMemberAssignmentTarget:
build: |
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, tokenizer_->offset()));
ComputedMemberExpression:
build: |
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, tokenizer_->offset()));
ConditionalExpression:
build: |
@ -831,10 +830,7 @@ LiteralStringExpression:
NewExpression:
build: |
auto result = arguments;
result->setKind(ParseNodeKind::New);
result->prepend(callee);
result->setOp(JSOP_NEW);
BINJS_TRY_DECL(result, factory_.newNewExpression(tokenizer_->pos(start).begin, callee, arguments));
ObjectExpression:
build:
@ -922,12 +918,28 @@ SwitchStatementWithDefault:
BINJS_TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope, true));
StaticMemberAssignmentTarget:
init:
size_t nameStart;
fields:
property:
block:
before: |
nameStart = tokenizer_->offset();
build: |
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
StaticMemberExpression:
init:
size_t nameStart;
fields:
property:
block:
before: |
nameStart = tokenizer_->offset();
build: |
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
ThisExpression:
build: |

Просмотреть файл

@ -1022,7 +1022,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
// Watch out for getters!
case ParseNodeKind::Dot:
MOZ_ASSERT(pn->isArity(PN_NAME));
MOZ_ASSERT(pn->isArity(PN_BINARY));
*answer = true;
return true;
@ -1260,6 +1260,14 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
case ParseNodeKind::Call:
case ParseNodeKind::TaggedTemplate:
case ParseNodeKind::SuperCall:
MOZ_ASSERT(pn->isArity(PN_BINARY));
*answer = true;
return true;
// Function arg lists can contain arbitrary expressions. Technically
// this only causes side-effects if one of the arguments does, but since
// the call being made will always trigger side-effects, it isn't needed.
case ParseNodeKind::Arguments:
MOZ_ASSERT(pn->isArity(PN_LIST));
*answer = true;
return true;
@ -1394,6 +1402,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate
case ParseNodeKind::PosHolder: // by ParseNodeKind::NewTarget
case ParseNodeKind::SuperBase: // by ParseNodeKind::Elem and others
case ParseNodeKind::PropertyName: // by ParseNodeKind::Dot
MOZ_CRASH("handled by parent nodes");
case ParseNodeKind::Limit: // invalid sentinel value
@ -1865,11 +1874,11 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn)
MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
MOZ_ASSERT(!pn->as<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()) {
@ -1877,9 +1886,9 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn)
ParseNode* pnup = nullptr;
ParseNode* pndown;
for (;;) {
/* Reverse pndot->pn_expr to point up, not down. */
pndown = pndot->pn_expr;
pndot->pn_expr = pnup;
/* Reverse pndot->pn_left to point up, not down. */
pndown = pndot->pn_left;
pndot->pn_left = pnup;
if (!pndown->isKind(ParseNodeKind::Dot) || pndown->as<PropertyAccess>().isSuper())
break;
pnup = pndot;
@ -1892,12 +1901,12 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn)
do {
/* Walk back up the list, emitting annotated name ops. */
if (!emitAtomOp(pndot, JSOP_GETPROP))
if (!emitAtomOp(pndot->pn_right, JSOP_GETPROP))
return false;
/* Reverse the pn_expr link again. */
pnup = pndot->pn_expr;
pndot->pn_expr = pndown;
/* Reverse the pn_left link again. */
pnup = pndot->pn_left;
pndot->pn_left = pndown;
pndown = pndot;
} while ((pndot = pnup) != nullptr);
return true;
@ -1922,7 +1931,7 @@ BytecodeEmitter::emitSuperPropLHS(ParseNode* superBase, bool isCall)
bool
BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op)
{
MOZ_ASSERT(pn->isArity(PN_NAME));
MOZ_ASSERT(pn->isArity(PN_BINARY));
if (!emitPropLHS(pn))
return false;
@ -1930,7 +1939,7 @@ BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op)
if (op == JSOP_CALLPROP && !emit1(JSOP_DUP))
return false;
if (!emitAtomOp(pn, op))
if (!emitAtomOp(pn->pn_right, op))
return false;
if (op == JSOP_CALLPROP && !emit1(JSOP_SWAP))
@ -1946,7 +1955,7 @@ BytecodeEmitter::emitSuperGetProp(ParseNode* pn, bool isCall)
if (!emitSuperPropLHS(base, isCall))
return false;
if (!emitAtomOp(pn, JSOP_GETPROP_SUPER))
if (!emitAtomOp(pn->pn_right, JSOP_GETPROP_SUPER))
return false;
if (isCall && !emit1(JSOP_SWAP))
@ -1976,7 +1985,7 @@ BytecodeEmitter::emitPropIncDec(ParseNode* pn)
if (!emit1(JSOP_DUP)) // OBJ OBJ
return false;
}
if (!emitAtomOp(pn->pn_kid, isSuper? JSOP_GETPROP_SUPER : JSOP_GETPROP)) // OBJ V
if (!emitAtomOp(pn->pn_kid->pn_right, isSuper ? JSOP_GETPROP_SUPER : JSOP_GETPROP)) // OBJ V
return false;
if (!emit1(JSOP_POS)) // OBJ N
return false;
@ -2002,7 +2011,7 @@ BytecodeEmitter::emitPropIncDec(ParseNode* pn)
JSOp setOp = isSuper ? sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER
: sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
if (!emitAtomOp(pn->pn_kid, setOp)) // N? N+1
if (!emitAtomOp(pn->pn_kid->pn_right, setOp)) // N? N+1
return false;
if (post && !emit1(JSOP_POP)) // RESULT
return false;
@ -2692,7 +2701,7 @@ BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, size_t* emitted)
return false;
*emitted = 2;
} else {
if (!emitTree(target->pn_expr))
if (!emitTree(target->pn_left))
return false;
*emitted = 1;
}
@ -2810,7 +2819,7 @@ BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, Destructuri
setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER;
else
setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
if (!emitAtomOp(target, setOp))
if (!emitAtomOp(target->pn_right, setOp))
return false;
break;
}
@ -3923,11 +3932,11 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, ParseNodeKind pnk, ParseNode* rh
return false;
offset += 2;
} else {
if (!emitTree(lhs->expr()))
if (!emitTree(lhs->pn_left))
return false;
offset += 1;
}
if (!makeAtomIndex(lhs->pn_atom, &atomIndex))
if (!makeAtomIndex(lhs->pn_right->pn_atom, &atomIndex))
return false;
break;
case ParseNodeKind::Elem: {
@ -3976,7 +3985,7 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, ParseNodeKind pnk, ParseNode* rh
} else {
if (!emit1(JSOP_DUP))
return false;
bool isLength = (lhs->pn_atom == cx->names().length);
bool isLength = (lhs->pn_right->pn_atom == cx->names().length);
getOp = isLength ? JSOP_LENGTH : JSOP_GETPROP;
}
if (!emitIndex32(getOp, atomIndex))
@ -4353,12 +4362,10 @@ BytecodeEmitter::emitIf(ParseNode* pn)
{
IfEmitter ifThenElse(this);
if_again:
// Make sure this code is attributed to the "if" so that it gets a useful
// column number, instead of the default 0 value.
if (!updateSourceCoordNotes(pn->pn_pos.begin))
if (!ifThenElse.emitIf(Some(pn->pn_pos.begin)))
return false;
if_again:
/* Emit code for the condition before pushing stmtInfo. */
if (!emitTree(pn->pn_kid1))
return false;
@ -4380,7 +4387,7 @@ BytecodeEmitter::emitIf(ParseNode* pn)
if (elseNode->isKind(ParseNodeKind::If)) {
pn = elseNode;
if (!ifThenElse.emitElseIf())
if (!ifThenElse.emitElseIf(Some(pn->pn_pos.begin)))
return false;
goto if_again;
@ -4821,7 +4828,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, const EmitterScope* headLexical
bool allowSelfHostedIter = false;
if (emitterMode == BytecodeEmitter::SelfHosting &&
forHeadExpr->isKind(ParseNodeKind::Call) &&
forHeadExpr->pn_head->name() == cx->names().allowContentIter)
forHeadExpr->pn_left->name() == cx->names().allowContentIter)
{
allowSelfHostedIter = true;
}
@ -6142,10 +6149,12 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn)
//
// argc is set to the amount of actually emitted args and the
// emitting of args below is disabled by setting emitArgs to false.
ParseNode* pn2 = pn->pn_head;
const char* errorName = SelfHostedCallFunctionName(pn2->name(), cx);
ParseNode* pn_callee = pn->pn_left;
ParseNode* pn_args = pn->pn_right;
if (pn->pn_count < 3) {
const char* errorName = SelfHostedCallFunctionName(pn_callee->name(), cx);
if (pn_args->pn_count < 2) {
reportError(pn, JSMSG_MORE_ARGS_NEEDED, errorName, "2", "s");
return false;
}
@ -6156,8 +6165,8 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn)
return false;
}
bool constructing = pn2->name() == cx->names().constructContentFunction;
ParseNode* funNode = pn2->pn_next;
bool constructing = pn_callee->name() == cx->names().constructContentFunction;
ParseNode* funNode = pn_args->pn_head;
if (constructing) {
callOp = JSOP_NEW;
} else if (funNode->getKind() == ParseNodeKind::Name &&
@ -6170,7 +6179,7 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn)
#ifdef DEBUG
if (emitterMode == BytecodeEmitter::SelfHosting &&
pn2->name() == cx->names().callFunction)
pn_callee->name() == cx->names().callFunction)
{
if (!emit1(JSOP_DEBUGCHECKSELFHOSTED))
return false;
@ -6199,7 +6208,7 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn)
return false;
}
uint32_t argc = pn->pn_count - 3;
uint32_t argc = pn_args->pn_count - 2;
if (!emitCall(callOp, argc))
return false;
@ -6210,15 +6219,15 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn)
bool
BytecodeEmitter::emitSelfHostedResumeGenerator(ParseNode* pn)
{
ParseNode* pn_args = pn->pn_right;
// Syntax: resumeGenerator(gen, value, 'next'|'throw'|'return')
if (pn->pn_count != 4) {
if (pn_args->pn_count != 3) {
reportError(pn, JSMSG_MORE_ARGS_NEEDED, "resumeGenerator", "1", "s");
return false;
}
ParseNode* funNode = pn->pn_head; // The resumeGenerator node.
ParseNode* genNode = funNode->pn_next;
ParseNode* genNode = pn_args->pn_head;
if (!emitTree(genNode))
return false;
@ -6250,24 +6259,26 @@ BytecodeEmitter::emitSelfHostedForceInterpreter()
bool
BytecodeEmitter::emitSelfHostedAllowContentIter(ParseNode* pn)
{
if (pn->pn_count != 2) {
ParseNode* pn_args = pn->pn_right;
if (pn_args->pn_count != 1) {
reportError(pn, JSMSG_MORE_ARGS_NEEDED, "allowContentIter", "1", "");
return false;
}
// We're just here as a sentinel. Pass the value through directly.
return emitTree(pn->pn_head->pn_next);
return emitTree(pn_args->pn_head);
}
bool
BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn)
{
// Only optimize when 3 arguments are passed (we use 4 to include |this|).
MOZ_ASSERT(pn->pn_count == 4);
ParseNode* pn_args = pn->pn_right;
ParseNode* funNode = pn->pn_head; // The _DefineDataProperty node.
// Only optimize when 3 arguments are passed.
MOZ_ASSERT(pn_args->pn_count == 3);
ParseNode* objNode = funNode->pn_next;
ParseNode* objNode = pn_args->pn_head;
if (!emitTree(objNode))
return false;
@ -6288,14 +6299,14 @@ BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn)
bool
BytecodeEmitter::emitSelfHostedHasOwn(ParseNode* pn)
{
if (pn->pn_count != 3) {
ParseNode* pn_args = pn->pn_right;
if (pn_args->pn_count != 2) {
reportError(pn, JSMSG_MORE_ARGS_NEEDED, "hasOwn", "2", "");
return false;
}
ParseNode* funNode = pn->pn_head; // The hasOwn node.
ParseNode* idNode = funNode->pn_next;
ParseNode* idNode = pn_args->pn_head;
if (!emitTree(idNode))
return false;
@ -6309,14 +6320,14 @@ BytecodeEmitter::emitSelfHostedHasOwn(ParseNode* pn)
bool
BytecodeEmitter::emitSelfHostedGetPropertySuper(ParseNode* pn)
{
if (pn->pn_count != 4) {
ParseNode* pn_args = pn->pn_right;
if (pn_args->pn_count != 3) {
reportError(pn, JSMSG_MORE_ARGS_NEEDED, "getPropertySuper", "3", "");
return false;
}
ParseNode* funNode = pn->pn_head; // The getPropertySuper node.
ParseNode* objNode = funNode->pn_next;
ParseNode* objNode = pn_args->pn_head;
ParseNode* idNode = objNode->pn_next;
ParseNode* receiverNode = idNode->pn_next;
@ -6345,11 +6356,11 @@ BytecodeEmitter::isRestParameter(ParseNode* pn)
if (!pn->isKind(ParseNodeKind::Name)) {
if (emitterMode == BytecodeEmitter::SelfHosting && pn->isKind(ParseNodeKind::Call)) {
ParseNode* pn2 = pn->pn_head;
if (pn2->getKind() == ParseNodeKind::Name &&
pn2->name() == cx->names().allowContentIter)
ParseNode* pn_callee = pn->pn_left;
if (pn_callee->getKind() == ParseNodeKind::Name &&
pn_callee->name() == cx->names().allowContentIter)
{
return isRestParameter(pn2->pn_next);
return isRestParameter(pn->pn_right->pn_head);
}
}
return false;
@ -6479,101 +6490,22 @@ BytecodeEmitter::emitPipeline(ParseNode* pn)
}
bool
BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */)
BytecodeEmitter::emitArguments(ParseNode* pn, bool callop, bool spread)
{
bool callop =
pn->isKind(ParseNodeKind::Call) || pn->isKind(ParseNodeKind::TaggedTemplate);
/*
* Emit callable invocation or operator new (constructor call) code.
* First, emit code for the left operand to evaluate the callable or
* constructable object expression.
*
* For operator new, we emit JSOP_GETPROP instead of JSOP_CALLPROP, etc.
* This is necessary to interpose the lambda-initialized method read
* barrier -- see the code in jsinterp.cpp for JSOP_LAMBDA followed by
* JSOP_{SET,INIT}PROP.
*
* Then (or in a call case that has no explicit reference-base
* object) we emit JSOP_UNDEFINED to produce the undefined |this|
* value required for calls (which non-strict mode functions
* will box into the global object).
*/
uint32_t argc = pn->pn_count - 1;
uint32_t argc = pn->pn_count;
if (argc >= ARGC_LIMIT) {
reportError(pn, callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS);
return false;
}
ParseNode* pn2 = pn->pn_head;
bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
if (pn2->isKind(ParseNodeKind::Name) && emitterMode == BytecodeEmitter::SelfHosting && !spread) {
// Calls to "forceInterpreter", "callFunction",
// "callContentFunction", or "resumeGenerator" in self-hosted
// code generate inline bytecode.
if (pn2->name() == cx->names().callFunction ||
pn2->name() == cx->names().callContentFunction ||
pn2->name() == cx->names().constructContentFunction)
{
return emitSelfHostedCallFunction(pn);
}
if (pn2->name() == cx->names().resumeGenerator)
return emitSelfHostedResumeGenerator(pn);
if (pn2->name() == cx->names().forceInterpreter)
return emitSelfHostedForceInterpreter();
if (pn2->name() == cx->names().allowContentIter)
return emitSelfHostedAllowContentIter(pn);
if (pn2->name() == cx->names().defineDataPropertyIntrinsic && pn->pn_count == 4)
return emitSelfHostedDefineDataProperty(pn);
if (pn2->name() == cx->names().hasOwn)
return emitSelfHostedHasOwn(pn);
if (pn2->name() == cx->names().getPropertySuper)
return emitSelfHostedGetPropertySuper(pn);
// Fall through
}
if (!emitCallee(pn2, pn, &callop))
return false;
bool isNewOp = pn->getOp() == JSOP_NEW || pn->getOp() == JSOP_SPREADNEW ||
pn->getOp() == JSOP_SUPERCALL || pn->getOp() == JSOP_SPREADSUPERCALL;
// Emit room for |this|.
if (!callop) {
if (isNewOp) {
if (!emit1(JSOP_IS_CONSTRUCTING))
return false;
} else {
if (!emit1(JSOP_UNDEFINED))
return false;
}
}
/*
* Emit code for each argument in order, then emit the JSOP_*CALL or
* JSOP_NEW bytecode with a two-byte immediate telling how many args
* were pushed on the operand stack.
*/
if (!spread) {
for (ParseNode* pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) {
for (ParseNode* pn3 = pn->pn_head; pn3; pn3 = pn3->pn_next) {
if (!emitTree(pn3))
return false;
}
if (isNewOp) {
if (pn->isKind(ParseNodeKind::SuperCall)) {
if (!emit1(JSOP_NEWTARGET))
return false;
} else {
// Repush the callee as new.target
if (!emitDupAt(argc + 1))
return false;
}
}
} else {
ParseNode* args = pn2->pn_next;
ParseNode* args = pn->pn_head;
bool emitOptCode = (argc == 1) && isRestParameter(args->pn_kid);
InternalIfEmitter ifNotOptimizable(this);
@ -6613,29 +6545,159 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUs
if (!ifNotOptimizable.emitEnd())
return false;
}
}
return true;
}
bool
BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */)
{
bool callop =
pn->isKind(ParseNodeKind::Call) || pn->isKind(ParseNodeKind::TaggedTemplate);
/*
* Emit callable invocation or operator new (constructor call) code.
* First, emit code for the left operand to evaluate the callable or
* constructable object expression.
*
* For operator new, we emit JSOP_GETPROP instead of JSOP_CALLPROP, etc.
* This is necessary to interpose the lambda-initialized method read
* barrier -- see the code in jsinterp.cpp for JSOP_LAMBDA followed by
* JSOP_{SET,INIT}PROP.
*
* Then (or in a call case that has no explicit reference-base
* object) we emit JSOP_UNDEFINED to produce the undefined |this|
* value required for calls (which non-strict mode functions
* will box into the global object).
*/
ParseNode* pn_callee = pn->pn_left;
ParseNode* pn_args = pn->pn_right;
bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
if (pn_callee->isKind(ParseNodeKind::Name) && emitterMode == BytecodeEmitter::SelfHosting && !spread) {
// Calls to "forceInterpreter", "callFunction",
// "callContentFunction", or "resumeGenerator" in self-hosted
// code generate inline bytecode.
if (pn_callee->name() == cx->names().callFunction ||
pn_callee->name() == cx->names().callContentFunction ||
pn_callee->name() == cx->names().constructContentFunction)
{
return emitSelfHostedCallFunction(pn);
}
if (pn_callee->name() == cx->names().resumeGenerator)
return emitSelfHostedResumeGenerator(pn);
if (pn_callee->name() == cx->names().forceInterpreter)
return emitSelfHostedForceInterpreter();
if (pn_callee->name() == cx->names().allowContentIter)
return emitSelfHostedAllowContentIter(pn);
if (pn_callee->name() == cx->names().defineDataPropertyIntrinsic && pn_args->pn_count == 3)
return emitSelfHostedDefineDataProperty(pn);
if (pn_callee->name() == cx->names().hasOwn)
return emitSelfHostedHasOwn(pn);
if (pn_callee->name() == cx->names().getPropertySuper)
return emitSelfHostedGetPropertySuper(pn);
// Fall through
}
if (!emitCallee(pn_callee, pn, &callop))
return false;
bool isNewOp = pn->getOp() == JSOP_NEW || pn->getOp() == JSOP_SPREADNEW ||
pn->getOp() == JSOP_SUPERCALL || pn->getOp() == JSOP_SPREADSUPERCALL;
// Emit room for |this|.
if (!callop) {
if (isNewOp) {
if (pn->isKind(ParseNodeKind::SuperCall)) {
if (!emit1(JSOP_NEWTARGET))
return false;
} else {
if (!emitDupAt(2))
return false;
if (!emit1(JSOP_IS_CONSTRUCTING))
return false;
} else {
if (!emit1(JSOP_UNDEFINED))
return false;
}
}
if (!emitArguments(pn_args, callop, spread))
return false;
uint32_t argc = pn_args->pn_count;
/*
* Emit code for each argument in order, then emit the JSOP_*CALL or
* JSOP_NEW bytecode with a two-byte immediate telling how many args
* were pushed on the operand stack.
*/
if (isNewOp) {
if (pn->isKind(ParseNodeKind::SuperCall)) {
if (!emit1(JSOP_NEWTARGET))
return false;
} else if (!spread) {
// Repush the callee as new.target
if (!emitDupAt(argc + 1))
return false;
} else {
if (!emitDupAt(2))
return false;
}
}
ParseNode* coordNode = pn;
if (pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL)) {
switch (pn_callee->getKind()) {
case ParseNodeKind::Dot: {
// Check if this member is a simple chain of simple chain of
// property accesses, e.g. x.y.z, this.x.y, super.x.y
bool simpleDotChain = false;
for (ParseNode* cur = pn_callee; cur->isKind(ParseNodeKind::Dot); cur = cur->pn_left) {
ParseNode* left = cur->pn_left;
if (left->isKind(ParseNodeKind::Name) || left->isKind(ParseNodeKind::This) ||
left->isKind(ParseNodeKind::SuperBase))
{
simpleDotChain = true;
}
}
if (!simpleDotChain) {
// obj().aprop() // expression
// ^ // column coord
//
// Note: Because of the constant folding logic in FoldElement,
// this case also applies for constant string properties.
//
// obj()['aprop']() // expression
// ^ // column coord
coordNode = pn_callee->pn_right;
}
break;
}
case ParseNodeKind::Elem:
// obj[expr]() // expression
// ^ // column coord
coordNode = pn_args;
break;
default:
break;
}
}
if (!spread) {
if (pn->getOp() == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) {
if (!emitCall(JSOP_CALL_IGNORES_RV, argc, pn))
if (!emitCall(JSOP_CALL_IGNORES_RV, argc, coordNode))
return false;
checkTypeSet(JSOP_CALL_IGNORES_RV);
} else {
if (!emitCall(pn->getOp(), argc, pn))
if (!emitCall(pn->getOp(), argc, coordNode))
return false;
checkTypeSet(pn->getOp());
}
} else {
if (coordNode) {
if (!updateSourceCoordNotes(coordNode->pn_pos.begin))
return false;
}
if (!emit1(pn->getOp()))
return false;
checkTypeSet(pn->getOp());
@ -6846,26 +6908,28 @@ bool
BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional,
ValueUsage valueUsage /* = ValueUsage::WantValue */)
{
/* Emit the condition, then branch if false to the else part. */
CondEmitter cond(this);
if (!cond.emitCond())
return false;
if (!emitTree(&conditional.condition()))
return false;
IfEmitter ifThenElse(this);
if (!ifThenElse.emitCond())
if (!cond.emitThenElse())
return false;
if (!emitTree(&conditional.thenExpression(), valueUsage))
return false;
if (!ifThenElse.emitElse())
if (!cond.emitElse())
return false;
if (!emitTree(&conditional.elseExpression(), valueUsage))
return false;
if (!ifThenElse.emitEnd())
if (!cond.emitEnd())
return false;
MOZ_ASSERT(ifThenElse.pushed() == 1);
MOZ_ASSERT(cond.pushed() == 1);
return true;
}
@ -7204,7 +7268,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count)
if (emitterMode == BytecodeEmitter::SelfHosting &&
expr->isKind(ParseNodeKind::Call) &&
expr->pn_head->name() == cx->names().allowContentIter)
expr->pn_left->name() == cx->names().allowContentIter)
{
allowSelfHostedIter = true;
}
@ -8252,8 +8316,9 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
return false;
break;
case ParseNodeKind::PropertyName:
case ParseNodeKind::PosHolder:
MOZ_FALLTHROUGH_ASSERT("Should never try to emit ParseNodeKind::PosHolder");
MOZ_FALLTHROUGH_ASSERT("Should never try to emit ParseNodeKind::PosHolder or ::Property");
default:
MOZ_ASSERT(0);

Просмотреть файл

@ -808,6 +808,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
bool isRestParameter(ParseNode* pn);
MOZ_MUST_USE bool emitArguments(ParseNode* pn, bool callop, bool spread);
MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue);
MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn);
MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn);

Просмотреть файл

@ -347,8 +347,10 @@ ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result)
case ParseNodeKind::Comma:
case ParseNodeKind::Array:
case ParseNodeKind::Object:
case ParseNodeKind::PropertyName:
case ParseNodeKind::Dot:
case ParseNodeKind::Elem:
case ParseNodeKind::Arguments:
case ParseNodeKind::Call:
case ParseNodeKind::Name:
case ParseNodeKind::TemplateString:
@ -1253,7 +1255,10 @@ FoldElement(JSContext* cx, ParseNode** nodePtr, PerHandlerParser<FullParseHandle
// 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());
@ -1409,8 +1414,9 @@ FoldCall(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& par
{
MOZ_ASSERT(node->isKind(ParseNodeKind::Call) ||
node->isKind(ParseNodeKind::SuperCall) ||
node->isKind(ParseNodeKind::New) ||
node->isKind(ParseNodeKind::TaggedTemplate));
MOZ_ASSERT(node->isArity(PN_LIST));
MOZ_ASSERT(node->isArity(PN_BINARY));
// Don't fold a parenthesized callable component in an invocation, as this
// might cause a different |this| value to be used, changing semantics:
@ -1423,10 +1429,26 @@ FoldCall(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& par
// assertEq(obj.f``, "obj");
//
// See bug 537673 and bug 1182373.
ParseNode** listp = &node->pn_head;
if ((*listp)->isInParens())
listp = &(*listp)->pn_next;
ParseNode** pn_callee = &node->pn_left;
if (node->isKind(ParseNodeKind::New) || !(*pn_callee)->isInParens()) {
if (!Fold(cx, pn_callee, parser))
return false;
}
ParseNode** pn_args = &node->pn_right;
if (!Fold(cx, pn_args, parser))
return false;
return true;
}
static bool
FoldArguments(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
{
MOZ_ASSERT(node->isKind(ParseNodeKind::Arguments));
MOZ_ASSERT(node->isArity(PN_LIST));
ParseNode** listp = &node->pn_head;
for (; *listp; listp = &(*listp)->pn_next) {
if (!Fold(cx, listp, parser))
return false;
@ -1482,14 +1504,14 @@ 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);
@ -1642,7 +1664,6 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser<FullParseHandler>& parser)
case ParseNodeKind::InstanceOf:
case ParseNodeKind::In:
case ParseNodeKind::Comma:
case ParseNodeKind::New:
case ParseNodeKind::Array:
case ParseNodeKind::Object:
case ParseNodeKind::StatementList:
@ -1694,10 +1715,14 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser<FullParseHandler>& parser)
return FoldAdd(cx, pnp, parser);
case ParseNodeKind::Call:
case ParseNodeKind::New:
case ParseNodeKind::SuperCall:
case ParseNodeKind::TaggedTemplate:
return FoldCall(cx, pn, parser);
case ParseNodeKind::Arguments:
return FoldArguments(cx, pn, parser);
case ParseNodeKind::Switch:
case ParseNodeKind::Colon:
case ParseNodeKind::Assign:
@ -1777,6 +1802,9 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser<FullParseHandler>& parser)
MOZ_ASSERT(pn->isArity(PN_NAME));
return Fold(cx, &pn->pn_expr, parser);
case ParseNodeKind::PropertyName:
MOZ_CRASH("unreachable, handled by ::Dot");
case ParseNodeKind::Dot:
return FoldDottedProperty(cx, pn, parser);

Просмотреть файл

@ -275,16 +275,20 @@ class FullParseHandler
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) {
@ -660,8 +664,12 @@ class FullParseHandler
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) {
@ -735,13 +743,8 @@ class FullParseHandler
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) {

Просмотреть файл

@ -13,15 +13,15 @@
using namespace js;
using namespace js::frontend;
IfEmitter::IfEmitter(BytecodeEmitter* bce, Kind kind)
using mozilla::Maybe;
BranchEmitterBase::BranchEmitterBase(BytecodeEmitter* bce, Kind kind)
: bce_(bce),
thenDepth_(0),
kind_(kind)
#ifdef DEBUG
, pushed_(0),
calculatedPushed_(false),
state_(State::Start)
#endif
{}
IfEmitter::IfEmitter(BytecodeEmitter* bce, Kind kind)
: BranchEmitterBase(bce, kind)
{}
IfEmitter::IfEmitter(BytecodeEmitter* bce)
@ -29,11 +29,8 @@ IfEmitter::IfEmitter(BytecodeEmitter* bce)
{}
bool
IfEmitter::emitIfInternal(SrcNoteType type)
BranchEmitterBase::emitThenInternal(SrcNoteType type)
{
MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome());
MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing());
// The end of TDZCheckCache for cond for else-if.
if (kind_ == Kind::MayContainLexicalAccessInBranch)
tdzCache_.reset();
@ -61,7 +58,7 @@ IfEmitter::emitIfInternal(SrcNoteType type)
}
void
IfEmitter::calculateOrCheckPushed()
BranchEmitterBase::calculateOrCheckPushed()
{
#ifdef DEBUG
if (!calculatedPushed_) {
@ -74,46 +71,7 @@ IfEmitter::calculateOrCheckPushed()
}
bool
IfEmitter::emitThen()
{
MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf);
if (!emitIfInternal(SRC_IF))
return false;
#ifdef DEBUG
state_ = State::Then;
#endif
return true;
}
bool
IfEmitter::emitCond()
{
MOZ_ASSERT(state_ == State::Start);
if (!emitIfInternal(SRC_COND))
return false;
#ifdef DEBUG
state_ = State::Cond;
#endif
return true;
}
bool
IfEmitter::emitThenElse()
{
MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf);
if (!emitIfInternal(SRC_IF_ELSE))
return false;
#ifdef DEBUG
state_ = State::ThenElse;
#endif
return true;
}
bool
IfEmitter::emitElseInternal()
BranchEmitterBase::emitElseInternal()
{
calculateOrCheckPushed();
@ -138,57 +96,17 @@ IfEmitter::emitElseInternal()
// Restore stack depth of the then part.
bce_->stackDepth = thenDepth_;
#ifdef DEBUG
state_ = State::Else;
#endif
return true;
}
bool
IfEmitter::emitElse()
{
MOZ_ASSERT(state_ == State::ThenElse || state_ == State::Cond);
if (!emitElseInternal())
return false;
// Enclose else-branch with TDZCheckCache.
if (kind_ == Kind::MayContainLexicalAccessInBranch)
tdzCache_.emplace(bce_);
#ifdef DEBUG
state_ = State::Else;
#endif
return true;
}
bool
IfEmitter::emitElseIf()
BranchEmitterBase::emitEndInternal()
{
MOZ_ASSERT(state_ == State::ThenElse);
if (!emitElseInternal())
return false;
// Enclose cond for else-if with TDZCheckCache.
if (kind_ == Kind::MayContainLexicalAccessInBranch)
tdzCache_.emplace(bce_);
#ifdef DEBUG
state_ = State::ElseIf;
#endif
return true;
}
bool
IfEmitter::emitEnd()
{
MOZ_ASSERT(state_ == State::Then || state_ == State::Else);
// If there was an else part for the last branch, jumpAroundThen_ is
// already fixed up when emitting the else part.
MOZ_ASSERT_IF(state_ == State::Then, jumpAroundThen_.offset != -1);
MOZ_ASSERT_IF(state_ == State::Else, jumpAroundThen_.offset == -1);
// The end of TDZCheckCache for then or else-clause.
if (kind_ == Kind::MayContainLexicalAccessInBranch) {
MOZ_ASSERT(tdzCache_.isSome());
@ -208,6 +126,106 @@ IfEmitter::emitEnd()
if (!bce_->emitJumpTargetAndPatch(jumpsAroundElse_))
return false;
return true;
}
bool
IfEmitter::emitIf(const Maybe<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
@ -216,4 +234,65 @@ IfEmitter::emitEnd()
InternalIfEmitter::InternalIfEmitter(BytecodeEmitter* bce)
: IfEmitter(bce, Kind::NoLexicalAccessInBranch)
{
#ifdef DEBUG
// Skip emitIf (see the comment above InternalIfEmitter declaration).
state_ = State::If;
#endif
}
CondEmitter::CondEmitter(BytecodeEmitter* bce)
: BranchEmitterBase(bce, Kind::MayContainLexicalAccessInBranch)
{}
bool
CondEmitter::emitCond()
{
MOZ_ASSERT(state_ == State::Start);
#ifdef DEBUG
state_ = State::Cond;
#endif
return true;
}
bool
CondEmitter::emitThenElse()
{
MOZ_ASSERT(state_ == State::Cond);
if (!emitThenInternal(SRC_COND))
return false;
#ifdef DEBUG
state_ = State::ThenElse;
#endif
return true;
}
bool
CondEmitter::emitElse()
{
MOZ_ASSERT(state_ == State::ThenElse);
if (!emitElseInternal())
return false;
#ifdef DEBUG
state_ = State::Else;
#endif
return true;
}
bool
CondEmitter::emitEnd()
{
MOZ_ASSERT(state_ == State::Else);
MOZ_ASSERT(jumpAroundThen_.offset == -1);
if (!emitEndInternal())
return false;
#ifdef DEBUG
state_ = State::End;
#endif
return true;
}

Просмотреть файл

@ -21,58 +21,23 @@ namespace frontend {
struct BytecodeEmitter;
// Class for emitting bytecode for blocks like if-then-else.
//
// This class can be used to emit single if-then-else block, or cascading
// else-if blocks.
//
// Usage: (check for the return value is omitted for simplicity)
//
// `if (cond) then_block`
// IfEmitter ifThen(this);
// emit(cond);
// ifThen.emitThen();
// emit(then_block);
// ifThen.emitEnd();
//
// `if (cond) then_block else else_block`
// IfEmitter ifThenElse(this);
// emit(cond);
// ifThenElse.emitThenElse();
// emit(then_block);
// ifThenElse.emitElse();
// emit(else_block);
// ifThenElse.emitEnd();
//
// `if (c1) b1 else if (c2) b2 else if (c3) b3 else b4`
// IfEmitter ifThenElse(this);
// emit(c1);
// ifThenElse.emitThenElse();
// emit(b1);
// ifThenElse.emitElseIf();
// emit(c2);
// ifThenElse.emitThenElse();
// emit(b2);
// ifThenElse.emitElseIf();
// emit(c3);
// ifThenElse.emitThenElse();
// emit(b3);
// ifThenElse.emitElse();
// emit(b4);
// ifThenElse.emitEnd();
//
// `cond ? then_expr : else_expr`
// IfEmitter condElse(this);
// emit(cond);
// condElse.emitCond();
// emit(then_block);
// condElse.emitElse();
// emit(else_block);
// condElse.emitEnd();
//
class MOZ_STACK_CLASS IfEmitter
class MOZ_STACK_CLASS BranchEmitterBase
{
public:
protected:
BytecodeEmitter* bce_;
// Jump around the then clause, to the beginning of the else clause.
JumpList jumpAroundThen_;
// Jump around the else clause, to the end of the entire branch.
JumpList jumpsAroundElse_;
// The stack depth before emitting the then block.
// Used for restoring stack depth before emitting the else block.
// Also used for assertion to make sure then and else blocks pushed the
// same number of values.
int32_t thenDepth_ = 0;
// Whether the then-clause, the else-clause, or else-if condition may
// contain declaration or access to lexical variables, which means they
// should have their own TDZCheckCache. Basically TDZCheckCache should be
@ -92,92 +57,25 @@ class MOZ_STACK_CLASS IfEmitter
// inside then-clause, else-clause, nor else-if condition.
NoLexicalAccessInBranch
};
private:
BytecodeEmitter* bce_;
// Jump around the then clause, to the beginning of the else clause.
JumpList jumpAroundThen_;
// Jump around the else clause, to the end of the entire branch.
JumpList jumpsAroundElse_;
// The stack depth before emitting the then block.
// Used for restoring stack depth before emitting the else block.
// Also used for assertion to make sure then and else blocks pushed the
// same number of values.
int32_t thenDepth_;
Kind kind_;
mozilla::Maybe<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`.
@ -193,11 +91,129 @@ class MOZ_STACK_CLASS IfEmitter
return -pushed_;
}
#endif
};
private:
MOZ_MUST_USE bool emitIfInternal(SrcNoteType type);
void calculateOrCheckPushed();
MOZ_MUST_USE bool emitElseInternal();
// Class for emitting bytecode for blocks like if-then-else.
//
// This class can be used to emit single if-then-else block, or cascading
// else-if blocks.
//
// Usage: (check for the return value is omitted for simplicity)
//
// `if (cond) then_block`
// IfEmitter ifThen(this);
// ifThen.emitIf(Some(offset_of_if));
// emit(cond);
// ifThen.emitThen();
// emit(then_block);
// ifThen.emitEnd();
//
// `if (cond) then_block else else_block`
// IfEmitter ifThenElse(this);
// ifThen.emitIf(Some(offset_of_if));
// emit(cond);
// ifThenElse.emitThenElse();
// emit(then_block);
// ifThenElse.emitElse();
// emit(else_block);
// ifThenElse.emitEnd();
//
// `if (c1) b1 else if (c2) b2 else if (c3) b3 else b4`
// IfEmitter ifThenElse(this);
// ifThen.emitIf(Some(offset_of_if));
// emit(c1);
// ifThenElse.emitThenElse();
// emit(b1);
// ifThenElse.emitElseIf(Some(offset_of_if));
// emit(c2);
// ifThenElse.emitThenElse();
// emit(b2);
// ifThenElse.emitElseIf(Some(offset_of_if));
// emit(c3);
// ifThenElse.emitThenElse();
// emit(b3);
// ifThenElse.emitElse();
// emit(b4);
// ifThenElse.emitEnd();
//
class MOZ_STACK_CLASS IfEmitter : public BranchEmitterBase
{
protected:
#ifdef DEBUG
// The state of this emitter.
//
// +-------+ emitIf +----+
// | Start |------->| If |-+
// +-------+ +----+ |
// |
// +--------------------+
// |
// v emitThen +------+ emitEnd +-----+
// +->+--------->| Then |---------------------------->+-------->| End |
// ^ | +------+ ^ +-----+
// | | |
// | | |
// | | |
// | | emitThenElse +----------+ emitElse +------+ |
// | +------------->| ThenElse |-+--------->| Else |-+
// | +----------+ | +------+
// | |
// | | emitElseIf +--------+
// | +----------->| ElseIf |-+
// | +--------+ |
// | |
// +------------------------------------------------------+
enum class State {
// The initial state.
Start,
// After calling emitIf.
If,
// After calling emitThen.
Then,
// After calling emitThenElse.
ThenElse,
// After calling emitElse.
Else,
// After calling emitElseIf.
ElseIf,
// After calling emitEnd.
End
};
State state_ = State::Start;
#endif
protected:
// For InternalIfEmitter.
IfEmitter(BytecodeEmitter* bce, Kind kind);
public:
explicit IfEmitter(BytecodeEmitter* bce);
// `ifPos` is the offset in the source code for the character below:
//
// if ( cond ) { ... } else if ( cond2 ) { ... }
// ^ ^
// | |
// | ifPos for emitElseIf
// |
// ifPos for emitIf
//
// Can be Nothing() if not available.
MOZ_MUST_USE bool emitIf(const mozilla::Maybe<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
@ -205,12 +221,83 @@ class MOZ_STACK_CLASS IfEmitter
//
// See the comments above NoLexicalAccessInBranch for more details when to use
// this instead of IfEmitter.
// Compared to IfEmitter, this class doesn't have emitIf method, given that
// it doesn't have syntactic `if`, and also the `cond` value can be already
// on the stack.
//
// Usage: (check for the return value is omitted for simplicity)
//
// `if (cond) then_block else else_block` (effectively)
// emit(cond);
// InternalIfEmitter ifThenElse(this);
// ifThenElse.emitThenElse();
// emit(then_block);
// ifThenElse.emitElse();
// emit(else_block);
// ifThenElse.emitEnd();
//
class MOZ_STACK_CLASS InternalIfEmitter : public IfEmitter
{
public:
explicit InternalIfEmitter(BytecodeEmitter* bce);
};
// Class for emitting bytecode for conditional expression.
//
// Usage: (check for the return value is omitted for simplicity)
//
// `cond ? then_expr : else_expr`
// CondEmitter condElse(this);
// condElse.emitCond();
// emit(cond);
// condElse.emitThenElse();
// emit(then_expr);
// condElse.emitElse();
// emit(else_expr);
// condElse.emitEnd();
//
class MOZ_STACK_CLASS CondEmitter : public BranchEmitterBase
{
#ifdef DEBUG
// The state of this emitter.
//
// +-------+ emitCond +------+ emitThenElse +----------+
// | Start |--------->| Cond |------------->| ThenElse |-+
// +-------+ +------+ +----------+ |
// |
// +-----------------+
// |
// | emitElse +------+ emitEnd +-----+
// +--------->| Else |-------->| End |
// +------+ +-----+
enum class State {
// The initial state.
Start,
// After calling emitCond.
Cond,
// After calling emitThenElse.
ThenElse,
// After calling emitElse.
Else,
// After calling emitEnd.
End
};
State state_ = State::Start;
#endif
public:
explicit CondEmitter(BytecodeEmitter* bce);
MOZ_MUST_USE bool emitCond();
MOZ_MUST_USE bool emitThenElse();
MOZ_MUST_USE bool emitElse();
MOZ_MUST_USE bool emitEnd();
};
} /* namespace frontend */
} /* namespace js */

Просмотреть файл

@ -75,11 +75,11 @@ class NameResolver
bool nameExpression(ParseNode* n, bool* foundName) {
switch (n->getKind()) {
case ParseNodeKind::Dot:
if (!nameExpression(n->expr(), foundName))
if (!nameExpression(n->pn_left, foundName))
return false;
if (!*foundName)
return true;
return appendPropertyReference(n->pn_atom);
return appendPropertyReference(n->pn_right->pn_atom);
case ParseNodeKind::Name:
*foundName = true;
@ -315,17 +315,17 @@ class NameResolver
bool resolveTaggedTemplate(ParseNode* node, HandleAtom prefix) {
MOZ_ASSERT(node->isKind(ParseNodeKind::TaggedTemplate));
ParseNode* element = node->pn_head;
ParseNode* tag = node->pn_left;
// The list head is a leading expression, e.g. |tag| in |tag`foo`|,
// The leading expression, e.g. |tag| in |tag`foo`|,
// that might contain functions.
if (!resolve(element, prefix))
if (!resolve(tag, prefix))
return false;
// Next is the callsite object node. This node only contains
// The callsite object node is first. This node only contains
// internal strings or undefined and an array -- no user-controlled
// expressions.
element = element->pn_next;
ParseNode* element = node->pn_right->pn_head;
#ifdef DEBUG
{
MOZ_ASSERT(element->isKind(ParseNodeKind::CallSiteObj));
@ -697,9 +697,6 @@ class NameResolver
case ParseNodeKind::Pow:
case ParseNodeKind::Pipeline:
case ParseNodeKind::Comma:
case ParseNodeKind::New:
case ParseNodeKind::Call:
case ParseNodeKind::SuperCall:
case ParseNodeKind::Array:
case ParseNodeKind::StatementList:
case ParseNodeKind::ParamsBody:
@ -733,11 +730,32 @@ class NameResolver
break;
case ParseNodeKind::TaggedTemplate:
MOZ_ASSERT(cur->isArity(PN_LIST));
MOZ_ASSERT(cur->isArity(PN_BINARY));
if (!resolveTaggedTemplate(cur, prefix))
return false;
break;
case ParseNodeKind::New:
case ParseNodeKind::Call:
case ParseNodeKind::SuperCall:
MOZ_ASSERT(cur->isArity(PN_BINARY));
if (!resolve(cur->pn_left, prefix))
return false;
if (!resolve(cur->pn_right, prefix))
return false;
break;
// Handles the arguments for new/call/supercall, but does _not_ handle
// the Arguments node used by tagged template literals, since that is
// special-cased inside of resolveTaggedTemplate.
case ParseNodeKind::Arguments:
MOZ_ASSERT(cur->isArity(PN_LIST));
for (ParseNode* element = cur->pn_head; element; element = element->pn_next) {
if (!resolve(element, prefix))
return false;
}
break;
// Import/export spec lists contain import/export specs containing
// only pairs of names. Alternatively, an export spec lists may
// contain a single export batch specifier.
@ -766,12 +784,12 @@ class NameResolver
}
case ParseNodeKind::Dot:
MOZ_ASSERT(cur->isArity(PN_NAME));
MOZ_ASSERT(cur->isArity(PN_BINARY));
// Super prop nodes do not have a meaningful LHS
if (cur->as<PropertyAccess>().isSuper())
break;
if (!resolve(cur->expr(), prefix))
if (!resolve(cur->pn_left, prefix))
return false;
break;
@ -810,6 +828,7 @@ class NameResolver
case ParseNodeKind::ExportSpec: // by ParseNodeKind::ExportSpecList
case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate
case ParseNodeKind::ClassNames: // by ParseNodeKind::Class
case ParseNodeKind::PropertyName: // by ParseNodeKind::Dot
MOZ_CRASH("should have been handled by a parent node");
case ParseNodeKind::Limit: // invalid sentinel value

Просмотреть файл

@ -216,6 +216,21 @@ UnaryNode::dump(GenericPrinter& out, int indent)
void
BinaryNode::dump(GenericPrinter& out, int indent)
{
if (isKind(ParseNodeKind::Dot)) {
out.put("(.");
DumpParseTree(pn_right, out, indent + 2);
out.putChar(' ');
if (as<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;
@ -288,10 +303,7 @@ DumpName(GenericPrinter& out, const CharT* s, size_t len)
void
NameNode::dump(GenericPrinter& out, int indent)
{
if (isKind(ParseNodeKind::Name) || isKind(ParseNodeKind::Dot)) {
if (isKind(ParseNodeKind::Dot))
out.put("(.");
if (isKind(ParseNodeKind::Name) || isKind(ParseNodeKind::PropertyName)) {
if (!pn_atom) {
out.put("#<null name>");
} else if (getOp() == JSOP_GETARG && pn_atom->length() == 0) {
@ -306,15 +318,6 @@ NameNode::dump(GenericPrinter& out, int indent)
else
DumpName(out, pn_atom->twoByteChars(nogc), pn_atom->length());
}
if (isKind(ParseNodeKind::Dot)) {
out.putChar(' ');
if (as<PropertyAccess>().isSuper())
out.put("super");
else
DumpParseTree(expr(), out, indent + 2);
out.printf(")");
}
return;
}

Просмотреть файл

@ -55,6 +55,7 @@ class ObjectBox;
F(PostIncrement) \
F(PreDecrement) \
F(PostDecrement) \
F(PropertyName) \
F(Dot) \
F(Elem) \
F(Array) \
@ -63,6 +64,7 @@ class ObjectBox;
F(Label) \
F(Object) \
F(Call) \
F(Arguments) \
F(Name) \
F(ObjectPropertyName) \
F(ComputedName) \
@ -371,9 +373,8 @@ IsTypeofKind(ParseNodeKind kind)
* PostIncrement,
* PreDecrement,
* PostDecrement
* New list pn_head: list of ctor, arg1, arg2, ... argN
* pn_count: 1 + N (where N is number of args)
* ctor is a MEMBER expr
* New binary pn_left: ctor expression on the left of the (
* pn_right: Arguments
* DeleteName unary pn_kid: Name expr
* DeleteProp unary pn_kid: Dot expr
* DeleteElem unary pn_kid: Elem expr
@ -382,13 +383,15 @@ IsTypeofKind(ParseNodeKind kind)
* for a more-specific PNK_DELETE* unless constant
* folding (or a similar parse tree manipulation) has
* occurred
* Dot name pn_expr: MEMBER expr to left of .
* pn_atom: name to right of .
* PropertyName name pn_atom: property name being accessed
* Dot binary pn_left: MEMBER expr to left of .
* pn_right: PropertyName to right of .
* Elem binary pn_left: MEMBER expr to left of [
* pn_right: expr between [ and ]
* Call list pn_head: list of call, arg1, arg2, ... argN
* pn_count: 1 + N (where N is number of args)
* call is a MEMBER expr naming a callable object
* Call binary pn_left: callee expression on the left of the (
* pn_right: Arguments
* Arguments list pn_head: list of arg1, arg2, ... argN
* pn_count: N (where N is number of args)
* Array list pn_head: list of pn_count array element exprs
* [,,] holes are represented by Elision nodes
* pn_xflags: PN_ENDCOMMA if extra comma at end
@ -408,8 +411,9 @@ IsTypeofKind(ParseNodeKind kind)
* list
* TemplateString pn_atom: template string atom
nullary pn_op: JSOP_NOP
* TaggedTemplate pn_head: list of call, call site object, arg1, arg2, ... argN
* list pn_count: 2 + N (N is the number of substitutions)
* TaggedTemplate pn_left: tag expression
* binary pn_right: Arguments, with the first being the
* call site object, then arg1, arg2, ... argN
* CallSiteObj list pn_head: a Array node followed by
* list of pn_count - 1 TemplateString nodes
* RegExp nullary pn_objbox: RegExp model object
@ -421,7 +425,7 @@ IsTypeofKind(ParseNodeKind kind)
*
* This, unary pn_kid: '.this' Name if function `this`, else nullptr
* SuperBase unary pn_kid: '.this' Name
*
* SuperCall binary pn_left: SuperBase pn_right: Arguments
* SetThis binary pn_left: '.this' Name, pn_right: SuperCall
*
* LexicalScope scope pn_u.scope.bindings: scope bindings
@ -571,8 +575,7 @@ class ParseNode
FunctionBox* funbox; /* function object */
};
ParseNode* expr; /* module or function body, var
initializer, argument default, or
base object of ParseNodeKind::Dot */
initializer, or argument default */
} name;
struct {
LexicalScope::Data* bindings;
@ -1176,30 +1179,33 @@ class RegExpLiteral : public NullaryNode
}
};
class PropertyAccess : public ParseNode
class PropertyAccess : public BinaryNode
{
public:
PropertyAccess(ParseNode* lhs, PropertyName* name, uint32_t begin, uint32_t end)
: ParseNode(ParseNodeKind::Dot, JSOP_NOP, PN_NAME, TokenPos(begin, end))
/*
* PropertyAccess nodes can have any expression/'super' as left-hand
* side, but the name must be a ParseNodeKind::PropertyName node.
*/
PropertyAccess(ParseNode* lhs, ParseNode* name, uint32_t begin, uint32_t end)
: BinaryNode(ParseNodeKind::Dot, JSOP_NOP, TokenPos(begin, end), lhs, name)
{
MOZ_ASSERT(lhs != nullptr);
MOZ_ASSERT(name != nullptr);
pn_u.name.expr = lhs;
pn_u.name.atom = name;
}
static bool test(const ParseNode& node) {
bool match = node.isKind(ParseNodeKind::Dot);
MOZ_ASSERT_IF(match, node.isArity(PN_NAME));
MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
MOZ_ASSERT_IF(match, node.pn_right->isKind(ParseNodeKind::PropertyName));
return match;
}
ParseNode& expression() const {
return *pn_u.name.expr;
return *pn_u.binary.left;
}
PropertyName& name() const {
return *pn_u.name.atom->asPropertyName();
return *pn_u.binary.right->pn_atom->asPropertyName();
}
bool isSuper() const {

Просмотреть файл

@ -3397,13 +3397,13 @@ GeneralParser<ParseHandler, CharT>::addExprAndGetNextTemplStrToken(YieldHandling
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))
@ -3411,10 +3411,10 @@ GeneralParser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling,
if (tt != TokenKind::TemplateHead)
break;
if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt))
if (!addExprAndGetNextTemplStrToken(yieldHandling, tagArgsList, &tt))
return false;
}
handler.setEndPosition(nodeList, callSiteObjNode);
handler.setEndPosition(tagArgsList, callSiteObjNode);
return true;
}
@ -8643,24 +8643,27 @@ GeneralParser<ParseHandler, CharT>::assignExprWithoutYieldOrAwait(YieldHandling
}
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;
@ -8669,18 +8672,18 @@ GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, No
Node argNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError);
if (!argNode)
return false;
return null();
if (spread) {
argNode = handler.newSpread(begin, argNode);
if (!argNode)
return false;
return null();
}
handler.addList(listNode, argNode);
handler.addList(argsList, argNode);
bool matched;
if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::Operand))
return false;
return null();
if (!matched)
break;
@ -8693,8 +8696,8 @@ GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, No
MUST_MATCH_TOKEN_MOD(TokenKind::Rp, TokenStream::Operand, JSMSG_PAREN_AFTER_ARGS);
handler.setEndPosition(listNode, pos().end);
return true;
handler.setEndPosition(argsList, pos().end);
return argsList;
}
bool
@ -8740,20 +8743,27 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
if (!ctorExpr)
return null();
lhs = handler.newNewExpression(newBegin, ctorExpr);
if (!lhs)
return null();
bool matched;
if (!tokenStream.matchToken(&matched, TokenKind::Lp))
return null();
bool isSpread = false;
Node args;
if (matched) {
bool isSpread = false;
if (!argumentList(yieldHandling, lhs, &isSpread))
return null();
if (isSpread)
handler.setOp(lhs, JSOP_SPREADNEW);
args = argumentList(yieldHandling, &isSpread);
} else {
args = handler.newArguments(pos());
}
if (!args)
return null();
lhs = handler.newNewExpression(newBegin, ctorExpr, args);
if (!lhs)
return null();
if (isSpread)
handler.setOp(lhs, JSOP_SPREADNEW);
}
} else if (tt == TokenKind::Super) {
Node thisName = newThisName();
@ -8790,7 +8800,12 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
error(JSMSG_BAD_SUPERPROP, "property");
return null();
}
nextMember = handler.newPropertyAccess(lhs, field, pos().end);
Node name = handler.newPropertyName(field, pos());
if (!name)
return null();
nextMember = handler.newPropertyAccess(lhs, name);
if (!nextMember)
return null();
} else {
@ -8826,15 +8841,16 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
return null();
}
nextMember = handler.newSuperCall(lhs);
if (!nextMember)
return null();
// Despite the fact that it's impossible to have |super()| in a
// generator, we still inherit the yieldHandling of the
// memberExpression, per spec. Curious.
bool isSpread = false;
if (!argumentList(yieldHandling, nextMember, &isSpread))
Node args = argumentList(yieldHandling, &isSpread);
if (!args)
return null();
nextMember = handler.newSuperCall(lhs, args);
if (!nextMember)
return null();
if (isSpread)
@ -8853,13 +8869,6 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
return null();
}
TokenPos nextMemberPos = pos();
nextMember = tt == TokenKind::Lp
? handler.newCall(nextMemberPos)
: handler.newTaggedTemplate(nextMemberPos);
if (!nextMember)
return null();
JSOp op = JSOP_CALL;
bool maybeAsyncArrow = false;
if (PropertyName* prop = handler.maybeDottedProperty(lhs)) {
@ -8901,13 +8910,11 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
}
}
handler.setBeginPosition(nextMember, lhs);
handler.addList(nextMember, lhs);
if (tt == TokenKind::Lp) {
bool isSpread = false;
PossibleError* asyncPossibleError = maybeAsyncArrow ? possibleError : nullptr;
if (!argumentList(yieldHandling, nextMember, &isSpread, asyncPossibleError))
Node args = argumentList(yieldHandling, &isSpread, asyncPossibleError);
if (!args)
return null();
if (isSpread) {
if (op == JSOP_EVAL)
@ -8917,8 +8924,20 @@ GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
else
op = JSOP_SPREADCALL;
}
nextMember = handler.newCall(lhs, args);
if (!nextMember)
return null();
} else {
if (!taggedTemplate(yieldHandling, nextMember, tt))
Node args = handler.newArguments(pos());
if (!args)
return null();
if (!taggedTemplate(yieldHandling, args, tt))
return null();
nextMember = handler.newTaggedTemplate(lhs, args);
if (!nextMember)
return null();
}
handler.setOp(nextMember, op);

Просмотреть файл

@ -569,8 +569,12 @@ class MOZ_STACK_CLASS PerHandlerParser
bool isValidSimpleAssignmentTarget(Node node,
FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) {
return handler.newPropertyAccess(expr, key, end);
Node newPropertyName(PropertyName* key, const TokenPos& pos) {
return handler.newPropertyName(key, pos);
}
Node newPropertyAccess(Node expr, Node key) {
return handler.newPropertyAccess(expr, key);
}
FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
@ -1155,7 +1159,7 @@ class MOZ_STACK_CLASS GeneralParser
Node condition(InHandling inHandling, YieldHandling yieldHandling);
bool argumentList(YieldHandling yieldHandling, Node listNode, bool* isSpread,
Node argumentList(YieldHandling yieldHandling, bool* isSpread,
PossibleError* possibleError = nullptr);
Node destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling,
TokenKind tt);

Просмотреть файл

@ -248,9 +248,11 @@ class SyntaxParseHandler
MOZ_MUST_USE bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; }
void addArrayElement(Node literal, Node element) { }
Node newCall(const TokenPos& pos) { return NodeFunctionCall; }
Node newSuperCall(Node callee) { return NodeGeneric; }
Node newTaggedTemplate(const TokenPos& pos) { return NodeGeneric; }
Node newArguments(const TokenPos& pos) { return NodeGeneric; }
Node newCall(Node callee, Node args) { return NodeFunctionCall; }
Node newSuperCall(Node callee, Node args) { return NodeGeneric; }
Node newTaggedTemplate(Node callee, Node args) { return NodeGeneric; }
Node newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; }
Node newClassMethodList(uint32_t begin) { return NodeGeneric; }
@ -332,8 +334,12 @@ class SyntaxParseHandler
}
Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) {
lastAtom = key;
Node newPropertyName(PropertyName* name, const TokenPos& pos) {
lastAtom = name;
return NodeGeneric;
}
Node newPropertyAccess(Node expr, Node key) {
return NodeDottedProperty;
}
@ -428,7 +434,7 @@ class SyntaxParseHandler
list == NodeFunctionCall);
}
Node newNewExpression(uint32_t begin, Node ctor) {
Node newNewExpression(uint32_t begin, Node ctor, Node args) {
return NodeGeneric;
}

Просмотреть файл

@ -2778,9 +2778,21 @@ ForegroundUpdateKinds(AllocKinds kinds)
void
GCRuntime::updateTypeDescrObjects(MovingTracer* trc, Zone* zone)
{
// We need to update each type descriptor object and any objects stored in
// its slots, since some of these contain array objects which also need to
// be updated.
zone->typeDescrObjects().sweep();
for (auto r = zone->typeDescrObjects().all(); !r.empty(); r.popFront())
UpdateCellPointers(trc, r.front());
for (auto r = zone->typeDescrObjects().all(); !r.empty(); r.popFront()) {
NativeObject* obj = &r.front()->as<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

Просмотреть файл

@ -33,7 +33,12 @@ function assertOffsetColumns(code, expectedBpts, expectedOrdering = null) {
// Set breakpoints everywhere and call the function.
const dbg = new Debugger;
const script = dbg.addDebuggee(global).makeDebuggeeValue(global.f).script;
let debuggeeFn = dbg.addDebuggee(global).makeDebuggeeValue(global.f);
if (debuggeeFn.isBoundFunction) {
debuggeeFn = debuggeeFn.boundTargetFunction;
}
const { script } = debuggeeFn;
for (const offset of script.getAllColumnOffsets()) {
assertEq(offset.lineNumber, 1);
assertEq(offset.columnNumber < execCode.length, true);

Просмотреть файл

@ -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]);
// 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]]);

Просмотреть файл

@ -83,3 +83,58 @@ assertOffsetColumns(
"function f(n) { do { print(n); } while(false); }",
" ^ ^ ^",
);
// getColumnOffsets correctly places the part of normal ::Dot node with identifier root.
assertOffsetColumns(
"var args = [];\n" +
"var obj = { base: { a(){ return { b(){} }; } } };\n" +
"function f(n) { obj.base.a().b(...args); }",
" ^ ^ ^ ^",
"0 2 1 3",
);
// getColumnOffsets correctly places the part of normal ::Dot node with "this" root.
assertOffsetColumns(
"var args = [];\n" +
"var obj = { base: { a(){ return { b(){} }; } } };\n" +
"var f = function() { this.base.a().b(...args); }.bind(obj);",
" ^ ^ ^ ^",
"0 2 1 3",
);
// getColumnOffsets correctly places the part of normal ::Dot node with "super" base.
assertOffsetColumns(
"var args = [];\n" +
"var obj = { base: { a(){ return { b(){} }; } } };\n" +
"var f = { __proto__: obj, f(n) { super.base.a().b(...args); } }.f;",
" ^ ^ ^ ^",
"0 2 1 3",
);
// getColumnOffsets correctly places the part of normal ::Dot node with other base.
assertOffsetColumns(
"var args = [];\n" +
"var obj = { base: { a(){ return { b(){} }; } } };\n" +
"function f(n) { (0, obj).base.a().b(...args); }",
" ^ ^ ^ ^ ^ ^",
"0 1 2 4 3 5",
);
// getColumnOffsets correctly places the part of folded ::Elem node.
assertOffsetColumns(
"var args = [];\n" +
"var obj = { base: { a(){ return { b(){} }; } } };\n" +
// Constant folding makes the static string behave like a dot access.
"function f(n) { obj.base['a']()['b'](...args); }",
" ^ ^ ^ ^",
"0 2 1 3",
);
// getColumnOffsets correctly places the part of computed ::Elem node.
assertOffsetColumns(
"var args = [], a = 'a', b = 'b';\n" +
"var obj = { base: { a(){ return { b(){} }; } } };\n" +
"function f(n) { obj.base[a]()[b](...args); }",
" ^ ^ ^^ ^",
"0 1 3 2 4",
);

Просмотреть файл

@ -0,0 +1,5 @@
v = new new TypedObject.StructType({
f: TypedObject.Any
})
gczeal(14);
var lfOffThreadGlobal = newGlobal();

Просмотреть файл

@ -5014,6 +5014,127 @@ ICBinaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
return tailCallVM(DoBinaryArithFallbackInfo, masm);
}
//
// 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
//

Просмотреть файл

@ -6,6 +6,7 @@
#include "jit/BaselineInspector.h"
#include "mozilla/Array.h"
#include "mozilla/DebugOnly.h"
#include "jit/BaselineIC.h"
@ -412,20 +413,144 @@ BaselineInspector::expectedResultType(jsbytecode* pc)
}
}
// Whether a baseline stub kind is suitable for a double comparison that
// converts its operands to doubles.
// Return the MIRtype corresponding to the guard the reader is pointing
// to, and ensure that afterwards the reader is pointing to the next op
// (consume operands).
//
// An expected parameter is provided to allow GuardType to check we read
// the guard from the expected operand in debug builds.
static bool
CanUseDoubleCompare(ICStub::Kind kind)
GuardType(CacheIRReader& reader, mozilla::Array<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.
static bool
CanUseInt32Compare(ICStub::Kind kind)
// 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)
{
return kind == ICStub::Compare_Int32 || kind == ICStub::Compare_Int32WithBoolean;
CacheIRReader reader(stub->stubInfo());
// Two element array to allow parsing the guards
// in whichever order they appear.
mozilla::Array<MIRType, 2> guards = { MIRType::None, MIRType::None };
// Parse out two guards
if (!GuardType(reader, guards))
return MCompare::Compare_Unknown;
if (!GuardType(reader, guards))
return MCompare::Compare_Unknown;
// The lhs and rhs ids are asserted in
// CompareIRGenerator::tryAttachStub.
MIRType lhs_guard = guards[0];
MIRType rhs_guard = guards[1];
if (lhs_guard == rhs_guard)
{
if (lhs_guard == MIRType::Int32)
return MCompare::Compare_Int32;
if (lhs_guard == MIRType::Double)
return MCompare::Compare_Double;
return MCompare::Compare_Unknown;
}
if ((lhs_guard == MIRType::Int32 && rhs_guard == MIRType::Boolean) ||
(lhs_guard == MIRType::Boolean && rhs_guard == MIRType::Int32))
{
// RHS is converting
if (rhs_guard == MIRType::Boolean)
return MCompare::Compare_Int32MaybeCoerceRHS;
return MCompare::Compare_Int32MaybeCoerceLHS;
}
if ((lhs_guard == MIRType::Double && rhs_guard == MIRType::Undefined) ||
(lhs_guard == MIRType::Undefined && rhs_guard == MIRType::Double))
{
// RHS is converting
if (rhs_guard == MIRType::Undefined)
return MCompare::Compare_DoubleMaybeCoerceRHS;
return MCompare::Compare_DoubleMaybeCoerceLHS;
}
return MCompare::Compare_Unknown;
}
static bool
CoercingCompare(MCompare::CompareType type)
{
//Prefer the coercing types if they exist, otherwise just use first's type.
if (type == MCompare::Compare_DoubleMaybeCoerceLHS ||
type == MCompare::Compare_DoubleMaybeCoerceRHS ||
type == MCompare::Compare_Int32MaybeCoerceLHS ||
type == MCompare::Compare_Int32MaybeCoerceRHS)
return true;
return false;
}
static MCompare::CompareType
CompatibleType(MCompare::CompareType first, MCompare::CompareType second)
{
// Caller should have dealt with this case.
MOZ_ASSERT(first != second);
//Prefer the coercing types if they exist, otherwise just use first's type.
if (CoercingCompare(first))
return first;
if (CoercingCompare(second))
return second;
return first;
}
MCompare::CompareType
@ -442,37 +567,19 @@ BaselineInspector::expectedCompareType(jsbytecode* pc)
return MCompare::Compare_Unknown;
}
if (CanUseInt32Compare(first->kind()) && (!second || CanUseInt32Compare(second->kind()))) {
ICCompare_Int32WithBoolean* coerce =
first->isCompare_Int32WithBoolean()
? first->toCompare_Int32WithBoolean()
: ((second && second->isCompare_Int32WithBoolean())
? second->toCompare_Int32WithBoolean()
: nullptr);
if (coerce) {
return coerce->lhsIsInt32()
? MCompare::Compare_Int32MaybeCoerceRHS
: MCompare::Compare_Int32MaybeCoerceLHS;
}
return MCompare::Compare_Int32;
}
MCompare::CompareType first_type = ParseCacheIRStubForCompareType(first->toCacheIR_Regular());
if (!second)
return first_type;
if (CanUseDoubleCompare(first->kind()) && (!second || CanUseDoubleCompare(second->kind()))) {
ICCompare_NumberWithUndefined* coerce =
first->isCompare_NumberWithUndefined()
? first->toCompare_NumberWithUndefined()
: (second && second->isCompare_NumberWithUndefined())
? second->toCompare_NumberWithUndefined()
: nullptr;
if (coerce) {
return coerce->lhsIsUndefined()
? MCompare::Compare_DoubleMaybeCoerceLHS
: MCompare::Compare_DoubleMaybeCoerceRHS;
}
return MCompare::Compare_Double;
}
MCompare::CompareType second_type = ParseCacheIRStubForCompareType(second->toCacheIR_Regular());
return MCompare::Compare_Unknown;
if (first_type == MCompare::Compare_Unknown || second_type == MCompare::Compare_Unknown)
return MCompare::Compare_Unknown;
if (first_type == second_type)
return first_type;
return CompatibleType(first_type, second_type);
}
static bool

Просмотреть файл

@ -37,6 +37,33 @@ CacheIRWriter::assertSameCompartment(JSObject* obj) {
assertSameCompartmentDebugOnly(cx_, obj);
}
StubField
CacheIRWriter::readStubFieldForIon(uint32_t offset, StubField::Type type) const
{
size_t index = 0;
size_t currentOffset = 0;
// If we've seen an offset earlier than this before, we know we can start the search
// there at least, otherwise, we start the search from the beginning.
if (lastOffset_ < offset) {
currentOffset = lastOffset_;
index = lastIndex_;
}
while (currentOffset != offset) {
currentOffset += StubField::sizeInBytes(stubFields_[index].type());
index++;
MOZ_ASSERT(index < stubFields_.length());
}
MOZ_ASSERT(stubFields_[index].type() == type);
lastOffset_ = currentOffset;
lastIndex_ = index;
return stubFields_[index];
}
IRGenerator::IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
ICState::Mode mode)
: writer(cx),
@ -4813,6 +4840,122 @@ CompareIRGenerator::tryAttachStrictDifferentTypes(ValOperandId lhsId, ValOperand
return true;
}
bool
CompareIRGenerator::tryAttachInt32(ValOperandId lhsId, ValOperandId rhsId)
{
if ((!lhsVal_.isInt32() && !lhsVal_.isBoolean()) ||
(!rhsVal_.isInt32() && !rhsVal_.isBoolean()))
{
return false;
}
Int32OperandId lhsIntId = lhsVal_.isBoolean() ? writer.guardIsBoolean(lhsId)
: writer.guardIsInt32(lhsId);
Int32OperandId rhsIntId = rhsVal_.isBoolean() ? writer.guardIsBoolean(rhsId)
: writer.guardIsInt32(rhsId);
// Strictly different types should have been handed by
// tryAttachStrictDifferentTypes
MOZ_ASSERT_IF(op_ == JSOP_STRICTEQ || op_ == JSOP_STRICTNE,
lhsVal_.isInt32() == rhsVal_.isInt32());
writer.compareInt32Result(op_, lhsIntId, rhsIntId);
writer.returnFromIC();
trackAttached(lhsVal_.isBoolean() ? "Boolean" : "Int32");
return true;
}
bool
CompareIRGenerator::tryAttachNumber(ValOperandId lhsId, ValOperandId rhsId)
{
if (!cx_->runtime()->jitSupportsFloatingPoint)
return false;
if (!lhsVal_.isNumber() || !rhsVal_.isNumber())
return false;
writer.guardIsNumber(lhsId);
writer.guardIsNumber(rhsId);
writer.compareDoubleResult(op_, lhsId, rhsId);
writer.returnFromIC();
trackAttached("Number");
return true;
}
bool
CompareIRGenerator::tryAttachObjectUndefined(ValOperandId lhsId, ValOperandId rhsId)
{
if (!(lhsVal_.isNullOrUndefined() && rhsVal_.isObject()) &&
!(rhsVal_.isNullOrUndefined() && lhsVal_.isObject()))
return false;
if (op_ != JSOP_EQ && op_ != JSOP_NE)
return false;
ValOperandId obj = rhsVal_.isObject() ? rhsId : lhsId;
ValOperandId undefOrNull = rhsVal_.isObject() ? lhsId : rhsId;
writer.guardIsNullOrUndefined(undefOrNull);
ObjOperandId objOperand = writer.guardIsObject(obj);
writer.compareObjectUndefinedNullResult(op_, objOperand);
writer.returnFromIC();
trackAttached("ObjectUndefined");
return true;
}
// Handle NumberUndefined comparisons
bool
CompareIRGenerator::tryAttachNumberUndefined(ValOperandId lhsId, ValOperandId rhsId)
{
if (!(lhsVal_.isUndefined() && rhsVal_.isNumber()) &&
!(rhsVal_.isUndefined() && lhsVal_.isNumber()))
{
return false;
}
lhsVal_.isNumber() ? writer.guardIsNumber(lhsId) : writer.guardIsUndefined(lhsId);
rhsVal_.isNumber() ? writer.guardIsNumber(rhsId) : writer.guardIsUndefined(rhsId);
// Comparing a number with undefined will always be true for NE/STRICTNE,
// and always be false for other compare ops.
writer.loadBooleanResult(op_ == JSOP_NE || op_ == JSOP_STRICTNE);
writer.returnFromIC();
trackAttached("NumberUndefined");
return true;
}
// Handle {null/undefined} x {null,undefined} equality comparisons
bool
CompareIRGenerator::tryAttachNullUndefined(ValOperandId lhsId, ValOperandId rhsId)
{
if (!lhsVal_.isNullOrUndefined() || !rhsVal_.isNullOrUndefined())
return false;
if (op_ == JSOP_EQ || op_ == JSOP_NE) {
writer.guardIsNullOrUndefined(lhsId);
writer.guardIsNullOrUndefined(rhsId);
// Sloppy equality means we actually only care about the op:
writer.loadBooleanResult(op_ == JSOP_EQ);
trackAttached("SloppyNullUndefined");
} else {
// Strict equality only hits this branch, and only in the
// undef {!,=}== undef and null {!,=}== null cases.
// The other cases should have hit compareStrictlyDifferentTypes.
MOZ_ASSERT(lhsVal_.isNull() == rhsVal_.isNull());
lhsVal_.isNull() ? writer.guardIsNull(lhsId) : writer.guardIsUndefined(lhsId);
rhsVal_.isNull() ? writer.guardIsNull(rhsId) : writer.guardIsUndefined(rhsId);
writer.loadBooleanResult(op_ == JSOP_STRICTEQ);
trackAttached("StrictNullUndefinedEquality");
}
writer.returnFromIC();
return true;
}
bool
CompareIRGenerator::tryAttachStub()
{
@ -4823,6 +4966,12 @@ CompareIRGenerator::tryAttachStub()
AutoAssertNoPendingException aanpe(cx_);
constexpr uint8_t lhsIndex = 0;
constexpr uint8_t rhsIndex = 1;
static_assert(lhsIndex == 0 && rhsIndex == 1,
"Indexes relied upon by baseline inspector");
ValOperandId lhsId(writer.setInputOperandId(0));
ValOperandId rhsId(writer.setInputOperandId(1));
@ -4833,13 +4982,30 @@ CompareIRGenerator::tryAttachStub()
return true;
if (tryAttachSymbol(lhsId, rhsId))
return true;
if (tryAttachObjectUndefined(lhsId, rhsId))
return true;
if (tryAttachStrictDifferentTypes(lhsId, rhsId))
return true;
trackAttached(IRGenerator::NotAttached);
return false;
// This should come after strictDifferent types to
// allow it to only handle sloppy equality.
if (tryAttachNullUndefined(lhsId, rhsId))
return true;
}
// This should preceed the Int32/Number cases to allow
// them to not concern themselves with handling undefined
// or null.
if (tryAttachNumberUndefined(lhsId, rhsId))
return true;
// We want these to be last, to allow us to bypass the
// strictly-different-types cases in the below attachment code
if (tryAttachInt32(lhsId, rhsId))
return true;
if (tryAttachNumber(lhsId, rhsId))
return true;
trackAttached(IRGenerator::NotAttached);
return false;
}
@ -4851,6 +5017,7 @@ CompareIRGenerator::trackAttached(const char* name)
if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
sp.valueProperty("lhs", lhsVal_);
sp.valueProperty("rhs", rhsVal_);
sp.opcodeProperty("op", op_);
}
#endif
}

Просмотреть файл

@ -182,6 +182,9 @@ extern const char* CacheKindNames[];
_(GuardIsObject) \
_(GuardIsObjectOrNull) \
_(GuardIsNullOrUndefined) \
_(GuardIsNotNullOrUndefined) \
_(GuardIsNull) \
_(GuardIsUndefined) \
_(GuardIsBoolean) \
_(GuardIsString) \
_(GuardIsSymbol) \
@ -324,6 +327,9 @@ extern const char* CacheKindNames[];
_(CompareStringResult) \
_(CompareObjectResult) \
_(CompareSymbolResult) \
_(CompareInt32Result) \
_(CompareDoubleResult) \
_(CompareObjectUndefinedNullResult) \
\
_(CallPrintString) \
_(Breakpoint) \
@ -438,6 +444,10 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
static const size_t MaxStubDataSizeInBytes = 20 * sizeof(uintptr_t);
bool tooLarge_;
// Basic caching to avoid quadatic lookup behaviour in readStubFieldForIon.
mutable uint32_t lastOffset_;
mutable uint32_t lastIndex_;
void assertSameCompartment(JSObject*);
void writeOp(CacheOp op) {
@ -501,7 +511,9 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
nextInstructionId_(0),
numInputOperands_(0),
stubDataSize_(0),
tooLarge_(false)
tooLarge_(false),
lastOffset_(0),
lastIndex_(0)
{}
bool failed() const { return buffer_.oom() || tooLarge_; }
@ -551,10 +563,7 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
// This should not be used when compiling Baseline code, as Baseline code
// shouldn't bake in stub values.
StubField readStubFieldForIon(size_t i, StubField::Type type) const {
MOZ_ASSERT(stubFields_[i].type() == type);
return stubFields_[i];
}
StubField readStubFieldForIon(uint32_t offset, StubField::Type type) const;
ObjOperandId guardIsObject(ValOperandId val) {
writeOpWithOperandId(CacheOp::GuardIsObject, val);
@ -600,6 +609,15 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
void guardIsNullOrUndefined(ValOperandId val) {
writeOpWithOperandId(CacheOp::GuardIsNullOrUndefined, val);
}
void guardIsNotNullOrUndefined(ValOperandId val) {
writeOpWithOperandId(CacheOp::GuardIsNotNullOrUndefined, val);
}
void guardIsNull(ValOperandId val) {
writeOpWithOperandId(CacheOp::GuardIsNull, val);
}
void guardIsUndefined(ValOperandId val) {
writeOpWithOperandId(CacheOp::GuardIsUndefined, val);
}
void guardShape(ObjOperandId obj, Shape* shape) {
MOZ_ASSERT(shape);
writeOpWithOperandId(CacheOp::GuardShape, obj);
@ -1257,11 +1275,25 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
writeOperandId(rhs);
buffer_.writeByte(uint32_t(op));
}
void compareObjectUndefinedNullResult(uint32_t op, ObjOperandId object) {
writeOpWithOperandId(CacheOp::CompareObjectUndefinedNullResult, object);
buffer_.writeByte(uint32_t(op));
}
void compareSymbolResult(uint32_t op, SymbolOperandId lhs, SymbolOperandId rhs) {
writeOpWithOperandId(CacheOp::CompareSymbolResult, lhs);
writeOperandId(rhs);
buffer_.writeByte(uint32_t(op));
}
void compareInt32Result(uint32_t op, Int32OperandId lhs, Int32OperandId rhs) {
writeOpWithOperandId(CacheOp::CompareInt32Result, lhs);
writeOperandId(rhs);
buffer_.writeByte(uint32_t(op));
}
void compareDoubleResult(uint32_t op, ValOperandId lhs, ValOperandId rhs) {
writeOpWithOperandId(CacheOp::CompareDoubleResult, lhs);
writeOperandId(rhs);
buffer_.writeByte(uint32_t(op));
}
void callPrintString(const char* str) {
writeOp(CacheOp::CallPrintString);
@ -1816,6 +1848,11 @@ class MOZ_RAII CompareIRGenerator : public IRGenerator
bool tryAttachObject(ValOperandId lhsId, ValOperandId rhsId);
bool tryAttachSymbol(ValOperandId lhsId, ValOperandId rhsId);
bool tryAttachStrictDifferentTypes(ValOperandId lhsId, ValOperandId rhsId);
bool tryAttachInt32(ValOperandId lhsId, ValOperandId rhsId);
bool tryAttachNumber(ValOperandId lhsId, ValOperandId rhsId);
bool tryAttachNumberUndefined(ValOperandId lhsId, ValOperandId rhsId);
bool tryAttachObjectUndefined(ValOperandId lhsId, ValOperandId rhsId);
bool tryAttachNullUndefined(ValOperandId lhsId, ValOperandId rhsId);
void trackAttached(const char* name);

Просмотреть файл

@ -93,7 +93,7 @@ CacheRegisterAllocator::useValueRegister(MacroAssembler& masm, ValOperandId op)
// Load a value operand directly into a float register. Caller must have
// guarded isNumber on the provided val.
void
CacheRegisterAllocator::loadDouble(MacroAssembler& masm, ValOperandId op, FloatRegister dest)
CacheRegisterAllocator::ensureDoubleRegister(MacroAssembler& masm, ValOperandId op, FloatRegister dest)
{
OperandLocation& loc = operandLocations_[op.id()];
@ -125,12 +125,12 @@ CacheRegisterAllocator::loadDouble(MacroAssembler& masm, ValOperandId op, FloatR
case OperandLocation::PayloadStack:
case OperandLocation::PayloadReg:
case OperandLocation::Uninitialized:
MOZ_CRASH("Unhandled operand type in loadDouble");
MOZ_CRASH("Unhandled operand type in ensureDoubleRegister");
return;
}
masm.jump(&done);
masm.bind(&failure);
masm.assumeUnreachable("Missing guard allowed non-number to hit loadDouble");
masm.assumeUnreachable("Missing guard allowed non-number to hit ensureDoubleRegister");
masm.bind(&done);
}
@ -1377,6 +1377,63 @@ CacheIRCompiler::emitGuardIsNullOrUndefined()
return true;
}
bool
CacheIRCompiler::emitGuardIsNotNullOrUndefined()
{
ValOperandId inputId = reader.valOperandId();
JSValueType knownType = allocator.knownType(inputId);
if (knownType == JSVAL_TYPE_UNDEFINED || knownType == JSVAL_TYPE_NULL)
return false;
ValueOperand input = allocator.useValueRegister(masm, inputId);
FailurePath* failure;
if (!addFailurePath(&failure))
return false;
masm.branchTestNull(Assembler::Equal, input, failure->label());
masm.branchTestUndefined(Assembler::Equal, input, failure->label());
return true;
}
bool
CacheIRCompiler::emitGuardIsNull()
{
ValOperandId inputId = reader.valOperandId();
JSValueType knownType = allocator.knownType(inputId);
if (knownType == JSVAL_TYPE_NULL)
return true;
ValueOperand input = allocator.useValueRegister(masm, inputId);
FailurePath* failure;
if (!addFailurePath(&failure))
return false;
Label success;
masm.branchTestNull(Assembler::NotEqual, input, failure->label());
return true;
}
bool
CacheIRCompiler::emitGuardIsUndefined()
{
ValOperandId inputId = reader.valOperandId();
JSValueType knownType = allocator.knownType(inputId);
if (knownType == JSVAL_TYPE_UNDEFINED)
return true;
ValueOperand input = allocator.useValueRegister(masm, inputId);
FailurePath* failure;
if (!addFailurePath(&failure))
return false;
masm.branchTestUndefined(Assembler::NotEqual, input, failure->label());
return true;
}
bool
CacheIRCompiler::emitGuardIsObjectOrNull()
{
@ -1994,8 +2051,8 @@ CacheIRCompiler::emitDoubleAddResult()
// Float register must be preserved. The BinaryArith ICs use
// the fact that baseline has them available, as well as fixed temps on
// LBinaryCache.
allocator.loadDouble(masm, reader.valOperandId(), FloatReg0);
allocator.loadDouble(masm, reader.valOperandId(), FloatReg1);
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
masm.addDouble(FloatReg1, FloatReg0);
masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
@ -2007,8 +2064,8 @@ CacheIRCompiler::emitDoubleSubResult()
{
AutoOutputRegister output(*this);
allocator.loadDouble(masm, reader.valOperandId(), FloatReg0);
allocator.loadDouble(masm, reader.valOperandId(), FloatReg1);
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
masm.subDouble(FloatReg1, FloatReg0);
masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
@ -2020,8 +2077,8 @@ CacheIRCompiler::emitDoubleMulResult()
{
AutoOutputRegister output(*this);
allocator.loadDouble(masm, reader.valOperandId(), FloatReg0);
allocator.loadDouble(masm, reader.valOperandId(), FloatReg1);
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
masm.mulDouble(FloatReg1, FloatReg0);
masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
@ -2033,8 +2090,8 @@ CacheIRCompiler::emitDoubleDivResult()
{
AutoOutputRegister output(*this);
allocator.loadDouble(masm, reader.valOperandId(), FloatReg0);
allocator.loadDouble(masm, reader.valOperandId(), FloatReg1);
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
masm.divDouble(FloatReg1, FloatReg0);
masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
@ -2047,8 +2104,8 @@ CacheIRCompiler::emitDoubleModResult()
AutoOutputRegister output(*this);
AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
allocator.loadDouble(masm, reader.valOperandId(), FloatReg0);
allocator.loadDouble(masm, reader.valOperandId(), FloatReg1);
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
LiveRegisterSet save(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
masm.PushRegsInMask(save);
@ -3092,6 +3149,79 @@ CacheIRCompiler::emitCompareSymbolResult()
return emitComparePointerResultShared(true);
}
bool
CacheIRCompiler::emitCompareInt32Result()
{
AutoOutputRegister output(*this);
Register left = allocator.useRegister(masm, reader.int32OperandId());
Register right = allocator.useRegister(masm, reader.int32OperandId());
JSOp op = reader.jsop();
Label ifTrue, done;
masm.branch32(JSOpToCondition(op, /* signed = */true), left, right, &ifTrue);
masm.moveValue(BooleanValue(false), output.valueReg());
masm.jump(&done);
masm.bind(&ifTrue);
masm.moveValue(BooleanValue(true), output.valueReg());
masm.bind(&done);
return true;
}
bool
CacheIRCompiler::emitCompareDoubleResult()
{
AutoOutputRegister output(*this);
FailurePath* failure;
if (!addFailurePath(&failure))
return false;
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
JSOp op = reader.jsop();
Label done, ifTrue;
masm.branchDouble(JSOpToDoubleCondition(op), FloatReg0, FloatReg1, &ifTrue);
masm.moveValue(BooleanValue(false), output.valueReg());
masm.jump(&done);
masm.bind(&ifTrue);
masm.moveValue(BooleanValue(true), output.valueReg());
masm.bind(&done);
return true;
}
bool
CacheIRCompiler::emitCompareObjectUndefinedNullResult()
{
AutoOutputRegister output(*this);
Register obj = allocator.useRegister(masm, reader.objOperandId());
JSOp op = reader.jsop();
FailurePath* failure;
if (!addFailurePath(&failure))
return false;
if (op == JSOP_STRICTEQ || op == JSOP_STRICTNE) {
// obj !== undefined/null for all objects.
masm.moveValue(BooleanValue(op == JSOP_STRICTNE), output.valueReg());
} else {
MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE);
AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
Label done, emulatesUndefined;
masm.branchIfObjectEmulatesUndefined(obj, scratch, failure->label(), &emulatesUndefined);
masm.moveValue(BooleanValue(op == JSOP_NE), output.valueReg());
masm.jump(&done);
masm.bind(&emulatesUndefined);
masm.moveValue(BooleanValue(op == JSOP_EQ), output.valueReg());
masm.bind(&done);
}
return true;
}
bool
CacheIRCompiler::emitCallPrintString()
{

Просмотреть файл

@ -19,6 +19,9 @@ namespace jit {
#define CACHE_IR_SHARED_OPS(_) \
_(GuardIsObject) \
_(GuardIsNullOrUndefined) \
_(GuardIsNotNullOrUndefined) \
_(GuardIsNull) \
_(GuardIsUndefined) \
_(GuardIsObjectOrNull) \
_(GuardIsBoolean) \
_(GuardIsString) \
@ -93,6 +96,9 @@ namespace jit {
_(LoadObjectTruthyResult) \
_(CompareObjectResult) \
_(CompareSymbolResult) \
_(CompareInt32Result) \
_(CompareDoubleResult) \
_(CompareObjectUndefinedNullResult) \
_(ArrayJoinResult) \
_(CallPrintString) \
_(Breakpoint) \
@ -488,8 +494,10 @@ class MOZ_RAII CacheRegisterAllocator
Register defineRegister(MacroAssembler& masm, TypedOperandId typedId);
ValueOperand defineValueRegister(MacroAssembler& masm, ValOperandId val);
// Loads (and unboxes) a value into a float register (caller guarded)
void loadDouble(MacroAssembler&, ValOperandId, FloatRegister);
// Loads (potentially coercing) and unboxes a value into a float register
// This is infallible, as there should have been a previous guard
// to ensure the ValOperandId is already a number.
void ensureDoubleRegister(MacroAssembler&, ValOperandId, FloatRegister);
// Returns |val|'s JSValueType or JSVAL_TYPE_UNKNOWN.
JSValueType knownType(ValOperandId val) const;
@ -634,8 +642,6 @@ class MOZ_RAII CacheIRCompiler
// sizeof(stubType)
uint32_t stubDataOffset_;
uint32_t nextStubField_;
enum class StubFieldPolicy {
Address,
Constant
@ -651,7 +657,6 @@ class MOZ_RAII CacheIRCompiler
liveFloatRegs_(FloatRegisterSet::All()),
mode_(mode),
stubDataOffset_(stubDataOffset),
nextStubField_(0),
stubFieldPolicy_(policy)
{
MOZ_ASSERT(!writer.failed());
@ -716,15 +721,12 @@ class MOZ_RAII CacheIRCompiler
uintptr_t readStubWord(uint32_t offset, StubField::Type type) {
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
MOZ_ASSERT((offset % sizeof(uintptr_t)) == 0);
// We use nextStubField_ to access the data as it's stored in an as-of-yet
// unpacked vector, and so using the offset can be incorrect where the index
// would change as a result of packing.
return writer_.readStubFieldForIon(nextStubField_++, type).asWord();
return writer_.readStubFieldForIon(offset, type).asWord();
}
uint64_t readStubInt64(uint32_t offset, StubField::Type type) {
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
MOZ_ASSERT((offset % sizeof(uintptr_t)) == 0);
return writer_.readStubFieldForIon(nextStubField_++, type).asInt64();
return writer_.readStubFieldForIon(offset, type).asInt64();
}
int32_t int32StubField(uint32_t offset) {
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -5830,7 +5830,7 @@ IonBuilder::jsop_compare(JSOp op, MDefinition* left, MDefinition* right)
return Ok();
}
MOZ_TRY(compareTrySharedStub(&emitted, left, right));
MOZ_TRY(compareTryBinaryStub(&emitted, left, right));
if (emitted)
return Ok();
@ -6025,7 +6025,7 @@ IonBuilder::compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDe
}
AbortReasonOr<Ok>
IonBuilder::compareTrySharedStub(bool* emitted, MDefinition* left, MDefinition* right)
IonBuilder::compareTryBinaryStub(bool* emitted, MDefinition* left, MDefinition* right)
{
MOZ_ASSERT(*emitted == false);
@ -6037,9 +6037,7 @@ IonBuilder::compareTrySharedStub(bool* emitted, MDefinition* left, MDefinition*
if (JSOp(*pc) == JSOP_CASE)
return Ok();
trackOptimizationAttempt(TrackedStrategy::Compare_SharedCache);
MBinarySharedStub* stub = MBinarySharedStub::New(alloc(), left, right);
MBinaryCache* stub = MBinaryCache::New(alloc(), left, right);
current->add(stub);
current->push(stub);
MOZ_TRY(resumeAfter(stub));

Просмотреть файл

@ -332,7 +332,7 @@ class IonBuilder
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,

Просмотреть файл

@ -537,8 +537,21 @@ IonCacheIRCompiler::init()
allocator.initInputLocation(1, ic->rhs());
break;
}
case CacheKind::Compare: {
IonCompareIC *ic = ic_->asCompareIC();
ValueOperand output = ic->output();
available.add(output);
liveRegs_.emplace(ic->liveRegs());
outputUnchecked_.emplace(TypedOrValueRegister(output));
MOZ_ASSERT(numInputs == 2);
allocator.initInputLocation(0, ic->lhs());
allocator.initInputLocation(1, ic->rhs());
break;
}
case CacheKind::Call:
case CacheKind::Compare:
case CacheKind::TypeOf:
case CacheKind::ToBool:
case CacheKind::GetIntrinsic:
@ -579,8 +592,6 @@ IonCacheIRCompiler::compile()
allocator.nextOp();
} while (reader.more());
MOZ_RELEASE_ASSERT(nextStubField_ == writer_.numStubFields());
masm.assumeUnreachable("Should have returned from IC");
// Done emitting the main IC code. Now emit the failure paths.

Просмотреть файл

@ -60,8 +60,9 @@ IonIC::scratchRegisterForEntryJump()
return asUnaryArithIC()->output().scratchReg();
case CacheKind::BinaryArith:
return asBinaryArithIC()->output().scratchReg();
case CacheKind::Call:
case CacheKind::Compare:
return asCompareIC()->output().scratchReg();
case CacheKind::Call:
case CacheKind::TypeOf:
case CacheKind::ToBool:
case CacheKind::GetIntrinsic:
@ -629,6 +630,83 @@ IonBinaryArithIC::update(JSContext* cx, HandleScript outerScript, IonBinaryArith
return true;
}
/* static */ bool
IonCompareIC::update(JSContext* cx, HandleScript outerScript, IonCompareIC* ic,
HandleValue lhs, HandleValue rhs, MutableHandleValue res)
{
IonScript* ionScript = outerScript->ionScript();
RootedScript script(cx, ic->script());
jsbytecode* pc = ic->pc();
JSOp op = JSOp(*pc);
// Case operations in a CONDSWITCH are performing strict equality.
if (op == JSOP_CASE)
op = JSOP_STRICTEQ;
// Don't pass lhs/rhs directly, we need the original values when
// generating stubs.
RootedValue lhsCopy(cx, lhs);
RootedValue rhsCopy(cx, rhs);
// Perform the compare operation.
bool out;
switch (op) {
case JSOP_LT:
if (!LessThan(cx, &lhsCopy, &rhsCopy, &out))
return false;
break;
case JSOP_LE:
if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
return false;
break;
case JSOP_GT:
if (!GreaterThan(cx, &lhsCopy, &rhsCopy, &out))
return false;
break;
case JSOP_GE:
if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
return false;
break;
case JSOP_EQ:
if (!LooselyEqual<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()
{

Просмотреть файл

@ -65,6 +65,7 @@ class IonGetIteratorIC;
class IonHasOwnIC;
class IonInIC;
class IonInstanceOfIC;
class IonCompareIC;
class IonUnaryArithIC;
class IonBinaryArithIC;
@ -174,6 +175,10 @@ class IonIC
MOZ_ASSERT(kind_ == CacheKind::InstanceOf);
return (IonInstanceOfIC*)this;
}
IonCompareIC* asCompareIC() {
MOZ_ASSERT(kind_ == CacheKind::Compare);
return (IonCompareIC*)this;
}
IonUnaryArithIC* asUnaryArithIC() {
MOZ_ASSERT(kind_ == CacheKind::UnaryArith);
return (IonUnaryArithIC*)this;
@ -485,6 +490,33 @@ class IonInstanceOfIC : public IonIC
HandleValue lhs, HandleObject rhs, bool* attached);
};
class IonCompareIC : public IonIC
{
LiveRegisterSet liveRegs_;
TypedOrValueRegister lhs_;
TypedOrValueRegister rhs_;
ValueOperand output_;
public:
IonCompareIC(LiveRegisterSet liveRegs, TypedOrValueRegister lhs, TypedOrValueRegister rhs, ValueOperand output)
: IonIC(CacheKind::Compare),
liveRegs_(liveRegs),
lhs_(lhs),
rhs_(rhs),
output_(output)
{ }
LiveRegisterSet liveRegs() const { return liveRegs_; }
TypedOrValueRegister lhs() const { return lhs_; }
TypedOrValueRegister rhs() const { return rhs_; }
ValueOperand output() const { return output_; }
static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonCompareIC* stub,
HandleValue lhs, HandleValue rhs, MutableHandleValue res);
};
class IonUnaryArithIC : public IonIC
{
LiveRegisterSet liveRegs_;

Просмотреть файл

@ -1602,16 +1602,6 @@ MacroAssembler::loadDependentStringBase(Register str, Register dest)
loadPtr(Address(str, JSDependentString::offsetOfBase()), dest);
}
void
MacroAssembler::leaNewDependentStringBase(Register str, Register dest)
{
MOZ_ASSERT(str != dest);
// Spectre-safe because this is a newly allocated dependent string, thus we
// are certain of its type and the type of its base field.
computeEffectiveAddress(Address(str, JSDependentString::offsetOfBase()), dest);
}
void
MacroAssembler::storeDependentStringBase(Register base, Register str)
{
@ -1649,12 +1639,12 @@ MacroAssembler::loadStringChar(Register str, Register index, Register output, Re
// because a TwoByte rope might have a Latin1 child.
branchLatin1String(output, &isLatin1);
loadStringChars(output, scratch, CharEncoding::TwoByte);
load16ZeroExtend(BaseIndex(scratch, index, TimesTwo), output);
loadChar(scratch, index, output, CharEncoding::TwoByte);
jump(&done);
bind(&isLatin1);
loadStringChars(output, scratch, CharEncoding::Latin1);
load8ZeroExtend(BaseIndex(scratch, index, TimesOne), output);
loadChar(scratch, index, output, CharEncoding::Latin1);
bind(&done);
}
@ -1673,6 +1663,27 @@ MacroAssembler::loadStringIndexValue(Register str, Register dest, Label* fail)
rshift32(Imm32(JSString::INDEX_VALUE_SHIFT), dest);
}
void
MacroAssembler::loadChar(Register chars, Register index, Register dest, CharEncoding encoding,
int32_t offset/* = 0 */)
{
if (encoding == CharEncoding::Latin1)
loadChar(BaseIndex(chars, index, TimesOne, offset), dest, encoding);
else
loadChar(BaseIndex(chars, index, TimesTwo, offset), dest, encoding);
}
void
MacroAssembler::addToCharPtr(Register chars, Register index, CharEncoding encoding)
{
if (encoding == CharEncoding::Latin1) {
static_assert(sizeof(char) == 1, "Latin-1 string index shouldn't need scaling");
addPtr(index, chars);
} else {
computeEffectiveAddress(BaseIndex(chars, index, TimesTwo), chars);
}
}
void
MacroAssembler::typeOfObject(Register obj, Register scratch, Label* slow,
Label* isObject, Label* isCallable, Label* isUndefined)

Просмотреть файл

@ -2121,10 +2121,43 @@ class MacroAssembler : public MacroAssemblerSpecific
void loadDependentStringBase(Register str, Register dest);
void storeDependentStringBase(Register base, Register str);
void leaNewDependentStringBase(Register str, Register dest);
void loadStringIndexValue(Register str, Register dest, Label* fail);
/**
* Store the character in |src| to |dest|.
*/
template <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);

Просмотреть файл

@ -671,542 +671,6 @@ SharedStubInfo::outerScript(JSContext* cx)
}
//
// Compare_Fallback
//
static bool
DoCompareFallback(JSContext* cx, void* payload, ICCompare_Fallback* stub_, HandleValue lhs,
HandleValue rhs, MutableHandleValue ret)
{
SharedStubInfo info(cx, payload, stub_->icEntry());
ICStubCompiler::Engine engine = info.engine();
// This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICCompare_Fallback*> stub(engine, info.maybeFrame(), stub_);
jsbytecode* pc = info.pc();
JSOp op = JSOp(*pc);
FallbackICSpew(cx, stub, "Compare(%s)", CodeName[op]);
// Case operations in a CONDSWITCH are performing strict equality.
if (op == JSOP_CASE)
op = JSOP_STRICTEQ;
// Don't pass lhs/rhs directly, we need the original values when
// generating stubs.
RootedValue lhsCopy(cx, lhs);
RootedValue rhsCopy(cx, rhs);
// Perform the compare operation.
bool out;
switch (op) {
case JSOP_LT:
if (!LessThan(cx, &lhsCopy, &rhsCopy, &out))
return false;
break;
case JSOP_LE:
if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
return false;
break;
case JSOP_GT:
if (!GreaterThan(cx, &lhsCopy, &rhsCopy, &out))
return false;
break;
case JSOP_GE:
if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
return false;
break;
case JSOP_EQ:
if (!LooselyEqual<true>(cx, &lhsCopy, &rhsCopy, &out))
return false;
break;
case JSOP_NE:
if (!LooselyEqual<false>(cx, &lhsCopy, &rhsCopy, &out))
return false;
break;
case JSOP_STRICTEQ:
if (!StrictlyEqual<true>(cx, &lhsCopy, &rhsCopy, &out))
return false;
break;
case JSOP_STRICTNE:
if (!StrictlyEqual<false>(cx, &lhsCopy, &rhsCopy, &out))
return false;
break;
default:
MOZ_ASSERT_UNREACHABLE("Unhandled baseline compare op");
return false;
}
ret.setBoolean(out);
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
// Check to see if a new stub should be generated.
if (stub->numOptimizedStubs() >= ICCompare_Fallback::MAX_OPTIMIZED_STUBS) {
// TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
// But for now we just bail.
return true;
}
if (engine == ICStubEngine::Baseline && !JitOptions.disableCacheIR) {
RootedScript script(cx, info.outerScript(cx));
CompareIRGenerator gen(cx, script, pc, stub->state().mode(), op, lhs, rhs);
bool attached = false;
if (gen.tryAttachStub()) {
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
BaselineCacheIRStubKind::Regular,
engine, script, stub, &attached);
if (newStub)
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
return true;
}
}
// Try to generate new stubs.
if (lhs.isInt32() && rhs.isInt32()) {
JitSpew(JitSpew_BaselineIC, " Generating %s(Int32, Int32) stub", CodeName[op]);
ICCompare_Int32::Compiler compiler(cx, op, engine);
ICStub* int32Stub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
if (!int32Stub)
return false;
stub->addNewStub(int32Stub);
return true;
}
if (!cx->runtime()->jitSupportsFloatingPoint && (lhs.isNumber() || rhs.isNumber()))
return true;
if (lhs.isNumber() && rhs.isNumber()) {
JitSpew(JitSpew_BaselineIC, " Generating %s(Number, Number) stub", CodeName[op]);
// Unlink int32 stubs, it's faster to always use the double stub.
stub->unlinkStubsWithKind(cx, ICStub::Compare_Int32);
ICCompare_Double::Compiler compiler(cx, op, engine);
ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
if (!doubleStub)
return false;
stub->addNewStub(doubleStub);
return true;
}
if ((lhs.isNumber() && rhs.isUndefined()) ||
(lhs.isUndefined() && rhs.isNumber()))
{
JitSpew(JitSpew_BaselineIC, " Generating %s(%s, %s) stub", CodeName[op],
rhs.isUndefined() ? "Number" : "Undefined",
rhs.isUndefined() ? "Undefined" : "Number");
ICCompare_NumberWithUndefined::Compiler compiler(cx, op, engine, lhs.isUndefined());
ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
if (!doubleStub)
return false;
stub->addNewStub(doubleStub);
return true;
}
if (lhs.isBoolean() && rhs.isBoolean()) {
JitSpew(JitSpew_BaselineIC, " Generating %s(Boolean, Boolean) stub", CodeName[op]);
ICCompare_Boolean::Compiler compiler(cx, op, engine);
ICStub* booleanStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
if (!booleanStub)
return false;
stub->addNewStub(booleanStub);
return true;
}
if ((lhs.isBoolean() && rhs.isInt32()) || (lhs.isInt32() && rhs.isBoolean())) {
JitSpew(JitSpew_BaselineIC, " Generating %s(%s, %s) stub", CodeName[op],
rhs.isInt32() ? "Boolean" : "Int32",
rhs.isInt32() ? "Int32" : "Boolean");
ICCompare_Int32WithBoolean::Compiler compiler(cx, op, engine, lhs.isInt32());
ICStub* optStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
if (!optStub)
return false;
stub->addNewStub(optStub);
return true;
}
if (IsEqualityOp(op)) {
if (lhs.isString() && rhs.isString() && !stub->hasStub(ICStub::Compare_String)) {
JitSpew(JitSpew_BaselineIC, " Generating %s(String, String) stub", CodeName[op]);
ICCompare_String::Compiler compiler(cx, op, engine);
ICStub* stringStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
if (!stringStub)
return false;
stub->addNewStub(stringStub);
return true;
}
if (lhs.isSymbol() && rhs.isSymbol() && !stub->hasStub(ICStub::Compare_Symbol)) {
JitSpew(JitSpew_BaselineIC, " Generating %s(Symbol, Symbol) stub", CodeName[op]);
ICCompare_Symbol::Compiler compiler(cx, op, engine);
ICStub* symbolStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
if (!symbolStub)
return false;
stub->addNewStub(symbolStub);
return true;
}
if (lhs.isObject() && rhs.isObject()) {
MOZ_ASSERT(!stub->hasStub(ICStub::Compare_Object));
JitSpew(JitSpew_BaselineIC, " Generating %s(Object, Object) stub", CodeName[op]);
ICCompare_Object::Compiler compiler(cx, op, engine);
ICStub* objectStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
if (!objectStub)
return false;
stub->addNewStub(objectStub);
return true;
}
if (lhs.isNullOrUndefined() || rhs.isNullOrUndefined()) {
JitSpew(JitSpew_BaselineIC, " Generating %s(Null/Undef or X, Null/Undef or X) stub",
CodeName[op]);
bool lhsIsUndefined = lhs.isNullOrUndefined();
bool compareWithNull = lhs.isNull() || rhs.isNull();
ICCompare_ObjectWithUndefined::Compiler compiler(cx, op, engine,
lhsIsUndefined, compareWithNull);
ICStub* objectStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
if (!objectStub)
return false;
stub->addNewStub(objectStub);
return true;
}
}
stub->noteUnoptimizableAccess();
return true;
}
typedef bool (*DoCompareFallbackFn)(JSContext*, void*, ICCompare_Fallback*,
HandleValue, HandleValue, MutableHandleValue);
static const VMFunction DoCompareFallbackInfo =
FunctionInfo<DoCompareFallbackFn>(DoCompareFallback, "DoCompareFallback", TailCall,
PopValues(2));
bool
ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
{
MOZ_ASSERT(R0 == JSReturnOperand);
// Restore the tail call register.
EmitRestoreTailCallReg(masm);
// Ensure stack is fully synced for the expression decompiler.
masm.pushValue(R0);
masm.pushValue(R1);
// Push arguments.
masm.pushValue(R1);
masm.pushValue(R0);
masm.push(ICStubReg);
pushStubPayload(masm, R0.scratchReg());
return tailCallVM(DoCompareFallbackInfo, masm);
}
//
// Compare_String
//
bool
ICCompare_String::Compiler::generateStubCode(MacroAssembler& masm)
{
Label failure, restore;
masm.branchTestString(Assembler::NotEqual, R0, &failure);
masm.branchTestString(Assembler::NotEqual, R1, &failure);
MOZ_ASSERT(IsEqualityOp(op));
// left/right are part of R0/R1. Restore R0 and R1 in the failure case.
Register left = R0.scratchReg();
Register right = R1.scratchReg();
masm.unboxString(R0, left);
masm.unboxString(R1, right);
AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
Register scratchReg = regs.takeAny();
masm.compareStrings(op, left, right, scratchReg, &restore);
masm.tagValue(JSVAL_TYPE_BOOLEAN, scratchReg, R0);
EmitReturnFromIC(masm);
masm.bind(&restore);
masm.tagValue(JSVAL_TYPE_STRING, left, R0);
masm.tagValue(JSVAL_TYPE_STRING, right, R1);
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
//
// Compare_Symbol
//
bool
ICCompare_Symbol::Compiler::generateStubCode(MacroAssembler& masm)
{
Label failure;
masm.branchTestSymbol(Assembler::NotEqual, R0, &failure);
masm.branchTestSymbol(Assembler::NotEqual, R1, &failure);
MOZ_ASSERT(IsEqualityOp(op));
Register left = masm.extractSymbol(R0, ExtractTemp0);
Register right = masm.extractSymbol(R1, ExtractTemp1);
Label ifTrue;
masm.branchPtr(JSOpToCondition(op, /* signed = */true), left, right, &ifTrue);
masm.moveValue(BooleanValue(false), R0);
EmitReturnFromIC(masm);
masm.bind(&ifTrue);
masm.moveValue(BooleanValue(true), R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
//
// Compare_Boolean
//
bool
ICCompare_Boolean::Compiler::generateStubCode(MacroAssembler& masm)
{
Label failure;
masm.branchTestBoolean(Assembler::NotEqual, R0, &failure);
masm.branchTestBoolean(Assembler::NotEqual, R1, &failure);
Register left = masm.extractInt32(R0, ExtractTemp0);
Register right = masm.extractInt32(R1, ExtractTemp1);
// Compare payload regs of R0 and R1.
Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
masm.cmp32Set(cond, left, right, left);
// Box the result and return
masm.tagValue(JSVAL_TYPE_BOOLEAN, left, R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
//
// Compare_NumberWithUndefined
//
bool
ICCompare_NumberWithUndefined::Compiler::generateStubCode(MacroAssembler& masm)
{
ValueOperand numberOperand, undefinedOperand;
if (lhsIsUndefined) {
numberOperand = R1;
undefinedOperand = R0;
} else {
numberOperand = R0;
undefinedOperand = R1;
}
Label failure;
masm.branchTestNumber(Assembler::NotEqual, numberOperand, &failure);
masm.branchTestUndefined(Assembler::NotEqual, undefinedOperand, &failure);
// Comparing a number with undefined will always be true for NE/STRICTNE,
// and always be false for other compare ops.
masm.moveValue(BooleanValue(op == JSOP_NE || op == JSOP_STRICTNE), R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
//
// Compare_Object
//
bool
ICCompare_Object::Compiler::generateStubCode(MacroAssembler& masm)
{
Label failure;
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
masm.branchTestObject(Assembler::NotEqual, R1, &failure);
MOZ_ASSERT(IsEqualityOp(op));
Register left = masm.extractObject(R0, ExtractTemp0);
Register right = masm.extractObject(R1, ExtractTemp1);
Label ifTrue;
masm.branchPtr(JSOpToCondition(op, /* signed = */true), left, right, &ifTrue);
masm.moveValue(BooleanValue(false), R0);
EmitReturnFromIC(masm);
masm.bind(&ifTrue);
masm.moveValue(BooleanValue(true), R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
//
// Compare_ObjectWithUndefined
//
bool
ICCompare_ObjectWithUndefined::Compiler::generateStubCode(MacroAssembler& masm)
{
MOZ_ASSERT(IsEqualityOp(op));
ValueOperand objectOperand, undefinedOperand;
if (lhsIsUndefined) {
objectOperand = R1;
undefinedOperand = R0;
} else {
objectOperand = R0;
undefinedOperand = R1;
}
Label failure;
if (compareWithNull)
masm.branchTestNull(Assembler::NotEqual, undefinedOperand, &failure);
else
masm.branchTestUndefined(Assembler::NotEqual, undefinedOperand, &failure);
Label notObject;
masm.branchTestObject(Assembler::NotEqual, objectOperand, &notObject);
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(&notObject);
// 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)
{

Просмотреть файл

@ -1684,269 +1684,6 @@ class ICCompare_Fallback : public ICFallbackStub
};
};
class ICCompare_Int32 : public ICStub
{
friend class ICStubSpace;
explicit ICCompare_Int32(JitCode* stubCode)
: ICStub(ICStub::Compare_Int32, stubCode) {}
public:
// Compiler for this stub kind.
class Compiler : public ICMultiStubCompiler {
protected:
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
public:
Compiler(JSContext* cx, JSOp op, Engine engine)
: ICMultiStubCompiler(cx, ICStub::Compare_Int32, op, engine) {}
ICStub* getStub(ICStubSpace* space) override {
return newStub<ICCompare_Int32>(space, getStubCode());
}
};
};
class ICCompare_Double : public ICStub
{
friend class ICStubSpace;
explicit ICCompare_Double(JitCode* stubCode)
: ICStub(ICStub::Compare_Double, stubCode)
{}
public:
class Compiler : public ICMultiStubCompiler {
protected:
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
public:
Compiler(JSContext* cx, JSOp op, Engine engine)
: ICMultiStubCompiler(cx, ICStub::Compare_Double, op, engine)
{}
ICStub* getStub(ICStubSpace* space) override {
return newStub<ICCompare_Double>(space, getStubCode());
}
};
};
class ICCompare_NumberWithUndefined : public ICStub
{
friend class ICStubSpace;
ICCompare_NumberWithUndefined(JitCode* stubCode, bool lhsIsUndefined)
: ICStub(ICStub::Compare_NumberWithUndefined, stubCode)
{
extra_ = lhsIsUndefined;
}
public:
bool lhsIsUndefined() {
return extra_;
}
class Compiler : public ICMultiStubCompiler {
protected:
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
bool lhsIsUndefined;
public:
Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsUndefined)
: ICMultiStubCompiler(cx, ICStub::Compare_NumberWithUndefined, op, engine),
lhsIsUndefined(lhsIsUndefined)
{}
virtual int32_t getKey() const override {
return static_cast<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(op) << 17) |
(static_cast<int32_t>(lhsIsUndefined) << 25);
}
ICStub* getStub(ICStubSpace* space) override {
return newStub<ICCompare_NumberWithUndefined>(space, getStubCode(),
lhsIsUndefined);
}
};
};
class ICCompare_String : public ICStub
{
friend class ICStubSpace;
explicit ICCompare_String(JitCode* stubCode)
: ICStub(ICStub::Compare_String, stubCode)
{}
public:
class Compiler : public ICMultiStubCompiler {
protected:
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
public:
Compiler(JSContext* cx, JSOp op, Engine engine)
: ICMultiStubCompiler(cx, ICStub::Compare_String, op, engine)
{}
ICStub* getStub(ICStubSpace* space) override {
return newStub<ICCompare_String>(space, getStubCode());
}
};
};
class ICCompare_Symbol : public ICStub
{
friend class ICStubSpace;
explicit ICCompare_Symbol(JitCode* stubCode)
: ICStub(ICStub::Compare_Symbol, stubCode)
{}
public:
class Compiler : public ICMultiStubCompiler {
protected:
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
public:
Compiler(JSContext* cx, JSOp op, Engine engine)
: ICMultiStubCompiler(cx, ICStub::Compare_Symbol, op, engine)
{}
ICStub* getStub(ICStubSpace* space) override {
return newStub<ICCompare_Symbol>(space, getStubCode());
}
};
};
class ICCompare_Boolean : public ICStub
{
friend class ICStubSpace;
explicit ICCompare_Boolean(JitCode* stubCode)
: ICStub(ICStub::Compare_Boolean, stubCode)
{}
public:
class Compiler : public ICMultiStubCompiler {
protected:
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
public:
Compiler(JSContext* cx, JSOp op, Engine engine)
: ICMultiStubCompiler(cx, ICStub::Compare_Boolean, op, engine)
{}
ICStub* getStub(ICStubSpace* space) override {
return newStub<ICCompare_Boolean>(space, getStubCode());
}
};
};
class ICCompare_Object : public ICStub
{
friend class ICStubSpace;
explicit ICCompare_Object(JitCode* stubCode)
: ICStub(ICStub::Compare_Object, stubCode)
{}
public:
class Compiler : public ICMultiStubCompiler {
protected:
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
public:
Compiler(JSContext* cx, JSOp op, Engine engine)
: ICMultiStubCompiler(cx, ICStub::Compare_Object, op, engine)
{}
ICStub* getStub(ICStubSpace* space) override {
return newStub<ICCompare_Object>(space, getStubCode());
}
};
};
class ICCompare_ObjectWithUndefined : public ICStub
{
friend class ICStubSpace;
explicit ICCompare_ObjectWithUndefined(JitCode* stubCode)
: ICStub(ICStub::Compare_ObjectWithUndefined, stubCode)
{}
public:
class Compiler : public ICMultiStubCompiler {
protected:
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
bool lhsIsUndefined;
bool compareWithNull;
public:
Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsUndefined, bool compareWithNull)
: ICMultiStubCompiler(cx, ICStub::Compare_ObjectWithUndefined, op, engine),
lhsIsUndefined(lhsIsUndefined),
compareWithNull(compareWithNull)
{}
virtual int32_t getKey() const override {
return static_cast<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(op) << 17) |
(static_cast<int32_t>(lhsIsUndefined) << 25) |
(static_cast<int32_t>(compareWithNull) << 26);
}
ICStub* getStub(ICStubSpace* space) override {
return newStub<ICCompare_ObjectWithUndefined>(space, getStubCode());
}
};
};
class ICCompare_Int32WithBoolean : public ICStub
{
friend class ICStubSpace;
ICCompare_Int32WithBoolean(JitCode* stubCode, bool lhsIsInt32)
: ICStub(ICStub::Compare_Int32WithBoolean, stubCode)
{
extra_ = lhsIsInt32;
}
public:
bool lhsIsInt32() const {
return extra_;
}
// Compiler for this stub kind.
class Compiler : public ICStubCompiler {
protected:
JSOp op_;
bool lhsIsInt32_;
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
virtual int32_t getKey() const override {
return static_cast<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(op_) << 17) |
(static_cast<int32_t>(lhsIsInt32_) << 25);
}
public:
Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsInt32)
: ICStubCompiler(cx, ICStub::Compare_Int32WithBoolean, engine),
op_(op),
lhsIsInt32_(lhsIsInt32)
{}
ICStub* getStub(ICStubSpace* space) override {
return newStub<ICCompare_Int32WithBoolean>(space, getStubCode(), lhsIsInt32_);
}
};
};
// Enum for stubs handling a combination of typed arrays and typed objects.
enum TypedThingLayout {
Layout_TypedArray,

Просмотреть файл

@ -15,15 +15,6 @@ namespace jit {
_(BinaryArith_Fallback) \
\
_(Compare_Fallback) \
_(Compare_Int32) \
_(Compare_Double) \
_(Compare_NumberWithUndefined) \
_(Compare_String) \
_(Compare_Symbol) \
_(Compare_Boolean) \
_(Compare_Object) \
_(Compare_ObjectWithUndefined) \
_(Compare_Int32WithBoolean) \
\
_(GetProp_Fallback) \
\

Просмотреть файл

@ -1,74 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/BaselineCompiler.h"
#include "jit/BaselineIC.h"
#include "jit/BaselineJIT.h"
#include "jit/Linker.h"
#include "jit/SharedICHelpers.h"
#include "jit/MacroAssembler-inl.h"
using namespace js;
using namespace js::jit;
namespace js {
namespace jit {
// ICCompare_Int32
bool
ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
{
// Guard that R0 is an integer and R1 is an integer.
Label failure;
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
// Compare payload regs of R0 and R1.
Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
masm.cmp32(R0.payloadReg(), R1.payloadReg());
masm.ma_mov(Imm32(1), R0.payloadReg(), cond);
masm.ma_mov(Imm32(0), R0.payloadReg(), Assembler::InvertCondition(cond));
// Result is implicitly boxed already.
masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.payloadReg(), R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub.
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
bool
ICCompare_Double::Compiler::generateStubCode(MacroAssembler& masm)
{
Label failure, isNaN;
masm.ensureDouble(R0, FloatReg0, &failure);
masm.ensureDouble(R1, FloatReg1, &failure);
Register dest = R0.scratchReg();
Assembler::DoubleCondition doubleCond = JSOpToDoubleCondition(op);
Assembler::Condition cond = Assembler::ConditionFromDoubleCondition(doubleCond);
masm.compareDouble(FloatReg0, FloatReg1);
masm.ma_mov(Imm32(0), dest);
masm.ma_mov(Imm32(1), dest, cond);
masm.tagValue(JSVAL_TYPE_BOOLEAN, dest, R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub.
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
} // namespace jit
} // namespace js

Просмотреть файл

@ -1,75 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/SharedIC.h"
#include "jit/SharedICHelpers.h"
#ifdef JS_SIMULATOR_ARM64
#include "jit/arm64/Assembler-arm64.h"
#include "jit/arm64/BaselineCompiler-arm64.h"
#include "jit/arm64/vixl/Debugger-vixl.h"
#endif
#include "jit/MacroAssembler-inl.h"
using namespace js;
using namespace js::jit;
namespace js {
namespace jit {
// ICCompare_Int32
bool
ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
{
// Guard that R0 is an integer and R1 is an integer.
Label failure;
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
// Compare payload regs of R0 and R1.
Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
masm.cmp32(R0.valueReg(), R1.valueReg());
masm.Cset(ARMRegister(R0.valueReg(), 32), cond);
// Result is implicitly boxed already.
masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.valueReg(), R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub.
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
bool
ICCompare_Double::Compiler::generateStubCode(MacroAssembler& masm)
{
Label failure, isNaN;
masm.ensureDouble(R0, FloatReg0, &failure);
masm.ensureDouble(R1, FloatReg1, &failure);
Register dest = R0.valueReg();
Assembler::DoubleCondition doubleCond = JSOpToDoubleCondition(op);
Assembler::Condition cond = Assembler::ConditionFromDoubleCondition(doubleCond);
masm.compareDouble(doubleCond, FloatReg0, FloatReg1);
masm.Cset(ARMRegister(dest, 32), cond);
masm.tagValue(JSVAL_TYPE_BOOLEAN, dest, R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub.
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
} // namespace jit
} // namespace js

Просмотреть файл

@ -1,45 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/BaselineCompiler.h"
#include "jit/BaselineIC.h"
#include "jit/BaselineJIT.h"
#include "jit/Linker.h"
#include "jit/SharedICHelpers.h"
using namespace js;
using namespace js::jit;
namespace js {
namespace jit {
// ICCompare_Int32
bool
ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
{
// Guard that R0 is an integer and R1 is an integer.
Label failure;
Label conditionTrue;
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
// Compare payload regs of R0 and R1.
Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
masm.ma_cmp_set(R0.payloadReg(), R0.payloadReg(), R1.payloadReg(), cond);
masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.payloadReg(), R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
} // namespace jit
} // namespace js

Просмотреть файл

@ -1,47 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/BaselineCompiler.h"
#include "jit/BaselineIC.h"
#include "jit/BaselineJIT.h"
#include "jit/Linker.h"
#include "jit/SharedICHelpers.h"
using namespace js;
using namespace js::jit;
namespace js {
namespace jit {
// ICCompare_Int32
bool
ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
{
// Guard that R0 is an integer and R1 is an integer.
Label failure;
Label conditionTrue;
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
// Compare payload regs of R0 and R1.
masm.unboxInt32(R0, ExtractTemp0);
masm.unboxInt32(R1, ExtractTemp1);
Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
masm.ma_cmp_set(R0.valueReg(), ExtractTemp0, ExtractTemp1, cond);
masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.valueReg(), R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
} // namespace jit
} // namespace js

Просмотреть файл

@ -42,5 +42,3 @@ BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& iter, Invalidati
MOZ_CRASH();
}
bool ICCompare_Int32::Compiler::generateStubCode(MacroAssembler&) { MOZ_CRASH(); }
bool ICCompare_Double::Compiler::generateStubCode(MacroAssembler&) { MOZ_CRASH(); }

Просмотреть файл

@ -885,10 +885,6 @@ class AssemblerShared
return embedsNurseryPointers_;
}
static bool canUseInSingleByteInstruction(Register reg) {
return true;
}
void addCodeLabel(CodeLabel label) {
propagateOOM(codeLabels_.append(label));
}

Просмотреть файл

@ -1,46 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/BaselineIC.h"
#include "jit/SharedICHelpers.h"
#include "jit/MacroAssembler-inl.h"
using namespace js;
using namespace js::jit;
namespace js {
namespace jit {
// ICCompare_Int32
bool
ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
{
// Guard that R0 is an integer and R1 is an integer.
Label failure;
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
// Directly compare the int32 payload of R0 and R1.
ScratchRegisterScope scratch(masm);
Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
masm.mov(ImmWord(0), scratch);
masm.cmp32(R0.valueReg(), R1.valueReg());
masm.setCC(cond, scratch);
// Box the result and return
masm.boxValue(JSVAL_TYPE_BOOLEAN, scratch, R0.valueReg());
EmitReturnFromIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
} // namespace jit
} // namespace js

Просмотреть файл

@ -1,44 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/BaselineIC.h"
#include "jit/SharedICHelpers.h"
#include "jit/MacroAssembler-inl.h"
using namespace js;
using namespace js::jit;
bool
ICCompare_Double::Compiler::generateStubCode(MacroAssembler& masm)
{
Label failure, notNaN;
masm.ensureDouble(R0, FloatReg0, &failure);
masm.ensureDouble(R1, FloatReg1, &failure);
Register dest = R0.scratchReg();
Assembler::DoubleCondition cond = JSOpToDoubleCondition(op);
masm.mov(ImmWord(0), dest);
masm.compareDouble(cond, FloatReg0, FloatReg1);
masm.setCC(Assembler::ConditionFromDoubleCondition(cond), dest);
// Check for NaN, if needed.
Assembler::NaNCond nanCond = Assembler::NaNCondFromDoubleCondition(cond);
if (nanCond != Assembler::NaN_HandledByCond) {
masm.j(Assembler::NoParity, &notNaN);
masm.mov(ImmWord(nanCond == Assembler::NaN_IsTrue), dest);
masm.bind(&notNaN);
}
masm.tagValue(JSVAL_TYPE_BOOLEAN, dest, R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}

Просмотреть файл

@ -1060,10 +1060,6 @@ class Assembler : public AssemblerX86Shared
masm.vmovups_rm(src.encoding(), dest.addr);
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

Просмотреть файл

@ -1,48 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/BaselineCompiler.h"
#include "jit/BaselineIC.h"
#include "jit/BaselineJIT.h"
#include "jit/Linker.h"
#include "jit/SharedICHelpers.h"
#include "jit/MacroAssembler-inl.h"
using namespace js;
using namespace js::jit;
namespace js {
namespace jit {
// ICCompare_Int32
bool
ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
{
// Guard that R0 is an integer and R1 is an integer.
Label failure;
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
// Compare payload regs of R0 and R1.
Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
masm.cmp32(R0.payloadReg(), R1.payloadReg());
masm.setCC(cond, R0.payloadReg());
masm.movzbl(R0.payloadReg(), R0.payloadReg());
// Box the result and return
masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.payloadReg(), R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
} // namespace jit
} // namespace js

Просмотреть файл

@ -259,7 +259,7 @@ BEGIN_TEST(testSavedStacks_selfHostedFrames)
result = JS::GetSavedFrameColumn(cx, principals, selfHostedFrame, &column,
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,

Просмотреть файл

@ -501,7 +501,6 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
'jit/x86-shared/Assembler-x86-shared.cpp',
'jit/x86-shared/AssemblerBuffer-x86-shared.cpp',
'jit/x86-shared/BaselineCompiler-x86-shared.cpp',
'jit/x86-shared/BaselineIC-x86-shared.cpp',
'jit/x86-shared/CodeGenerator-x86-shared.cpp',
'jit/x86-shared/Lowering-x86-shared.cpp',
'jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp',
@ -517,7 +516,6 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
'jit/x64/Assembler-x64.cpp',
'jit/x64/Bailouts-x64.cpp',
'jit/x64/BaselineCompiler-x64.cpp',
'jit/x64/BaselineIC-x64.cpp',
'jit/x64/CodeGenerator-x64.cpp',
'jit/x64/Lowering-x64.cpp',
'jit/x64/MacroAssembler-x64.cpp',
@ -529,7 +527,6 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
'jit/x86/Assembler-x86.cpp',
'jit/x86/Bailouts-x86.cpp',
'jit/x86/BaselineCompiler-x86.cpp',
'jit/x86/BaselineIC-x86.cpp',
'jit/x86/CodeGenerator-x86.cpp',
'jit/x86/Lowering-x86.cpp',
'jit/x86/MacroAssembler-x86.cpp',
@ -542,7 +539,6 @@ elif CONFIG['JS_CODEGEN_ARM']:
'jit/arm/Assembler-arm.cpp',
'jit/arm/Bailouts-arm.cpp',
'jit/arm/BaselineCompiler-arm.cpp',
'jit/arm/BaselineIC-arm.cpp',
'jit/arm/CodeGenerator-arm.cpp',
'jit/arm/disasm/Constants-arm.cpp',
'jit/arm/disasm/Disasm-arm.cpp',
@ -566,7 +562,6 @@ elif CONFIG['JS_CODEGEN_ARM64']:
'jit/arm64/Architecture-arm64.cpp',
'jit/arm64/Assembler-arm64.cpp',
'jit/arm64/Bailouts-arm64.cpp',
'jit/arm64/BaselineIC-arm64.cpp',
'jit/arm64/CodeGenerator-arm64.cpp',
'jit/arm64/Disassembler-arm64.cpp',
'jit/arm64/Lowering-arm64.cpp',
@ -598,7 +593,6 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
'jit/mips-shared/Assembler-mips-shared.cpp',
'jit/mips-shared/Bailouts-mips-shared.cpp',
'jit/mips-shared/BaselineCompiler-mips-shared.cpp',
'jit/mips-shared/BaselineIC-mips-shared.cpp',
'jit/mips-shared/CodeGenerator-mips-shared.cpp',
'jit/mips-shared/Lowering-mips-shared.cpp',
'jit/mips-shared/MacroAssembler-mips-shared.cpp',
@ -611,7 +605,6 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
'jit/mips32/Assembler-mips32.cpp',
'jit/mips32/Bailouts-mips32.cpp',
'jit/mips32/BaselineCompiler-mips32.cpp',
'jit/mips32/BaselineIC-mips32.cpp',
'jit/mips32/CodeGenerator-mips32.cpp',
'jit/mips32/Lowering-mips32.cpp',
'jit/mips32/MacroAssembler-mips32.cpp',
@ -629,7 +622,6 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
'jit/mips64/Assembler-mips64.cpp',
'jit/mips64/Bailouts-mips64.cpp',
'jit/mips64/BaselineCompiler-mips64.cpp',
'jit/mips64/BaselineIC-mips64.cpp',
'jit/mips64/CodeGenerator-mips64.cpp',
'jit/mips64/Lowering-mips64.cpp',
'jit/mips64/MacroAssembler-mips64.cpp',

Просмотреть файл

@ -4491,7 +4491,7 @@ JSScript::AutoDelazify::dropScript()
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());

Просмотреть файл

@ -21,13 +21,15 @@
namespace js {
struct MatchPair
struct MatchPair final
{
int32_t start;
int32_t limit;
static constexpr int32_t NoMatch = -1;
MatchPair()
: start(-1), limit(-1)
: start(NoMatch), limit(NoMatch)
{ }
MatchPair(int32_t start, int32_t limit)
@ -39,8 +41,8 @@ struct MatchPair
inline bool check() const {
MOZ_ASSERT(limit >= start);
MOZ_ASSERT_IF(start < 0, start == -1);
MOZ_ASSERT_IF(limit < 0, limit == -1);
MOZ_ASSERT_IF(start < 0, start == NoMatch);
MOZ_ASSERT_IF(limit < 0, limit == NoMatch);
return true;
}
};

Просмотреть файл

@ -1251,9 +1251,9 @@ RegExpRealm::createMatchResultTemplateObject(JSContext* cx)
// 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

Просмотреть файл

@ -325,6 +325,9 @@ class RegExpRealm
void sweep();
static const size_t MatchResultObjectIndexSlot = 0;
static const size_t MatchResultObjectInputSlot = 1;
/* Get or create template object used to base the result of .exec() on. */
ArrayObject* getOrCreateMatchResultTemplateObject(JSContext* cx) {
if (matchResultTemplateObject_)

Просмотреть файл

@ -416,22 +416,21 @@ static inline ParseNode*
CallCallee(ParseNode* pn)
{
MOZ_ASSERT(pn->isKind(ParseNodeKind::Call));
return ListHead(pn);
return BinaryLeft(pn);
}
static inline unsigned
CallArgListLength(ParseNode* pn)
{
MOZ_ASSERT(pn->isKind(ParseNodeKind::Call));
MOZ_ASSERT(ListLength(pn) >= 1);
return ListLength(pn) - 1;
return ListLength(BinaryRight(pn));
}
static inline ParseNode*
CallArgList(ParseNode* pn)
{
MOZ_ASSERT(pn->isKind(ParseNodeKind::Call));
return NextNode(ListHead(pn));
return ListHead(BinaryRight(pn));
}
static inline ParseNode*
@ -594,16 +593,16 @@ static ParseNode*
DotBase(ParseNode* pn)
{
MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
MOZ_ASSERT(pn->isArity(PN_NAME));
return pn->expr();
MOZ_ASSERT(pn->isArity(PN_BINARY));
return pn->pn_left;
}
static PropertyName*
DotMember(ParseNode* pn)
{
MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
MOZ_ASSERT(pn->isArity(PN_NAME));
return pn->pn_atom->asPropertyName();
MOZ_ASSERT(pn->isArity(PN_BINARY));
return pn->pn_right->pn_atom->asPropertyName();
}
static ParseNode*
@ -2800,9 +2799,11 @@ IsArrayViewCtorName(ModuleValidator& m, PropertyName* name, Scalar::Type* type)
}
static bool
CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* ctorExpr, PropertyName* bufferName)
CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* newExpr, PropertyName* bufferName)
{
ParseNode* bufArg = NextNode(ctorExpr);
ParseNode* ctorExpr = BinaryLeft(newExpr);
ParseNode* ctorArgs = BinaryRight(newExpr);
ParseNode* bufArg = ListHead(ctorArgs);
if (!bufArg || NextNode(bufArg) != nullptr)
return m.fail(ctorExpr, "array view constructor takes exactly one argument");
@ -2823,7 +2824,7 @@ CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr)
if (!bufferName)
return m.fail(newExpr, "cannot create array view without an asm.js heap parameter");
ParseNode* ctorExpr = ListHead(newExpr);
ParseNode* ctorExpr = BinaryLeft(newExpr);
PropertyName* field;
Scalar::Type type;
@ -2852,7 +2853,7 @@ CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr)
type = global->viewType();
}
if (!CheckNewArrayViewArgs(m, ctorExpr, bufferName))
if (!CheckNewArrayViewArgs(m, newExpr, bufferName))
return false;
return m.addArrayView(varName, type, field);

Просмотреть файл

@ -1028,8 +1028,12 @@ ReadScriptOrFunction(nsIObjectInputStream* stream, JSContext* cx,
// We don't serialize mutedError-ness of scripts, which is fine as long as
// we only serialize system and XUL-y things. We can detect this by checking
// where the caller wants us to deserialize.
//
// CompilationScope() could theoretically GC, so get that out of the way
// before comparing to the cx global.
JSObject* loaderGlobal = xpc::CompilationScope();
MOZ_RELEASE_ASSERT(nsContentUtils::IsSystemCaller(cx) ||
CurrentGlobalOrNull(cx) == xpc::CompilationScope());
CurrentGlobalOrNull(cx) == loaderGlobal);
uint32_t size;
rv = stream->Read32(&size);

Просмотреть файл

@ -39,11 +39,11 @@ add_task(async function() {
equal(frame.source, "thing.js", "Frame source");
equal(frame.line, 5, "Frame line");
equal(frame.column, 14, "Frame column");
equal(frame.column, 18, "Frame column");
equal(frame.functionDisplayName, "it", "Frame function name");
equal(frame.parent, null, "Frame parent");
equal(String(frame), "it@thing.js:5:14\n", "Stringified frame");
equal(String(frame), "it@thing.js:5:18\n", "Stringified frame");
// reportError
@ -55,7 +55,7 @@ add_task(async function() {
let [msg] = messages.filter(m => m.message.includes("Meh"));
equal(msg.stack, frame, "reportError stack frame");
equal(msg.message, '[JavaScript Error: "Meh" {file: "thing.js" line: 5}]\nit@thing.js:5:14\n');
equal(msg.message, '[JavaScript Error: "Meh" {file: "thing.js" line: 5}]\nit@thing.js:5:18\n');
Assert.throws(() => { Cu.reportError("Meh", {}); },
err => err.result == Cr.NS_ERROR_INVALID_ARG,
@ -82,5 +82,5 @@ add_task(async function() {
equal(error.message, "Meh", "Error message");
equal(error.fileName, "thing.js", "Error filename");
equal(error.lineNumber, 5, "Error line");
equal(error.columnNumber, 14, "Error column");
equal(error.columnNumber, 18, "Error column");
});

Просмотреть файл

@ -0,0 +1,22 @@
diff --git a/media/libvpx/libvpx/vp8/common/postproc.c b/media/libvpx/libvpx/vp8/common/postproc.c
--- a/media/libvpx/libvpx/vp8/common/postproc.c
+++ b/media/libvpx/libvpx/vp8/common/postproc.c
@@ -60,17 +60,17 @@ static void vp8_de_mblock(YV12_BUFFER_CO
}
void vp8_deblock(VP8_COMMON *cm, YV12_BUFFER_CONFIG *source,
YV12_BUFFER_CONFIG *post, int q, int low_var_thresh,
int flag) {
double level = 6.0e-05 * q * q * q - .0067 * q * q + .306 * q + .0065;
int ppl = (int)(level + .5);
- const MODE_INFO *mode_info_context = cm->show_frame_mi;
+ const MODE_INFO *mode_info_context = cm->mi;
int mbr, mbc;
/* The pixel thresholds are adjusted according to if or not the macroblock
* is a skipped block. */
unsigned char *ylimits = cm->pp_limits_buffer;
unsigned char *uvlimits = cm->pp_limits_buffer + 16 * cm->mb_cols;
(void)low_var_thresh;
(void)flag;

Просмотреть файл

@ -65,7 +65,7 @@ void vp8_deblock(VP8_COMMON *cm, YV12_BUFFER_CONFIG *source,
double level = 6.0e-05 * q * q * q - .0067 * q * q + .306 * q + .0065;
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

Просмотреть файл

@ -41,6 +41,8 @@ def apply_patches():
# Avoid c/asm name collision for loopfilter_sse2
os.system("patch -p1 < rename_duplicate_files.patch")
os.system("mv libvpx/vpx_dsp/x86/loopfilter_sse2.c libvpx/vpx_dsp/x86/loopfilter_intrin_sse2.c")
# Cherrypick fix from upstream
os.system("patch -p3 < bug1480092.patch")
def update_readme(commit):

Просмотреть файл

@ -145,36 +145,20 @@ class NavigationDelegateTest : BaseSessionTest() {
containsString(mobileSubStr))
}
fun telemetryTest(process: String) {
@Test fun telemetrySnapshots() {
sessionRule.session.loadTestPath(HELLO_HTML_PATH)
sessionRule.waitForPageStop()
val telemetry = sessionRule.runtime.telemetry
val result = sessionRule.waitForResult(telemetry.getSnapshots(true))
val snapshots = result?.get(process) as GeckoBundle
val result = sessionRule.waitForResult(telemetry.getSnapshots(false))
assertThat("Snapshots should not be null",
snapshots, notNullValue())
result?.get("parent"), notNullValue())
assertThat("Histograms should not be null",
snapshots.get("histograms"), notNullValue())
assertThat("Keyed histograms should not be null",
snapshots.get("keyedHistograms"), notNullValue())
assertThat("Scalars should not be null",
snapshots.get("scalars"), notNullValue())
assertThat("Keyed scalars should not be null",
snapshots.get("keyedScalars"), notNullValue())
}
@Test fun telemetryParent() {
telemetryTest("parent")
}
@Test fun telemetryContent() {
assumeThat(sessionRule.env.isMultiprocess, equalTo(true));
telemetryTest("content")
if (sessionRule.env.isMultiprocess) {
assertThat("Snapshots should not be null",
result?.get("content"), notNullValue())
}
}
@Test fun load() {

Просмотреть файл

@ -102,7 +102,7 @@ function run_analysis () {
(
cd "$analysis_dir"
$PYTHON "$ANALYSIS_SRCDIR/analyze.py" --buildcommand="$GECKO_DIR/taskcluster/scripts/builder/hazard-${build_type}.sh"
$PYTHON "$ANALYSIS_SRCDIR/analyze.py" -v --buildcommand="$GECKO_DIR/taskcluster/scripts/builder/hazard-${build_type}.sh"
)
}

Просмотреть файл

@ -3,4 +3,4 @@
mkdir -p "$ANALYZED_OBJDIR"
cd "$ANALYZED_OBJDIR"
$SOURCE/js/src/configure --enable-debug --enable-optimize --enable-ctypes --enable-nspr-build
make -j12 -s
make -j8 -s

Просмотреть файл

@ -140,7 +140,8 @@ class ADBCommand(object):
adb_port=None,
logger_name='adb',
timeout=300,
verbose=False):
verbose=False,
require_root=True):
"""Initializes the ADBCommand object.
:param str adb: path to adb executable. Defaults to 'adb'.
@ -149,6 +150,8 @@ class ADBCommand(object):
:param adb_port: port of the adb server.
:type adb_port: integer or None
:param str logger_name: logging logger name. Defaults to 'adb'.
:param bool verbose: provide verbose output
:param bool require_root: check that we have root permissions on device
:raises: * ADBError
* ADBTimeoutError
@ -158,6 +161,7 @@ class ADBCommand(object):
self._logger = self._get_logger(logger_name)
self._verbose = verbose
self._require_root = require_root
self._adb_path = adb
self._adb_host = adb_host
self._adb_port = adb_port
@ -818,15 +822,15 @@ class ADBDevice(ADBCommand):
def _try_test_root(self, test_root):
base_path, sub_path = posixpath.split(test_root)
if not self.is_dir(base_path, root=True):
if not self.is_dir(base_path, root=self._require_root):
return False
try:
dummy_dir = posixpath.join(test_root, 'dummy')
if self.is_dir(dummy_dir, root=True):
self.rm(dummy_dir, recursive=True, root=True)
self.mkdir(dummy_dir, parents=True, root=True)
self.chmod(test_root, recursive=True, root=True)
if self.is_dir(dummy_dir, root=self._require_root):
self.rm(dummy_dir, recursive=True, root=self._require_root)
self.mkdir(dummy_dir, parents=True, root=self._require_root)
self.chmod(test_root, recursive=True, root=self._require_root)
except ADBError:
self._logger.debug("%s is not writable" % test_root)
return False

Просмотреть файл

@ -8,7 +8,7 @@ from __future__ import absolute_import
from setuptools import setup
PACKAGE_NAME = 'mozdevice'
PACKAGE_VERSION = '1.0.0'
PACKAGE_VERSION = '1.0.1'
deps = ['mozfile >= 1.0',
'mozlog >= 3.0',

Просмотреть файл

@ -43,7 +43,7 @@
.then(t.step_func(e => {
assert_equals(e.blockedURI, "inline");
assert_equals(e.lineNumber, 131);
assert_equals(e.columnNumber, 4);
assert_in_array(e.columnNumber, [4, 59]);
assert_equals(e.target, document, "Elements created in this document, but pushed into a same-origin frame trigger on that frame's document, not on this frame's document.");
return watcher.wait_for('securitypolicyviolation');
}))

Просмотреть файл

@ -3,6 +3,8 @@
// See also browser/base/content/test/newtab/.
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
// A small 1x1 test png
const image1x1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==";
@ -679,7 +681,8 @@ add_task(async function getTopFrecentSites() {
Assert.equal(links[0].url, testURI, "added visit corresponds to added url");
});
add_task(async function getTopFrecentSites_improveSearch() {
add_task({ skip_if: () => AppConstants.MOZ_APP_NAME == "thunderbird" },
async function getTopFrecentSites_improveSearch() {
await setUpActivityStreamTest();
const SEARCH_SHORTCUTS_EXPERIMENT_PREF = "browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts";
Services.prefs.setBoolPref(SEARCH_SHORTCUTS_EXPERIMENT_PREF, true);

Просмотреть файл

@ -405,7 +405,9 @@ namespace recordreplay {
MACRO(SLDisplayCopyColorSpace) \
MACRO(SLDisplayIOServicePort) \
MACRO(SLEventSourceCounterForEventType) \
MACRO(SLMainDisplayID)
MACRO(SLMainDisplayID) \
MACRO(SLSSetDenyWindowServerConnections) \
MACRO(SLSShutdownServerConnections)
#define MAKE_CALL_EVENT(aName) CallEvent_ ##aName ,
@ -2679,6 +2681,8 @@ RRFunction2(SLEventSourceCounterForEventType)
RRFunction1(SLDisplayCopyColorSpace)
RRFunction1(SLDisplayIOServicePort)
RRFunction0(SLMainDisplayID)
RRFunction1(SLSSetDenyWindowServerConnections)
RRFunctionVoid0(SLSShutdownServerConnections)
///////////////////////////////////////////////////////////////////////////////
// Redirection generation

Просмотреть файл

@ -16,10 +16,13 @@
#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;
@ -33,6 +36,7 @@ nsAlertsIconListener::notify_notification_show_t nsAlertsIconListener::notify_no
nsAlertsIconListener::notify_notification_set_icon_from_pixbuf_t nsAlertsIconListener::notify_notification_set_icon_from_pixbuf = nullptr;
nsAlertsIconListener::notify_notification_add_action_t nsAlertsIconListener::notify_notification_add_action = nullptr;
nsAlertsIconListener::notify_notification_close_t nsAlertsIconListener::notify_notification_close = nullptr;
nsAlertsIconListener::notify_notification_set_hint_t nsAlertsIconListener::notify_notification_set_hint = nullptr;
static void notify_action_cb(NotifyNotification *notification,
gchar *action, gpointer user_data)
@ -111,6 +115,7 @@ nsAlertsIconListener::nsAlertsIconListener(nsSystemAlertsService* aBackend,
notify_notification_set_icon_from_pixbuf = (notify_notification_set_icon_from_pixbuf_t)dlsym(libNotifyHandle, "notify_notification_set_icon_from_pixbuf");
notify_notification_add_action = (notify_notification_add_action_t)dlsym(libNotifyHandle, "notify_notification_add_action");
notify_notification_close = (notify_notification_close_t)dlsym(libNotifyHandle, "notify_notification_close");
notify_notification_set_hint = (notify_notification_set_hint_t)dlsym(libNotifyHandle, "notify_notification_set_hint");
if (!notify_is_initted || !notify_init || !notify_get_server_caps || !notify_notification_new || !notify_notification_show || !notify_notification_set_icon_from_pixbuf || !notify_notification_add_action || !notify_notification_close) {
dlclose(libNotifyHandle);
libNotifyHandle = nullptr;
@ -175,6 +180,20 @@ nsAlertsIconListener::ShowAlert(GdkPixbuf* aPixbuf)
notify_action_cb, this, nullptr);
}
if (notify_notification_set_hint) {
// If MOZ_DESKTOP_FILE_NAME variable is set, use it as the application id,
// otherwise use gAppData->name
if (getenv("MOZ_DESKTOP_FILE_NAME")) {
// Send the desktop name to identify the application
// The desktop-entry is the part before the .desktop
notify_notification_set_hint(mNotification, "desktop-entry",
g_variant_new("s", getenv("MOZ_DESKTOP_FILE_NAME")));
} else {
notify_notification_set_hint(mNotification, "desktop-entry",
g_variant_new("s", gAppData->remotingName));
}
}
// Fedora 10 calls NotifyNotification "closed" signal handlers with a
// different signature, so a marshaller is used instead of a C callback to
// get the user_data (this) in a parseable format. |closure| is created

Просмотреть файл

@ -57,6 +57,7 @@ protected:
typedef void (*notify_notification_set_icon_from_pixbuf_t)(void*, GdkPixbuf*);
typedef void (*notify_notification_add_action_t)(void*, const char*, const char*, NotifyActionCallback, gpointer, GFreeFunc);
typedef bool (*notify_notification_close_t)(void*, GError**);
typedef void (*notify_notification_set_hint_t)(NotifyNotification*, const char*, GVariant*);
nsCOMPtr<nsICancelable> mIconRequest;
nsCString mAlertTitle;
@ -80,6 +81,7 @@ protected:
static notify_notification_set_icon_from_pixbuf_t notify_notification_set_icon_from_pixbuf;
static notify_notification_add_action_t notify_notification_add_action;
static notify_notification_close_t notify_notification_close;
static notify_notification_set_hint_t notify_notification_set_hint;
NotifyNotification* mNotification;
gulong mClosureHandler;