diff options
Diffstat (limited to 'js/src')
111 files changed, 2153 insertions, 2061 deletions
diff --git a/js/src/Makefile.in b/js/src/Makefile.in index b007954b1..20678c68c 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -138,27 +138,6 @@ distclean:: CFLAGS += $(MOZ_ZLIB_CFLAGS) -# Silence warnings on AIX/HP-UX from non-GNU compilers -ifndef GNU_CC -ifeq ($(OS_ARCH),AIX) -# Suppress warnings from xlC -# 1540-1281: offsetof() on null non-POD types -# 1540-1608: anonymous unions using static data members -CFLAGS += -qsuppress=1540-1281 -qsuppress=1540-1608 -CXXFLAGS += -qsuppress=1540-1281 -qsuppress=1540-1608 -endif -endif -ifeq ($(OS_ARCH),SunOS) -ifeq ($(TARGET_CPU),sparc) - -ifdef GNU_CC -CFLAGS += -mcpu=v9 -CXXFLAGS += -mcpu=v9 -endif # GNU_CC - -endif -endif - $(LIBRARY_NAME).pc: js.pc cp $^ $@ diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index cd4ac122c..389bb57db 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -9,11 +9,13 @@ #include "mozilla/ArrayUtils.h" #include "jscntxt.h" +#include "jsstr.h" #include "builtin/Eval.h" #include "frontend/BytecodeCompiler.h" #include "jit/InlinableNatives.h" #include "js/UniquePtr.h" +#include "vm/AsyncFunction.h" #include "vm/StringBuffer.h" #include "jsobjinlines.h" @@ -124,6 +126,27 @@ obj_toSource(JSContext* cx, unsigned argc, Value* vp) return true; } +template <typename CharT> +static bool +Consume(const CharT*& s, const CharT* e, const char *chars) +{ + size_t len = strlen(chars); + if (s + len >= e) + return false; + if (!EqualChars(s, chars, len)) + return false; + s += len; + return true; +} + +template <typename CharT> +static void +ConsumeSpaces(const CharT*& s, const CharT* e) +{ + while (*s == ' ' && s < e) + s++; +} + /* * Given a function source string, return the offset and length of the part * between '(function $name' and ')'. @@ -133,37 +156,53 @@ static bool ArgsAndBodySubstring(mozilla::Range<const CharT> chars, size_t* outOffset, size_t* outLen) { const CharT* const start = chars.begin().get(); - const CharT* const end = chars.end().get(); const CharT* s = start; + const CharT* e = chars.end().get(); - uint8_t parenChomp = 0; - if (s[0] == '(') { - s++; - parenChomp = 1; - } - - /* Try to jump "function" keyword. */ - s = js_strchr_limit(s, ' ', end); - if (!s) + if (s == e) return false; - /* - * Jump over the function's name: it can't be encoded as part - * of an ECMA getter or setter. - */ - s = js_strchr_limit(s, '(', end); - if (!s) - return false; + // Remove enclosing parentheses. + if (*s == '(' && *(e - 1) == ')') { + s++; + e--; + } - if (*s == ' ') + (void) Consume(s, e, "async"); + ConsumeSpaces(s, e); + (void) (Consume(s, e, "function") || Consume(s, e, "get") || Consume(s, e, "set")); + ConsumeSpaces(s, e); + (void) Consume(s, e, "*"); + ConsumeSpaces(s, e); + + // Jump over the function's name. + if (Consume(s, e, "[")) { + s = js_strchr_limit(s, ']', e); + if (!s) + return false; s++; + ConsumeSpaces(s, e); + if (*s != '(') + return false; + } else { + s = js_strchr_limit(s, '(', e); + if (!s) + return false; + } *outOffset = s - start; - *outLen = end - s - parenChomp; + *outLen = e - s; MOZ_ASSERT(*outOffset + *outLen <= chars.length()); return true; } +enum class PropertyKind { + Getter, + Setter, + Method, + Normal +}; + JSString* js::ObjectToSource(JSContext* cx, HandleObject obj) { @@ -182,59 +221,28 @@ js::ObjectToSource(JSContext* cx, HandleObject obj) if (!buf.append('{')) return nullptr; - RootedValue v0(cx), v1(cx); - MutableHandleValue val[2] = {&v0, &v1}; - - RootedString str0(cx), str1(cx); - MutableHandleString gsop[2] = {&str0, &str1}; - AutoIdVector idv(cx); if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_SYMBOLS, &idv)) return nullptr; bool comma = false; - for (size_t i = 0; i < idv.length(); ++i) { - RootedId id(cx, idv[i]); - Rooted<PropertyDescriptor> desc(cx); - if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) - return nullptr; - - int valcnt = 0; - if (desc.object()) { - if (desc.isAccessorDescriptor()) { - if (desc.hasGetterObject() && desc.getterObject()) { - val[valcnt].setObject(*desc.getterObject()); - gsop[valcnt].set(cx->names().get); - valcnt++; - } - if (desc.hasSetterObject() && desc.setterObject()) { - val[valcnt].setObject(*desc.setterObject()); - gsop[valcnt].set(cx->names().set); - valcnt++; - } - } else { - valcnt = 1; - val[0].set(desc.value()); - gsop[0].set(nullptr); - } - } - + auto AddProperty = [cx, &comma, &buf](HandleId id, HandleValue val, PropertyKind kind) -> bool { /* Convert id to a string. */ RootedString idstr(cx); if (JSID_IS_SYMBOL(id)) { RootedValue v(cx, SymbolValue(JSID_TO_SYMBOL(id))); idstr = ValueToSource(cx, v); if (!idstr) - return nullptr; + return false; } else { RootedValue idv(cx, IdToValue(id)); idstr = ToString<CanGC>(cx, idv); if (!idstr) - return nullptr; + return false; /* - * If id is a string that's not an identifier, or if it's a negative - * integer, then it must be quoted. + * If id is a string that's not an identifier, or if it's a + * negative integer, then it must be quoted. */ if (JSID_IS_ATOM(id) ? !IsIdentifier(JSID_TO_ATOM(id)) @@ -242,28 +250,65 @@ js::ObjectToSource(JSContext* cx, HandleObject obj) { idstr = QuoteString(cx, idstr, char16_t('\'')); if (!idstr) - return nullptr; + return false; } } - for (int j = 0; j < valcnt; j++) { - /* Convert val[j] to its canonical source form. */ - JSString* valsource = ValueToSource(cx, val[j]); - if (!valsource) - return nullptr; + RootedString valsource(cx, ValueToSource(cx, val)); + if (!valsource) + return false; - RootedLinearString valstr(cx, valsource->ensureLinear(cx)); - if (!valstr) - return nullptr; + RootedLinearString valstr(cx, valsource->ensureLinear(cx)); + if (!valstr) + return false; + + if (comma && !buf.append(", ")) + return false; + comma = true; + + size_t voffset, vlength; + + // Methods and accessors can return exact syntax of source, that fits + // into property without adding property name or "get"/"set" prefix. + // Use the exact syntax when the following conditions are met: + // + // * It's a function object + // (exclude proxies) + // * Function's kind and property's kind are same + // (this can be false for dynamically defined properties) + // * Function has explicit name + // (this can be false for computed property and dynamically defined + // properties) + // * Function's name and property's name are same + // (this can be false for dynamically defined properties) + if (kind == PropertyKind::Getter || kind == PropertyKind::Setter || + kind == PropertyKind::Method) + { + RootedFunction fun(cx); + if (val.toObject().is<JSFunction>()) { + fun = &val.toObject().as<JSFunction>(); + // Method's case should be checked on caller. + if (((fun->isGetter() && kind == PropertyKind::Getter) || + (fun->isSetter() && kind == PropertyKind::Setter) || + kind == PropertyKind::Method) && + fun->explicitName()) + { + bool result; + if (!EqualStrings(cx, fun->explicitName(), idstr, &result)) + return false; - size_t voffset = 0; - size_t vlength = valstr->length(); + if (result) { + if (!buf.append(valstr)) + return false; + return true; + } + } + } - /* - * Remove '(function ' from the beginning of valstr and ')' from the - * end so that we can put "get" in front of the function definition. - */ - if (gsop[j] && IsFunctionObject(val[j])) { + { + // When falling back try to generate a better string + // representation by skipping the prelude, and also removing + // the enclosing parentheses. bool success; JS::AutoCheckCannotGC nogc; if (valstr->hasLatin1Chars()) @@ -271,29 +316,90 @@ js::ObjectToSource(JSContext* cx, HandleObject obj) else success = ArgsAndBodySubstring(valstr->twoByteRange(nogc), &voffset, &vlength); if (!success) - gsop[j].set(nullptr); + kind = PropertyKind::Normal; } - if (comma && !buf.append(", ")) - return nullptr; - comma = true; + if (kind == PropertyKind::Getter) { + if (!buf.append("get ")) + return false; + } else if (kind == PropertyKind::Setter) { + if (!buf.append("set ")) + return false; + } else if (kind == PropertyKind::Method && fun) { + if (IsWrappedAsyncFunction(fun)) { + if (!buf.append("async ")) + return false; + } - if (gsop[j]) { - if (!buf.append(gsop[j]) || !buf.append(' ')) - return nullptr; + if (fun->isStarGenerator()) { + if (!buf.append('*')) + return false; + } } - if (JSID_IS_SYMBOL(id) && !buf.append('[')) - return nullptr; - if (!buf.append(idstr)) - return nullptr; - if (JSID_IS_SYMBOL(id) && !buf.append(']')) - return nullptr; - if (!buf.append(gsop[j] ? ' ' : ':')) - return nullptr; + } + bool needsBracket = JSID_IS_SYMBOL(id); + if (needsBracket && !buf.append('[')) + return false; + if (!buf.append(idstr)) + return false; + if (needsBracket && !buf.append(']')) + return false; + + if (kind == PropertyKind::Getter || kind == PropertyKind::Setter || + kind == PropertyKind::Method) + { if (!buf.appendSubstring(valstr, voffset, vlength)) - return nullptr; + return false; + } else { + if (!buf.append(':')) + return false; + if (!buf.append(valstr)) + return false; + } + return true; + }; + + RootedId id(cx); + Rooted<PropertyDescriptor> desc(cx); + RootedValue val(cx); + RootedFunction fun(cx); + for (size_t i = 0; i < idv.length(); ++i) { + id = idv[i]; + if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) + return nullptr; + + if (!desc.object()) + continue; + + if (desc.isAccessorDescriptor()) { + if (desc.hasGetterObject() && desc.getterObject()) { + val.setObject(*desc.getterObject()); + if (!AddProperty(id, val, PropertyKind::Getter)) + return nullptr; + } + if (desc.hasSetterObject() && desc.setterObject()) { + val.setObject(*desc.setterObject()); + if (!AddProperty(id, val, PropertyKind::Setter)) + return nullptr; + } + continue; } + + val.set(desc.value()); + if (IsFunctionObject(val, fun.address())) { + if (IsWrappedAsyncFunction(fun)) + fun = GetUnwrappedAsyncFunction(fun); + + if (fun->isMethod()) { + if (!AddProperty(id, val, PropertyKind::Method)) + return nullptr; + continue; + } + } + + if (!AddProperty(id, val, PropertyKind::Normal)) + return nullptr; } if (!buf.append('}')) diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 373b6c9ed..c896ce5d1 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -240,11 +240,7 @@ GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) if (!JS_SetProperty(cx, info, "intl-api", value)) return false; -#if defined(SOLARIS) - value = BooleanValue(false); -#else value = BooleanValue(true); -#endif if (!JS_SetProperty(cx, info, "mapped-array-buffer", value)) return false; diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 0facd0009..d6adfac2c 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -20,10 +20,6 @@ #include <float.h> #endif -#if defined(SOLARIS) -#include <ieeefp.h> -#endif - #ifdef HAVE_SSIZE_T #include <sys/types.h> #endif diff --git a/js/src/ctypes/libffi/testsuite/libffi.call/float2.c b/js/src/ctypes/libffi/testsuite/libffi.call/float2.c index a0b296cf4..dfdef0598 100644 --- a/js/src/ctypes/libffi/testsuite/libffi.call/float2.c +++ b/js/src/ctypes/libffi/testsuite/libffi.call/float2.c @@ -32,21 +32,11 @@ int main (void) f = 3.14159; -#if 1 - /* This is ifdef'd out for now. long double support under SunOS/gcc - is pretty much non-existent. You'll get the odd bus error in library - routines like printf(). */ printf ("%Lf\n", ldblit(f)); -#endif ld = 666; ffi_call(&cif, FFI_FN(ldblit), &ld, values); -#if 1 - /* This is ifdef'd out for now. long double support under SunOS/gcc - is pretty much non-existent. You'll get the odd bus error in library - routines like printf(). */ printf ("%Lf, %Lf, %Lf, %Lf\n", ld, ldblit(f), ld - ldblit(f), LDBL_EPSILON); -#endif /* These are not always the same!! Check for a reasonable delta */ if (ld - ldblit(f) < LDBL_EPSILON) diff --git a/js/src/ds/LifoAlloc.h b/js/src/ds/LifoAlloc.h index f349cd476..b4e9c3418 100644 --- a/js/src/ds/LifoAlloc.h +++ b/js/src/ds/LifoAlloc.h @@ -15,6 +15,8 @@ #include "mozilla/TemplateLib.h" #include "mozilla/TypeTraits.h" +#include <new> + // This data structure supports stacky LIFO allocation (mark/release and // LifoAllocScope). It does not maintain one contiguous segment; instead, it // maintains a bunch of linked memory segments. In order to prevent malloc/free @@ -285,6 +287,20 @@ class LifoAlloc return allocImpl(n); } + template<typename T, typename... Args> + MOZ_ALWAYS_INLINE T* + allocInSize(size_t n, Args&&... args) + { + MOZ_ASSERT(n >= sizeof(T), "must request enough space to store a T"); + static_assert(alignof(T) <= detail::LIFO_ALLOC_ALIGN, + "LifoAlloc must provide enough alignment to store T"); + void* ptr = alloc(n); + if (!ptr) + return nullptr; + + return new (ptr) T(mozilla::Forward<Args>(args)...); + } + MOZ_ALWAYS_INLINE void* allocInfallible(size_t n) { AutoEnterOOMUnsafeRegion oomUnsafe; diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index 76afe80b1..b5be5f5ac 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -77,7 +77,7 @@ class MOZ_STACK_CLASS BytecodeCompiler bool canLazilyParse(); bool createParser(); bool createSourceAndParser(Maybe<uint32_t> parameterListEnd = Nothing()); - bool createScript(); + bool createScript(uint32_t preludeStart = 0); bool emplaceEmitter(Maybe<BytecodeEmitter>& emitter, SharedContext* sharedContext); bool handleParseFailure(const Directives& newDirectives); bool deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObject environment); @@ -242,10 +242,11 @@ BytecodeCompiler::createSourceAndParser(Maybe<uint32_t> parameterListEnd /* = No } bool -BytecodeCompiler::createScript() +BytecodeCompiler::createScript(uint32_t preludeStart /* = 0 */) { script = JSScript::Create(cx, options, - sourceObject, /* sourceStart = */ 0, sourceBuffer.length()); + sourceObject, /* sourceStart = */ 0, sourceBuffer.length(), + preludeStart); return script != nullptr; } @@ -456,7 +457,7 @@ BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun, if (fn->pn_funbox->function()->isInterpreted()) { MOZ_ASSERT(fun == fn->pn_funbox->function()); - if (!createScript()) + if (!createScript(fn->pn_funbox->preludeStart)) return false; Maybe<BytecodeEmitter> emitter; @@ -650,7 +651,8 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha MOZ_ASSERT(sourceObject); Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject, - lazy->begin(), lazy->end())); + lazy->begin(), lazy->end(), + lazy->preludeStart())); if (!script) return false; diff --git a/js/src/frontend/BytecodeCompiler.h b/js/src/frontend/BytecodeCompiler.h index 72e967639..0bc1ab2ab 100644 --- a/js/src/frontend/BytecodeCompiler.h +++ b/js/src/frontend/BytecodeCompiler.h @@ -109,6 +109,8 @@ IsIdentifier(JSLinearString* str); * As above, but taking chars + length. */ bool +IsIdentifier(const char* chars, size_t length); +bool IsIdentifier(const char16_t* chars, size_t length); /* True if str is a keyword. Defined in TokenStream.cpp. */ diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index c7c615ccf..b3dd6d777 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -319,7 +319,7 @@ ScopeKindIsInBody(ScopeKind kind) static inline void MarkAllBindingsClosedOver(LexicalScope::Data& data) { - BindingName* names = data.names; + TrailingNamesArray& names = data.trailingNames; for (uint32_t i = 0; i < data.length; i++) names[i] = BindingName(names[i].name(), true); } @@ -3559,9 +3559,11 @@ BytecodeEmitter::maybeSetSourceMap() if (parser->options().sourceMapURL()) { // Warn about the replacement, but use the new one. if (parser->ss->hasSourceMapURL()) { - if(!parser->report(ParseWarning, false, nullptr, JSMSG_ALREADY_HAS_PRAGMA, - parser->ss->filename(), "//# sourceMappingURL")) + if (!parser->reportNoOffset(ParseWarning, false, JSMSG_ALREADY_HAS_PRAGMA, + parser->ss->filename(), "//# sourceMappingURL")) + { return false; + } } if (!parser->ss->setSourceMapURL(cx, parser->options().sourceMapURL())) @@ -3606,13 +3608,13 @@ BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...) } bool -BytecodeEmitter::reportStrictWarning(ParseNode* pn, unsigned errorNumber, ...) +BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...) { TokenPos pos = pn ? pn->pn_pos : tokenStream()->currentToken().pos; va_list args; va_start(args, errorNumber); - bool result = tokenStream()->reportStrictWarningErrorNumberVA(pos.begin, errorNumber, args); + bool result = tokenStream()->reportExtraWarningErrorNumberVA(pos.begin, errorNumber, args); va_end(args); return result; } @@ -7834,7 +7836,8 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) Rooted<JSObject*> sourceObject(cx, script->sourceObject()); Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject, - funbox->bufStart, funbox->bufEnd)); + funbox->bufStart, funbox->bufEnd, + funbox->preludeStart)); if (!script) return false; @@ -8704,13 +8707,13 @@ BytecodeEmitter::emitStatement(ParseNode* pn) } if (directive) { - if (!reportStrictWarning(pn2, JSMSG_CONTRARY_NONDIRECTIVE, directive)) + if (!reportExtraWarning(pn2, JSMSG_CONTRARY_NONDIRECTIVE, directive)) return false; } } else { current->currentLine = parser->tokenStream.srcCoords.lineNum(pn2->pn_pos.begin); current->lastColumn = 0; - if (!reportStrictWarning(pn2, JSMSG_USELESS_EXPR)) + if (!reportExtraWarning(pn2, JSMSG_USELESS_EXPR)) return false; } } @@ -8978,7 +8981,8 @@ BytecodeEmitter::isRestParameter(ParseNode* pn, bool* result) if (bindings->nonPositionalFormalStart > 0) { // |paramName| can be nullptr when the rest destructuring syntax is // used: `function f(...[]) {}`. - JSAtom* paramName = bindings->names[bindings->nonPositionalFormalStart - 1].name(); + JSAtom* paramName = + bindings->trailingNames[bindings->nonPositionalFormalStart - 1].name(); *result = paramName && name == paramName; return true; } diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 04307c8c1..32668a34c 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -388,7 +388,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter } bool reportError(ParseNode* pn, unsigned errorNumber, ...); - bool reportStrictWarning(ParseNode* pn, unsigned errorNumber, ...); + bool reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...); bool reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...); // If pn contains a useful expression, return true with *answer set to true. diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 0fd137796..b619cf24c 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -670,9 +670,18 @@ class FullParseHandler pn->setDirectRHSAnonFunction(true); } - ParseNode* newFunctionDefinition() { - return new_<CodeNode>(PNK_FUNCTION, pos()); + ParseNode* newFunctionStatement() { + return new_<CodeNode>(PNK_FUNCTION, JSOP_NOP, pos()); } + + ParseNode* newFunctionExpression() { + return new_<CodeNode>(PNK_FUNCTION, JSOP_LAMBDA, pos()); + } + + ParseNode* newArrowFunction() { + return new_<CodeNode>(PNK_FUNCTION, JSOP_LAMBDA_ARROW, pos()); + } + bool setComprehensionLambdaBody(ParseNode* pn, ParseNode* body) { MOZ_ASSERT(body->isKind(PNK_STATEMENTLIST)); ParseNode* paramsBody = newList(PNK_PARAMSBODY, body); @@ -699,7 +708,7 @@ class FullParseHandler } ParseNode* newModule() { - return new_<CodeNode>(PNK_MODULE, pos()); + return new_<CodeNode>(PNK_MODULE, JSOP_NOP, pos()); } ParseNode* newLexicalScope(LexicalScope::Data* bindings, ParseNode* body) { @@ -845,7 +854,7 @@ class FullParseHandler MOZ_MUST_USE ParseNode* setLikelyIIFE(ParseNode* pn) { return parenthesize(pn); } - void setPrologue(ParseNode* pn) { + void setInDirectivePrologue(ParseNode* pn) { pn->pn_prologue = true; } diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index ece3a45df..91f17625c 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -838,7 +838,7 @@ LexicalScopeNode::dump(int indent) if (!isEmptyScope()) { LexicalScope::Data* bindings = scopeBindings(); for (uint32_t i = 0; i < bindings->length; i++) { - JSAtom* name = bindings->names[i].name(); + JSAtom* name = bindings->trailingNames[i].name(); JS::AutoCheckCannotGC nogc; if (name->hasLatin1Chars()) DumpName(name->latin1Chars(nogc), name->length()); diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index c58dab431..c3523afe4 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -926,10 +926,14 @@ struct ListNode : public ParseNode struct CodeNode : public ParseNode { - CodeNode(ParseNodeKind kind, const TokenPos& pos) - : ParseNode(kind, JSOP_NOP, PN_CODE, pos) + CodeNode(ParseNodeKind kind, JSOp op, const TokenPos& pos) + : ParseNode(kind, op, PN_CODE, pos) { MOZ_ASSERT(kind == PNK_FUNCTION || kind == PNK_MODULE); + MOZ_ASSERT_IF(kind == PNK_MODULE, op == JSOP_NOP); + MOZ_ASSERT(op == JSOP_NOP || // statement, module + op == JSOP_LAMBDA_ARROW || // arrow function + op == JSOP_LAMBDA); // expression, method, comprehension, accessor, &c. MOZ_ASSERT(!pn_body); MOZ_ASSERT(!pn_objbox); } diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 623379f61..daacbb50b 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -19,6 +19,8 @@ #include "frontend/Parser.h" +#include <new> + #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" @@ -61,13 +63,13 @@ using BindingIter = ParseContext::Scope::BindingIter; using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr; /* Read a token. Report an error and return null() if that token isn't of type tt. */ -#define MUST_MATCH_TOKEN_MOD(tt, modifier, errno) \ +#define MUST_MATCH_TOKEN_MOD(tt, modifier, errorNumber) \ JS_BEGIN_MACRO \ TokenKind token; \ if (!tokenStream.getToken(&token, modifier)) \ return null(); \ if (token != tt) { \ - report(ParseError, false, null(), errno); \ + error(errorNumber); \ return null(); \ } \ JS_END_MACRO @@ -439,7 +441,8 @@ UsedNameTracker::rewind(RewindToken token) } FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead, - JSFunction* fun, Directives directives, bool extraWarnings, + JSFunction* fun, uint32_t preludeStart, + Directives directives, bool extraWarnings, GeneratorKind generatorKind, FunctionAsyncKind asyncKind) : ObjectBox(fun, traceListHead), SharedContext(cx, Kind::ObjectBox, directives, extraWarnings), @@ -452,6 +455,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* trac bufEnd(0), startLine(1), startColumn(0), + preludeStart(preludeStart), length(0), generatorKindBits_(GeneratorKindAsBits(generatorKind)), asyncKindBits_(AsyncKindAsBits(asyncKind)), @@ -567,61 +571,116 @@ FunctionBox::initWithEnclosingScope(Scope* enclosingScope) } template <typename ParseHandler> +void +Parser<ParseHandler>::error(unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); +#ifdef DEBUG + bool result = +#endif + tokenStream.reportCompileErrorNumberVA(pos().begin, JSREPORT_ERROR, errorNumber, args); + MOZ_ASSERT(!result, "reporting an error returned true?"); + va_end(args); +} + +template <typename ParseHandler> +void +Parser<ParseHandler>::errorAt(uint32_t offset, unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); +#ifdef DEBUG + bool result = +#endif + tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args); + MOZ_ASSERT(!result, "reporting an error returned true?"); + va_end(args); +} + +template <typename ParseHandler> bool -Parser<ParseHandler>::reportHelper(ParseReportKind kind, bool strict, uint32_t offset, - unsigned errorNumber, va_list args) +Parser<ParseHandler>::warning(unsigned errorNumber, ...) { - bool result = false; - switch (kind) { - case ParseError: - result = tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args); - break; - case ParseWarning: - result = - tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args); - break; - case ParseExtraWarning: - result = tokenStream.reportStrictWarningErrorNumberVA(offset, errorNumber, args); - break; - case ParseStrictError: - result = tokenStream.reportStrictModeErrorNumberVA(offset, strict, errorNumber, args); - break; - } + va_list args; + va_start(args, errorNumber); + bool result = + tokenStream.reportCompileErrorNumberVA(pos().begin, JSREPORT_WARNING, errorNumber, args); + va_end(args); return result; } template <typename ParseHandler> bool -Parser<ParseHandler>::report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...) +Parser<ParseHandler>::warningAt(uint32_t offset, unsigned errorNumber, ...) { - uint32_t offset = (pn ? handler.getPosition(pn) : pos()).begin; - va_list args; va_start(args, errorNumber); - bool result = reportHelper(kind, strict, offset, errorNumber, args); + bool result = + tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args); va_end(args); return result; } template <typename ParseHandler> bool -Parser<ParseHandler>::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...) +Parser<ParseHandler>::extraWarning(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = reportHelper(kind, strict, TokenStream::NoOffset, errorNumber, args); + bool result = tokenStream.reportExtraWarningErrorNumberVA(pos().begin, errorNumber, args); va_end(args); return result; } template <typename ParseHandler> bool -Parser<ParseHandler>::reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset, - unsigned errorNumber, ...) +Parser<ParseHandler>::strictModeError(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = reportHelper(kind, strict, offset, errorNumber, args); + bool res = + tokenStream.reportStrictModeErrorNumberVA(pos().begin, pc->sc()->strict(), + errorNumber, args); + va_end(args); + return res; +} + +template <typename ParseHandler> +bool +Parser<ParseHandler>::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); + bool res = + tokenStream.reportStrictModeErrorNumberVA(offset, pc->sc()->strict(), errorNumber, args); + va_end(args); + return res; +} + +template <typename ParseHandler> +bool +Parser<ParseHandler>::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); + bool result = false; + uint32_t offset = TokenStream::NoOffset; + switch (kind) { + case ParseError: + result = tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args); + break; + case ParseWarning: + result = + tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args); + break; + case ParseExtraWarning: + result = tokenStream.reportExtraWarningErrorNumberVA(offset, errorNumber, args); + break; + case ParseStrictError: + result = tokenStream.reportStrictModeErrorNumberVA(offset, strict, errorNumber, args); + break; + } va_end(args); return result; } @@ -736,7 +795,8 @@ Parser<ParseHandler>::newObjectBox(JSObject* obj) template <typename ParseHandler> FunctionBox* -Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, Directives inheritedDirectives, +Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, uint32_t preludeStart, + Directives inheritedDirectives, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB) { @@ -751,8 +811,9 @@ Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, Directives inheri * function. */ FunctionBox* funbox = - alloc.new_<FunctionBox>(context, alloc, traceListHead, fun, inheritedDirectives, - options().extraWarningsOption, generatorKind, asyncKind); + alloc.new_<FunctionBox>(context, alloc, traceListHead, fun, preludeStart, + inheritedDirectives, options().extraWarningsOption, + generatorKind, asyncKind); if (!funbox) { ReportOutOfMemory(context); return nullptr; @@ -820,8 +881,7 @@ Parser<ParseHandler>::parse() if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); if (tt != TOK_EOF) { - report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT, - "script", TokenKindToDesc(tt)); + error(JSMSG_GARBAGE_AFTER_INPUT, "script", TokenKindToDesc(tt)); return null(); } if (foldConstants) { @@ -832,21 +892,6 @@ Parser<ParseHandler>::parse() return pn; } -template <typename ParseHandler> -bool -Parser<ParseHandler>::reportBadReturn(Node pn, ParseReportKind kind, - unsigned errnum, unsigned anonerrnum) -{ - JSAutoByteString name; - if (JSAtom* atom = pc->functionBox()->function()->explicitName()) { - if (!AtomToPrintableString(context, atom, &name)) - return false; - } else { - errnum = anonerrnum; - } - return report(kind, pc->sc()->strict(), pn, errnum, name.ptr()); -} - /* * Strict mode forbids introducing new definitions for 'eval', 'arguments', or * for any strict mode reserved keyword. @@ -877,8 +922,7 @@ Parser<ParseHandler>::checkStrictBinding(PropertyName* name, TokenPos pos) JSAutoByteString bytes; if (!AtomToPrintableString(context, name, &bytes)) return false; - return reportWithOffset(ParseStrictError, pc->sc()->strict(), pos.begin, - JSMSG_BAD_BINDING, bytes.ptr()); + return strictModeErrorAt(pos.begin, JSMSG_BAD_BINDING, bytes.ptr()); } return true; @@ -914,8 +958,7 @@ Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKi JSAutoByteString bytes; if (!AtomToPrintableString(context, name, &bytes)) return; - reportWithOffset(ParseError, false, pos.begin, JSMSG_REDECLARED_VAR, - DeclarationKindString(kind), bytes.ptr()); + errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(kind), bytes.ptr()); } // notePositionalFormalParameter is called for both the arguments of a regular @@ -935,7 +978,7 @@ Parser<ParseHandler>::notePositionalFormalParameter(Node fn, HandlePropertyName { if (AddDeclaredNamePtr p = pc->functionScope().lookupDeclaredNameForAdd(name)) { if (disallowDuplicateParams) { - report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); + error(JSMSG_BAD_DUP_ARGS); return false; } @@ -947,11 +990,8 @@ Parser<ParseHandler>::notePositionalFormalParameter(Node fn, HandlePropertyName JSAutoByteString bytes; if (!AtomToPrintableString(context, name, &bytes)) return false; - if (!report(ParseStrictError, pc->sc()->strict(), null(), - JSMSG_DUPLICATE_FORMAL, bytes.ptr())) - { + if (!strictModeError(JSMSG_DUPLICATE_FORMAL, bytes.ptr())) return false; - } } *duplicatedParam = true; @@ -1187,11 +1227,11 @@ Parser<ParseHandler>::checkLexicalDeclarationDirectlyWithinBlock(ParseContext::S if (!StatementKindIsBraced(stmt.kind()) && stmt.kind() != StatementKind::ForLoopLexicalHead) { - reportWithOffset(ParseError, false, pos.begin, - stmt.kind() == StatementKind::Label - ? JSMSG_LEXICAL_DECL_LABEL - : JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, - DeclarationKindString(kind)); + errorAt(pos.begin, + stmt.kind() == StatementKind::Label + ? JSMSG_LEXICAL_DECL_LABEL + : JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, + DeclarationKindString(kind)); return false; } @@ -1233,7 +1273,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind AddDeclaredNamePtr p = pc->functionScope().lookupDeclaredNameForAdd(name); if (p) { - report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); + error(JSMSG_BAD_DUP_ARGS); return false; } @@ -1247,7 +1287,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind // Functions in block have complex allowances in sloppy mode for being // labelled that other lexical declarations do not have. Those checks // are more complex than calling checkLexicalDeclarationDirectlyWithin- - // Block and are done in checkFunctionDefinition. + // Block and are done inline in callers. ParseContext::Scope* scope = pc->innermostScope(); if (AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name)) { @@ -1281,7 +1321,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind // contain 'let'. (CatchParameter is the only lexical binding form // without this restriction.) if (name == context->names().let) { - reportWithOffset(ParseError, false, pos.begin, JSMSG_LEXICAL_DECL_DEFINES_LET); + errorAt(pos.begin, JSMSG_LEXICAL_DECL_DEFINES_LET); return false; } @@ -1440,8 +1480,7 @@ Parser<FullParseHandler>::checkStatementsEOF() if (!tokenStream.peekToken(&tt, TokenStream::Operand)) return false; if (tt != TOK_EOF) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "expression", TokenKindToDesc(tt)); + error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt)); return false; } return true; @@ -1451,16 +1490,26 @@ template <typename Scope> static typename Scope::Data* NewEmptyBindingData(ExclusiveContext* cx, LifoAlloc& alloc, uint32_t numBindings) { + using Data = typename Scope::Data; size_t allocSize = Scope::sizeOfData(numBindings); - typename Scope::Data* bindings = static_cast<typename Scope::Data*>(alloc.alloc(allocSize)); - if (!bindings) { + auto* bindings = alloc.allocInSize<Data>(allocSize, numBindings); + if (!bindings) ReportOutOfMemory(cx); - return nullptr; - } - PodZero(bindings); return bindings; } +/** + * Copy-construct |BindingName|s from |bindings| into |cursor|, then return + * the location one past the newly-constructed |BindingName|s. + */ +static MOZ_MUST_USE BindingName* +FreshlyInitializeBindings(BindingName* cursor, const Vector<BindingName>& bindings) +{ + for (const BindingName& binding : bindings) + new (cursor++) BindingName(binding); + return cursor; +} + template <> Maybe<GlobalScope::Data*> Parser<FullParseHandler>::newGlobalScopeData(ParseContext::Scope& scope) @@ -1505,22 +1554,20 @@ Parser<FullParseHandler>::newGlobalScopeData(ParseContext::Scope& scope) return Nothing(); // The ordering here is important. See comments in GlobalScope. - BindingName* start = bindings->names; + BindingName* start = bindings->trailingNames.start(); BindingName* cursor = start; - PodCopy(cursor, funs.begin(), funs.length()); - cursor += funs.length(); + cursor = FreshlyInitializeBindings(cursor, funs); bindings->varStart = cursor - start; - PodCopy(cursor, vars.begin(), vars.length()); - cursor += vars.length(); + cursor = FreshlyInitializeBindings(cursor, vars); bindings->letStart = cursor - start; - PodCopy(cursor, lets.begin(), lets.length()); - cursor += lets.length(); + cursor = FreshlyInitializeBindings(cursor, lets); bindings->constStart = cursor - start; - PodCopy(cursor, consts.begin(), consts.length()); + cursor = FreshlyInitializeBindings(cursor, consts); + bindings->length = numBindings; } @@ -1572,22 +1619,20 @@ Parser<FullParseHandler>::newModuleScopeData(ParseContext::Scope& scope) return Nothing(); // The ordering here is important. See comments in ModuleScope. - BindingName* start = bindings->names; + BindingName* start = bindings->trailingNames.start(); BindingName* cursor = start; - PodCopy(cursor, imports.begin(), imports.length()); - cursor += imports.length(); + cursor = FreshlyInitializeBindings(cursor, imports); bindings->varStart = cursor - start; - PodCopy(cursor, vars.begin(), vars.length()); - cursor += vars.length(); + cursor = FreshlyInitializeBindings(cursor, vars); bindings->letStart = cursor - start; - PodCopy(cursor, lets.begin(), lets.length()); - cursor += lets.length(); + cursor = FreshlyInitializeBindings(cursor, lets); bindings->constStart = cursor - start; - PodCopy(cursor, consts.begin(), consts.length()); + cursor = FreshlyInitializeBindings(cursor, consts); + bindings->length = numBindings; } @@ -1623,16 +1668,16 @@ Parser<FullParseHandler>::newEvalScopeData(ParseContext::Scope& scope) if (!bindings) return Nothing(); - BindingName* start = bindings->names; + BindingName* start = bindings->trailingNames.start(); BindingName* cursor = start; // Keep track of what vars are functions. This is only used in BCE to omit // superfluous DEFVARs. - PodCopy(cursor, funs.begin(), funs.length()); - cursor += funs.length(); + cursor = FreshlyInitializeBindings(cursor, funs); bindings->varStart = cursor - start; - PodCopy(cursor, vars.begin(), vars.length()); + cursor = FreshlyInitializeBindings(cursor, vars); + bindings->length = numBindings; } @@ -1719,18 +1764,17 @@ Parser<FullParseHandler>::newFunctionScopeData(ParseContext::Scope& scope, bool return Nothing(); // The ordering here is important. See comments in FunctionScope. - BindingName* start = bindings->names; + BindingName* start = bindings->trailingNames.start(); BindingName* cursor = start; - PodCopy(cursor, positionalFormals.begin(), positionalFormals.length()); - cursor += positionalFormals.length(); + cursor = FreshlyInitializeBindings(cursor, positionalFormals); bindings->nonPositionalFormalStart = cursor - start; - PodCopy(cursor, formals.begin(), formals.length()); - cursor += formals.length(); + cursor = FreshlyInitializeBindings(cursor, formals); bindings->varStart = cursor - start; - PodCopy(cursor, vars.begin(), vars.length()); + cursor = FreshlyInitializeBindings(cursor, vars); + bindings->length = numBindings; } @@ -1760,10 +1804,11 @@ Parser<FullParseHandler>::newVarScopeData(ParseContext::Scope& scope) return Nothing(); // The ordering here is important. See comments in FunctionScope. - BindingName* start = bindings->names; + BindingName* start = bindings->trailingNames.start(); BindingName* cursor = start; - PodCopy(cursor, vars.begin(), vars.length()); + cursor = FreshlyInitializeBindings(cursor, vars); + bindings->length = numBindings; } @@ -1808,14 +1853,14 @@ Parser<FullParseHandler>::newLexicalScopeData(ParseContext::Scope& scope) return Nothing(); // The ordering here is important. See comments in LexicalScope. - BindingName* cursor = bindings->names; + BindingName* cursor = bindings->trailingNames.start(); BindingName* start = cursor; - PodCopy(cursor, lets.begin(), lets.length()); - cursor += lets.length(); + cursor = FreshlyInitializeBindings(cursor, lets); bindings->constStart = cursor - start; - PodCopy(cursor, consts.begin(), consts.length()); + cursor = FreshlyInitializeBindings(cursor, consts); + bindings->length = numBindings; } @@ -1900,7 +1945,7 @@ Parser<FullParseHandler>::evalBody(EvalSharedContext* evalsc) // script. if (hasUsedName(context->names().arguments)) { if (IsArgumentsUsedInLegacyGenerator(context, pc->sc()->compilationEnclosingScope())) { - report(ParseError, false, nullptr, JSMSG_BAD_GENEXP_BODY, js_arguments_str); + error(JSMSG_BAD_GENEXP_BODY, js_arguments_str); return nullptr; } } @@ -1998,7 +2043,7 @@ Parser<FullParseHandler>::moduleBody(ModuleSharedContext* modulesc) if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); if (tt != TOK_EOF) { - report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT, "module", TokenKindToDesc(tt)); + error(JSMSG_GARBAGE_AFTER_INPUT, "module", TokenKindToDesc(tt)); return null(); } @@ -2206,6 +2251,7 @@ Parser<SyntaxParseHandler>::finishFunction() LazyScript* lazy = LazyScript::Create(context, fun, pc->closedOverBindingsForLazy(), pc->innerFunctionsForLazy, versionNumber(), funbox->bufStart, funbox->bufEnd, + funbox->preludeStart, funbox->startLine, funbox->startColumn); if (!lazy) return false; @@ -2259,7 +2305,34 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun, { MOZ_ASSERT(checkOptionsCalled); - Node fn = handler.newFunctionDefinition(); + // Skip prelude. + TokenKind tt; + if (!tokenStream.getToken(&tt)) + return null(); + if (asyncKind == AsyncFunction) { + MOZ_ASSERT(tt == TOK_ASYNC); + if (!tokenStream.getToken(&tt)) + return null(); + } + MOZ_ASSERT(tt == TOK_FUNCTION); + + if (!tokenStream.getToken(&tt)) + return null(); + if (generatorKind == StarGenerator && asyncKind == SyncFunction) { + MOZ_ASSERT(tt == TOK_MUL); + if (!tokenStream.getToken(&tt)) + return null(); + } + + // Skip function name, if present. + if (tt == TOK_NAME || tt == TOK_YIELD) { + MOZ_ASSERT(tokenStream.currentName() == fun->explicitName()); + } else { + MOZ_ASSERT(fun->explicitName() == nullptr); + tokenStream.ungetToken(); + } + + Node fn = handler.newFunctionStatement(); if (!fn) return null(); @@ -2268,8 +2341,8 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun, return null(); fn->pn_body = argsbody; - FunctionBox* funbox = newFunctionBox(fn, fun, inheritedDirectives, generatorKind, - asyncKind, /* tryAnnexB = */ false); + FunctionBox* funbox = newFunctionBox(fn, fun, /* preludeStart = */ 0, inheritedDirectives, + generatorKind, asyncKind, /* tryAnnexB = */ false); if (!funbox) return null(); funbox->initStandaloneFunction(enclosingScope); @@ -2287,12 +2360,10 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun, return null(); } - TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); if (tt != TOK_EOF) { - report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT, - "function body", TokenKindToDesc(tt)); + error(JSMSG_GARBAGE_AFTER_INPUT, "function body", TokenKindToDesc(tt)); return null(); } @@ -2697,8 +2768,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn if (!tokenStream.getToken(&tt, firstTokenModifier)) return false; if (tt != TOK_LP) { - report(ParseError, false, null(), - kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL); + error(kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL); return false; } @@ -2730,13 +2800,13 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn AtomVector& positionalFormals = pc->positionalFormalParameterNames(); if (IsGetterKind(kind)) { - report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s"); + error(JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s"); return false; } while (true) { if (hasRest) { - report(ParseError, false, null(), JSMSG_PARAMETER_AFTER_REST); + error(JSMSG_PARAMETER_AFTER_REST); return false; } @@ -2748,15 +2818,14 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn if (tt == TOK_TRIPLEDOT) { if (IsSetterKind(kind)) { - report(ParseError, false, null(), - JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); + error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); return false; } disallowDuplicateParams = true; if (duplicatedParam) { // Has duplicated args before the rest parameter. - report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); + error(JSMSG_BAD_DUP_ARGS); return false; } @@ -2767,7 +2836,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn return false; if (tt != TOK_NAME && tt != TOK_YIELD && tt != TOK_LB && tt != TOK_LC) { - report(ParseError, false, null(), JSMSG_NO_REST_NAME); + error(JSMSG_NO_REST_NAME); return false; } } @@ -2778,7 +2847,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn disallowDuplicateParams = true; if (duplicatedParam) { // Has duplicated args before the destructuring parameter. - report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); + error(JSMSG_BAD_DUP_ARGS); return false; } @@ -2806,7 +2875,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn // case: // // async await => 1 - report(ParseError, false, null(), JSMSG_RESERVED_ID, "await"); + error(JSMSG_RESERVED_ID, "await"); return false; } @@ -2826,12 +2895,12 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn } default: - report(ParseError, false, null(), JSMSG_MISSING_FORMAL); + error(JSMSG_MISSING_FORMAL); return false; } if (positionalFormals.length() >= ARGNO_LIMIT) { - report(ParseError, false, null(), JSMSG_TOO_MANY_FUN_ARGS); + error(JSMSG_TOO_MANY_FUN_ARGS); return false; } @@ -2846,12 +2915,12 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn MOZ_ASSERT(!parenFreeArrow); if (hasRest) { - report(ParseError, false, null(), JSMSG_REST_WITH_DEFAULT); + error(JSMSG_REST_WITH_DEFAULT); return false; } disallowDuplicateParams = true; if (duplicatedParam) { - report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); + error(JSMSG_BAD_DUP_ARGS); return false; } @@ -2895,12 +2964,11 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn return false; if (tt != TOK_RP) { if (IsSetterKind(kind)) { - report(ParseError, false, null(), - JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); + error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); return false; } - report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL); + error(JSMSG_PAREN_AFTER_FORMAL); return false; } } @@ -2913,78 +2981,17 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn funbox->function()->setArgCount(positionalFormals.length()); } else if (IsSetterKind(kind)) { - report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); + error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); return false; } return true; } -template <typename ParseHandler> -bool -Parser<ParseHandler>::checkFunctionDefinition(HandleAtom funAtom, Node pn, FunctionSyntaxKind kind, - GeneratorKind generatorKind, bool* tryAnnexB) -{ - if (kind == Statement) { - TokenPos pos = handler.getPosition(pn); - RootedPropertyName funName(context, funAtom->asPropertyName()); - - // In sloppy mode, Annex B.3.2 allows labelled function - // declarations. Otherwise it is a parse error. - ParseContext::Statement* declaredInStmt = pc->innermostStatement(); - if (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) { - MOZ_ASSERT(!pc->sc()->strict(), - "labeled functions shouldn't be parsed in strict mode"); - - // Find the innermost non-label statement. Report an error if it's - // unbraced: functions can't appear in it. Otherwise the statement - // (or its absence) determines the scope the function's bound in. - while (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) - declaredInStmt = declaredInStmt->enclosing(); - - if (declaredInStmt && !StatementKindIsBraced(declaredInStmt->kind())) { - reportWithOffset(ParseError, false, pos.begin, JSMSG_SLOPPY_FUNCTION_LABEL); - return false; - } - } - - if (declaredInStmt) { - MOZ_ASSERT(declaredInStmt->kind() != StatementKind::Label); - MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind())); - - if (!pc->sc()->strict() && generatorKind == NotGenerator) { - // Under sloppy mode, try Annex B.3.3 semantics. If making an - // additional 'var' binding of the same name does not throw an - // early error, do so. This 'var' binding would be assigned - // the function object when its declaration is reached, not at - // the start of the block. - - if (!tryDeclareVarForAnnexBLexicalFunction(funName, tryAnnexB)) - return false; - } - - if (!noteDeclaredName(funName, DeclarationKind::LexicalFunction, pos)) - return false; - } else { - if (!noteDeclaredName(funName, DeclarationKind::BodyLevelFunction, pos)) - return false; - - // Body-level functions in modules are always closed over. - if (pc->atModuleLevel()) - pc->varScope().lookupDeclaredName(funName)->value()->setClosedOver(); - } - } else { - // A function expression does not introduce any binding. - handler.setOp(pn, kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA); - } - - return true; -} - template <> bool -Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKind kind, - bool tryAnnexB) +Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, uint32_t preludeStart, + FunctionSyntaxKind kind, bool tryAnnexB) { // When a lazily-parsed function is called, we only fully parse (and emit) // that function, not any of its nested children. The initial syntax-only @@ -2993,7 +3000,7 @@ Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKin RootedFunction fun(context, handler.nextLazyInnerFunction()); MOZ_ASSERT(!fun->isLegacyGenerator()); - FunctionBox* funbox = newFunctionBox(pn, fun, Directives(/* strict = */ false), + FunctionBox* funbox = newFunctionBox(pn, fun, preludeStart, Directives(/* strict = */ false), fun->generatorKind(), fun->asyncKind(), tryAnnexB); if (!funbox) return false; @@ -3023,8 +3030,8 @@ Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKin template <> bool -Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, FunctionSyntaxKind kind, - bool tryAnnexB) +Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, uint32_t preludeStart, + FunctionSyntaxKind kind, bool tryAnnexB) { MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing"); } @@ -3043,7 +3050,7 @@ Parser<ParseHandler>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling if (!tokenStream.getToken(&tt)) return false; if (tt != TOK_RC) { - report(ParseError, false, null(), JSMSG_TEMPLSTR_UNTERM_EXPR); + error(JSMSG_TEMPLSTR_UNTERM_EXPR); return false; } @@ -3100,31 +3107,20 @@ Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling) template <typename ParseHandler> typename ParseHandler::Node -Parser<ParseHandler>::functionDefinition(InHandling inHandling, YieldHandling yieldHandling, +Parser<ParseHandler>::functionDefinition(uint32_t preludeStart, Node pn, InHandling inHandling, + YieldHandling yieldHandling, HandleAtom funName, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, - InvokedPrediction invoked) + bool tryAnnexB /* = false */) { MOZ_ASSERT_IF(kind == Statement, funName); MOZ_ASSERT_IF(asyncKind == AsyncFunction, generatorKind == StarGenerator); - Node pn = handler.newFunctionDefinition(); - if (!pn) - return null(); - - if (invoked) - pn = handler.setLikelyIIFE(pn); - - // Note the declared name and check for early errors. - bool tryAnnexB = false; - if (!checkFunctionDefinition(funName, pn, kind, generatorKind, &tryAnnexB)) - return null(); - // When fully parsing a LazyScript, we do not fully reparse its inner // functions, which are also lazy. Instead, their free variables and // source extents are recorded and may be skipped. if (handler.canSkipLazyInnerFunctions()) { - if (!skipLazyInnerFunction(pn, kind, tryAnnexB)) + if (!skipLazyInnerFunction(pn, preludeStart, kind, tryAnnexB)) return null(); return pn; } @@ -3157,8 +3153,9 @@ Parser<ParseHandler>::functionDefinition(InHandling inHandling, YieldHandling yi // reparse a function due to failed syntax parsing and encountering new // "use foo" directives. while (true) { - if (trySyntaxParseInnerFunction(pn, fun, inHandling, yieldHandling, kind, generatorKind, - asyncKind, tryAnnexB, directives, &newDirectives)) + if (trySyntaxParseInnerFunction(pn, fun, preludeStart, inHandling, yieldHandling, kind, + generatorKind, asyncKind, tryAnnexB, directives, + &newDirectives)) { break; } @@ -3185,6 +3182,7 @@ Parser<ParseHandler>::functionDefinition(InHandling inHandling, YieldHandling yi template <> bool Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunction fun, + uint32_t preludeStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, @@ -3218,14 +3216,15 @@ Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct // Make a FunctionBox before we enter the syntax parser, because |pn| // still expects a FunctionBox to be attached to it during BCE, and // the syntax parser cannot attach one to it. - FunctionBox* funbox = newFunctionBox(pn, fun, inheritedDirectives, generatorKind, - asyncKind, tryAnnexB); + FunctionBox* funbox = newFunctionBox(pn, fun, preludeStart, inheritedDirectives, + generatorKind, asyncKind, tryAnnexB); if (!funbox) return false; funbox->initWithEnclosingParseContext(pc, kind); - if (!parser->innerFunction(SyntaxParseHandler::NodeGeneric, pc, funbox, inHandling, - yieldHandling, kind, inheritedDirectives, newDirectives)) + if (!parser->innerFunction(SyntaxParseHandler::NodeGeneric, pc, funbox, preludeStart, + inHandling, yieldHandling, kind, + inheritedDirectives, newDirectives)) { if (parser->hadAbortedSyntaxParse()) { // Try again with a full parse. UsedNameTracker needs to be @@ -3251,13 +3250,14 @@ Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct } while (false); // We failed to do a syntax parse above, so do the full parse. - return innerFunction(pn, pc, fun, inHandling, yieldHandling, kind, generatorKind, asyncKind, - tryAnnexB, inheritedDirectives, newDirectives); + return innerFunction(pn, pc, fun, preludeStart, inHandling, yieldHandling, kind, + generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives); } template <> bool Parser<SyntaxParseHandler>::trySyntaxParseInnerFunction(Node pn, HandleFunction fun, + uint32_t preludeStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, @@ -3268,13 +3268,14 @@ Parser<SyntaxParseHandler>::trySyntaxParseInnerFunction(Node pn, HandleFunction Directives* newDirectives) { // This is already a syntax parser, so just parse the inner function. - return innerFunction(pn, pc, fun, inHandling, yieldHandling, kind, generatorKind, asyncKind, - tryAnnexB, inheritedDirectives, newDirectives); + return innerFunction(pn, pc, fun, preludeStart, inHandling, yieldHandling, kind, + generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives); } template <typename ParseHandler> bool Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, + uint32_t preludeStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, Directives inheritedDirectives, Directives* newDirectives) @@ -3298,6 +3299,7 @@ Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, FunctionBox* template <typename ParseHandler> bool Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, + uint32_t preludeStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, @@ -3309,14 +3311,14 @@ Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, HandleFuncti // parser. In that case, outerpc is a ParseContext from the full parser // instead of the current top of the stack of the syntax parser. - FunctionBox* funbox = newFunctionBox(pn, fun, inheritedDirectives, generatorKind, - asyncKind, tryAnnexB); + FunctionBox* funbox = newFunctionBox(pn, fun, preludeStart, inheritedDirectives, + generatorKind, asyncKind, tryAnnexB); if (!funbox) return false; funbox->initWithEnclosingParseContext(outerpc, kind); - return innerFunction(pn, outerpc, funbox, inHandling, yieldHandling, kind, inheritedDirectives, - newDirectives); + return innerFunction(pn, outerpc, funbox, preludeStart, inHandling, yieldHandling, kind, + inheritedDirectives, newDirectives); } template <typename ParseHandler> @@ -3346,13 +3348,13 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict { MOZ_ASSERT(checkOptionsCalled); - Node pn = handler.newFunctionDefinition(); + Node pn = handler.newFunctionStatement(); if (!pn) return null(); Directives directives(strict); - FunctionBox* funbox = newFunctionBox(pn, fun, directives, generatorKind, asyncKind, - /* tryAnnexB = */ false); + FunctionBox* funbox = newFunctionBox(pn, fun, /* preludeStart = */ 0, directives, + generatorKind, asyncKind, /* tryAnnexB = */ false); if (!funbox) return null(); funbox->initFromLazyFunction(); @@ -3428,7 +3430,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, if (!tokenStream.matchToken(&matched, TOK_ARROW)) return false; if (!matched) { - report(ParseError, false, null(), JSMSG_BAD_ARROW_ARGS); + error(JSMSG_BAD_ARROW_ARGS); return false; } } @@ -3436,7 +3438,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, // When parsing something for new Function() we have to make sure to // only treat a certain part of the source as a parameter list. if (parameterListEnd.isSome() && parameterListEnd.value() != pos().begin) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_PARAMLIST_END); + error(JSMSG_UNEXPECTED_PARAMLIST_END); return false; } @@ -3449,17 +3451,16 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, if ((funbox->isStarGenerator() && !funbox->isAsync()) || kind == Method || kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure || IsConstructorKind(kind)) { - report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY); + error(JSMSG_CURLY_BEFORE_BODY); return false; } if (kind != Arrow) { #if JS_HAS_EXPR_CLOSURES - addTelemetry(JSCompartment::DeprecatedExpressionClosure); if (!warnOnceAboutExprClosure()) return false; #else - report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY); + error(JSMSG_CURLY_BEFORE_BODY); return false; #endif } @@ -3492,7 +3493,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, if (!tokenStream.matchToken(&matched, TOK_RC, TokenStream::Operand)) return false; if (!matched) { - report(ParseError, false, null(), JSMSG_CURLY_AFTER_BODY); + error(JSMSG_CURLY_AFTER_BODY); return false; } funbox->bufEnd = pos().end; @@ -3522,8 +3523,8 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, template <typename ParseHandler> typename ParseHandler::Node -Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling, - FunctionAsyncKind asyncKind) +Parser<ParseHandler>::functionStmt(uint32_t preludeStart, YieldHandling yieldHandling, + DefaultHandling defaultHandling, FunctionAsyncKind asyncKind) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); @@ -3542,15 +3543,33 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling } } - RootedPropertyName name(context); - GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator; + // In sloppy mode, Annex B.3.2 allows labelled function declarations. + // Otherwise it's a parse error. + ParseContext::Statement* declaredInStmt = pc->innermostStatement(); + if (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) { + MOZ_ASSERT(!pc->sc()->strict(), + "labeled functions shouldn't be parsed in strict mode"); + + // Find the innermost non-label statement. Report an error if it's + // unbraced: functions can't appear in it. Otherwise the statement + // (or its absence) determines the scope the function's bound in. + while (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) + declaredInStmt = declaredInStmt->enclosing(); + + if (declaredInStmt && !StatementKindIsBraced(declaredInStmt->kind())) { + error(JSMSG_SLOPPY_FUNCTION_LABEL); + return null(); + } + } + TokenKind tt; if (!tokenStream.getToken(&tt)) return null(); + GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator; if (tt == TOK_MUL) { if (asyncKind != SyncFunction) { - report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR); + error(JSMSG_ASYNC_GENERATOR); return null(); } generatorKind = StarGenerator; @@ -3558,6 +3577,7 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling return null(); } + RootedPropertyName name(context); if (tt == TOK_NAME || tt == TOK_YIELD) { name = bindingIdentifier(yieldHandling); if (!name) @@ -3567,13 +3587,44 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling tokenStream.ungetToken(); } else { /* Unnamed function expressions are forbidden in statement context. */ - report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT); + error(JSMSG_UNNAMED_FUNCTION_STMT); return null(); } + // Note the declared name and check for early errors. + bool tryAnnexB = false; + if (declaredInStmt) { + MOZ_ASSERT(declaredInStmt->kind() != StatementKind::Label); + MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind())); + + if (!pc->sc()->strict() && generatorKind == NotGenerator) { + // In sloppy mode, try Annex B.3.3 semantics. If making an + // additional 'var' binding of the same name does not throw an + // early error, do so. This 'var' binding would be assigned + // the function object when its declaration is reached, not at + // the start of the block. + if (!tryDeclareVarForAnnexBLexicalFunction(name, &tryAnnexB)) + return null(); + } + + if (!noteDeclaredName(name, DeclarationKind::LexicalFunction, pos())) + return null(); + } else { + if (!noteDeclaredName(name, DeclarationKind::BodyLevelFunction, pos())) + return null(); + + // Body-level functions in modules are always closed over. + if (pc->atModuleLevel()) + pc->varScope().lookupDeclaredName(name)->value()->setClosedOver(); + } + + Node pn = handler.newFunctionStatement(); + if (!pn) + return null(); + YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind); - Node fun = functionDefinition(InAllowed, newYieldHandling, name, Statement, generatorKind, - asyncKind, PredictUninvoked); + Node fun = functionDefinition(preludeStart, pn, InAllowed, newYieldHandling, + name, Statement, generatorKind, asyncKind, tryAnnexB); if (!fun) return null(); @@ -3590,7 +3641,8 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling template <typename ParseHandler> typename ParseHandler::Node -Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind asyncKind) +Parser<ParseHandler>::functionExpr(uint32_t preludeStart, InvokedPrediction invoked, + FunctionAsyncKind asyncKind) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); @@ -3602,7 +3654,7 @@ Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind if (tt == TOK_MUL) { if (asyncKind != SyncFunction) { - report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR); + error(JSMSG_ASYNC_GENERATOR); return null(); } generatorKind = StarGenerator; @@ -3621,8 +3673,15 @@ Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind tokenStream.ungetToken(); } - return functionDefinition(InAllowed, yieldHandling, name, Expression, generatorKind, - asyncKind, invoked); + Node pn = handler.newFunctionExpression(); + if (!pn) + return null(); + + if (invoked) + pn = handler.setLikelyIIFE(pn); + + return functionDefinition(preludeStart, pn, InAllowed, yieldHandling, name, Expression, + generatorKind, asyncKind); } /* @@ -3647,10 +3706,11 @@ template <typename ParseHandler> bool Parser<ParseHandler>::checkUnescapedName() { - if (!tokenStream.currentToken().nameContainsEscape()) + const Token& token = tokenStream.currentToken(); + if (!token.nameContainsEscape()) return true; - report(ParseError, false, null(), JSMSG_ESCAPED_KEYWORD); + errorAt(token.pos.begin, JSMSG_ESCAPED_KEYWORD); return false; } @@ -3726,10 +3786,10 @@ Parser<FullParseHandler>::asmJS(Node list) */ template <typename ParseHandler> bool -Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont) +Parser<ParseHandler>::maybeParseDirective(Node list, Node possibleDirective, bool* cont) { TokenPos directivePos; - JSAtom* directive = handler.isStringExprStatement(pn, &directivePos); + JSAtom* directive = handler.isStringExprStatement(possibleDirective, &directivePos); *cont = !!directive; if (!*cont) @@ -3746,7 +3806,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont) // directive in the future. We don't want to interfere with people // taking advantage of directive-prologue-enabled features that appear // in other browsers first. - handler.setPrologue(pn); + handler.setInDirectivePrologue(possibleDirective); if (directive == context->names().useStrict) { // Functions with non-simple parameter lists (destructuring, @@ -3760,8 +3820,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont) : funbox->hasParameterExprs ? "default" : "rest"; - reportWithOffset(ParseError, false, directivePos.begin, - JSMSG_STRICT_NON_SIMPLE_PARAMS, parameterKind); + errorAt(directivePos.begin, JSMSG_STRICT_NON_SIMPLE_PARAMS, parameterKind); return false; } } @@ -3774,7 +3833,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont) // occur in the directive prologue -- octal escapes -- and // complain now. if (tokenStream.sawOctalEscape()) { - report(ParseError, false, null(), JSMSG_DEPRECATED_OCTAL); + error(JSMSG_DEPRECATED_OCTAL); return false; } pc->sc()->strictScript = true; @@ -3782,7 +3841,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont) } else if (directive == context->names().useAsm) { if (pc->isFunctionBox()) return asmJS(list); - return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL); + return warningAt(directivePos.begin, JSMSG_USE_ASM_DIRECTIVE_FAIL); } } return true; @@ -3814,10 +3873,8 @@ Parser<ParseHandler>::statementList(YieldHandling yieldHandling) if (tt == TOK_EOF || tt == TOK_RC) break; if (afterReturn) { - TokenPos pos(0, 0); - if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand)) + if (!tokenStream.peekOffset(&statementBegin, TokenStream::Operand)) return null(); - statementBegin = pos.begin; } Node next = statementListItem(yieldHandling, canHaveDirectives); if (!next) { @@ -3828,11 +3885,9 @@ Parser<ParseHandler>::statementList(YieldHandling yieldHandling) if (!warnedAboutStatementsAfterReturn) { if (afterReturn) { if (!handler.isStatementPermittedAfterReturnStatement(next)) { - if (!reportWithOffset(ParseWarning, false, statementBegin, - JSMSG_STMT_AFTER_RETURN)) - { + if (!warningAt(statementBegin, JSMSG_STMT_AFTER_RETURN)) return null(); - } + warnedAboutStatementsAfterReturn = true; } } else if (handler.isReturnStatement(next)) { @@ -3863,7 +3918,7 @@ Parser<ParseHandler>::condition(InHandling inHandling, YieldHandling yieldHandli /* Check for (a = b) and warn about possible (a == b) mistype. */ if (handler.isUnparenthesizedAssignment(pn)) { - if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN)) + if (!extraWarning(JSMSG_EQUAL_AS_ASSIGN)) return null(); } return pn; @@ -3920,7 +3975,8 @@ Parser<ParseHandler>::PossibleError::hasError(ErrorKind kind) template <typename ParseHandler> void -Parser<ParseHandler>::PossibleError::setPending(ErrorKind kind, Node pn, unsigned errorNumber) +Parser<ParseHandler>::PossibleError::setPending(ErrorKind kind, const TokenPos& pos, + unsigned errorNumber) { // Don't overwrite a previously recorded error. if (hasError(kind)) @@ -3929,23 +3985,25 @@ Parser<ParseHandler>::PossibleError::setPending(ErrorKind kind, Node pn, unsigne // If we report an error later, we'll do it from the position where we set // the state to pending. Error& err = error(kind); - err.offset_ = (pn ? parser_.handler.getPosition(pn) : parser_.pos()).begin; + err.offset_ = pos.begin; err.errorNumber_ = errorNumber; err.state_ = ErrorState::Pending; } template <typename ParseHandler> void -Parser<ParseHandler>::PossibleError::setPendingDestructuringError(Node pn, unsigned errorNumber) +Parser<ParseHandler>::PossibleError::setPendingDestructuringErrorAt(const TokenPos& pos, + unsigned errorNumber) { - setPending(ErrorKind::Destructuring, pn, errorNumber); + setPending(ErrorKind::Destructuring, pos, errorNumber); } template <typename ParseHandler> void -Parser<ParseHandler>::PossibleError::setPendingExpressionError(Node pn, unsigned errorNumber) +Parser<ParseHandler>::PossibleError::setPendingExpressionErrorAt(const TokenPos& pos, + unsigned errorNumber) { - setPending(ErrorKind::Expression, pn, errorNumber); + setPending(ErrorKind::Expression, pos, errorNumber); } template <typename ParseHandler> @@ -3956,7 +4014,7 @@ Parser<ParseHandler>::PossibleError::checkForError(ErrorKind kind) return true; Error& err = error(kind); - parser_.reportWithOffset(ParseError, false, err.offset_, err.errorNumber_); + parser_.errorAt(err.offset_, err.errorNumber_); return false; } @@ -4010,19 +4068,6 @@ Parser<ParseHandler>::PossibleError::transferErrorsTo(PossibleError* other) transferErrorTo(ErrorKind::Expression, other); } -template <typename ParseHandler> -bool -Parser<ParseHandler>::checkAssignmentToCall(Node target, unsigned msg) -{ - MOZ_ASSERT(handler.isFunctionCall(target)); - - // Assignment to function calls is forbidden in ES6. We're still somewhat - // concerned about sites using this in dead code, so forbid it only in - // strict mode code (or if the werror option has been set), and otherwise - // warn. - return report(ParseStrictError, pc->sc()->strict(), target, msg); -} - template <> bool Parser<FullParseHandler>::checkDestructuringName(ParseNode* expr, Maybe<DeclarationKind> maybeDecl) @@ -4033,7 +4078,7 @@ Parser<FullParseHandler>::checkDestructuringName(ParseNode* expr, Maybe<Declarat // around names). Use our nicer error message for parenthesized, nested // patterns. if (handler.isParenthesizedDestructuringPattern(expr)) { - report(ParseError, false, expr, JSMSG_BAD_DESTRUCT_PARENS); + errorAt(expr->pn_pos.begin, JSMSG_BAD_DESTRUCT_PARENS); return false; } @@ -4043,31 +4088,29 @@ Parser<FullParseHandler>::checkDestructuringName(ParseNode* expr, Maybe<Declarat // Destructuring patterns in declarations must only contain // unparenthesized names. if (!handler.isUnparenthesizedName(expr)) { - report(ParseError, false, expr, JSMSG_NO_VARIABLE_NAME); + errorAt(expr->pn_pos.begin, JSMSG_NO_VARIABLE_NAME); return false; } RootedPropertyName name(context, expr->name()); - return noteDeclaredName(name, *maybeDecl, handler.getPosition(expr)); + return noteDeclaredName(name, *maybeDecl, expr->pn_pos); } // Otherwise this is an expression in destructuring outside a declaration. - if (!reportIfNotValidSimpleAssignmentTarget(expr, KeyedDestructuringAssignment)) - return false; - - MOZ_ASSERT(!handler.isFunctionCall(expr), - "function calls shouldn't be considered valid targets in " - "destructuring patterns"); - if (handler.isNameAnyParentheses(expr)) { - // The arguments/eval identifiers are simple in non-strict mode code. - // Warn to discourage their use nonetheless. - return reportIfArgumentsEvalTarget(expr); + if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(expr, context)) { + if (!strictModeErrorAt(expr->pn_pos.begin, JSMSG_BAD_STRICT_ASSIGN, chars)) + return false; + } + + return true; } - // Nothing further to do for property accesses. - MOZ_ASSERT(handler.isPropertyAccess(expr)); - return true; + if (handler.isPropertyAccess(expr)) + return true; + + errorAt(expr->pn_pos.begin, JSMSG_BAD_DESTRUCT_TARGET); + return false; } template <> @@ -4125,7 +4168,7 @@ Parser<FullParseHandler>::checkDestructuringArray(ParseNode* arrayPattern, ParseNode* target; if (element->isKind(PNK_SPREAD)) { if (element->pn_next) { - report(ParseError, false, element->pn_next, JSMSG_PARAMETER_AFTER_REST); + errorAt(element->pn_next->pn_pos.begin, JSMSG_PARAMETER_AFTER_REST); return false; } target = element->pn_kid; @@ -4188,7 +4231,7 @@ Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern, PossibleError* possibleError /* = nullptr */) { if (pattern->isKind(PNK_ARRAYCOMP)) { - report(ParseError, false, pattern, JSMSG_ARRAY_COMP_LEFTSIDE); + errorAt(pattern->pn_pos.begin, JSMSG_ARRAY_COMP_LEFTSIDE); return false; } @@ -4245,11 +4288,11 @@ Parser<ParseHandler>::destructuringDeclarationWithoutYieldOrAwait(DeclarationKin Node res = destructuringDeclaration(kind, yieldHandling, tt); if (res) { if (pc->lastYieldOffset != startYieldOffset) { - reportWithOffset(ParseError, false, pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT); + errorAt(pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT); return null(); } if (pc->lastAwaitOffset != startAwaitOffset) { - reportWithOffset(ParseError, false, pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT); + errorAt(pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT); return null(); } } @@ -4327,14 +4370,7 @@ Parser<ParseHandler>::declarationPattern(Node decl, DeclarationKind declKind, To } } - TokenKind token; - if (!tokenStream.getToken(&token, TokenStream::None)) - return null(); - - if (token != TOK_ASSIGN) { - report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_DECL); - return null(); - } + MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL); Node init = assignExpr(forHeadKind ? InProhibited : InAllowed, yieldHandling, TripledotProhibited); @@ -4367,6 +4403,10 @@ Parser<ParseHandler>::initializerInNameDeclaration(Node decl, Node binding, { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN)); + uint32_t initializerOffset; + if (!tokenStream.peekOffset(&initializerOffset, TokenStream::Operand)) + return false; + Node initializer = assignExpr(forHeadKind ? InProhibited : InAllowed, yieldHandling, TripledotProhibited); if (!initializer) @@ -4384,7 +4424,7 @@ Parser<ParseHandler>::initializerInNameDeclaration(Node decl, Node binding, // // for (var/let/const x = ... of ...); // BAD if (isForOf) { - report(ParseError, false, binding, JSMSG_BAD_FOR_LEFTSIDE); + errorAt(initializerOffset, JSMSG_OF_AFTER_FOR_LOOP_DECL); return false; } @@ -4393,18 +4433,15 @@ Parser<ParseHandler>::initializerInNameDeclaration(Node decl, Node binding, // // for (let/const x = ... in ...); // BAD if (DeclarationKindIsLexical(declKind)) { - report(ParseError, false, binding, JSMSG_BAD_FOR_LEFTSIDE); + errorAt(initializerOffset, JSMSG_IN_AFTER_LEXICAL_FOR_DECL); return false; } // This leaves only initialized for-in |var| declarations. ES6 // forbids these; later ES un-forbids in non-strict mode code. *forHeadKind = PNK_FORIN; - if (!report(ParseStrictError, pc->sc()->strict(), initializer, - JSMSG_INVALID_FOR_IN_DECL_WITH_INIT)) - { + if (!strictModeErrorAt(initializerOffset, JSMSG_INVALID_FOR_IN_DECL_WITH_INIT)) return false; - } *forInOrOfExpression = expressionAfterForInOrOf(PNK_FORIN, yieldHandling); if (!*forInOrOfExpression) @@ -4449,7 +4486,7 @@ Parser<ParseHandler>::declarationName(Node decl, DeclarationKind declKind, Token { // Anything other than TOK_YIELD or TOK_NAME is an error. if (tt != TOK_NAME && tt != TOK_YIELD) { - report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME); + error(JSMSG_NO_VARIABLE_NAME); return null(); } @@ -4509,7 +4546,7 @@ Parser<ParseHandler>::declarationName(Node decl, DeclarationKind declKind, Token // Normal const declarations, and const declarations in for(;;) // heads, must be initialized. if (declKind == DeclarationKind::Const) { - report(ParseError, false, binding, JSMSG_BAD_CONST_DECL); + errorAt(namePos.begin, JSMSG_BAD_CONST_DECL); return null(); } } @@ -4649,7 +4686,7 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor return false; if (afterAs != TOK_NAME && afterAs != TOK_YIELD) { - report(ParseError, false, null(), JSMSG_NO_BINDING_NAME); + error(JSMSG_NO_BINDING_NAME); return false; } } else { @@ -4661,7 +4698,7 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor JSAutoByteString bytes; if (!AtomToPrintableString(context, importName, &bytes)) return false; - report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr()); + error(JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr()); return false; } } @@ -4703,7 +4740,7 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor return false; if (tt != TOK_NAME || tokenStream.currentName() != context->names().as) { - report(ParseError, false, null(), JSMSG_AS_AFTER_IMPORT_STAR); + error(JSMSG_AS_AFTER_IMPORT_STAR); return false; } @@ -4758,7 +4795,7 @@ Parser<FullParseHandler>::importDeclaration() MOZ_ASSERT(tokenStream.currentToken().type == TOK_IMPORT); if (!pc->atModuleLevel()) { - report(ParseError, false, null(), JSMSG_IMPORT_DECL_AT_TOP_LEVEL); + error(JSMSG_IMPORT_DECL_AT_TOP_LEVEL); return null(); } @@ -4807,7 +4844,7 @@ Parser<FullParseHandler>::importDeclaration() return null(); if (tt != TOK_LC && tt != TOK_MUL) { - report(ParseError, false, null(), JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT); + error(JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT); return null(); } @@ -4823,7 +4860,7 @@ Parser<FullParseHandler>::importDeclaration() return null(); if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) { - report(ParseError, false, null(), JSMSG_FROM_AFTER_IMPORT_CLAUSE); + error(JSMSG_FROM_AFTER_IMPORT_CLAUSE); return null(); } @@ -4836,7 +4873,7 @@ Parser<FullParseHandler>::importDeclaration() // equivalent to |import {} from 'a'|. importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin; } else { - report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_IMPORT); + error(JSMSG_DECLARATION_AFTER_IMPORT); return null(); } @@ -4874,7 +4911,7 @@ Parser<FullParseHandler>::checkExportedName(JSAtom* exportName) if (!AtomToPrintableString(context, exportName, &str)) return false; - report(ParseError, false, null(), JSMSG_DUPLICATE_EXPORT_NAME, str.ptr()); + error(JSMSG_DUPLICATE_EXPORT_NAME, str.ptr()); return false; } @@ -4917,7 +4954,7 @@ Parser<FullParseHandler>::exportDeclaration() MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT); if (!pc->atModuleLevel()) { - report(ParseError, false, null(), JSMSG_EXPORT_DECL_AT_TOP_LEVEL); + error(JSMSG_EXPORT_DECL_AT_TOP_LEVEL); return null(); } @@ -4950,14 +4987,8 @@ Parser<FullParseHandler>::exportDeclaration() bool foundAs; if (!tokenStream.matchContextualKeyword(&foundAs, context->names().as)) return null(); - if (foundAs) { - if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName)) - return null(); - if (tt != TOK_NAME) { - report(ParseError, false, null(), JSMSG_NO_EXPORT_NAME); - return null(); - } - } + if (foundAs) + MUST_MATCH_TOKEN_MOD(TOK_NAME, TokenStream::KeywordIsName, JSMSG_NO_EXPORT_NAME); Node exportName = newName(tokenStream.currentName()); if (!exportName) @@ -5042,7 +5073,7 @@ Parser<FullParseHandler>::exportDeclaration() if (!tokenStream.getToken(&tt)) return null(); if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) { - report(ParseError, false, null(), JSMSG_FROM_AFTER_EXPORT_STAR); + error(JSMSG_FROM_AFTER_EXPORT_STAR); return null(); } @@ -5067,7 +5098,7 @@ Parser<FullParseHandler>::exportDeclaration() } case TOK_FUNCTION: - kid = functionStmt(YieldIsKeyword, NameRequired); + kid = functionStmt(pos().begin, YieldIsKeyword, NameRequired); if (!kid) return null(); @@ -5107,7 +5138,7 @@ Parser<FullParseHandler>::exportDeclaration() ParseNode* nameNode = nullptr; switch (tt) { case TOK_FUNCTION: - kid = functionStmt(YieldIsKeyword, AllowDefaultName); + kid = functionStmt(pos().begin, YieldIsKeyword, AllowDefaultName); if (!kid) return null(); break; @@ -5124,7 +5155,7 @@ Parser<FullParseHandler>::exportDeclaration() if (nextSameLine == TOK_FUNCTION) { tokenStream.consumeKnownToken(nextSameLine); - kid = functionStmt(YieldIsName, AllowDefaultName, AsyncFunction); + kid = functionStmt(pos().begin, YieldIsName, AllowDefaultName, AsyncFunction); if (!kid) return null(); break; @@ -5178,7 +5209,7 @@ Parser<FullParseHandler>::exportDeclaration() MOZ_FALLTHROUGH; default: - report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_EXPORT); + error(JSMSG_DECLARATION_AFTER_EXPORT); return null(); } @@ -5225,7 +5256,7 @@ Parser<ParseHandler>::consequentOrAlternative(YieldHandling yieldHandling) // will report the strict mode error. if (!pc->sc()->strict()) { tokenStream.consumeKnownToken(next, TokenStream::Operand); - return functionStmt(yieldHandling, NameRequired); + return functionStmt(pos().begin, yieldHandling, NameRequired); } } @@ -5254,7 +5285,7 @@ Parser<ParseHandler>::ifStatement(YieldHandling yieldHandling) if (!tokenStream.peekToken(&tt, TokenStream::Operand)) return null(); if (tt == TOK_SEMI) { - if (!report(ParseExtraWarning, false, null(), JSMSG_EMPTY_CONSEQUENT)) + if (!extraWarning(JSMSG_EMPTY_CONSEQUENT)) return null(); } @@ -5355,37 +5386,6 @@ Parser<ParseHandler>::matchInOrOf(bool* isForInp, bool* isForOfp) template <class ParseHandler> bool -Parser<ParseHandler>::validateForInOrOfLHSExpression(Node target, PossibleError* possibleError) -{ - if (handler.isUnparenthesizedDestructuringPattern(target)) - return checkDestructuringPattern(target, Nothing(), possibleError); - - // All other permitted targets are simple. - if (!reportIfNotValidSimpleAssignmentTarget(target, ForInOrOfTarget)) - return false; - - if (handler.isPropertyAccess(target)) - return true; - - if (handler.isNameAnyParentheses(target)) { - // The arguments/eval identifiers are simple in non-strict mode code, - // but warn to discourage use nonetheless. - if (!reportIfArgumentsEvalTarget(target)) - return false; - - handler.adjustGetToSet(target); - return true; - } - - if (handler.isFunctionCall(target)) - return checkAssignmentToCall(target, JSMSG_BAD_FOR_LEFTSIDE); - - report(ParseError, false, target, JSMSG_BAD_FOR_LEFTSIDE); - return false; -} - -template <class ParseHandler> -bool Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling, ParseNodeKind* forHeadKind, Node* forInitialPart, @@ -5464,6 +5464,10 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling, return *forInitialPart != null(); } + uint32_t exprOffset; + if (!tokenStream.peekOffset(&exprOffset, TokenStream::Operand)) + return false; + // Finally, handle for-loops that start with expressions. Pass // |InProhibited| so that |in| isn't parsed in a RelationalExpression as a // binary operator. |in| makes it a for-in loop, *not* an |in| expression. @@ -5501,14 +5505,35 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling, // // See ES6 13.7. if (isForOf && letIsIdentifier) { - report(ParseError, false, *forInitialPart, JSMSG_LET_STARTING_FOROF_LHS); + errorAt(exprOffset, JSMSG_LET_STARTING_FOROF_LHS); return false; } *forHeadKind = isForIn ? PNK_FORIN : PNK_FOROF; - if (!validateForInOrOfLHSExpression(*forInitialPart, &possibleError)) + // Verify the left-hand side expression doesn't have a forbidden form. + if (handler.isUnparenthesizedDestructuringPattern(*forInitialPart)) { + if (!checkDestructuringPattern(*forInitialPart, Nothing(), &possibleError)) + return false; + } else if (handler.isNameAnyParentheses(*forInitialPart)) { + const char* chars = handler.nameIsArgumentsEvalAnyParentheses(*forInitialPart, context); + if (chars) { + // |chars| is "arguments" or "eval" here. + if (!strictModeErrorAt(exprOffset, JSMSG_BAD_STRICT_ASSIGN, chars)) + return false; + } + + handler.adjustGetToSet(*forInitialPart); + } else if (handler.isPropertyAccess(*forInitialPart)) { + // Permitted: no additional testing/fixup needed. + } else if (handler.isFunctionCall(*forInitialPart)) { + if (!strictModeErrorAt(exprOffset, JSMSG_BAD_FOR_LEFTSIDE)) + return false; + } else { + errorAt(exprOffset, JSMSG_BAD_FOR_LEFTSIDE); return false; + } + if (!possibleError.checkForExpressionError()) return false; @@ -5537,7 +5562,6 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling) if (matched) { iflags = JSITER_FOREACH; isForEach = true; - addTelemetry(JSCompartment::DeprecatedForEach); if (!warnOnceAboutForEach()) return null(); } @@ -5593,7 +5617,7 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling) Node init = startNode; if (isForEach) { - reportWithOffset(ParseError, false, begin, JSMSG_BAD_FOR_EACH_LOOP); + errorAt(begin, JSMSG_BAD_FOR_EACH_LOOP); return null(); } @@ -5653,19 +5677,13 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling) iflags |= JSITER_ENUMERATE; } else { if (isForEach) { - report(ParseError, false, startNode, JSMSG_BAD_FOR_EACH_LOOP); + errorAt(begin, JSMSG_BAD_FOR_EACH_LOOP); return null(); } stmt.refineForKind(StatementKind::ForOfLoop); } - if (!handler.isDeclarationList(target)) { - MOZ_ASSERT(!forLoopLexicalScope); - if (!checkAndMarkAsAssignmentLhs(target, PlainAssignment)) - return null(); - } - // Parser::declaration consumed everything up to the closing ')'. That // token follows an {Assignment,}Expression, so the next token must be // consumed as if an operator continued the expression, i.e. as None. @@ -5729,7 +5747,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling) switch (tt) { case TOK_DEFAULT: if (seenDefault) { - report(ParseError, false, null(), JSMSG_TOO_MANY_DEFAULTS); + error(JSMSG_TOO_MANY_DEFAULTS); return null(); } seenDefault = true; @@ -5743,7 +5761,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling) break; default: - report(ParseError, false, null(), JSMSG_BAD_SWITCH); + error(JSMSG_BAD_SWITCH); return null(); } @@ -5762,10 +5780,8 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling) if (tt == TOK_RC || tt == TOK_CASE || tt == TOK_DEFAULT) break; if (afterReturn) { - TokenPos pos(0, 0); - if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand)) + if (!tokenStream.peekOffset(&statementBegin, TokenStream::Operand)) return null(); - statementBegin = pos.begin; } Node stmt = statementListItem(yieldHandling); if (!stmt) @@ -5773,11 +5789,9 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling) if (!warnedAboutStatementsAfterReturn) { if (afterReturn) { if (!handler.isStatementPermittedAfterReturnStatement(stmt)) { - if (!reportWithOffset(ParseWarning, false, statementBegin, - JSMSG_STMT_AFTER_RETURN)) - { + if (!warningAt(statementBegin, JSMSG_STMT_AFTER_RETURN)) return null(); - } + warnedAboutStatementsAfterReturn = true; } } else if (handler.isReturnStatement(stmt)) { @@ -5827,8 +5841,10 @@ Parser<ParseHandler>::continueStatement(YieldHandling yieldHandling) for (;;) { stmt = ParseContext::Statement::findNearest(stmt, isLoop); if (!stmt) { - report(ParseError, false, null(), - foundLoop ? JSMSG_LABEL_NOT_FOUND : JSMSG_BAD_CONTINUE); + if (foundLoop) + error(JSMSG_LABEL_NOT_FOUND); + else + errorAt(begin, JSMSG_BAD_CONTINUE); return null(); } @@ -5848,7 +5864,7 @@ Parser<ParseHandler>::continueStatement(YieldHandling yieldHandling) break; } } else if (!pc->findInnermostStatement(isLoop)) { - report(ParseError, false, null(), JSMSG_BAD_CONTINUE); + error(JSMSG_BAD_CONTINUE); return null(); } @@ -5878,7 +5894,7 @@ Parser<ParseHandler>::breakStatement(YieldHandling yieldHandling) }; if (!pc->findInnermostStatement<ParseContext::LabelStatement>(hasSameLabel)) { - report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND); + error(JSMSG_LABEL_NOT_FOUND); return null(); } } else { @@ -5887,7 +5903,7 @@ Parser<ParseHandler>::breakStatement(YieldHandling yieldHandling) }; if (!pc->findInnermostStatement(isBreakTarget)) { - report(ParseError, false, null(), JSMSG_TOUGH_BREAK); + errorAt(begin, JSMSG_TOUGH_BREAK); return null(); } } @@ -5943,10 +5959,9 @@ Parser<ParseHandler>::returnStatement(YieldHandling yieldHandling) if (!pn) return null(); + /* Disallow "return v;" in legacy generators. */ if (pc->isLegacyGenerator() && exprNode) { - /* Disallow "return v;" in legacy generators. */ - reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN, - JSMSG_BAD_ANON_GENERATOR_RETURN); + errorAt(begin, JSMSG_BAD_GENERATOR_RETURN); return null(); } @@ -6037,13 +6052,12 @@ Parser<ParseHandler>::yieldExpression(InHandling inHandling) return null(); if (!pc->isFunctionBox()) { - report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str); + error(JSMSG_BAD_RETURN_OR_YIELD, js_yield_str); return null(); } if (pc->functionBox()->isArrow()) { - reportWithOffset(ParseError, false, begin, - JSMSG_YIELD_IN_ARROW, js_yield_str); + errorAt(begin, JSMSG_YIELD_IN_ARROW, js_yield_str); return null(); } @@ -6051,8 +6065,7 @@ Parser<ParseHandler>::yieldExpression(InHandling inHandling) pc->functionBox()->function()->isGetter() || pc->functionBox()->function()->isSetter()) { - reportWithOffset(ParseError, false, begin, - JSMSG_YIELD_IN_METHOD, js_yield_str); + errorAt(begin, JSMSG_YIELD_IN_METHOD, js_yield_str); return null(); } @@ -6063,13 +6076,11 @@ Parser<ParseHandler>::yieldExpression(InHandling inHandling) ) { /* As in Python (see PEP-255), disallow return v; in generators. */ - reportBadReturn(null(), ParseError, JSMSG_BAD_GENERATOR_RETURN, - JSMSG_BAD_ANON_GENERATOR_RETURN); + errorAt(begin, JSMSG_BAD_GENERATOR_RETURN); return null(); } pc->functionBox()->setGeneratorKind(LegacyGenerator); - addTelemetry(JSCompartment::DeprecatedLegacyGenerator); MOZ_FALLTHROUGH; @@ -6119,14 +6130,13 @@ Parser<ParseHandler>::withStatement(YieldHandling yieldHandling) MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH)); uint32_t begin = pos().begin; - // In most cases, we want the constructs forbidden in strict mode code to be - // a subset of those that JSOPTION_EXTRA_WARNINGS warns about, and we should - // use reportStrictModeError. However, 'with' is the sole instance of a - // construct that is forbidden in strict mode code, but doesn't even merit a - // warning under JSOPTION_EXTRA_WARNINGS. See + // Usually we want the constructs forbidden in strict mode code to be a + // subset of those that ContextOptions::extraWarnings() warns about, and we + // use strictModeError directly. But while 'with' is forbidden in strict + // mode code, it doesn't even merit a warning in non-strict code. See // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1. if (pc->sc()->strict()) { - if (!report(ParseStrictError, true, null(), JSMSG_STRICT_CODE_WITH)) + if (!strictModeError(JSMSG_STRICT_CODE_WITH)) return null(); } @@ -6165,7 +6175,7 @@ Parser<ParseHandler>::labeledItem(YieldHandling yieldHandling) // GeneratorDeclaration is only matched by HoistableDeclaration in // StatementListItem, so generators can't be inside labels. if (next == TOK_MUL) { - report(ParseError, false, null(), JSMSG_GENERATOR_LABEL); + error(JSMSG_GENERATOR_LABEL); return null(); } @@ -6173,11 +6183,11 @@ Parser<ParseHandler>::labeledItem(YieldHandling yieldHandling) // is ever matched. Per Annex B.3.2 that modifies this text, this // applies only to strict mode code. if (pc->sc()->strict()) { - report(ParseError, false, null(), JSMSG_FUNCTION_LABEL); + error(JSMSG_FUNCTION_LABEL); return null(); } - return functionStmt(yieldHandling, NameRequired); + return functionStmt(pos().begin, yieldHandling, NameRequired); } tokenStream.ungetToken(); @@ -6196,13 +6206,13 @@ Parser<ParseHandler>::labeledStatement(YieldHandling yieldHandling) return stmt->label() == label; }; + uint32_t begin = pos().begin; + if (pc->findInnermostStatement<ParseContext::LabelStatement>(hasSameLabel)) { - report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL); + errorAt(begin, JSMSG_DUPLICATE_LABEL); return null(); } - uint32_t begin = pos().begin; - tokenStream.consumeKnownToken(TOK_COLON); /* Push a label struct and parse the statement. */ @@ -6226,11 +6236,11 @@ Parser<ParseHandler>::throwStatement(YieldHandling yieldHandling) if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand)) return null(); if (tt == TOK_EOF || tt == TOK_SEMI || tt == TOK_RC) { - report(ParseError, false, null(), JSMSG_MISSING_EXPR_AFTER_THROW); + error(JSMSG_MISSING_EXPR_AFTER_THROW); return null(); } if (tt == TOK_EOL) { - report(ParseError, false, null(), JSMSG_LINE_BREAK_AFTER_THROW); + error(JSMSG_LINE_BREAK_AFTER_THROW); return null(); } @@ -6304,7 +6314,7 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling) /* Check for another catch after unconditional catch. */ if (hasUnconditionalCatch) { - report(ParseError, false, null(), JSMSG_CATCH_AFTER_GENERAL); + error(JSMSG_CATCH_AFTER_GENERAL); return null(); } @@ -6352,7 +6362,7 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling) } default: - report(ParseError, false, null(), JSMSG_CATCH_IDENTIFIER); + error(JSMSG_CATCH_IDENTIFIER); return null(); } @@ -6420,7 +6430,7 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling) tokenStream.ungetToken(); } if (!catchList && !finallyBlock) { - report(ParseError, false, null(), JSMSG_CATCH_OR_FINALLY); + error(JSMSG_CATCH_OR_FINALLY); return null(); } @@ -6497,49 +6507,6 @@ JSOpFromPropertyType(PropertyType propType) } } -static FunctionSyntaxKind -FunctionSyntaxKindFromPropertyType(PropertyType propType) -{ - switch (propType) { - case PropertyType::Getter: - return Getter; - case PropertyType::GetterNoExpressionClosure: - return GetterNoExpressionClosure; - case PropertyType::Setter: - return Setter; - case PropertyType::SetterNoExpressionClosure: - return SetterNoExpressionClosure; - case PropertyType::Method: - case PropertyType::GeneratorMethod: - case PropertyType::AsyncMethod: - return Method; - case PropertyType::Constructor: - return ClassConstructor; - case PropertyType::DerivedConstructor: - return DerivedClassConstructor; - default: - MOZ_CRASH("unexpected property type"); - } -} - -static GeneratorKind -GeneratorKindFromPropertyType(PropertyType propType) -{ - if (propType == PropertyType::GeneratorMethod) - return StarGenerator; - if (propType == PropertyType::AsyncMethod) - return StarGenerator; - return NotGenerator; -} - -static FunctionAsyncKind -AsyncKindFromPropertyType(PropertyType propType) -{ - if (propType == PropertyType::AsyncMethod) - return AsyncFunction; - return SyncFunction; -} - template <typename ParseHandler> typename ParseHandler::Node Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, @@ -6565,7 +6532,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, tokenStream.ungetToken(); } else { // Class statements must have a bound name - report(ParseError, false, null(), JSMSG_UNNAMED_CLASS_STMT); + error(JSMSG_UNNAMED_CLASS_STMT); return null(); } } else { @@ -6626,8 +6593,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, return null(); if (tt == TOK_RC) { tokenStream.consumeKnownToken(tt, TokenStream::KeywordIsName); - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "property name", TokenKindToDesc(tt)); + error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt)); return null(); } @@ -6644,6 +6610,10 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, tokenStream.ungetToken(); } + uint32_t nameOffset; + if (!tokenStream.peekOffset(&nameOffset)) + return null(); + PropertyType propType; Node propName = propertyName(yieldHandling, classMethods, &propType, &propAtom); if (!propName) @@ -6654,7 +6624,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, propType != PropertyType::AsyncMethod && propType != PropertyType::Constructor && propType != PropertyType::DerivedConstructor) { - report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF); + errorAt(nameOffset, JSMSG_BAD_METHOD_DEF); return null(); } @@ -6664,17 +6634,17 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, propType = PropertyType::SetterNoExpressionClosure; if (!isStatic && propAtom == context->names().constructor) { if (propType != PropertyType::Method) { - report(ParseError, false, propName, JSMSG_BAD_METHOD_DEF); + errorAt(nameOffset, JSMSG_BAD_METHOD_DEF); return null(); } if (seenConstructor) { - report(ParseError, false, propName, JSMSG_DUPLICATE_PROPERTY, "constructor"); + errorAt(nameOffset, JSMSG_DUPLICATE_PROPERTY, "constructor"); return null(); } seenConstructor = true; propType = hasHeritage ? PropertyType::DerivedConstructor : PropertyType::Constructor; } else if (isStatic && propAtom == context->names().prototype) { - report(ParseError, false, propName, JSMSG_BAD_METHOD_DEF); + errorAt(nameOffset, JSMSG_BAD_METHOD_DEF); return null(); } @@ -6696,7 +6666,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, if (!tokenStream.isCurrentTokenType(TOK_RB)) funName = propAtom; } - Node fn = methodDefinition(propType, funName); + Node fn = methodDefinition(nameOffset, propType, funName); if (!fn) return null(); @@ -6901,8 +6871,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling) } if (forbiddenLetDeclaration) { - report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, - "lexical declarations"); + error(JSMSG_FORBIDDEN_AS_STATEMENT, "lexical declarations"); return null(); } } @@ -6956,7 +6925,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling) // detected this way, so don't bother passing around an extra parameter // everywhere. if (!pc->isFunctionBox()) { - report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str); + error(JSMSG_BAD_RETURN_OR_YIELD, js_return_str); return null(); } return returnStatement(yieldHandling); @@ -6984,12 +6953,12 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling) // statement of |if| or |else|, but Parser::consequentOrAlternative // handles that). case TOK_FUNCTION: - report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations"); + error(JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations"); return null(); // |class| is also forbidden by lookahead restriction. case TOK_CLASS: - report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "classes"); + error(JSMSG_FORBIDDEN_AS_STATEMENT, "classes"); return null(); // ImportDeclaration (only inside modules) @@ -7003,11 +6972,11 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling) // Miscellaneous error cases arguably better caught here than elsewhere. case TOK_CATCH: - report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY); + error(JSMSG_CATCH_WITHOUT_TRY); return null(); case TOK_FINALLY: - report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY); + error(JSMSG_FINALLY_WITHOUT_TRY); return null(); // NOTE: default case handled in the ExpressionStatement section. @@ -7048,7 +7017,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling, if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) { if (!abortIfSyntaxParser()) return null(); - if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL)) + if (!warning(JSMSG_USE_ASM_DIRECTIVE_FAIL)) return null(); } return expressionStatement(yieldHandling); @@ -7089,8 +7058,9 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling, if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); if (nextSameLine == TOK_FUNCTION) { + uint32_t preludeStart = pos().begin; tokenStream.consumeKnownToken(TOK_FUNCTION); - return functionStmt(yieldHandling, NameRequired, AsyncFunction); + return functionStmt(preludeStart, yieldHandling, NameRequired, AsyncFunction); } } @@ -7141,7 +7111,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling, // detected this way, so don't bother passing around an extra parameter // everywhere. if (!pc->isFunctionBox()) { - report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str); + error(JSMSG_BAD_RETURN_OR_YIELD, js_return_str); return null(); } return returnStatement(yieldHandling); @@ -7169,7 +7139,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling, // HoistableDeclaration[?Yield, ~Default] case TOK_FUNCTION: - return functionStmt(yieldHandling, NameRequired); + return functionStmt(pos().begin, yieldHandling, NameRequired); // ClassDeclaration[?Yield, ~Default] case TOK_CLASS: @@ -7193,11 +7163,11 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling, // Miscellaneous error cases arguably better caught here than elsewhere. case TOK_CATCH: - report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY); + error(JSMSG_CATCH_WITHOUT_TRY); return null(); case TOK_FINALLY: - report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY); + error(JSMSG_FINALLY_WITHOUT_TRY); return null(); // NOTE: default case handled in the ExpressionStatement section. @@ -7242,8 +7212,7 @@ Parser<ParseHandler>::expr(InHandling inHandling, YieldHandling yieldHandling, if (!tokenStream.peekToken(&tt)) return null(); if (tt != TOK_ARROW) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "expression", TokenKindToDesc(TOK_RP)); + error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TOK_RP)); return null(); } @@ -7400,7 +7369,7 @@ Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling return null(); // Report an error for unary expressions on the LHS of **. if (tok == TOK_POW && handler.isUnparenthesizedUnaryExpression(pn)) { - report(ParseError, false, null(), JSMSG_BAD_POW_LEFTSIDE); + error(JSMSG_BAD_POW_LEFTSIDE); return null(); } pnk = BinaryOpTokenKindToParseNodeKind(tok); @@ -7470,45 +7439,6 @@ Parser<ParseHandler>::condExpr1(InHandling inHandling, YieldHandling yieldHandli return handler.newConditional(condition, thenExpr, elseExpr); } -template <typename ParseHandler> -bool -Parser<ParseHandler>::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor flavor, - PossibleError* possibleError) -{ - MOZ_ASSERT(flavor != KeyedDestructuringAssignment, - "destructuring must use special checking/marking code, not " - "this method"); - - if (handler.isUnparenthesizedDestructuringPattern(target)) { - if (flavor == CompoundAssignment) { - report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS); - return false; - } - - return checkDestructuringPattern(target, Nothing(), possibleError); - } - - // All other permitted targets are simple. - if (!reportIfNotValidSimpleAssignmentTarget(target, flavor)) - return false; - - if (handler.isPropertyAccess(target)) - return true; - - if (handler.isNameAnyParentheses(target)) { - // The arguments/eval identifiers are simple in non-strict mode code, - // but warn to discourage use nonetheless. - if (!reportIfArgumentsEvalTarget(target)) - return false; - - handler.adjustGetToSet(target); - return true; - } - - MOZ_ASSERT(handler.isFunctionCall(target)); - return checkAssignmentToCall(target, JSMSG_BAD_LEFTSIDE_OF_ASS); -} - class AutoClearInDestructuringDecl { ParseContext* pc_; @@ -7553,6 +7483,8 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); + uint32_t exprOffset = pos().begin; + bool endsExpr; if (tt == TOK_NAME) { @@ -7620,8 +7552,7 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl if (!tokenStream.getToken(&tt)) return null(); if (tt != TOK_ARROW) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "'=>' after argument list", TokenKindToDesc(tt)); + error(JSMSG_UNEXPECTED_TOKEN, "'=>' after argument list", TokenKindToDesc(tt)); return null(); } @@ -7659,7 +7590,7 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl MOZ_ASSERT(next == TOK_ARROW || next == TOK_EOL); if (next != TOK_ARROW) { - report(ParseError, false, null(), JSMSG_LINE_BREAK_BEFORE_ARROW); + error(JSMSG_LINE_BREAK_BEFORE_ARROW); return null(); } tokenStream.consumeKnownToken(TOK_ARROW); @@ -7672,8 +7603,10 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl tokenStream.seek(start); - if (!tokenStream.peekToken(&next, TokenStream::Operand)) + if (!tokenStream.getToken(&next, TokenStream::Operand)) return null(); + uint32_t preludeStart = pos().begin; + tokenStream.ungetToken(); GeneratorKind generatorKind = NotGenerator; FunctionAsyncKind asyncKind = SyncFunction; @@ -7697,7 +7630,11 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl } } - Node arrowFunc = functionDefinition(inHandling, yieldHandling, nullptr, + Node pn = handler.newArrowFunction(); + if (!pn) + return null(); + + Node arrowFunc = functionDefinition(preludeStart, pn, inHandling, yieldHandling, nullptr, Arrow, generatorKind, asyncKind); if (!arrowFunc) return null(); @@ -7747,9 +7684,33 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl return lhs; } - AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment; - if (!checkAndMarkAsAssignmentLhs(lhs, flavor, &possibleErrorInner)) + // Verify the left-hand side expression doesn't have a forbidden form. + if (handler.isUnparenthesizedDestructuringPattern(lhs)) { + if (kind != PNK_ASSIGN) { + error(JSMSG_BAD_DESTRUCT_ASS); + return null(); + } + + if (!checkDestructuringPattern(lhs, Nothing(), &possibleErrorInner)) + return null(); + } else if (handler.isNameAnyParentheses(lhs)) { + if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) { + // |chars| is "arguments" or "eval" here. + if (!strictModeErrorAt(exprOffset, JSMSG_BAD_STRICT_ASSIGN, chars)) + return null(); + } + + handler.adjustGetToSet(lhs); + } else if (handler.isPropertyAccess(lhs)) { + // Permitted: no additional testing/fixup needed. + } else if (handler.isFunctionCall(lhs)) { + if (!strictModeErrorAt(exprOffset, JSMSG_BAD_LEFTSIDE_OF_ASS)) + return null(); + } else { + errorAt(exprOffset, JSMSG_BAD_LEFTSIDE_OF_ASS); return null(); + } + if (!possibleErrorInner.checkForExpressionError()) return null(); @@ -7796,91 +7757,29 @@ Parser<ParseHandler>::isValidSimpleAssignmentTarget(Node node, template <typename ParseHandler> bool -Parser<ParseHandler>::reportIfArgumentsEvalTarget(Node nameNode) +Parser<ParseHandler>::checkIncDecOperand(Node operand, uint32_t operandOffset) { - const char* chars = handler.nameIsArgumentsEvalAnyParentheses(nameNode, context); - if (!chars) - return true; - - if (!report(ParseStrictError, pc->sc()->strict(), nameNode, JSMSG_BAD_STRICT_ASSIGN, chars)) - return false; - - MOZ_ASSERT(!pc->sc()->strict(), - "an error should have been reported if this was strict mode " - "code"); - return true; -} - -template <typename ParseHandler> -bool -Parser<ParseHandler>::reportIfNotValidSimpleAssignmentTarget(Node target, AssignmentFlavor flavor) -{ - FunctionCallBehavior behavior = flavor == KeyedDestructuringAssignment - ? ForbidAssignmentToFunctionCalls - : PermitAssignmentToFunctionCalls; - if (isValidSimpleAssignmentTarget(target, behavior)) - return true; - - if (handler.isNameAnyParentheses(target)) { - // Use a special error if the target is arguments/eval. This ensures - // targeting these names is consistently a SyntaxError (which error numbers - // below don't guarantee) while giving us a nicer error message. - if (!reportIfArgumentsEvalTarget(target)) + if (handler.isNameAnyParentheses(operand)) { + if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(operand, context)) { + if (!strictModeErrorAt(operandOffset, JSMSG_BAD_STRICT_ASSIGN, chars)) + return false; + } + } else if (handler.isPropertyAccess(operand)) { + // Permitted: no additional testing/fixup needed. + } else if (handler.isFunctionCall(operand)) { + // Assignment to function calls is forbidden in ES6. We're still + // somewhat concerned about sites using this in dead code, so forbid it + // only in strict mode code (or if the werror option has been set), and + // otherwise warn. + if (!strictModeErrorAt(operandOffset, JSMSG_BAD_INCOP_OPERAND)) return false; - } - - unsigned errnum = 0; - const char* extra = nullptr; - - switch (flavor) { - case IncrementAssignment: - errnum = JSMSG_BAD_OPERAND; - extra = "increment"; - break; - - case DecrementAssignment: - errnum = JSMSG_BAD_OPERAND; - extra = "decrement"; - break; - - case KeyedDestructuringAssignment: - errnum = JSMSG_BAD_DESTRUCT_TARGET; - break; - - case PlainAssignment: - case CompoundAssignment: - errnum = JSMSG_BAD_LEFTSIDE_OF_ASS; - break; - - case ForInOrOfTarget: - errnum = JSMSG_BAD_FOR_LEFTSIDE; - break; - } - - report(ParseError, pc->sc()->strict(), target, errnum, extra); - return false; -} - -template <typename ParseHandler> -bool -Parser<ParseHandler>::checkAndMarkAsIncOperand(Node target, AssignmentFlavor flavor) -{ - MOZ_ASSERT(flavor == IncrementAssignment || flavor == DecrementAssignment); - - // Check. - if (!reportIfNotValidSimpleAssignmentTarget(target, flavor)) + } else { + errorAt(operandOffset, JSMSG_BAD_INCOP_OPERAND); return false; - - // Mark. - if (handler.isNameAnyParentheses(target)) { - // Assignment to arguments/eval is allowed outside strict mode code, - // but it's dodgy. Report a strict warning (error, if werror was set). - if (!reportIfArgumentsEvalTarget(target)) - return false; - } else if (handler.isFunctionCall(target)) { - if (!checkAssignmentToCall(target, JSMSG_BAD_INCOP_OPERAND)) - return false; } + + MOZ_ASSERT(isValidSimpleAssignmentTarget(operand, PermitAssignmentToFunctionCalls), + "inconsistent increment/decrement operand validation"); return true; } @@ -7944,18 +7843,21 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t TokenKind tt2; if (!tokenStream.getToken(&tt2, TokenStream::Operand)) return null(); - Node pn2 = memberExpr(yieldHandling, TripledotProhibited, tt2); - if (!pn2) - return null(); - AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment; - if (!checkAndMarkAsIncOperand(pn2, flavor)) + + uint32_t operandOffset = pos().begin; + Node operand = memberExpr(yieldHandling, TripledotProhibited, tt2); + if (!operand || !checkIncDecOperand(operand, operandOffset)) return null(); + return handler.newUpdate((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT, - begin, - pn2); + begin, operand); } case TOK_DELETE: { + uint32_t exprOffset; + if (!tokenStream.peekOffset(&exprOffset, TokenStream::Operand)) + return null(); + Node expr = unaryExpr(yieldHandling, TripledotProhibited); if (!expr) return null(); @@ -7963,8 +7865,9 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t // Per spec, deleting any unary expression is valid -- it simply // returns true -- except for one case that is illegal in strict mode. if (handler.isNameAnyParentheses(expr)) { - if (!report(ParseStrictError, pc->sc()->strict(), expr, JSMSG_DEPRECATED_DELETE_OPERAND)) + if (!strictModeErrorAt(exprOffset, JSMSG_DEPRECATED_DELETE_OPERAND)) return null(); + pc->sc()->setBindingsAccessedDynamically(); } @@ -7975,7 +7878,7 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t if (!pc->isAsync()) { // TOK_AWAIT can be returned in module, even if it's not inside // async function. - report(ParseError, false, null(), JSMSG_RESERVED_ID, "await"); + error(JSMSG_RESERVED_ID, "await"); return null(); } @@ -7987,24 +7890,23 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t } default: { - Node pn = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true, + Node expr = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true, possibleError, invoked); - if (!pn) + if (!expr) return null(); /* Don't look across a newline boundary for a postfix incop. */ if (!tokenStream.peekTokenSameLine(&tt)) return null(); - if (tt == TOK_INC || tt == TOK_DEC) { - tokenStream.consumeKnownToken(tt); - AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment; - if (!checkAndMarkAsIncOperand(pn, flavor)) - return null(); - return handler.newUpdate((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT, - begin, - pn); - } - return pn; + + if (tt != TOK_INC && tt != TOK_DEC) + return expr; + + tokenStream.consumeKnownToken(tt); + if (!checkIncDecOperand(expr, begin)) + return null(); + return handler.newUpdate((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT, + begin, expr); } } } @@ -8026,10 +7928,9 @@ template <typename ParseHandler> typename ParseHandler::Node Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin) { - Node genfn = handler.newFunctionDefinition(); + Node genfn = handler.newFunctionExpression(); if (!genfn) return null(); - handler.setOp(genfn, JSOP_LAMBDA); ParseContext* outerpc = pc; @@ -8049,8 +7950,8 @@ Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin) // Create box for fun->object early to root it. Directives directives(/* strict = */ outerpc->sc()->strict()); - FunctionBox* genFunbox = newFunctionBox(genfn, fun, directives, StarGenerator, SyncFunction, - /* tryAnnexB = */ false); + FunctionBox* genFunbox = newFunctionBox(genfn, fun, /* preludeStart = */ 0, directives, + StarGenerator, SyncFunction, /* tryAnnexB = */ false); if (!genFunbox) return null(); genFunbox->isGenexpLambda = true; @@ -8082,12 +7983,14 @@ Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin) MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); + uint32_t end = pos().end; handler.setBeginPosition(comp, begin); - handler.setEndPosition(comp, pos().end); + handler.setEndPosition(comp, end); + genFunbox->bufEnd = end; handler.addStatementToList(body, comp); - handler.setEndPosition(body, pos().end); + handler.setEndPosition(body, end); handler.setBeginPosition(genfn, begin); - handler.setEndPosition(genfn, pos().end); + handler.setEndPosition(genfn, end); Node generator = newDotGeneratorName(); if (!generator) @@ -8126,7 +8029,7 @@ Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind) MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME); RootedPropertyName name(context, tokenStream.currentName()); if (name == context->names().let) { - report(ParseError, false, null(), JSMSG_LET_COMP_BINDING); + error(JSMSG_LET_COMP_BINDING); return null(); } TokenPos namePos = pos(); @@ -8137,7 +8040,7 @@ Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind) if (!tokenStream.matchContextualKeyword(&matched, context->names().of)) return null(); if (!matched) { - report(ParseError, false, null(), JSMSG_OF_AFTER_FOR_NAME); + error(JSMSG_OF_AFTER_FOR_NAME); return null(); } @@ -8198,7 +8101,7 @@ Parser<ParseHandler>::comprehensionIf(GeneratorKind comprehensionKind) /* Check for (a = b) and warn about possible (a == b) mistype. */ if (handler.isUnparenthesizedAssignment(cond)) { - if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN)) + if (!extraWarning(JSMSG_EQUAL_AS_ASSIGN)) return null(); } @@ -8259,8 +8162,7 @@ Parser<ParseHandler>::comprehension(GeneratorKind comprehensionKind) return null(); if (comprehensionKind != NotGenerator && pc->lastYieldOffset != startYieldOffset) { - reportWithOffset(ParseError, false, pc->lastYieldOffset, - JSMSG_BAD_GENEXP_BODY, js_yield_str); + errorAt(pc->lastYieldOffset, JSMSG_BAD_GENEXP_BODY, js_yield_str); return null(); } @@ -8322,11 +8224,11 @@ Parser<ParseHandler>::assignExprWithoutYieldOrAwait(YieldHandling yieldHandling) Node res = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (res) { if (pc->lastYieldOffset != startYieldOffset) { - reportWithOffset(ParseError, false, pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT); + errorAt(pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT); return null(); } if (pc->lastAwaitOffset != startAwaitOffset) { - reportWithOffset(ParseError, false, pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT); + errorAt(pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT); return null(); } } @@ -8383,13 +8285,8 @@ Parser<ParseHandler>::argumentList(YieldHandling yieldHandling, Node listNode, b } } - TokenKind tt; - if (!tokenStream.getToken(&tt)) - return false; - if (tt != TOK_RP) { - report(ParseError, false, null(), JSMSG_PAREN_AFTER_ARGS); - return false; - } + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_ARGS); + handler.setEndPosition(listNode, pos().end); return true; } @@ -8480,14 +8377,14 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling if (tt == TOK_NAME) { PropertyName* field = tokenStream.currentName(); if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) { - report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "property"); + error(JSMSG_BAD_SUPERPROP, "property"); return null(); } nextMember = handler.newPropertyAccess(lhs, field, pos().end); if (!nextMember) return null(); } else { - report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT); + error(JSMSG_NAME_AFTER_DOT); return null(); } } else if (tt == TOK_LB) { @@ -8498,7 +8395,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX); if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) { - report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "member"); + error(JSMSG_BAD_SUPERPROP, "member"); return null(); } nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end); @@ -8510,12 +8407,12 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling { if (handler.isSuperBase(lhs)) { if (!pc->sc()->allowSuperCall()) { - report(ParseError, false, null(), JSMSG_BAD_SUPERCALL); + error(JSMSG_BAD_SUPERCALL); return null(); } if (tt != TOK_LP) { - report(ParseError, false, null(), JSMSG_BAD_SUPER); + error(JSMSG_BAD_SUPER); return null(); } @@ -8542,7 +8439,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling return null(); } else { if (options().selfHostingMode && handler.isPropertyAccess(lhs)) { - report(ParseError, false, null(), JSMSG_SELFHOSTED_METHOD_CALL); + error(JSMSG_SELFHOSTED_METHOD_CALL); return null(); } @@ -8624,7 +8521,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling } if (handler.isSuperBase(lhs)) { - report(ParseError, false, null(), JSMSG_BAD_SUPER); + error(JSMSG_BAD_SUPER); return null(); } @@ -8677,7 +8574,7 @@ Parser<ParseHandler>::labelOrIdentifierReference(YieldHandling yieldHandling, ? "static" : nullptr; if (badName) { - report(ParseError, false, null(), JSMSG_RESERVED_ID, badName); + error(JSMSG_RESERVED_ID, badName); return nullptr; } } @@ -8686,7 +8583,7 @@ Parser<ParseHandler>::labelOrIdentifierReference(YieldHandling yieldHandling, pc->sc()->strict() || versionNumber() >= JSVERSION_1_7) { - report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield"); + error(JSMSG_RESERVED_ID, "yield"); return nullptr; } } @@ -8722,7 +8619,7 @@ Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling) ? "eval" : nullptr; if (badName) { - report(ParseError, false, null(), JSMSG_BAD_STRICT_ASSIGN, badName); + error(JSMSG_BAD_STRICT_ASSIGN, badName); return nullptr; } @@ -8732,7 +8629,7 @@ Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling) ? "static" : nullptr; if (badName) { - report(ParseError, false, null(), JSMSG_RESERVED_ID, badName); + error(JSMSG_RESERVED_ID, badName); return nullptr; } } @@ -8741,7 +8638,7 @@ Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling) pc->sc()->strict() || versionNumber() >= JSVERSION_1_7) { - report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield"); + error(JSMSG_RESERVED_ID, "yield"); return nullptr; } } @@ -8840,7 +8737,7 @@ Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling, PossibleErro TokenStream::Modifier modifier = TokenStream::Operand; for (; ; index++) { if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) { - report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG); + error(JSMSG_ARRAY_INIT_TOO_BIG); return null(); } @@ -8883,7 +8780,7 @@ Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling, PossibleErro break; } if (tt == TOK_TRIPLEDOT && possibleError) - possibleError->setPendingDestructuringError(null(), JSMSG_REST_WITH_COMMA); + possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA); } } @@ -8950,7 +8847,7 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList, } if (isAsync && isGenerator) { - report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR); + error(JSMSG_ASYNC_GENERATOR); return null(); } @@ -9063,7 +8960,7 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList, } default: - report(ParseError, false, null(), JSMSG_BAD_PROP_ID); + error(JSMSG_BAD_PROP_ID); return null(); } @@ -9073,7 +8970,7 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList, if (tt == TOK_COLON) { if (isGenerator) { - report(ParseError, false, null(), JSMSG_BAD_PROP_ID); + error(JSMSG_BAD_PROP_ID); return null(); } *propType = PropertyType::Normal; @@ -9082,7 +8979,7 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList, if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN)) { if (isGenerator) { - report(ParseError, false, null(), JSMSG_BAD_PROP_ID); + error(JSMSG_BAD_PROP_ID); return null(); } tokenStream.ungetToken(); @@ -9103,7 +9000,7 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList, return propName; } - report(ParseError, false, null(), JSMSG_COLON_AFTER_ID); + error(JSMSG_COLON_AFTER_ID); return null(); } @@ -9153,6 +9050,8 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* if (tt == TOK_RC) break; + TokenPos namePos = pos(); + tokenStream.ungetToken(); PropertyType propType; @@ -9176,14 +9075,14 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* // Directly report the error when we're not in a // destructuring context. if (!possibleError) { - report(ParseError, false, propName, JSMSG_DUPLICATE_PROTO_PROPERTY); + errorAt(namePos.begin, JSMSG_DUPLICATE_PROTO_PROPERTY); return null(); } // Otherwise delay error reporting until we've determined // whether or not we're destructuring. - possibleError->setPendingExpressionError(propName, - JSMSG_DUPLICATE_PROTO_PROPERTY); + possibleError->setPendingExpressionErrorAt(namePos, + JSMSG_DUPLICATE_PROTO_PROPERTY); } seenPrototypeMutation = true; @@ -9191,8 +9090,7 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* // __proto__: v mutates [[Prototype]]. Getters, setters, // method/generator definitions, computed property name // versions of all of these, and shorthands do not. - uint32_t begin = handler.getPosition(propName).begin; - if (!handler.addPrototypeMutation(literal, begin, propExpr)) + if (!handler.addPrototypeMutation(literal, namePos.begin, propExpr)) return null(); } else { if (!handler.isConstant(propExpr)) @@ -9212,7 +9110,7 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* return null(); if (propToken != TOK_NAME && propToken != TOK_YIELD) { - report(ParseError, false, null(), JSMSG_RESERVED_ID, TokenKindToDesc(propToken)); + error(JSMSG_RESERVED_ID, TokenKindToDesc(propToken)); return null(); } @@ -9237,7 +9135,7 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* return null(); if (propToken != TOK_NAME && propToken != TOK_YIELD) { - report(ParseError, false, null(), JSMSG_RESERVED_ID, TokenKindToDesc(propToken)); + error(JSMSG_RESERVED_ID, TokenKindToDesc(propToken)); return null(); } @@ -9261,14 +9159,14 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* // Destructuring defaults are definitely not allowed in this object literal, // because of something the caller knows about the preceding code. // For example, maybe the preceding token is an operator: `x + {y=z}`. - report(ParseError, false, null(), JSMSG_COLON_AFTER_ID); + error(JSMSG_COLON_AFTER_ID); return null(); } // Here we set a pending error so that later in the parse, once we've // determined whether or not we're destructuring, the error can be // reported or ignored appropriately. - possibleError->setPendingExpressionError(null(), JSMSG_COLON_AFTER_ID); + possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID); } Node rhs; @@ -9304,7 +9202,7 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* } } - Node fn = methodDefinition(propType, funName); + Node fn = methodDefinition(namePos.begin, propType, funName); if (!fn) return null(); @@ -9320,7 +9218,7 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* if (tt == TOK_RC) break; if (tt != TOK_COMMA) { - report(ParseError, false, null(), JSMSG_CURLY_AFTER_LIST); + error(JSMSG_CURLY_AFTER_LIST); return null(); } } @@ -9331,13 +9229,62 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* template <typename ParseHandler> typename ParseHandler::Node -Parser<ParseHandler>::methodDefinition(PropertyType propType, HandleAtom funName) +Parser<ParseHandler>::methodDefinition(uint32_t preludeStart, PropertyType propType, + HandleAtom funName) { - FunctionSyntaxKind kind = FunctionSyntaxKindFromPropertyType(propType); - GeneratorKind generatorKind = GeneratorKindFromPropertyType(propType); - FunctionAsyncKind asyncKind = AsyncKindFromPropertyType(propType); + FunctionSyntaxKind kind; + switch (propType) { + case PropertyType::Getter: + kind = Getter; + break; + + case PropertyType::GetterNoExpressionClosure: + kind = GetterNoExpressionClosure; + break; + + case PropertyType::Setter: + kind = Setter; + break; + + case PropertyType::SetterNoExpressionClosure: + kind = SetterNoExpressionClosure; + break; + + case PropertyType::Method: + case PropertyType::GeneratorMethod: + case PropertyType::AsyncMethod: + kind = Method; + break; + + case PropertyType::Constructor: + kind = ClassConstructor; + break; + + case PropertyType::DerivedConstructor: + kind = DerivedClassConstructor; + break; + + default: + MOZ_CRASH("Parser: methodDefinition: unexpected property type"); + } + + GeneratorKind generatorKind = (propType == PropertyType::GeneratorMethod || + propType == PropertyType::AsyncMethod) + ? StarGenerator + : NotGenerator; + + FunctionAsyncKind asyncKind = (propType == PropertyType::AsyncMethod) + ? AsyncFunction + : SyncFunction; + YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); - return functionDefinition(InAllowed, yieldHandling, funName, kind, generatorKind, asyncKind); + + Node pn = handler.newFunctionExpression(); + if (!pn) + return null(); + + return functionDefinition(preludeStart, pn, InAllowed, yieldHandling, funName, + kind, generatorKind, asyncKind); } template <typename ParseHandler> @@ -9367,8 +9314,7 @@ Parser<ParseHandler>::tryNewTarget(Node &newTarget) if (!tokenStream.getToken(&next)) return false; if (next != TOK_NAME || tokenStream.currentName() != context->names().target) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "target", TokenKindToDesc(next)); + error(JSMSG_UNEXPECTED_TOKEN, "target", TokenKindToDesc(next)); return false; } @@ -9376,7 +9322,7 @@ Parser<ParseHandler>::tryNewTarget(Node &newTarget) return false; if (!pc->sc()->allowNewTarget()) { - reportWithOffset(ParseError, false, begin, JSMSG_BAD_NEWTARGET); + errorAt(begin, JSMSG_BAD_NEWTARGET); return false; } @@ -9399,7 +9345,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling switch (tt) { case TOK_FUNCTION: - return functionExpr(invoked); + return functionExpr(pos().begin, invoked); case TOK_CLASS: return classDefinition(yieldHandling, ClassExpression, NameRequired); @@ -9423,8 +9369,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling if (!tokenStream.peekToken(&next)) return null(); if (next != TOK_ARROW) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "expression", TokenKindToDesc(TOK_RP)); + error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TOK_RP)); return null(); } @@ -9466,8 +9411,9 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling return null(); if (nextSameLine == TOK_FUNCTION) { + uint32_t preludeStart = pos().begin; tokenStream.consumeKnownToken(TOK_FUNCTION); - return functionExpr(PredictUninvoked, AsyncFunction); + return functionExpr(preludeStart, PredictUninvoked, AsyncFunction); } } @@ -9510,8 +9456,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling // name, closing parenthesis, and arrow, and allow it only if all are // present. if (tripledotHandling != TripledotAllowed) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "expression", TokenKindToDesc(tt)); + error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt)); return null(); } @@ -9533,8 +9478,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling // or "arguments" should be prohibited. Argument-parsing code // handles that. if (next != TOK_NAME && next != TOK_YIELD) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "rest argument name", TokenKindToDesc(next)); + error(JSMSG_UNEXPECTED_TOKEN, "rest argument name", TokenKindToDesc(next)); return null(); } } @@ -9542,8 +9486,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling if (!tokenStream.getToken(&next)) return null(); if (next != TOK_RP) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "closing parenthesis", TokenKindToDesc(next)); + error(JSMSG_UNEXPECTED_TOKEN, "closing parenthesis", TokenKindToDesc(next)); return null(); } @@ -9552,8 +9495,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling if (next != TOK_ARROW) { // Advance the scanner for proper error location reporting. tokenStream.consumeKnownToken(next); - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "'=>' after argument list", TokenKindToDesc(next)); + error(JSMSG_UNEXPECTED_TOKEN, "'=>' after argument list", TokenKindToDesc(next)); return null(); } @@ -9564,8 +9506,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling } default: - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "expression", TokenKindToDesc(tt)); + error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt)); return null(); } } @@ -9581,16 +9522,6 @@ Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHan } template <typename ParseHandler> -void -Parser<ParseHandler>::addTelemetry(JSCompartment::DeprecatedLanguageExtension e) -{ - JSContext* cx = context->maybeJSContext(); - if (!cx) - return; - cx->compartment()->addTelemetry(getFilename(), e); -} - -template <typename ParseHandler> bool Parser<ParseHandler>::warnOnceAboutExprClosure() { @@ -9600,7 +9531,7 @@ Parser<ParseHandler>::warnOnceAboutExprClosure() return true; if (!cx->compartment()->warnedAboutExprClosure) { - if (!report(ParseWarning, false, null(), JSMSG_DEPRECATED_EXPR_CLOSURE)) + if (!warning(JSMSG_DEPRECATED_EXPR_CLOSURE)) return false; cx->compartment()->warnedAboutExprClosure = true; } @@ -9618,7 +9549,7 @@ Parser<ParseHandler>::warnOnceAboutForEach() if (!cx->compartment()->warnedAboutForEach) { // Disabled warning spew. - // if (!report(ParseWarning, false, null(), JSMSG_DEPRECATED_FOR_EACH)) + // if (!warning(JSMSG_DEPRECATED_FOR_EACH)) // return false; cx->compartment()->warnedAboutForEach = true; } diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index b58b021cd..156a1c1b0 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -769,13 +769,13 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter * * Ex: * PossibleError possibleError(*this); - * possibleError.setPendingExpressionError(pn, JSMSG_BAD_PROP_ID); + * possibleError.setPendingExpressionErrorAt(pos, JSMSG_BAD_PROP_ID); * // A JSMSG_BAD_PROP_ID ParseError is reported, returns false. * if (!possibleError.checkForExpressionError()) * return false; // we reach this point with a pending exception * * PossibleError possibleError(*this); - * possibleError.setPendingExpressionError(pn, JSMSG_BAD_PROP_ID); + * possibleError.setPendingExpressionErrorAt(pos, JSMSG_BAD_PROP_ID); * // Returns true, no error is reported. * if (!possibleError.checkForDestructuringError()) * return false; // not reached, no pending exception @@ -815,7 +815,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter // Set a pending error. Only a single error may be set per instance and // error kind. - void setPending(ErrorKind kind, Node pn, unsigned errorNumber); + void setPending(ErrorKind kind, const TokenPos& pos, unsigned errorNumber); // If there is a pending error, report it and return false, otherwise // return true. @@ -830,12 +830,12 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter // Set a pending destructuring error. Only a single error may be set // per instance, i.e. subsequent calls to this method are ignored and // won't overwrite the existing pending error. - void setPendingDestructuringError(Node pn, unsigned errorNumber); + void setPendingDestructuringErrorAt(const TokenPos& pos, unsigned errorNumber); // Set a pending expression error. Only a single error may be set per // instance, i.e. subsequent calls to this method are ignored and won't // overwrite the existing pending error. - void setPendingExpressionError(Node pn, unsigned errorNumber); + void setPendingExpressionErrorAt(const TokenPos& pos, unsigned errorNumber); // If there is a pending destructuring error, report it and return // false, otherwise return true. Clears any pending expression error. @@ -903,14 +903,40 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter void prepareNodeForMutation(Node node) { handler.prepareNodeForMutation(node); } void freeTree(Node node) { handler.freeTree(node); } - private: - bool reportHelper(ParseReportKind kind, bool strict, uint32_t offset, - unsigned errorNumber, va_list args); public: - bool report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...); bool reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...); - bool reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset, unsigned errorNumber, - ...); + + /* Report the given error at the current offset. */ + void error(unsigned errorNumber, ...); + + /* Report the given error at the given offset. */ + void errorAt(uint32_t offset, unsigned errorNumber, ...); + + /* + * Handle a strict mode error at the current offset. Report an error if in + * strict mode code, or warn if not, using the given error number and + * arguments. + */ + MOZ_MUST_USE bool strictModeError(unsigned errorNumber, ...); + + /* + * Handle a strict mode error at the given offset. Report an error if in + * strict mode code, or warn if not, using the given error number and + * arguments. + */ + MOZ_MUST_USE bool strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...); + + /* Report the given warning at the current offset. */ + MOZ_MUST_USE bool warning(unsigned errorNumber, ...); + + /* Report the given warning at the given offset. */ + MOZ_MUST_USE bool warningAt(uint32_t offset, unsigned errorNumber, ...); + + /* + * If extra warnings are enabled, report the given warning at the current + * offset. + */ + MOZ_MUST_USE bool extraWarning(unsigned errorNumber, ...); Parser(ExclusiveContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options, const char16_t* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames, @@ -954,7 +980,8 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter * cx->tempLifoAlloc. */ ObjectBox* newObjectBox(JSObject* obj); - FunctionBox* newFunctionBox(Node fn, JSFunction* fun, Directives directives, + FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t preludeStart, + Directives directives, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB); @@ -1034,8 +1061,9 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter // Parse an inner function given an enclosing ParseContext and a // FunctionBox for the inner function. - bool innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, InHandling inHandling, - YieldHandling yieldHandling, FunctionSyntaxKind kind, + bool innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, uint32_t preludeStart, + InHandling inHandling, YieldHandling yieldHandling, + FunctionSyntaxKind kind, Directives inheritedDirectives, Directives* newDirectives); // Parse a function's formal parameters and its body assuming its function @@ -1088,9 +1116,10 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter * Some parsers have two versions: an always-inlined version (with an 'i' * suffix) and a never-inlined version (with an 'n' suffix). */ - Node functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling, + Node functionStmt(uint32_t preludeStart, + YieldHandling yieldHandling, DefaultHandling defaultHandling, FunctionAsyncKind asyncKind = SyncFunction); - Node functionExpr(InvokedPrediction invoked = PredictUninvoked, + Node functionExpr(uint32_t preludeStart, InvokedPrediction invoked = PredictUninvoked, FunctionAsyncKind asyncKind = SyncFunction); Node statementList(YieldHandling yieldHandling); @@ -1106,7 +1135,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter Node* forInitialPart, mozilla::Maybe<ParseContext::Scope>& forLetImpliedScope, Node* forInOrOfExpression); - bool validateForInOrOfLHSExpression(Node target, PossibleError* possibleError); Node expressionAfterForInOrOf(ParseNodeKind forHeadKind, YieldHandling yieldHandling); Node switchStatement(YieldHandling yieldHandling); @@ -1222,7 +1250,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter bool tryNewTarget(Node& newTarget); bool checkAndMarkSuperScope(); - Node methodDefinition(PropertyType propType, HandleAtom funName); + Node methodDefinition(uint32_t preludeStart, PropertyType propType, HandleAtom funName); /* * Additional JS parsers. @@ -1230,10 +1258,11 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind, Node funcpn); - Node functionDefinition(InHandling inHandling, YieldHandling yieldHandling, HandleAtom name, + Node functionDefinition(uint32_t preludeStart, Node pn, + InHandling inHandling, YieldHandling yieldHandling, HandleAtom name, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, - InvokedPrediction invoked = PredictUninvoked); + bool tryAnnexB = false); // Parse a function body. Pass StatementListBody if the body is a list of // statements; pass ExpressionBody if the body is a single expression. @@ -1298,17 +1327,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter #endif } - enum AssignmentFlavor { - PlainAssignment, - CompoundAssignment, - KeyedDestructuringAssignment, - IncrementAssignment, - DecrementAssignment, - ForInOrOfTarget - }; - - bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor, - PossibleError* possibleError=nullptr); bool matchInOrOf(bool* isForInp, bool* isForOfp); bool hasUsedFunctionSpecialName(HandlePropertyName name); @@ -1319,16 +1337,16 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter Node newDotGeneratorName(); bool declareDotGeneratorName(); - bool checkFunctionDefinition(HandleAtom funAtom, Node pn, FunctionSyntaxKind kind, - GeneratorKind generatorKind, bool* tryAnnexB); - bool skipLazyInnerFunction(Node pn, FunctionSyntaxKind kind, bool tryAnnexB); - bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, + bool skipLazyInnerFunction(Node pn, uint32_t preludeStart, FunctionSyntaxKind kind, + bool tryAnnexB); + bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, uint32_t preludeStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives, Directives* newDirectives); - bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, InHandling inHandling, - YieldHandling yieldHandling, FunctionSyntaxKind kind, + bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, uint32_t preludeStart, + InHandling inHandling, YieldHandling yieldHandling, + FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives, Directives* newDirectives); @@ -1346,10 +1364,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls); private: - bool reportIfArgumentsEvalTarget(Node nameNode); - bool reportIfNotValidSimpleAssignmentTarget(Node target, AssignmentFlavor flavor); - - bool checkAndMarkAsIncOperand(Node kid, AssignmentFlavor flavor); + bool checkIncDecOperand(Node operand, uint32_t operandOffset); bool checkStrictAssignment(Node lhs); bool checkStrictBinding(PropertyName* name, TokenPos pos); @@ -1404,24 +1419,18 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter bool checkDestructuringObject(Node objectPattern, mozilla::Maybe<DeclarationKind> maybeDecl); bool checkDestructuringName(Node expr, mozilla::Maybe<DeclarationKind> maybeDecl); - bool checkAssignmentToCall(Node node, unsigned errnum); - Node newNumber(const Token& tok) { return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos); } static Node null() { return ParseHandler::null(); } - bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum); - JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom); TokenPos pos() const { return tokenStream.currentToken().pos; } bool asmJS(Node list); - void addTelemetry(JSCompartment::DeprecatedLanguageExtension e); - bool warnOnceAboutExprClosure(); bool warnOnceAboutForEach(); }; diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index a6ac542f6..b20417d5d 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -450,6 +450,7 @@ class FunctionBox : public ObjectBox, public SharedContext uint32_t bufEnd; uint32_t startLine; uint32_t startColumn; + uint32_t preludeStart; uint16_t length; uint8_t generatorKindBits_; /* The GeneratorKind of this function. */ @@ -476,8 +477,8 @@ class FunctionBox : public ObjectBox, public SharedContext FunctionContextFlags funCxFlags; FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead, JSFunction* fun, - Directives directives, bool extraWarnings, GeneratorKind generatorKind, - FunctionAsyncKind asyncKind); + uint32_t preludeStart, Directives directives, bool extraWarnings, + GeneratorKind generatorKind, FunctionAsyncKind asyncKind); MutableHandle<LexicalScope::Data*> namedLambdaBindings() { MOZ_ASSERT(context->compartment()->runtimeFromAnyThread()->keepAtoms()); diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index b7f00605b..00ea9d35d 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -342,7 +342,10 @@ class SyntaxParseHandler void checkAndSetIsDirectRHSAnonFunction(Node pn) {} - Node newFunctionDefinition() { return NodeFunctionDefinition; } + Node newFunctionStatement() { return NodeFunctionDefinition; } + Node newFunctionExpression() { return NodeFunctionDefinition; } + Node newArrowFunction() { return NodeFunctionDefinition; } + bool setComprehensionLambdaBody(Node pn, Node body) { return true; } void setFunctionFormalParametersAndBody(Node pn, Node kid) {} void setFunctionBody(Node pn, Node kid) {} @@ -519,7 +522,7 @@ class SyntaxParseHandler MOZ_MUST_USE Node setLikelyIIFE(Node pn) { return pn; // Remain in syntax-parse mode. } - void setPrologue(Node pn) {} + void setInDirectivePrologue(Node pn) {} bool isConstant(Node pn) { return false; } diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 179a7c244..8438ff7c5 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -172,6 +172,12 @@ frontend::IsIdentifier(JSLinearString* str) } bool +frontend::IsIdentifier(const char* chars, size_t length) +{ + return ::IsIdentifier(chars, length); +} + +bool frontend::IsIdentifier(const char16_t* chars, size_t length) { return ::IsIdentifier(chars, length); @@ -780,7 +786,7 @@ TokenStream::reportWarning(unsigned errorNumber, ...) } bool -TokenStream::reportStrictWarningErrorNumberVA(uint32_t offset, unsigned errorNumber, va_list args) +TokenStream::reportExtraWarningErrorNumberVA(uint32_t offset, unsigned errorNumber, va_list args) { if (!options().extraWarningsOption) return true; diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 5d6b4b795..6ba9fba5a 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -375,8 +375,7 @@ class MOZ_STACK_CLASS TokenStream va_list args); bool reportStrictModeErrorNumberVA(uint32_t offset, bool strictMode, unsigned errorNumber, va_list args); - bool reportStrictWarningErrorNumberVA(uint32_t offset, unsigned errorNumber, - va_list args); + bool reportExtraWarningErrorNumberVA(uint32_t offset, unsigned errorNumber, va_list args); // asm.js reporter void reportAsmJSError(uint32_t offset, unsigned errorNumber, ...); @@ -570,6 +569,14 @@ class MOZ_STACK_CLASS TokenStream return true; } + MOZ_MUST_USE bool peekOffset(uint32_t* offset, Modifier modifier = None) { + TokenPos pos; + if (!peekTokenPos(&pos, modifier)) + return false; + *offset = pos.begin; + return true; + } + // This is like peekToken(), with one exception: if there is an EOL // between the end of the current token and the start of the next token, it // return true and store TOK_EOL in |*ttp|. In that case, no token with diff --git a/js/src/gc/GCInternals.h b/js/src/gc/GCInternals.h index 4919b87a5..e8df0bb70 100644 --- a/js/src/gc/GCInternals.h +++ b/js/src/gc/GCInternals.h @@ -9,7 +9,6 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/Maybe.h" -#include "mozilla/PodOperations.h" #include "jscntxt.h" @@ -102,9 +101,9 @@ struct TenureCountCache static const size_t EntryShift = 4; static const size_t EntryCount = 1 << EntryShift; - TenureCount entries[EntryCount]; + TenureCount entries[EntryCount] = {}; // zeroes - TenureCountCache() { mozilla::PodZero(this); } + TenureCountCache() = default; HashNumber hash(ObjectGroup* group) { #if JS_BITS_PER_WORD == 32 diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index b2c105999..3ea4c9d29 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -1231,34 +1231,34 @@ BindingIter::trace(JSTracer* trc) void LexicalScope::Data::trace(JSTracer* trc) { - TraceBindingNames(trc, names, length); + TraceBindingNames(trc, trailingNames.start(), length); } void FunctionScope::Data::trace(JSTracer* trc) { TraceNullableEdge(trc, &canonicalFunction, "scope canonical function"); - TraceNullableBindingNames(trc, names, length); + TraceNullableBindingNames(trc, trailingNames.start(), length); } void VarScope::Data::trace(JSTracer* trc) { - TraceBindingNames(trc, names, length); + TraceBindingNames(trc, trailingNames.start(), length); } void GlobalScope::Data::trace(JSTracer* trc) { - TraceBindingNames(trc, names, length); + TraceBindingNames(trc, trailingNames.start(), length); } void EvalScope::Data::trace(JSTracer* trc) { - TraceBindingNames(trc, names, length); + TraceBindingNames(trc, trailingNames.start(), length); } void ModuleScope::Data::trace(JSTracer* trc) { TraceNullableEdge(trc, &module, "scope module"); - TraceBindingNames(trc, names, length); + TraceBindingNames(trc, trailingNames.start(), length); } void Scope::traceChildren(JSTracer* trc) @@ -1302,13 +1302,13 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope) traverseEdge(scope, static_cast<Scope*>(scope->enclosing_)); if (scope->environmentShape_) traverseEdge(scope, static_cast<Shape*>(scope->environmentShape_)); - BindingName* names = nullptr; + TrailingNamesArray* names = nullptr; uint32_t length = 0; switch (scope->kind_) { case ScopeKind::Function: { FunctionScope::Data* data = reinterpret_cast<FunctionScope::Data*>(scope->data_); traverseEdge(scope, static_cast<JSObject*>(data->canonicalFunction)); - names = data->names; + names = &data->trailingNames; length = data->length; break; } @@ -1316,7 +1316,7 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope) case ScopeKind::FunctionBodyVar: case ScopeKind::ParameterExpressionVar: { VarScope::Data* data = reinterpret_cast<VarScope::Data*>(scope->data_); - names = data->names; + names = &data->trailingNames; length = data->length; break; } @@ -1327,7 +1327,7 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope) case ScopeKind::NamedLambda: case ScopeKind::StrictNamedLambda: { LexicalScope::Data* data = reinterpret_cast<LexicalScope::Data*>(scope->data_); - names = data->names; + names = &data->trailingNames; length = data->length; break; } @@ -1335,7 +1335,7 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope) case ScopeKind::Global: case ScopeKind::NonSyntactic: { GlobalScope::Data* data = reinterpret_cast<GlobalScope::Data*>(scope->data_); - names = data->names; + names = &data->trailingNames; length = data->length; break; } @@ -1343,7 +1343,7 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope) case ScopeKind::Eval: case ScopeKind::StrictEval: { EvalScope::Data* data = reinterpret_cast<EvalScope::Data*>(scope->data_); - names = data->names; + names = &data->trailingNames; length = data->length; break; } @@ -1351,7 +1351,7 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope) case ScopeKind::Module: { ModuleScope::Data* data = reinterpret_cast<ModuleScope::Data*>(scope->data_); traverseEdge(scope, static_cast<JSObject*>(data->module)); - names = data->names; + names = &data->trailingNames; length = data->length; break; } @@ -1361,12 +1361,12 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope) } if (scope->kind_ == ScopeKind::Function) { for (uint32_t i = 0; i < length; i++) { - if (JSAtom* name = names[i].name()) + if (JSAtom* name = names->operator[](i).name()) traverseEdge(scope, static_cast<JSString*>(name)); } } else { for (uint32_t i = 0; i < length; i++) - traverseEdge(scope, static_cast<JSString*>(names[i].name())); + traverseEdge(scope, static_cast<JSString*>(names->operator[](i).name())); } } diff --git a/js/src/gc/Memory.cpp b/js/src/gc/Memory.cpp index 26da75469..268e1e489 100644 --- a/js/src/gc/Memory.cpp +++ b/js/src/gc/Memory.cpp @@ -17,11 +17,6 @@ #include "jswin.h" #include <psapi.h> -#elif defined(SOLARIS) - -#include <sys/mman.h> -#include <unistd.h> - #elif defined(XP_UNIX) #include <algorithm> @@ -408,86 +403,6 @@ DeallocateMappedContent(void* p, size_t length) # endif -#elif defined(SOLARIS) - -#ifndef MAP_NOSYNC -# define MAP_NOSYNC 0 -#endif - -void -InitMemorySubsystem() -{ - if (pageSize == 0) - pageSize = allocGranularity = size_t(sysconf(_SC_PAGESIZE)); -} - -void* -MapAlignedPages(size_t size, size_t alignment) -{ - MOZ_ASSERT(size >= alignment); - MOZ_ASSERT(size >= allocGranularity); - MOZ_ASSERT(size % alignment == 0); - MOZ_ASSERT(size % pageSize == 0); - MOZ_ASSERT_IF(alignment < allocGranularity, allocGranularity % alignment == 0); - MOZ_ASSERT_IF(alignment > allocGranularity, alignment % allocGranularity == 0); - - int prot = PROT_READ | PROT_WRITE; - int flags = MAP_PRIVATE | MAP_ANON | MAP_ALIGN | MAP_NOSYNC; - - void* p = mmap((caddr_t)alignment, size, prot, flags, -1, 0); - if (p == MAP_FAILED) - return nullptr; - return p; -} - -static void* -MapAlignedPagesLastDitch(size_t size, size_t alignment) -{ - return nullptr; -} - -void -UnmapPages(void* p, size_t size) -{ - MOZ_ALWAYS_TRUE(0 == munmap((caddr_t)p, size)); -} - -bool -MarkPagesUnused(void* p, size_t size) -{ - MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0); - return true; -} - -bool -MarkPagesInUse(void* p, size_t size) -{ - if (!DecommitEnabled()) - return; - - MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0); -} - -size_t -GetPageFaultCount() -{ - return 0; -} - -void* -AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment) -{ - // Not implemented. - return nullptr; -} - -// Deallocate mapped memory for object. -void -DeallocateMappedContent(void* p, size_t length) -{ - // Not implemented. -} - #elif defined(XP_UNIX) void diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index 55ca5a059..93a0eb6a8 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -210,6 +210,7 @@ js::Nursery::disable() return; updateNumChunks(0); currentEnd_ = 0; + position_ = 0; runtime()->gc.storeBuffer.disable(); } @@ -530,7 +531,6 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason) // the nursery is full, look for object groups that are getting promoted // excessively and try to pretenure them. maybeStartProfile(ProfileKey::Pretenure); - uint32_t pretenureCount = 0; if (promotionRate > 0.8 || reason == JS::gcreason::FULL_STORE_BUFFER) { JSContext* cx = rt->contextFromMainThread(); for (auto& entry : tenureCounts.entries) { @@ -539,7 +539,6 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason) if (group->canPreTenure()) { AutoCompartment ac(cx, group->compartment()); group->setShouldPreTenure(cx); - pretenureCount++; } } } @@ -556,12 +555,6 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason) minorGcCount_++; int64_t totalTime = profileTimes_[ProfileKey::Total]; - rt->addTelemetry(JS_TELEMETRY_GC_MINOR_US, totalTime); - rt->addTelemetry(JS_TELEMETRY_GC_MINOR_REASON, reason); - if (totalTime > 1000) - rt->addTelemetry(JS_TELEMETRY_GC_MINOR_REASON_LONG, reason); - rt->addTelemetry(JS_TELEMETRY_GC_NURSERY_BYTES, sizeOfHeapCommitted()); - rt->addTelemetry(JS_TELEMETRY_GC_PRETENURE_COUNT, pretenureCount); rt->gc.stats.endNurseryCollection(reason); TraceMinorGCEnd(); diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index 0d215d997..a839a4979 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -245,6 +245,7 @@ class Nursery // Free space remaining, not counting chunk trailers. MOZ_ALWAYS_INLINE size_t freeSpace() const { + MOZ_ASSERT(isEnabled()); MOZ_ASSERT(currentEnd_ - position_ <= NurseryChunkUsableSize); return (currentEnd_ - position_) + (numChunks() - currentChunk_ - 1) * NurseryChunkUsableSize; diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 19f9986dd..8a9f4e135 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -34,13 +34,6 @@ using mozilla::MakeRange; using mozilla::PodArrayZero; using mozilla::PodZero; -/* - * If this fails, then you can either delete this assertion and allow all - * larger-numbered reasons to pile up in the last telemetry bucket, or switch - * to GC_REASON_3 and bump the max value. - */ -JS_STATIC_ASSERT(JS::gcreason::NUM_TELEMETRY_REASONS >= JS::gcreason::NUM_REASONS); - const char* js::gcstats::ExplainInvocationKind(JSGCInvocationKind gckind) { @@ -92,7 +85,6 @@ struct PhaseInfo Phase index; const char* name; Phase parent; - const uint8_t telemetryBucket; }; // The zeroth entry in the timing arrays is used for phases that have a @@ -134,78 +126,74 @@ struct DagChildEdge { */ static const PhaseInfo phases[] = { - { PHASE_MUTATOR, "Mutator Running", PHASE_NO_PARENT, 0 }, - { PHASE_GC_BEGIN, "Begin Callback", PHASE_NO_PARENT, 1 }, - { PHASE_WAIT_BACKGROUND_THREAD, "Wait Background Thread", PHASE_NO_PARENT, 2 }, - { PHASE_MARK_DISCARD_CODE, "Mark Discard Code", PHASE_NO_PARENT, 3 }, - { PHASE_RELAZIFY_FUNCTIONS, "Relazify Functions", PHASE_NO_PARENT, 4 }, - { PHASE_PURGE, "Purge", PHASE_NO_PARENT, 5 }, - { PHASE_MARK, "Mark", PHASE_NO_PARENT, 6 }, - { PHASE_UNMARK, "Unmark", PHASE_MARK, 7 }, + { PHASE_MUTATOR, "Mutator Running", PHASE_NO_PARENT }, + { PHASE_GC_BEGIN, "Begin Callback", PHASE_NO_PARENT }, + { PHASE_WAIT_BACKGROUND_THREAD, "Wait Background Thread", PHASE_NO_PARENT }, + { PHASE_MARK_DISCARD_CODE, "Mark Discard Code", PHASE_NO_PARENT }, + { PHASE_RELAZIFY_FUNCTIONS, "Relazify Functions", PHASE_NO_PARENT }, + { PHASE_PURGE, "Purge", PHASE_NO_PARENT }, + { PHASE_MARK, "Mark", PHASE_NO_PARENT }, + { PHASE_UNMARK, "Unmark", PHASE_MARK }, /* PHASE_MARK_ROOTS */ - { PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK, 8 }, - { PHASE_SWEEP, "Sweep", PHASE_NO_PARENT, 9 }, - { PHASE_SWEEP_MARK, "Mark During Sweeping", PHASE_SWEEP, 10 }, - { PHASE_SWEEP_MARK_TYPES, "Mark Types During Sweeping", PHASE_SWEEP_MARK, 11 }, - { PHASE_SWEEP_MARK_INCOMING_BLACK, "Mark Incoming Black Pointers", PHASE_SWEEP_MARK, 12 }, - { PHASE_SWEEP_MARK_WEAK, "Mark Weak", PHASE_SWEEP_MARK, 13 }, - { PHASE_SWEEP_MARK_INCOMING_GRAY, "Mark Incoming Gray Pointers", PHASE_SWEEP_MARK, 14 }, - { PHASE_SWEEP_MARK_GRAY, "Mark Gray", PHASE_SWEEP_MARK, 15 }, - { PHASE_SWEEP_MARK_GRAY_WEAK, "Mark Gray and Weak", PHASE_SWEEP_MARK, 16 }, - { PHASE_FINALIZE_START, "Finalize Start Callbacks", PHASE_SWEEP, 17 }, - { PHASE_WEAK_ZONEGROUP_CALLBACK, "Per-Slice Weak Callback", PHASE_FINALIZE_START, 57 }, - { PHASE_WEAK_COMPARTMENT_CALLBACK, "Per-Compartment Weak Callback", PHASE_FINALIZE_START, 58 }, - { PHASE_SWEEP_ATOMS, "Sweep Atoms", PHASE_SWEEP, 18 }, - { PHASE_SWEEP_SYMBOL_REGISTRY, "Sweep Symbol Registry", PHASE_SWEEP, 19 }, - { PHASE_SWEEP_COMPARTMENTS, "Sweep Compartments", PHASE_SWEEP, 20 }, - { PHASE_SWEEP_DISCARD_CODE, "Sweep Discard Code", PHASE_SWEEP_COMPARTMENTS, 21 }, - { PHASE_SWEEP_INNER_VIEWS, "Sweep Inner Views", PHASE_SWEEP_COMPARTMENTS, 22 }, - { PHASE_SWEEP_CC_WRAPPER, "Sweep Cross Compartment Wrappers", PHASE_SWEEP_COMPARTMENTS, 23 }, - { PHASE_SWEEP_BASE_SHAPE, "Sweep Base Shapes", PHASE_SWEEP_COMPARTMENTS, 24 }, - { PHASE_SWEEP_INITIAL_SHAPE, "Sweep Initial Shapes", PHASE_SWEEP_COMPARTMENTS, 25 }, - { PHASE_SWEEP_TYPE_OBJECT, "Sweep Type Objects", PHASE_SWEEP_COMPARTMENTS, 26 }, - { PHASE_SWEEP_BREAKPOINT, "Sweep Breakpoints", PHASE_SWEEP_COMPARTMENTS, 27 }, - { PHASE_SWEEP_REGEXP, "Sweep Regexps", PHASE_SWEEP_COMPARTMENTS, 28 }, - { PHASE_SWEEP_MISC, "Sweep Miscellaneous", PHASE_SWEEP_COMPARTMENTS, 29 }, - { PHASE_SWEEP_TYPES, "Sweep type information", PHASE_SWEEP_COMPARTMENTS, 30 }, - { PHASE_SWEEP_TYPES_BEGIN, "Sweep type tables and compilations", PHASE_SWEEP_TYPES, 31 }, - { PHASE_SWEEP_TYPES_END, "Free type arena", PHASE_SWEEP_TYPES, 32 }, - { PHASE_SWEEP_OBJECT, "Sweep Object", PHASE_SWEEP, 33 }, - { PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP, 34 }, - { PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP, 35 }, - { PHASE_SWEEP_SCOPE, "Sweep Scope", PHASE_SWEEP, 59 }, - { PHASE_SWEEP_SHAPE, "Sweep Shape", PHASE_SWEEP, 36 }, - { PHASE_SWEEP_JITCODE, "Sweep JIT code", PHASE_SWEEP, 37 }, - { PHASE_FINALIZE_END, "Finalize End Callback", PHASE_SWEEP, 38 }, - { PHASE_DESTROY, "Deallocate", PHASE_SWEEP, 39 }, - { PHASE_COMPACT, "Compact", PHASE_NO_PARENT, 40 }, - { PHASE_COMPACT_MOVE, "Compact Move", PHASE_COMPACT, 41 }, - { PHASE_COMPACT_UPDATE, "Compact Update", PHASE_COMPACT, 42 }, + { PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK }, + { PHASE_SWEEP, "Sweep", PHASE_NO_PARENT }, + { PHASE_SWEEP_MARK, "Mark During Sweeping", PHASE_SWEEP }, + { PHASE_SWEEP_MARK_TYPES, "Mark Types During Sweeping", PHASE_SWEEP_MARK }, + { PHASE_SWEEP_MARK_INCOMING_BLACK, "Mark Incoming Black Pointers", PHASE_SWEEP_MARK }, + { PHASE_SWEEP_MARK_WEAK, "Mark Weak", PHASE_SWEEP_MARK }, + { PHASE_SWEEP_MARK_INCOMING_GRAY, "Mark Incoming Gray Pointers", PHASE_SWEEP_MARK }, + { PHASE_SWEEP_MARK_GRAY, "Mark Gray", PHASE_SWEEP_MARK }, + { PHASE_SWEEP_MARK_GRAY_WEAK, "Mark Gray and Weak", PHASE_SWEEP_MARK }, + { PHASE_FINALIZE_START, "Finalize Start Callbacks", PHASE_SWEEP }, + { PHASE_WEAK_ZONEGROUP_CALLBACK, "Per-Slice Weak Callback", PHASE_FINALIZE_START }, + { PHASE_WEAK_COMPARTMENT_CALLBACK, "Per-Compartment Weak Callback", PHASE_FINALIZE_START }, + { PHASE_SWEEP_ATOMS, "Sweep Atoms", PHASE_SWEEP }, + { PHASE_SWEEP_SYMBOL_REGISTRY, "Sweep Symbol Registry", PHASE_SWEEP }, + { PHASE_SWEEP_COMPARTMENTS, "Sweep Compartments", PHASE_SWEEP }, + { PHASE_SWEEP_DISCARD_CODE, "Sweep Discard Code", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_INNER_VIEWS, "Sweep Inner Views", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_CC_WRAPPER, "Sweep Cross Compartment Wrappers", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_BASE_SHAPE, "Sweep Base Shapes", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_INITIAL_SHAPE, "Sweep Initial Shapes", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_TYPE_OBJECT, "Sweep Type Objects", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_BREAKPOINT, "Sweep Breakpoints", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_REGEXP, "Sweep Regexps", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_MISC, "Sweep Miscellaneous", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_TYPES, "Sweep type information", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_TYPES_BEGIN, "Sweep type tables and compilations", PHASE_SWEEP_TYPES }, + { PHASE_SWEEP_TYPES_END, "Free type arena", PHASE_SWEEP_TYPES }, + { PHASE_SWEEP_OBJECT, "Sweep Object", PHASE_SWEEP }, + { PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP }, + { PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP }, + { PHASE_SWEEP_SCOPE, "Sweep Scope", PHASE_SWEEP }, + { PHASE_SWEEP_SHAPE, "Sweep Shape", PHASE_SWEEP }, + { PHASE_SWEEP_JITCODE, "Sweep JIT code", PHASE_SWEEP }, + { PHASE_FINALIZE_END, "Finalize End Callback", PHASE_SWEEP }, + { PHASE_DESTROY, "Deallocate", PHASE_SWEEP }, + { PHASE_COMPACT, "Compact", PHASE_NO_PARENT }, + { PHASE_COMPACT_MOVE, "Compact Move", PHASE_COMPACT }, + { PHASE_COMPACT_UPDATE, "Compact Update", PHASE_COMPACT }, /* PHASE_MARK_ROOTS */ - { PHASE_COMPACT_UPDATE_CELLS, "Compact Update Cells", PHASE_COMPACT_UPDATE, 43 }, - { PHASE_GC_END, "End Callback", PHASE_NO_PARENT, 44 }, - { PHASE_MINOR_GC, "All Minor GCs", PHASE_NO_PARENT, 45 }, + { PHASE_COMPACT_UPDATE_CELLS, "Compact Update Cells", PHASE_COMPACT_UPDATE }, + { PHASE_GC_END, "End Callback", PHASE_NO_PARENT }, + { PHASE_MINOR_GC, "All Minor GCs", PHASE_NO_PARENT }, /* PHASE_MARK_ROOTS */ - { PHASE_EVICT_NURSERY, "Minor GCs to Evict Nursery", PHASE_NO_PARENT, 46 }, + { PHASE_EVICT_NURSERY, "Minor GCs to Evict Nursery", PHASE_NO_PARENT }, /* PHASE_MARK_ROOTS */ - { PHASE_TRACE_HEAP, "Trace Heap", PHASE_NO_PARENT, 47 }, + { PHASE_TRACE_HEAP, "Trace Heap", PHASE_NO_PARENT }, /* PHASE_MARK_ROOTS */ - { PHASE_BARRIER, "Barriers", PHASE_NO_PARENT, 55 }, - { PHASE_UNMARK_GRAY, "Unmark gray", PHASE_BARRIER, 56 }, - { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MULTI_PARENTS, 48 }, - { PHASE_BUFFER_GRAY_ROOTS, "Buffer Gray Roots", PHASE_MARK_ROOTS, 49 }, - { PHASE_MARK_CCWS, "Mark Cross Compartment Wrappers", PHASE_MARK_ROOTS, 50 }, - { PHASE_MARK_STACK, "Mark C and JS stacks", PHASE_MARK_ROOTS, 51 }, - { PHASE_MARK_RUNTIME_DATA, "Mark Runtime-wide Data", PHASE_MARK_ROOTS, 52 }, - { PHASE_MARK_EMBEDDING, "Mark Embedding", PHASE_MARK_ROOTS, 53 }, - { PHASE_MARK_COMPARTMENTS, "Mark Compartments", PHASE_MARK_ROOTS, 54 }, - { PHASE_PURGE_SHAPE_TABLES, "Purge ShapeTables", PHASE_NO_PARENT, 60 }, - - { PHASE_LIMIT, nullptr, PHASE_NO_PARENT, 60 } - - // Current number of telemetryBuckets is 60. If you insert new phases - // somewhere, start at that number and count up. Do not change any existing - // numbers. + { PHASE_BARRIER, "Barriers", PHASE_NO_PARENT }, + { PHASE_UNMARK_GRAY, "Unmark gray", PHASE_BARRIER }, + { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MULTI_PARENTS }, + { PHASE_BUFFER_GRAY_ROOTS, "Buffer Gray Roots", PHASE_MARK_ROOTS }, + { PHASE_MARK_CCWS, "Mark Cross Compartment Wrappers", PHASE_MARK_ROOTS }, + { PHASE_MARK_STACK, "Mark C and JS stacks", PHASE_MARK_ROOTS }, + { PHASE_MARK_RUNTIME_DATA, "Mark Runtime-wide Data", PHASE_MARK_ROOTS }, + { PHASE_MARK_EMBEDDING, "Mark Embedding", PHASE_MARK_ROOTS }, + { PHASE_MARK_COMPARTMENTS, "Mark Compartments", PHASE_MARK_ROOTS }, + { PHASE_PURGE_SHAPE_TABLES, "Purge ShapeTables", PHASE_NO_PARENT }, + + { PHASE_LIMIT, nullptr, PHASE_NO_PARENT } }; static ExtraPhaseInfo phaseExtra[PHASE_LIMIT] = { { 0, 0 } }; @@ -845,12 +833,6 @@ Statistics::~Statistics() /* static */ bool Statistics::initialize() { - for (size_t i = 0; i < PHASE_LIMIT; i++) { - MOZ_ASSERT(phases[i].index == i); - for (size_t j = 0; j < PHASE_LIMIT; j++) - MOZ_ASSERT_IF(i != j, phases[i].telemetryBucket != phases[j].telemetryBucket); - } - // Create a static table of descendants for every phase with multiple // children. This assumes that all descendants come linearly in the // list, which is reasonable since full dags are not supported; any @@ -925,32 +907,6 @@ Statistics::getMaxGCPauseSinceClear() return maxPauseInInterval; } -// Sum up the time for a phase, including instances of the phase with different -// parents. -static int64_t -SumPhase(Phase phase, const Statistics::PhaseTimeTable times) -{ - int64_t sum = 0; - for (auto i : MakeRange(Statistics::NumTimingArrays)) - sum += times[i][phase]; - return sum; -} - -static Phase -LongestPhase(const Statistics::PhaseTimeTable times) -{ - int64_t longestTime = 0; - Phase longestPhase = PHASE_NONE; - for (size_t i = 0; i < PHASE_LIMIT; ++i) { - int64_t phaseTime = SumPhase(Phase(i), times); - if (phaseTime > longestTime) { - longestTime = phaseTime; - longestPhase = Phase(i); - } - } - return longestPhase; -} - void Statistics::printStats() { @@ -985,34 +941,6 @@ Statistics::endGC() int64_t total, longest; gcDuration(&total, &longest); - int64_t sccTotal, sccLongest; - sccDurations(&sccTotal, &sccLongest); - - runtime->addTelemetry(JS_TELEMETRY_GC_IS_ZONE_GC, !zoneStats.isCollectingAllZones()); - runtime->addTelemetry(JS_TELEMETRY_GC_MS, t(total)); - runtime->addTelemetry(JS_TELEMETRY_GC_MAX_PAUSE_MS, t(longest)); - int64_t markTotal = SumPhase(PHASE_MARK, phaseTimes); - int64_t markRootsTotal = SumPhase(PHASE_MARK_ROOTS, phaseTimes); - runtime->addTelemetry(JS_TELEMETRY_GC_MARK_MS, t(markTotal)); - runtime->addTelemetry(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[PHASE_DAG_NONE][PHASE_SWEEP])); - if (runtime->gc.isCompactingGc()) { - runtime->addTelemetry(JS_TELEMETRY_GC_COMPACT_MS, - t(phaseTimes[PHASE_DAG_NONE][PHASE_COMPACT])); - } - runtime->addTelemetry(JS_TELEMETRY_GC_MARK_ROOTS_MS, t(markRootsTotal)); - runtime->addTelemetry(JS_TELEMETRY_GC_MARK_GRAY_MS, t(phaseTimes[PHASE_DAG_NONE][PHASE_SWEEP_MARK_GRAY])); - runtime->addTelemetry(JS_TELEMETRY_GC_NON_INCREMENTAL, nonincremental()); - if (nonincremental()) - runtime->addTelemetry(JS_TELEMETRY_GC_NON_INCREMENTAL_REASON, uint32_t(nonincrementalReason_)); - runtime->addTelemetry(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gc.isIncrementalGCAllowed()); - runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, t(sccTotal)); - runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, t(sccLongest)); - - if (!aborted) { - double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC); - runtime->addTelemetry(JS_TELEMETRY_GC_MMU_50, mmu50 * 100); - } - if (fp) printStats(); @@ -1061,8 +989,6 @@ Statistics::beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind, return; } - runtime->addTelemetry(JS_TELEMETRY_GC_REASON, reason); - // Slice callbacks should only fire for the outermost level. if (gcDepth == 1) { bool wasFullGC = zoneStats.isCollectingAllZones(); @@ -1082,25 +1008,6 @@ Statistics::endSlice() slices.back().endFaults = GetPageFaultCount(); slices.back().finalState = runtime->gc.state(); - int64_t sliceTime = slices.back().end - slices.back().start; - runtime->addTelemetry(JS_TELEMETRY_GC_SLICE_MS, t(sliceTime)); - runtime->addTelemetry(JS_TELEMETRY_GC_RESET, slices.back().wasReset()); - if (slices.back().wasReset()) - runtime->addTelemetry(JS_TELEMETRY_GC_RESET_REASON, uint32_t(slices.back().resetReason)); - - if (slices.back().budget.isTimeBudget()) { - int64_t budget_ms = slices.back().budget.timeBudget.budget; - runtime->addTelemetry(JS_TELEMETRY_GC_BUDGET_MS, budget_ms); - if (budget_ms == runtime->gc.defaultSliceBudget()) - runtime->addTelemetry(JS_TELEMETRY_GC_ANIMATION_MS, t(sliceTime)); - - // Record any phase that goes more than 2x over its budget. - if (sliceTime > 2 * budget_ms * 1000) { - Phase longest = LongestPhase(slices.back().phaseTimes); - runtime->addTelemetry(JS_TELEMETRY_GC_SLOW_PHASE, phases[longest].telemetryBucket); - } - } - sliceCount_++; } diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index ca1969b2c..08a2810cf 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -10,7 +10,6 @@ #include "mozilla/EnumeratedArray.h" #include "mozilla/IntegerRange.h" #include "mozilla/Maybe.h" -#include "mozilla/PodOperations.h" #include "jsalloc.h" #include "jsgc.h" @@ -112,29 +111,26 @@ enum Stat { struct ZoneGCStats { /* Number of zones collected in this GC. */ - int collectedZoneCount; + int collectedZoneCount = 0; /* Total number of zones in the Runtime at the start of this GC. */ - int zoneCount; + int zoneCount = 0; /* Number of zones swept in this GC. */ - int sweptZoneCount; + int sweptZoneCount = 0; /* Total number of compartments in all zones collected. */ - int collectedCompartmentCount; + int collectedCompartmentCount = 0; /* Total number of compartments in the Runtime at the start of this GC. */ - int compartmentCount; + int compartmentCount = 0; /* Total number of compartments swept by this GC. */ - int sweptCompartmentCount; + int sweptCompartmentCount = 0; bool isCollectingAllZones() const { return collectedZoneCount == zoneCount; } - ZoneGCStats() - : collectedZoneCount(0), zoneCount(0), sweptZoneCount(0), - collectedCompartmentCount(0), compartmentCount(0), sweptCompartmentCount(0) - {} + ZoneGCStats() = default; }; #define FOR_EACH_GC_PROFILE_TIME(_) \ diff --git a/js/src/jit-test/tests/asm.js/testSource.js b/js/src/jit-test/tests/asm.js/testSource.js index b44c52a6b..d7ad42864 100644 --- a/js/src/jit-test/tests/asm.js/testSource.js +++ b/js/src/jit-test/tests/asm.js/testSource.js @@ -32,7 +32,7 @@ var f0 = function() { } -funcBody1 = funcBody.replace('function f0','function '); +funcBody1 = funcBody.replace('function f0','function'); assertEq(f0.toString(), funcBody1); assertEq(f0.toSource(), '(' + funcBody1 + ')'); @@ -48,14 +48,14 @@ assertEq(g.toString(), funcBody2); assertEq(g.toSource(), '(' + funcBody2 + ')'); f0 = new Function(bodyOnly); -assertEq(f0.toString(), "function anonymous() {\n" + bodyOnly + "\n}"); -assertEq(f0.toSource(), "(function anonymous() {\n" + bodyOnly + "\n})"); +assertEq(f0.toString(), "function anonymous(\n) {\n" + bodyOnly + "\n}"); +assertEq(f0.toSource(), "(function anonymous(\n) {\n" + bodyOnly + "\n})"); if (isAsmJSCompilationAvailable() && isCachingEnabled()) { var m = new Function(bodyOnly); assertEq(isAsmJSModuleLoadedFromCache(m), true); - assertEq(m.toString(), "function anonymous() {\n" + bodyOnly + "\n}"); - assertEq(m.toSource(), "(function anonymous() {\n" + bodyOnly + "\n})"); + assertEq(m.toString(), "function anonymous(\n) {\n" + bodyOnly + "\n}"); + assertEq(m.toSource(), "(function anonymous(\n) {\n" + bodyOnly + "\n})"); } })(); @@ -91,7 +91,7 @@ f1 = function(glob) { } -funcBody1 = funcBody.replace('function f1', 'function '); +funcBody1 = funcBody.replace('function f1', 'function'); assertEq(f1.toString(), funcBody1); assertEq(f1.toSource(), '(' + funcBody1 + ')'); @@ -107,14 +107,14 @@ assertEq(g.toString(), funcBody2); assertEq(g.toSource(), '(' + funcBody2 + ')'); f1 = new Function('glob', bodyOnly); -assertEq(f1.toString(), "function anonymous(glob) {\n" + bodyOnly + "\n}"); -assertEq(f1.toSource(), "(function anonymous(glob) {\n" + bodyOnly + "\n})"); +assertEq(f1.toString(), "function anonymous(glob\n) {\n" + bodyOnly + "\n}"); +assertEq(f1.toSource(), "(function anonymous(glob\n) {\n" + bodyOnly + "\n})"); if (isAsmJSCompilationAvailable() && isCachingEnabled()) { var m = new Function('glob', bodyOnly); assertEq(isAsmJSModuleLoadedFromCache(m), true); - assertEq(m.toString(), "function anonymous(glob) {\n" + bodyOnly + "\n}"); - assertEq(m.toSource(), "(function anonymous(glob) {\n" + bodyOnly + "\n})"); + assertEq(m.toString(), "function anonymous(glob\n) {\n" + bodyOnly + "\n}"); + assertEq(m.toSource(), "(function anonymous(glob\n) {\n" + bodyOnly + "\n})"); } })(); @@ -144,14 +144,14 @@ var funcBody = 'function f2(glob, ffi) {\n\ assertEq(f2.toString(), funcBody); assertEq(f2.toSource(), funcBody); -f2 = function (glob, ffi) { +f2 = function(glob, ffi) { "use asm"; function g() {} return g; } -funcBody1 = funcBody.replace('function f2', 'function '); +funcBody1 = funcBody.replace('function f2', 'function'); assertEq(f2.toString(), funcBody1); assertEq(f2.toSource(), '(' + funcBody1 + ')'); @@ -167,14 +167,14 @@ assertEq(g.toString(), funcBody2); assertEq(g.toSource(), '(' + funcBody2 + ')'); f2 = new Function('glob', 'ffi', bodyOnly); -assertEq(f2.toString(), "function anonymous(glob, ffi) {\n" + bodyOnly + "\n}"); -assertEq(f2.toSource(), "(function anonymous(glob, ffi) {\n" + bodyOnly + "\n})"); +assertEq(f2.toString(), "function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n}"); +assertEq(f2.toSource(), "(function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n})"); if (isAsmJSCompilationAvailable() && isCachingEnabled()) { var m = new Function('glob', 'ffi', bodyOnly); assertEq(isAsmJSModuleLoadedFromCache(m), true); - assertEq(m.toString(), "function anonymous(glob, ffi) {\n" + bodyOnly + "\n}"); - assertEq(m.toSource(), "(function anonymous(glob, ffi) {\n" + bodyOnly + "\n})"); + assertEq(m.toString(), "function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n}"); + assertEq(m.toSource(), "(function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n})"); } })(); @@ -204,14 +204,14 @@ var funcBody = 'function f3(glob, ffi, heap) {\n\ assertEq(f3.toString(), funcBody); assertEq(f3.toSource(), funcBody); -f3 = function (glob, ffi, heap) { +f3 = function(glob, ffi, heap) { "use asm"; function g() {} return g; } -funcBody1 = funcBody.replace('function f3', 'function '); +funcBody1 = funcBody.replace('function f3', 'function'); assertEq(f3.toString(), funcBody1); assertEq(f3.toSource(), '(' + funcBody1 + ')'); @@ -227,14 +227,14 @@ assertEq(g.toString(), funcBody2); assertEq(g.toSource(), '(' + funcBody2 + ')'); f3 = new Function('glob', 'ffi', 'heap', bodyOnly); -assertEq(f3.toString(), "function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n}"); -assertEq(f3.toSource(), "(function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n})"); +assertEq(f3.toString(), "function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n}"); +assertEq(f3.toSource(), "(function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n})"); if (isAsmJSCompilationAvailable() && isCachingEnabled()) { var m = new Function('glob', 'ffi', 'heap', bodyOnly); assertEq(isAsmJSModuleLoadedFromCache(m), true); - assertEq(m.toString(), "function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n}"); - assertEq(m.toSource(), "(function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n})"); + assertEq(m.toString(), "function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n}"); + assertEq(m.toSource(), "(function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n})"); } })(); @@ -243,7 +243,7 @@ if (isAsmJSCompilationAvailable() && isCachingEnabled()) { (function() { var funcSource = - `function (glob, ffi, heap) { + `function(glob, ffi, heap) { "use asm"; function g() {} return g; @@ -252,7 +252,7 @@ var funcSource = var f4 = eval("\"use strict\";\n(" + funcSource + ")"); var expectedToString = funcSource; -var expectedToSource = '(' + expectedToString + ')' +var expectedToSource = '(' + expectedToString + ')'; assertEq(f4.toString(), expectedToString); assertEq(f4.toSource(), expectedToSource); diff --git a/js/src/jit-test/tests/basic/function-tosource-bug779694.js b/js/src/jit-test/tests/basic/function-tosource-bug779694.js index 3893d3ff2..782b2594b 100644 --- a/js/src/jit-test/tests/basic/function-tosource-bug779694.js +++ b/js/src/jit-test/tests/basic/function-tosource-bug779694.js @@ -5,4 +5,4 @@ for (var i=0; i<400; ++i) { x += String.fromCharCode(i * 289); } var s = "'" + x + "'"; -assertEq(Function("evt", s).toString(), "function anonymous(evt) {\n" + s + "\n}"); +assertEq(Function("evt", s).toString(), "function anonymous(evt\n) {\n" + s + "\n}"); diff --git a/js/src/jit-test/tests/basic/function-tosource-constructor.js b/js/src/jit-test/tests/basic/function-tosource-constructor.js index e1d144364..9a8961fe2 100644 --- a/js/src/jit-test/tests/basic/function-tosource-constructor.js +++ b/js/src/jit-test/tests/basic/function-tosource-constructor.js @@ -1,14 +1,14 @@ var f = Function("a", "b", "return a + b;"); -assertEq(f.toString(), "function anonymous(a, b) {\nreturn a + b;\n}"); -assertEq(f.toSource(), "(function anonymous(a, b) {\nreturn a + b;\n})"); +assertEq(f.toString(), "function anonymous(a,b\n) {\nreturn a + b;\n}"); +assertEq(f.toSource(), "(function anonymous(a,b\n) {\nreturn a + b;\n})"); assertEq(decompileFunction(f), f.toString()); f = Function("a", "...rest", "return rest[42] + b;"); -assertEq(f.toString(), "function anonymous(a, ...rest) {\nreturn rest[42] + b;\n}"); -assertEq(f.toSource(), "(function anonymous(a, ...rest) {\nreturn rest[42] + b;\n})") +assertEq(f.toString(), "function anonymous(a,...rest\n) {\nreturn rest[42] + b;\n}"); +assertEq(f.toSource(), "(function anonymous(a,...rest\n) {\nreturn rest[42] + b;\n})") assertEq(decompileFunction(f), f.toString()); f = Function(""); -assertEq(f.toString(), "function anonymous() {\n\n}"); +assertEq(f.toString(), "function anonymous(\n) {\n\n}"); f = Function("", "(abc)"); -assertEq(f.toString(), "function anonymous() {\n(abc)\n}"); -f = Function("", "return function (a, b) a + b;")(); -assertEq(f.toString(), "function (a, b) a + b"); +assertEq(f.toString(), "function anonymous(\n) {\n(abc)\n}"); +f = Function("", "return function (a,b) a + b;")(); +assertEq(f.toString(), "function (a,b) a + b"); diff --git a/js/src/jit-test/tests/basic/function-tosource-getset.js b/js/src/jit-test/tests/basic/function-tosource-getset.js index 36c6d010e..1804d38f2 100644 --- a/js/src/jit-test/tests/basic/function-tosource-getset.js +++ b/js/src/jit-test/tests/basic/function-tosource-getset.js @@ -1,7 +1,7 @@ var o = {get prop() a + b, set prop(x) a + b}; var prop = Object.getOwnPropertyDescriptor(o, "prop"); -assertEq(prop.get.toString(), "function get prop() a + b"); -assertEq(prop.get.toSource(), "(function get prop() a + b)"); -assertEq(prop.set.toString(), "function set prop(x) a + b"); -assertEq(prop.set.toSource(), "(function set prop(x) a + b)"); -assertEq(o.toSource(), "({get prop () a + b, set prop (x) a + b})"); +assertEq(prop.get.toString(), "get prop() { a + b; }"); +assertEq(prop.get.toSource(), "get prop() { a + b; }"); +assertEq(prop.set.toString(), "set prop(x) { a + b; }"); +assertEq(prop.set.toSource(), "set prop(x) { a + b; }"); +assertEq(o.toSource(), "({get prop() { a + b; }, set prop(x) { a + b; }})"); diff --git a/js/src/jit-test/tests/basic/testLet.js b/js/src/jit-test/tests/basic/testLet.js index 263c3eb8a..9a2f39197 100644 --- a/js/src/jit-test/tests/basic/testLet.js +++ b/js/src/jit-test/tests/basic/testLet.js @@ -9,7 +9,7 @@ function test(str, arg, result) var fun = new Function('x', str); var got = fun.toSource(); - var expect = '(function anonymous(x) {\n' + str + '\n})'; + var expect = '(function anonymous(x\n) {\n' + str + '\n})'; if (got !== expect) { print("GOT: " + got); print("EXPECT: " + expect); diff --git a/js/src/jit-test/tests/debug/Script-gc-02.js b/js/src/jit-test/tests/debug/Script-gc-02.js index 04dd4b220..9689a6ebe 100644 --- a/js/src/jit-test/tests/debug/Script-gc-02.js +++ b/js/src/jit-test/tests/debug/Script-gc-02.js @@ -10,5 +10,5 @@ assertEq(arr.length, 10); gc(); for (var i = 0; i < arr.length; i++) - assertEq(arr[i].lineCount, 3); + assertEq(arr[i].lineCount, 4); diff --git a/js/src/jit-test/tests/debug/Script-gc-03.js b/js/src/jit-test/tests/debug/Script-gc-03.js index 30c3e8dbc..5ecb4556f 100644 --- a/js/src/jit-test/tests/debug/Script-gc-03.js +++ b/js/src/jit-test/tests/debug/Script-gc-03.js @@ -10,6 +10,6 @@ assertEq(arr.length, 100); gc(g); for (var i = 0; i < arr.length; i++) - assertEq(arr[i].lineCount, 3); + assertEq(arr[i].lineCount, 4); gc(); diff --git a/js/src/jit-test/tests/debug/Script-sourceStart-04.js b/js/src/jit-test/tests/debug/Script-sourceStart-04.js index 2aa382b7b..4546818e4 100644 --- a/js/src/jit-test/tests/debug/Script-sourceStart-04.js +++ b/js/src/jit-test/tests/debug/Script-sourceStart-04.js @@ -20,6 +20,6 @@ function test(string, range) { } test("eval('2 * 3')", [0, 5]); -test("new Function('2 * 3')", [0, 12]); -test("new Function('x', 'x * x')", [0, 13]); +test("new Function('2 * 3')", [0, 31]); +test("new Function('x', 'x * x')", [0, 32]); assertEq(count, 6); diff --git a/js/src/jit-test/tests/debug/Source-text-02.js b/js/src/jit-test/tests/debug/Source-text-02.js index 64cfce92a..46e76015e 100644 --- a/js/src/jit-test/tests/debug/Source-text-02.js +++ b/js/src/jit-test/tests/debug/Source-text-02.js @@ -3,6 +3,7 @@ let g = newGlobal(); let dbg = new Debugger(g); +var text; var count = 0; dbg.onNewScript = function (script) { ++count; diff --git a/js/src/jit-test/tests/ion/bug1493900-1.js b/js/src/jit-test/tests/ion/bug1493900-1.js new file mode 100644 index 000000000..643c1943d --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1493900-1.js @@ -0,0 +1,17 @@ +function f() { + var objs = []; + for (var i = 0; i < 100; i++) { + objs[i] = {}; + } + var o = objs[0]; + var a = new Float64Array(1024); + function g(a, b) { + let p = b; + for (; p.x < 0; p = p.x) { + while (p === p) {} + } + for (var i = 0; i < 10000; ++i) {} + } + g(a, o); +} +f(); diff --git a/js/src/jit-test/tests/ion/bug1493900-2.js b/js/src/jit-test/tests/ion/bug1493900-2.js new file mode 100644 index 000000000..7e7f5fdec --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1493900-2.js @@ -0,0 +1,7 @@ +function f(a, b) { + for (; b.x < 0; b = b.x) { + while (b === b) {}; + } + for (var i = 0; i < 99999; ++i) {} +} +f(0, 0); diff --git a/js/src/jit-test/tests/latin1/assorted.js b/js/src/jit-test/tests/latin1/assorted.js index cef79cb96..1389a1ada 100644 --- a/js/src/jit-test/tests/latin1/assorted.js +++ b/js/src/jit-test/tests/latin1/assorted.js @@ -12,18 +12,18 @@ var o = {}; Object.defineProperty(o, "prop", {get: function() { return 1; }, set: function() { return 2; }, enumerable: true, configurable: true}); -assertEq(o.toSource(), "({get prop () { return 1; }, set prop () { return 2; }})"); +assertEq(o.toSource(), "({get prop() { return 1; }, set prop() { return 2; }})"); // obj.toSource TwoByte Object.defineProperty(o, "prop", {get: function() { return "\u1200"; }, set: function() { return "\u1200"; }, enumerable: true}); -assertEq(o.toSource(), '({get prop () { return "\\u1200"; }, set prop () { return "\\u1200"; }})'); +assertEq(o.toSource(), '({get prop() { return "\\u1200"; }, set prop() { return "\\u1200"; }})'); var ff = function() { return 10; }; ff.toSource = function() { return "((11))"; } Object.defineProperty(o, "prop", {get: ff, set: ff, enumerable: true}); -assertEq(o.toSource(), "({prop:((11)), prop:((11))})"); +assertEq(o.toSource(), "({get prop(11), set prop(11)})"); // XDR load(libdir + 'bytecode-cache.js'); diff --git a/js/src/jit-test/tests/latin1/function.js b/js/src/jit-test/tests/latin1/function.js index a0dedf251..07a76733a 100644 --- a/js/src/jit-test/tests/latin1/function.js +++ b/js/src/jit-test/tests/latin1/function.js @@ -6,11 +6,11 @@ function test() { var f = Function(arg1TwoByte, arg2Latin1, bodyLatin1); assertEq(f(10, 20), 60); - assertEq(f.toSource().includes("arg1\u1200, arg2"), true); + assertEq(f.toSource().includes("arg1\u1200,arg2"), true); var bodyTwoByte = "return arg1\u1200 + arg2;"; f = Function(arg1TwoByte, arg2Latin1, bodyTwoByte); assertEq(f(30, 40), 70); - assertEq(f.toSource().includes("arg1\u1200, arg2"), true); + assertEq(f.toSource().includes("arg1\u1200,arg2"), true); } test(); diff --git a/js/src/jit/AliasAnalysisShared.cpp b/js/src/jit/AliasAnalysisShared.cpp index ae28327ca..81c0fd067 100644 --- a/js/src/jit/AliasAnalysisShared.cpp +++ b/js/src/jit/AliasAnalysisShared.cpp @@ -102,7 +102,6 @@ GetObject(const MDefinition* ins) case MDefinition::Op_SetDisjointTypedElements: case MDefinition::Op_ArrayPopShift: case MDefinition::Op_ArrayPush: - case MDefinition::Op_ArraySlice: case MDefinition::Op_LoadTypedArrayElementHole: case MDefinition::Op_StoreTypedArrayElementHole: case MDefinition::Op_LoadFixedSlot: @@ -126,6 +125,7 @@ GetObject(const MDefinition* ins) object = ins->getOperand(0); break; case MDefinition::Op_GetPropertyCache: + case MDefinition::Op_CallGetProperty: case MDefinition::Op_LoadTypedArrayElementStatic: case MDefinition::Op_StoreTypedArrayElementStatic: case MDefinition::Op_GetDOMProperty: @@ -148,6 +148,7 @@ GetObject(const MDefinition* ins) case MDefinition::Op_WasmLoadGlobalVar: case MDefinition::Op_WasmStoreGlobalVar: case MDefinition::Op_ArrayJoin: + case MDefinition::Op_ArraySlice: return nullptr; default: #ifdef DEBUG diff --git a/js/src/jit/BacktrackingAllocator.cpp b/js/src/jit/BacktrackingAllocator.cpp index 94ef25785..645aefc4f 100644 --- a/js/src/jit/BacktrackingAllocator.cpp +++ b/js/src/jit/BacktrackingAllocator.cpp @@ -378,7 +378,6 @@ BacktrackingAllocator::init() size_t numVregs = graph.numVirtualRegisters(); if (!vregs.init(mir->alloc(), numVregs)) return false; - memset(&vregs[0], 0, sizeof(VirtualRegister) * numVregs); for (uint32_t i = 0; i < numVregs; i++) new(&vregs[i]) VirtualRegister(); @@ -1101,9 +1100,9 @@ BacktrackingAllocator::mergeAndQueueRegisters() if (iter->isParameter()) { for (size_t i = 0; i < iter->numDefs(); i++) { DebugOnly<bool> found = false; - VirtualRegister ¶mVreg = vreg(iter->getDef(i)); + VirtualRegister& paramVreg = vreg(iter->getDef(i)); for (; original < paramVreg.vreg(); original++) { - VirtualRegister &originalVreg = vregs[original]; + VirtualRegister& originalVreg = vregs[original]; if (*originalVreg.def()->output() == *iter->getDef(i)->output()) { MOZ_ASSERT(originalVreg.ins()->isParameter()); if (!tryMergeBundles(originalVreg.firstBundle(), paramVreg.firstBundle())) @@ -1136,7 +1135,7 @@ BacktrackingAllocator::mergeAndQueueRegisters() LBlock* block = graph.getBlock(i); for (size_t j = 0; j < block->numPhis(); j++) { LPhi* phi = block->getPhi(j); - VirtualRegister &outputVreg = vreg(phi->getDef(0)); + VirtualRegister& outputVreg = vreg(phi->getDef(0)); for (size_t k = 0, kend = phi->numOperands(); k < kend; k++) { VirtualRegister& inputVreg = vreg(phi->getOperand(k)->toUse()); if (!tryMergeBundles(inputVreg.firstBundle(), outputVreg.firstBundle())) @@ -1334,7 +1333,7 @@ BacktrackingAllocator::computeRequirement(LiveBundle* bundle, for (LiveRange::BundleLinkIterator iter = bundle->rangesBegin(); iter; iter++) { LiveRange* range = LiveRange::get(*iter); - VirtualRegister ® = vregs[range->vreg()]; + VirtualRegister& reg = vregs[range->vreg()]; if (range->hasDefinition()) { // Deal with any definition constraints/hints. @@ -1396,7 +1395,7 @@ BacktrackingAllocator::tryAllocateRegister(PhysicalRegister& r, LiveBundle* bund for (LiveRange::BundleLinkIterator iter = bundle->rangesBegin(); iter; iter++) { LiveRange* range = LiveRange::get(*iter); - VirtualRegister ® = vregs[range->vreg()]; + VirtualRegister& reg = vregs[range->vreg()]; if (!reg.isCompatible(r.reg)) return true; @@ -1737,6 +1736,18 @@ BacktrackingAllocator::deadRange(LiveRange* range) } bool +BacktrackingAllocator::moveAtEdge(LBlock* predecessor, LBlock* successor, LiveRange* from, + LiveRange* to, LDefinition::Type type) +{ + if (successor->mir()->numPredecessors() > 1) { + MOZ_ASSERT(predecessor->mir()->numSuccessors() == 1); + return moveAtExit(predecessor, from, to, type); + } + + return moveAtEntry(successor, from, to, type); +} + +bool BacktrackingAllocator::resolveControlFlow() { // Add moves to handle changing assignments for vregs over their lifetime. @@ -1844,10 +1855,15 @@ BacktrackingAllocator::resolveControlFlow() LiveRange* from = vreg(input).rangeFor(exitOf(predecessor), /* preferRegister = */ true); MOZ_ASSERT(from); - if (!alloc().ensureBallast()) + if (!alloc().ensureBallast()) { return false; - if (!moveAtExit(predecessor, from, to, def->type())) + } + + // Note: we have to use moveAtEdge both here and below (for edge + // resolution) to avoid conflicting moves. See bug 1493900. + if (!moveAtEdge(predecessor, successor, from, to, def->type())) { return false; + } } } } @@ -1876,16 +1892,12 @@ BacktrackingAllocator::resolveControlFlow() if (targetRange->covers(exitOf(predecessor))) continue; - if (!alloc().ensureBallast()) + if (!alloc().ensureBallast()) { return false; + } LiveRange* from = reg.rangeFor(exitOf(predecessor), true); - if (successor->mir()->numPredecessors() > 1) { - MOZ_ASSERT(predecessor->mir()->numSuccessors() == 1); - if (!moveAtExit(predecessor, from, targetRange, reg.type())) - return false; - } else { - if (!moveAtEntry(successor, from, targetRange, reg.type())) - return false; + if (!moveAtEdge(predecessor, successor, from, targetRange, reg.type())) { + return false; } } } diff --git a/js/src/jit/BacktrackingAllocator.h b/js/src/jit/BacktrackingAllocator.h index 6d14ffacd..c6cf26695 100644 --- a/js/src/jit/BacktrackingAllocator.h +++ b/js/src/jit/BacktrackingAllocator.h @@ -108,8 +108,9 @@ class Requirement } MOZ_ASSERT(newRequirement.kind() == Requirement::REGISTER); - if (kind() == Requirement::FIXED) + if (kind() == Requirement::FIXED) { return allocation().isRegister(); + } *this = newRequirement; return true; @@ -353,10 +354,12 @@ class LiveRange : public TempObject // Comparator for use in range splay trees. static int compare(LiveRange* v0, LiveRange* v1) { // LiveRange includes 'from' but excludes 'to'. - if (v0->to() <= v1->from()) + if (v0->to() <= v1->from()) { return -1; - if (v0->from() >= v1->to()) + } + if (v0->from() >= v1->to()) { return 1; + } return 0; } }; @@ -478,34 +481,31 @@ class LiveBundle : public TempObject class VirtualRegister { // Instruction which defines this register. - LNode* ins_; + LNode* ins_ = nullptr; // Definition in the instruction for this register. - LDefinition* def_; + LDefinition* def_ = nullptr; // All live ranges for this register. These may overlap each other, and are // ordered by their start position. InlineForwardList<LiveRange::RegisterLink> ranges_; // Whether def_ is a temp or an output. - bool isTemp_; + bool isTemp_ = false; // Whether this vreg is an input for some phi. This use is not reflected in // any range on the vreg. - bool usedByPhi_; + bool usedByPhi_ = false; // If this register's definition is MUST_REUSE_INPUT, whether a copy must // be introduced before the definition that relaxes the policy. - bool mustCopyInput_; + bool mustCopyInput_ = false; void operator=(const VirtualRegister&) = delete; VirtualRegister(const VirtualRegister&) = delete; public: - explicit VirtualRegister() - { - // Note: This class is zeroed before it is constructed. - } + VirtualRegister() = default; void init(LNode* ins, LDefinition* def, bool isTemp) { MOZ_ASSERT(!ins_); @@ -645,10 +645,12 @@ class BacktrackingAllocator : protected RegisterAllocator // Comparator for use in splay tree. static int compare(CallRange* v0, CallRange* v1) { - if (v0->range.to <= v1->range.from) + if (v0->range.to <= v1->range.from) { return -1; - if (v0->range.from >= v1->range.to) + } + if (v0->range.from >= v1->range.to) { return 1; + } return 0; } }; @@ -747,36 +749,43 @@ class BacktrackingAllocator : protected RegisterAllocator MOZ_MUST_USE bool moveInput(LInstruction* ins, LiveRange* from, LiveRange* to, LDefinition::Type type) { - if (from->bundle()->allocation() == to->bundle()->allocation()) + if (from->bundle()->allocation() == to->bundle()->allocation()) { return true; + } LMoveGroup* moves = getInputMoveGroup(ins); return addMove(moves, from, to, type); } MOZ_MUST_USE bool moveAfter(LInstruction* ins, LiveRange* from, LiveRange* to, LDefinition::Type type) { - if (from->bundle()->allocation() == to->bundle()->allocation()) + if (from->bundle()->allocation() == to->bundle()->allocation()) { return true; + } LMoveGroup* moves = getMoveGroupAfter(ins); return addMove(moves, from, to, type); } MOZ_MUST_USE bool moveAtExit(LBlock* block, LiveRange* from, LiveRange* to, LDefinition::Type type) { - if (from->bundle()->allocation() == to->bundle()->allocation()) + if (from->bundle()->allocation() == to->bundle()->allocation()) { return true; + } LMoveGroup* moves = block->getExitMoveGroup(alloc()); return addMove(moves, from, to, type); } MOZ_MUST_USE bool moveAtEntry(LBlock* block, LiveRange* from, LiveRange* to, LDefinition::Type type) { - if (from->bundle()->allocation() == to->bundle()->allocation()) + if (from->bundle()->allocation() == to->bundle()->allocation()) { return true; + } LMoveGroup* moves = block->getEntryMoveGroup(alloc()); return addMove(moves, from, to, type); } + MOZ_MUST_USE bool moveAtEdge(LBlock* predecessor, LBlock* successor, LiveRange* from, + LiveRange* to, LDefinition::Type type); + // Debugging methods. void dumpAllocations(); diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index 2c9ffb607..d255c32a8 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -2306,7 +2306,7 @@ jit::RemoveUnmarkedBlocks(MIRGenerator* mir, MIRGraph& graph, uint32_t numMarked // bailout. for (PostorderIterator it(graph.poBegin()); it != graph.poEnd();) { MBasicBlock* block = *it++; - if (!block->isMarked()) + if (block->isMarked()) continue; FlagAllOperandsAsHavingRemovedUses(mir, block); @@ -3127,6 +3127,15 @@ ExtractMathSpace(MDefinition* ins) MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unknown TruncateKind"); } +static bool MonotoneAdd(int32_t lhs, int32_t rhs) { + return (lhs >= 0 && rhs >= 0) || (lhs <= 0 && rhs <= 0); +} + +static bool MonotoneSub(int32_t lhs, int32_t rhs) { + return (lhs >= 0 && rhs <= 0) || (lhs <= 0 && rhs >= 0); +} + + // Extract a linear sum from ins, if possible (otherwise giving the sum 'ins + 0'). SimpleLinearSum jit::ExtractLinearSum(MDefinition* ins, MathSpace space) @@ -3168,10 +3177,12 @@ jit::ExtractLinearSum(MDefinition* ins, MathSpace space) // Check if this is of the form <SUM> + n or n + <SUM>. if (ins->isAdd()) { int32_t constant; - if (space == MathSpace::Modulo) + if (space == MathSpace::Modulo) { constant = lsum.constant + rsum.constant; - else if (!SafeAdd(lsum.constant, rsum.constant, &constant)) + } else if (!SafeAdd(lsum.constant, rsum.constant, &constant) || + !MonotoneAdd(lsum.constant, rsum.constant)) { return SimpleLinearSum(ins, 0); + } return SimpleLinearSum(lsum.term ? lsum.term : rsum.term, constant); } @@ -3179,10 +3190,12 @@ jit::ExtractLinearSum(MDefinition* ins, MathSpace space) // Check if this is of the form <SUM> - n. if (lsum.term) { int32_t constant; - if (space == MathSpace::Modulo) + if (space == MathSpace::Modulo) { constant = lsum.constant - rsum.constant; - else if (!SafeSub(lsum.constant, rsum.constant, &constant)) + } else if (!SafeSub(lsum.constant, rsum.constant, &constant) || + !MonotoneSub(lsum.constant, rsum.constant)) { return SimpleLinearSum(ins, 0); + } return SimpleLinearSum(lsum.term, constant); } diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h index c581aa62e..55c3d4dad 100644 --- a/js/src/jit/IonCode.h +++ b/js/src/jit/IonCode.h @@ -9,7 +9,6 @@ #include "mozilla/Atomics.h" #include "mozilla/MemoryReporting.h" -#include "mozilla/PodOperations.h" #include "jstypes.h" @@ -692,17 +691,15 @@ struct IonScriptCounts { private: // Any previous invalidated compilation(s) for the script. - IonScriptCounts* previous_; + IonScriptCounts* previous_ = nullptr; // Information about basic blocks in this script. - size_t numBlocks_; - IonBlockCounts* blocks_; + size_t numBlocks_ = 0; + IonBlockCounts* blocks_ = nullptr; public: - IonScriptCounts() { - mozilla::PodZero(this); - } + IonScriptCounts() = default; ~IonScriptCounts() { for (size_t i = 0; i < numBlocks_; i++) diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp index eb5a6c1c2..b9a7c7b27 100644 --- a/js/src/jit/JitOptions.cpp +++ b/js/src/jit/JitOptions.cpp @@ -222,7 +222,7 @@ DefaultJitOptions::DefaultJitOptions() } // Toggles whether unboxed plain objects can be created by the VM. - SET_DEFAULT(disableUnboxedObjects, false); + SET_DEFAULT(disableUnboxedObjects, true); // Test whether Atomics are allowed in asm.js code. SET_DEFAULT(asmJSAtomicsEnable, false); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 6ec05af76..fb0f22fc3 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -8272,7 +8272,10 @@ class MGetFirstDollarIndex : MUnaryInstruction(str) { setResultType(MIRType::Int32); - setMovable(); + + // Codegen assumes string length > 0 but that's not guaranteed in RegExp. + // Don't allow LICM to move this. + MOZ_ASSERT(!isMovable()); } public: @@ -9907,10 +9910,6 @@ class MArraySlice return unboxedType_; } - AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedType()) | - AliasSet::ObjectFields); - } bool possiblyCalls() const override { return true; } @@ -11834,7 +11833,8 @@ class MCallGetProperty AliasSet getAliasSet() const override { if (!idempotent_) return AliasSet::Store(AliasSet::Any); - return AliasSet::None(); + return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | + AliasSet::DynamicSlot); } bool possiblyCalls() const override { return true; diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 9dbbe7624..f633b9b7b 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -2214,12 +2214,6 @@ MacroAssembler::finish() } MacroAssemblerSpecific::finish(); - - MOZ_RELEASE_ASSERT(size() <= MaxCodeBytesPerProcess, - "AssemblerBuffer should ensure we don't exceed MaxCodeBytesPerProcess"); - - if (bytesNeeded() > MaxCodeBytesPerProcess) - setOOM(); } void diff --git a/js/src/jit/ProcessExecutableMemory.cpp b/js/src/jit/ProcessExecutableMemory.cpp index 301541541..71c2ab0dc 100644 --- a/js/src/jit/ProcessExecutableMemory.cpp +++ b/js/src/jit/ProcessExecutableMemory.cpp @@ -385,6 +385,14 @@ class PageBitSet #endif }; +// Limit on the number of bytes of executable memory to prevent JIT spraying +// attacks. +#if JS_BITS_PER_WORD == 32 +static const size_t MaxCodeBytesPerProcess = 128 * 1024 * 1024; +#else +static const size_t MaxCodeBytesPerProcess = 1 * 1024 * 1024 * 1024; +#endif + // Per-process executable memory allocator. It reserves a block of memory of // MaxCodeBytesPerProcess bytes, then allocates/deallocates pages from that. // diff --git a/js/src/jit/ProcessExecutableMemory.h b/js/src/jit/ProcessExecutableMemory.h index a0e2fab98..078ce7cb7 100644 --- a/js/src/jit/ProcessExecutableMemory.h +++ b/js/src/jit/ProcessExecutableMemory.h @@ -17,14 +17,6 @@ namespace jit { // alignment though. static const size_t ExecutableCodePageSize = 64 * 1024; -// Limit on the number of bytes of executable memory to prevent JIT spraying -// attacks. -#if JS_BITS_PER_WORD == 32 -static const size_t MaxCodeBytesPerProcess = 128 * 1024 * 1024; -#else -static const size_t MaxCodeBytesPerProcess = 1 * 1024 * 1024 * 1024; -#endif - enum class ProtectionSetting { Protected, // Not readable, writable, or executable. Writable, diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index 95484c249..d64f9b8ca 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -2167,7 +2167,7 @@ RangeAnalysis::analyzeLoopPhi(MBasicBlock* header, LoopIterationBound* loopBound if (initial->block()->isMarked()) return; - SimpleLinearSum modified = ExtractLinearSum(phi->getLoopBackedgeOperand()); + SimpleLinearSum modified = ExtractLinearSum(phi->getLoopBackedgeOperand(), MathSpace::Infinite); if (modified.term != phi || modified.constant == 0) return; diff --git a/js/src/jit/StupidAllocator.cpp b/js/src/jit/StupidAllocator.cpp index 8e3ea6286..55431e8e0 100644 --- a/js/src/jit/StupidAllocator.cpp +++ b/js/src/jit/StupidAllocator.cpp @@ -407,7 +407,6 @@ StupidAllocator::allocateForDefinition(LInstruction* ins, LDefinition* def) { uint32_t vreg = def->virtualRegister(); - CodePosition from; if ((def->output()->isRegister() && def->policy() == LDefinition::FIXED) || def->policy() == LDefinition::MUST_REUSE_INPUT) { diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index aac9687b8..8044e75cb 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -7,8 +7,6 @@ #ifndef jit_shared_Assembler_shared_h #define jit_shared_Assembler_shared_h -#include "mozilla/PodOperations.h" - #include <limits.h> #include "jit/AtomicOp.h" @@ -491,10 +489,10 @@ class CodeLabel class CodeOffsetJump { - size_t offset_; + size_t offset_ = 0; #ifdef JS_SMALL_BRANCH - size_t jumpTableIndex_; + size_t jumpTableIndex_ = 0; #endif public: @@ -510,9 +508,7 @@ class CodeOffsetJump explicit CodeOffsetJump(size_t offset) : offset_(offset) {} #endif - CodeOffsetJump() { - mozilla::PodZero(this); - } + CodeOffsetJump() = default; size_t offset() const { return offset_; diff --git a/js/src/jit/shared/IonAssemblerBuffer.h b/js/src/jit/shared/IonAssemblerBuffer.h index 3a6552696..cc20e26d2 100644 --- a/js/src/jit/shared/IonAssemblerBuffer.h +++ b/js/src/jit/shared/IonAssemblerBuffer.h @@ -181,10 +181,6 @@ class AssemblerBuffer protected: virtual Slice* newSlice(LifoAlloc& a) { - if (size() > MaxCodeBytesPerProcess - sizeof(Slice)) { - fail_oom(); - return nullptr; - } Slice* tmp = static_cast<Slice*>(a.alloc(sizeof(Slice))); if (!tmp) { fail_oom(); diff --git a/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h b/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h index fe678fc7d..8343579c8 100644 --- a/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h +++ b/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h @@ -68,33 +68,6 @@ namespace js { namespace jit { - // AllocPolicy for AssemblerBuffer. OOMs when trying to allocate more than - // MaxCodeBytesPerProcess bytes. Use private inheritance to make sure we - // explicitly have to expose SystemAllocPolicy methods. - class AssemblerBufferAllocPolicy : private SystemAllocPolicy - { - public: - using SystemAllocPolicy::checkSimulatedOOM; - using SystemAllocPolicy::reportAllocOverflow; - using SystemAllocPolicy::free_; - - template <typename T> T* pod_realloc(T* p, size_t oldSize, size_t newSize) { - static_assert(sizeof(T) == 1, - "AssemblerBufferAllocPolicy should only be used with byte vectors"); - MOZ_ASSERT(oldSize <= MaxCodeBytesPerProcess); - if (MOZ_UNLIKELY(newSize > MaxCodeBytesPerProcess)) - return nullptr; - return SystemAllocPolicy::pod_realloc<T>(p, oldSize, newSize); - } - template <typename T> T* pod_malloc(size_t numElems) { - static_assert(sizeof(T) == 1, - "AssemblerBufferAllocPolicy should only be used with byte vectors"); - if (MOZ_UNLIKELY(numElems > MaxCodeBytesPerProcess)) - return nullptr; - return SystemAllocPolicy::pod_malloc<T>(numElems); - } - }; - class AssemblerBuffer { template<size_t size, typename T> @@ -120,10 +93,8 @@ namespace jit { void ensureSpace(size_t space) { - // This should only be called with small |space| values to ensure - // we don't overflow below. - MOZ_ASSERT(space <= 16); - if (MOZ_UNLIKELY(!m_buffer.reserve(m_buffer.length() + space))) + if (MOZ_UNLIKELY(m_buffer.length() > (SIZE_MAX - space) || + !m_buffer.reserve(m_buffer.length() + space))) oomDetected(); } @@ -198,7 +169,7 @@ namespace jit { m_buffer.clear(); } - PageProtectingVector<unsigned char, 256, AssemblerBufferAllocPolicy> m_buffer; + PageProtectingVector<unsigned char, 256, SystemAllocPolicy> m_buffer; bool m_oom; }; diff --git a/js/src/jit/x86/Assembler-x86.h b/js/src/jit/x86/Assembler-x86.h index 3fb5efaff..5939583d9 100644 --- a/js/src/jit/x86/Assembler-x86.h +++ b/js/src/jit/x86/Assembler-x86.h @@ -421,20 +421,11 @@ class Assembler : public AssemblerX86Shared MOZ_ASSERT(dest.size() == 16); masm.vhaddpd_rr(src.encoding(), dest.encoding()); } - void vsubpd(const Operand& src1, FloatRegister src0, FloatRegister dest) { + void vsubpd(FloatRegister src1, FloatRegister src0, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); MOZ_ASSERT(src0.size() == 16); MOZ_ASSERT(dest.size() == 16); - switch (src1.kind()) { - case Operand::MEM_REG_DISP: - masm.vsubpd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); - break; - case Operand::MEM_ADDRESS32: - masm.vsubpd_mr(src1.address(), src0.encoding(), dest.encoding()); - break; - default: - MOZ_CRASH("unexpected operand kind"); - } + masm.vsubpd_rr(src1.encoding(), src0.encoding(), dest.encoding()); } void vpunpckldq(FloatRegister src1, FloatRegister src0, FloatRegister dest) { diff --git a/js/src/jit/x86/BaseAssembler-x86.h b/js/src/jit/x86/BaseAssembler-x86.h index 5b16311d0..caaef3f82 100644 --- a/js/src/jit/x86/BaseAssembler-x86.h +++ b/js/src/jit/x86/BaseAssembler-x86.h @@ -152,14 +152,6 @@ class BaseAssemblerX86 : public BaseAssembler { twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, src1, src0, dst); } - void vsubpd_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) - { - twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, offset, base, src0, dst); - } - void vsubpd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) - { - twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, address, src0, dst); - } void vpunpckldq_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vpunpckldq", VEX_PD, OP2_PUNPCKLDQ, src1, src0, dst); diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index dc97b5b5b..429a71fa9 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -21,15 +21,6 @@ using namespace js; using namespace js::jit; -// vpunpckldq requires 16-byte boundary for memory operand. -// See convertUInt64ToDouble for the details. -MOZ_ALIGNED_DECL(static const uint64_t, 16) TO_DOUBLE[4] = { - 0x4530000043300000LL, - 0x0LL, - 0x4330000000000000LL, - 0x4530000000000000LL -}; - static const double TO_DOUBLE_HIGH_SCALE = 0x100000000; bool @@ -90,8 +81,16 @@ MacroAssemblerX86::convertUInt64ToDouble(Register64 src, FloatRegister dest, Reg // here, each 64-bit part of dest represents following double: // HI(dest) = 0x 1.00000HHHHHHHH * 2**84 == 2**84 + 0x HHHHHHHH 00000000 // LO(dest) = 0x 1.00000LLLLLLLL * 2**52 == 2**52 + 0x 00000000 LLLLLLLL - movePtr(ImmWord((uintptr_t)TO_DOUBLE), temp); - vpunpckldq(Operand(temp, 0), dest128, dest128); + // See convertUInt64ToDouble for the details. + static const int32_t CST1[4] = { + 0x43300000, + 0x45300000, + 0x0, + 0x0, + }; + + loadConstantSimd128Int(SimdConstant::CreateX4(CST1), ScratchSimd128Reg); + vpunpckldq(ScratchSimd128Reg, dest128, dest128); // Subtract a constant C2 from dest, for each 64-bit part: // C2 = 0x 45300000 00000000 43300000 00000000 @@ -101,7 +100,15 @@ MacroAssemblerX86::convertUInt64ToDouble(Register64 src, FloatRegister dest, Reg // after the operation each 64-bit part of dest represents following: // HI(dest) = double(0x HHHHHHHH 00000000) // LO(dest) = double(0x 00000000 LLLLLLLL) - vsubpd(Operand(temp, sizeof(uint64_t) * 2), dest128, dest128); + static const int32_t CST2[4] = { + 0x0, + 0x43300000, + 0x0, + 0x45300000, + }; + + loadConstantSimd128Int(SimdConstant::CreateX4(CST2), ScratchSimd128Reg); + vsubpd(ScratchSimd128Reg, dest128, dest128); // Add HI(dest) and LO(dest) in double and store it into LO(dest), // LO(dest) = double(0x HHHHHHHH 00000000) + double(0x 00000000 LLLLLLLL) diff --git a/js/src/js.msg b/js/src/js.msg index a276dab94..4ded69a25 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -186,7 +186,6 @@ MSG_DEF(JSMSG_AS_AFTER_IMPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'as' MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'") MSG_DEF(JSMSG_ASYNC_GENERATOR, 0, JSEXN_SYNTAXERR, "generator function or method can't be async") MSG_DEF(JSMSG_AWAIT_IN_DEFAULT, 0, JSEXN_SYNTAXERR, "await can't be used in default expression") -MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "anonymous generator function returns a value") MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)") MSG_DEF(JSMSG_BAD_BINDING, 1, JSEXN_SYNTAXERR, "redefining {0} is deprecated") MSG_DEF(JSMSG_BAD_CONST_DECL, 0, JSEXN_SYNTAXERR, "missing = in const declaration") @@ -200,12 +199,12 @@ MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 0, JSEXN_SYNTAXERR, "invalid for each loo MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid for-in/of left-hand side") MSG_DEF(JSMSG_LEXICAL_DECL_DEFINES_LET,0, JSEXN_SYNTAXERR, "a lexical declaration can't define a 'let' binding") MSG_DEF(JSMSG_LET_STARTING_FOROF_LHS, 0, JSEXN_SYNTAXERR, "an expression X in 'for (X of Y)' must not start with 'let'") -MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 1, JSEXN_TYPEERR, "generator function {0} returns a value") +MSG_DEF(JSMSG_BAD_FUNCTION_YIELD, 0, JSEXN_TYPEERR, "can't use 'yield' in a function that can return a value") +MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "generator function can't return a value") MSG_DEF(JSMSG_BAD_GENEXP_BODY, 1, JSEXN_SYNTAXERR, "illegal use of {0} in generator expression") -MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 0, JSEXN_REFERENCEERR, "invalid increment/decrement operand") +MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 0, JSEXN_SYNTAXERR, "invalid increment/decrement operand") MSG_DEF(JSMSG_BAD_METHOD_DEF, 0, JSEXN_SYNTAXERR, "bad method definition") MSG_DEF(JSMSG_BAD_OCTAL, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant") -MSG_DEF(JSMSG_BAD_OPERAND, 1, JSEXN_SYNTAXERR, "invalid {0} operand") MSG_DEF(JSMSG_BAD_POW_LEFTSIDE, 0, JSEXN_SYNTAXERR, "unparenthesized unary expression can't appear on the left-hand side of '**'") MSG_DEF(JSMSG_BAD_PROP_ID, 0, JSEXN_SYNTAXERR, "invalid property id") MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 1, JSEXN_SYNTAXERR, "{0} not in function") @@ -262,6 +261,8 @@ MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT, 2, JSEXN_SYNTAXERR, "unexpected garbage a MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER, 0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal") MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 0, JSEXN_SYNTAXERR, "illegal character") MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module") +MSG_DEF(JSMSG_OF_AFTER_FOR_LOOP_DECL, 0, JSEXN_SYNTAXERR, "a declaration in the head of a for-of loop can't have an initializer") +MSG_DEF(JSMSG_IN_AFTER_LEXICAL_FOR_DECL,0,JSEXN_SYNTAXERR, "a lexical declaration in the head of a for-in loop can't have an initializer") MSG_DEF(JSMSG_INVALID_FOR_IN_DECL_WITH_INIT,0,JSEXN_SYNTAXERR,"for-in loop head declarations may not have initializers") MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found") MSG_DEF(JSMSG_LET_COMP_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable") diff --git a/js/src/jsapi-tests/testGCAllocator.cpp b/js/src/jsapi-tests/testGCAllocator.cpp index 2c5c58a29..d203019ec 100644 --- a/js/src/jsapi-tests/testGCAllocator.cpp +++ b/js/src/jsapi-tests/testGCAllocator.cpp @@ -14,8 +14,6 @@ #if defined(XP_WIN) #include "jswin.h" #include <psapi.h> -#elif defined(SOLARIS) -// This test doesn't apply to Solaris. #elif defined(XP_UNIX) #include <algorithm> #include <errno.h> @@ -39,8 +37,6 @@ BEGIN_TEST(testGCAllocator) # else // Various APIs are unavailable. This test is disabled. return true; # endif -#elif defined(SOLARIS) - return true; #elif defined(XP_UNIX) PageSize = size_t(sysconf(_SC_PAGESIZE)); #else @@ -301,12 +297,6 @@ void* mapMemory(size_t length) { return nullptr; } void unmapPages(void* p, size_t size) { } # endif -#elif defined(SOLARIS) // This test doesn't apply to Solaris. - -void* mapMemoryAt(void* desired, size_t length) { return nullptr; } -void* mapMemory(size_t length) { return nullptr; } -void unmapPages(void* p, size_t size) { } - #elif defined(XP_UNIX) void* @@ -377,7 +367,7 @@ unmapPages(void* p, size_t size) MOZ_RELEASE_ASSERT(errno == ENOMEM); } -#else // !defined(XP_WIN) && !defined(SOLARIS) && !defined(XP_UNIX) +#else // !defined(XP_WIN) && !defined(XP_UNIX) #error "Memory mapping functions are not defined for your OS." #endif END_TEST(testGCAllocator) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 85a38bba4..f78f94dc1 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2003,10 +2003,10 @@ JS_GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, const char* name, } JS_PUBLIC_API(bool) -JS_GetOwnUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* name, +JS_GetOwnUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, MutableHandle<PropertyDescriptor> desc) { - JSAtom* atom = AtomizeChars(cx, name, js_strlen(name)); + JSAtom* atom = AtomizeChars(cx, name, namelen); if (!atom) return false; RootedId id(cx, AtomToId(atom)); @@ -2028,7 +2028,19 @@ JS_GetPropertyDescriptor(JSContext* cx, HandleObject obj, const char* name, if (!atom) return false; RootedId id(cx, AtomToId(atom)); - return atom && JS_GetPropertyDescriptorById(cx, obj, id, desc); + return JS_GetPropertyDescriptorById(cx, obj, id, desc); +} + +JS_PUBLIC_API(bool) +JS_GetUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, + MutableHandle<PropertyDescriptor> desc) +{ + JSAtom* atom = AtomizeChars(cx, name, namelen); + if (!atom) { + return false; + } + RootedId id(cx, AtomToId(atom)); + return JS_GetPropertyDescriptorById(cx, obj, id, desc); } static bool @@ -4238,7 +4250,7 @@ JS_GetFunctionScript(JSContext* cx, HandleFunction fun) */ static bool CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, - const char* name, + HandleAtom name, bool isInvalidName, SourceBufferHolder& srcBuf, uint32_t parameterListEnd, HandleObject enclosingEnv, HandleScope enclosingScope, MutableHandleFunction fun) @@ -4249,13 +4261,8 @@ CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, assertSameCompartment(cx, enclosingEnv); RootedAtom funAtom(cx); - if (name) { - funAtom = Atomize(cx, name, strlen(name)); - if (!funAtom) - return false; - } - - fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL, funAtom, + fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL, + isInvalidName ? nullptr : name, /* proto = */ nullptr, gc::AllocKind::FUNCTION, TenuredObject, enclosingEnv)); @@ -4273,11 +4280,17 @@ CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, return false; } + // When function name is not a valid identifier, the generated function + // source in srcBuf doesn't have a function name. Set it here. + if (isInvalidName) + fun->setAtom(name); + return true; } static MOZ_MUST_USE bool -BuildFunctionString(unsigned nargs, const char* const* argnames, +BuildFunctionString(const char* name, size_t nameLen, + unsigned nargs, const char* const* argnames, const SourceBufferHolder& srcBuf, StringBuffer* out, uint32_t* parameterListEnd) { @@ -4286,6 +4299,12 @@ BuildFunctionString(unsigned nargs, const char* const* argnames, if (!out->ensureTwoByteChars()) return false; + if (!out->append("function ")) + return false; + if (name) { + if (!out->append(name, nameLen)) + return false; + } if (!out->append("(")) return false; for (unsigned i = 0; i < nargs; i++) { @@ -4322,15 +4341,32 @@ JS::CompileFunction(JSContext* cx, AutoObjectVector& envChain, if (!CreateNonSyntacticEnvironmentChain(cx, envChain, &env, &scope)) return false; + size_t nameLen = 0; + bool isInvalidName = false; + RootedAtom nameAtom(cx); + if (name) { + nameLen = strlen(name); + nameAtom = Atomize(cx, name, nameLen); + if (!nameAtom) + return false; + + // If name is not valid identifier + if (!js::frontend::IsIdentifier(name, nameLen)) + isInvalidName = true; + } + uint32_t parameterListEnd; StringBuffer funStr(cx); - if (!BuildFunctionString(nargs, argnames, srcBuf, &funStr, ¶meterListEnd)) + if (!BuildFunctionString(isInvalidName ? nullptr : name, nameLen, nargs, argnames, srcBuf, + &funStr, ¶meterListEnd)) { return false; + } size_t newLen = funStr.length(); SourceBufferHolder newSrcBuf(funStr.stealChars(), newLen, SourceBufferHolder::GiveOwnership); - return CompileFunction(cx, options, name, newSrcBuf, parameterListEnd, env, scope, fun); + return CompileFunction(cx, options, nameAtom, isInvalidName, newSrcBuf, parameterListEnd, env, + scope, fun); } JS_PUBLIC_API(bool) @@ -6398,6 +6434,9 @@ JS_SetGlobalJitCompilerOption(JSContext* cx, JSJitCompilerOption opt, uint32_t v } jit::JitOptions.jumpThreshold = value; break; + case JSJITCOMPILER_UNBOXED_OBJECTS: + jit::JitOptions.disableUnboxedObjects = !value; + break; case JSJITCOMPILER_ASMJS_ATOMICS_ENABLE: jit::JitOptions.asmJSAtomicsEnable = !!value; break; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index c1195cc00..1f726f2e5 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2154,6 +2154,13 @@ namespace JS { extern JS_PUBLIC_API(bool) OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool* bp); +// Implementation of +// https://www.ecma-international.org/ecma-262/6.0/#sec-instanceofoperator +// This is almost identical to JS_HasInstance, except the latter may call a +// custom hasInstance class op instead of InstanceofOperator. +extern JS_PUBLIC_API(bool) +InstanceofOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp); + } // namespace JS extern JS_PUBLIC_API(void*) @@ -2917,7 +2924,7 @@ JS_GetOwnPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char* nam JS::MutableHandle<JS::PropertyDescriptor> desc); extern JS_PUBLIC_API(bool) -JS_GetOwnUCPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char16_t* name, +JS_GetOwnUCPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen, JS::MutableHandle<JS::PropertyDescriptor> desc); /** @@ -2934,6 +2941,10 @@ extern JS_PUBLIC_API(bool) JS_GetPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char* name, JS::MutableHandle<JS::PropertyDescriptor> desc); +extern JS_PUBLIC_API(bool) +JS_GetUCPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen, + JS::MutableHandle<JS::PropertyDescriptor> desc); + /** * Define a property on obj. * @@ -5772,19 +5783,20 @@ JS_SetParallelParsingEnabled(JSContext* cx, bool enabled); extern JS_PUBLIC_API(void) JS_SetOffthreadIonCompilationEnabled(JSContext* cx, bool enabled); -#define JIT_COMPILER_OPTIONS(Register) \ - Register(BASELINE_WARMUP_TRIGGER, "baseline.warmup.trigger") \ - Register(ION_WARMUP_TRIGGER, "ion.warmup.trigger") \ - Register(ION_GVN_ENABLE, "ion.gvn.enable") \ - Register(ION_FORCE_IC, "ion.forceinlineCaches") \ - Register(ION_ENABLE, "ion.enable") \ +#define JIT_COMPILER_OPTIONS(Register) \ + Register(BASELINE_WARMUP_TRIGGER, "baseline.warmup.trigger") \ + Register(ION_WARMUP_TRIGGER, "ion.warmup.trigger") \ + Register(ION_GVN_ENABLE, "ion.gvn.enable") \ + Register(ION_FORCE_IC, "ion.forceinlineCaches") \ + Register(ION_ENABLE, "ion.enable") \ Register(ION_INTERRUPT_WITHOUT_SIGNAL, "ion.interrupt-without-signals") \ - Register(ION_CHECK_RANGE_ANALYSIS, "ion.check-range-analysis") \ - Register(BASELINE_ENABLE, "baseline.enable") \ - Register(OFFTHREAD_COMPILATION_ENABLE, "offthread-compilation.enable") \ - Register(JUMP_THRESHOLD, "jump-threshold") \ - Register(ASMJS_ATOMICS_ENABLE, "asmjs.atomics.enable") \ - Register(WASM_TEST_MODE, "wasm.test-mode") \ + Register(ION_CHECK_RANGE_ANALYSIS, "ion.check-range-analysis") \ + Register(BASELINE_ENABLE, "baseline.enable") \ + Register(OFFTHREAD_COMPILATION_ENABLE, "offthread-compilation.enable") \ + Register(JUMP_THRESHOLD, "jump-threshold") \ + Register(UNBOXED_OBJECTS, "unboxed_objects") \ + Register(ASMJS_ATOMICS_ENABLE, "asmjs.atomics.enable") \ + Register(WASM_TEST_MODE, "wasm.test-mode") \ Register(WASM_FOLD_OFFSETS, "wasm.fold-offsets") typedef enum JSJitCompilerOption { @@ -6562,7 +6574,7 @@ struct JS_PUBLIC_API(PerformanceGroup) { uint64_t refCount_; }; -using PerformanceGroupVector = mozilla::Vector<RefPtr<js::PerformanceGroup>, 0, SystemAllocPolicy>; +using PerformanceGroupVector = mozilla::Vector<RefPtr<js::PerformanceGroup>, 8, SystemAllocPolicy>; /** * Commit any Performance Monitoring data. @@ -6601,10 +6613,6 @@ SetStopwatchIsMonitoringJank(JSContext*, bool); extern JS_PUBLIC_API(bool) GetStopwatchIsMonitoringJank(JSContext*); -// Extract the CPU rescheduling data. -extern JS_PUBLIC_API(void) -GetPerfMonitoringTestCpuRescheduling(JSContext*, uint64_t* stayed, uint64_t* moved); - /** * Add a number of microseconds to the time spent waiting on CPOWs diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 4e4ccdf2a..a48bb0ffe 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -41,7 +41,6 @@ using namespace js::gc; using namespace js::jit; using mozilla::DebugOnly; -using mozilla::PodArrayZero; JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = JS::CompartmentOptions()) : creationOptions_(options.creationOptions()), @@ -91,7 +90,6 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = unmappedArgumentsTemplate_(nullptr), lcovOutput() { - PodArrayZero(sawDeprecatedLanguageExtension); runtime_->numCompartments++; MOZ_ASSERT_IF(creationOptions_.mergeable(), creationOptions_.invisibleToDebugger()); @@ -99,8 +97,6 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = JSCompartment::~JSCompartment() { - reportTelemetry(); - // Write the code coverage information in a file. JSRuntime* rt = runtimeFromMainThread(); if (rt->lcovOutput.isEnabled()) @@ -1268,39 +1264,6 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, *privateData += callback(mallocSizeOf, this); } -void -JSCompartment::reportTelemetry() -{ - // Only report telemetry for web content and add-ons, not chrome JS. - if (isSystem_) - return; - - // Hazard analysis can't tell that the telemetry callbacks don't GC. - JS::AutoSuppressGCAnalysis nogc; - - int id = creationOptions_.addonIdOrNull() - ? JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS - : JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT; - - // Call back into Firefox's Telemetry reporter. - for (size_t i = 0; i < DeprecatedLanguageExtensionCount; i++) { - if (sawDeprecatedLanguageExtension[i]) - runtime_->addTelemetry(id, i); - } -} - -void -JSCompartment::addTelemetry(const char* filename, DeprecatedLanguageExtension e) -{ - // Only report telemetry for web content and add-ons, not chrome JS. - if (isSystem_) - return; - if (!creationOptions_.addonIdOrNull() && (!filename || strncmp(filename, "http", 4) != 0)) - return; - - sawDeprecatedLanguageExtension[e] = true; -} - HashNumber JSCompartment::randomHashCode() { diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 7bfeee1f6..98c8fe200 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -344,13 +344,6 @@ struct JSCompartment isAtomsCompartment_ = true; } - // Used to approximate non-content code when reporting telemetry. - inline bool isProbablySystemOrAddonCode() const { - if (creationOptions_.addonIdOrNull()) - return true; - - return isSystem_; - } private: JSPrincipals* principals_; bool isSystem_; @@ -879,34 +872,10 @@ struct JSCompartment return jitCompartment_; } - enum DeprecatedLanguageExtension { - DeprecatedForEach = 0, // JS 1.6+ - // NO LONGER USING 1 - DeprecatedLegacyGenerator = 2, // JS 1.7+ - DeprecatedExpressionClosure = 3, // Added in JS 1.8 - // NO LONGER USING 4 - // NO LONGER USING 5 - // NO LONGER USING 6 - // NO LONGER USING 7 - // NO LONGER USING 8 - // NO LONGER USING 9 - DeprecatedBlockScopeFunRedecl = 10, - DeprecatedLanguageExtensionCount - }; - js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped); js::ArgumentsObject* maybeArgumentsTemplateObject(bool mapped) const; - private: - // Used for collecting telemetry on SpiderMonkey's deprecated language extensions. - bool sawDeprecatedLanguageExtension[DeprecatedLanguageExtensionCount]; - - void reportTelemetry(); - - public: - void addTelemetry(const char* filename, DeprecatedLanguageExtension e); - public: // Aggregated output used to collect JSScript hit counts when code coverage // is enabled. diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 9a8e364ed..1e70a3890 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -707,67 +707,6 @@ ErrorReport::~ErrorReport() { } -void -ErrorReport::ReportAddonExceptionToTelementry(JSContext* cx) -{ - MOZ_ASSERT(exnObject); - RootedObject unwrapped(cx, UncheckedUnwrap(exnObject)); - MOZ_ASSERT(unwrapped, "UncheckedUnwrap failed?"); - - // There is not much we can report if the exception is not an ErrorObject, let's ignore those. - if (!unwrapped->is<ErrorObject>()) - return; - - Rooted<ErrorObject*> errObj(cx, &unwrapped->as<ErrorObject>()); - RootedObject stack(cx, errObj->stack()); - - // Let's ignore TOP level exceptions. For regular add-ons those will not be reported anyway, - // for SDK based once it should not be a valid case either. - // At this point the frame stack is unwound but the exception object stored the stack so let's - // use that for getting the function name. - if (!stack) - return; - - JSCompartment* comp = stack->compartment(); - JSAddonId* addonId = comp->creationOptions().addonIdOrNull(); - - // We only want to send the report if the scope that just have thrown belongs to an add-on. - // Let's check the compartment of the youngest function on the stack, to determine that. - if (!addonId) - return; - - RootedString funnameString(cx); - JS::SavedFrameResult result = GetSavedFrameFunctionDisplayName(cx, stack, &funnameString); - // AccessDenied should never be the case here for add-ons but let's not risk it. - JSAutoByteString bytes; - const char* funname = nullptr; - bool denied = result == JS::SavedFrameResult::AccessDenied; - funname = denied ? "unknown" - : funnameString ? AtomToPrintableString(cx, - &funnameString->asAtom(), - &bytes) - : "anonymous"; - - UniqueChars addonIdChars(JS_EncodeString(cx, addonId)); - - const char* filename = nullptr; - if (reportp && reportp->filename) { - filename = strrchr(reportp->filename, '/'); - if (filename) - filename++; - } - if (!filename) { - filename = "FILE_NOT_FOUND"; - } - char histogramKey[64]; - SprintfLiteral(histogramKey, "%s %s %s %u", - addonIdChars.get(), - funname, - filename, - (reportp ? reportp->lineno : 0) ); - cx->runtime()->addTelemetry(JS_TELEMETRY_ADDON_EXCEPTIONS, 1, histogramKey); -} - bool ErrorReport::init(JSContext* cx, HandleValue exn, SniffingBehavior sniffingBehavior) @@ -786,10 +725,6 @@ ErrorReport::init(JSContext* cx, HandleValue exn, JSMSG_ERR_DURING_THROW); return false; } - - // Let's see if the exception is from add-on code, if so, it should be reported - // to telementry. - ReportAddonExceptionToTelementry(cx); } diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 595a21410..f5cd56a9b 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -543,11 +543,6 @@ js::SetPreserveWrapperCallback(JSContext* cx, PreserveWrapperCallback callback) cx->preserveWrapperCallback = callback; } -/* - * The below code is for temporary telemetry use. It can be removed when - * sufficient data has been harvested. - */ - namespace js { // Defined in vm/GlobalObject.cpp. extern size_t sSetProtoCalled; @@ -643,12 +638,6 @@ js::StringToLinearStringSlow(JSContext* cx, JSString* str) return str->ensureLinear(cx); } -JS_FRIEND_API(void) -JS_SetAccumulateTelemetryCallback(JSContext* cx, JSAccumulateTelemetryDataCallback callback) -{ - cx->setTelemetryCallback(cx, callback); -} - JS_FRIEND_API(JSObject*) JS_CloneObject(JSContext* cx, HandleObject obj, HandleObject protoArg) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 722085549..d29285483 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -105,46 +105,6 @@ JS_TraceShapeCycleCollectorChildren(JS::CallbackTracer* trc, JS::GCCellPtr shape extern JS_FRIEND_API(void) JS_TraceObjectGroupCycleCollectorChildren(JS::CallbackTracer* trc, JS::GCCellPtr group); -enum { - JS_TELEMETRY_GC_REASON, - JS_TELEMETRY_GC_IS_ZONE_GC, - JS_TELEMETRY_GC_MS, - JS_TELEMETRY_GC_BUDGET_MS, - JS_TELEMETRY_GC_ANIMATION_MS, - JS_TELEMETRY_GC_MAX_PAUSE_MS, - JS_TELEMETRY_GC_MARK_MS, - JS_TELEMETRY_GC_SWEEP_MS, - JS_TELEMETRY_GC_COMPACT_MS, - JS_TELEMETRY_GC_MARK_ROOTS_MS, - JS_TELEMETRY_GC_MARK_GRAY_MS, - JS_TELEMETRY_GC_SLICE_MS, - JS_TELEMETRY_GC_SLOW_PHASE, - JS_TELEMETRY_GC_MMU_50, - JS_TELEMETRY_GC_RESET, - JS_TELEMETRY_GC_RESET_REASON, - JS_TELEMETRY_GC_INCREMENTAL_DISABLED, - JS_TELEMETRY_GC_NON_INCREMENTAL, - JS_TELEMETRY_GC_NON_INCREMENTAL_REASON, - JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, - JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, - JS_TELEMETRY_GC_MINOR_REASON, - JS_TELEMETRY_GC_MINOR_REASON_LONG, - JS_TELEMETRY_GC_MINOR_US, - JS_TELEMETRY_GC_NURSERY_BYTES, - JS_TELEMETRY_GC_PRETENURE_COUNT, - JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, - JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS, - JS_TELEMETRY_ADDON_EXCEPTIONS, - JS_TELEMETRY_AOT_USAGE, - JS_TELEMETRY_END -}; - -typedef void -(*JSAccumulateTelemetryDataCallback)(int id, uint32_t sample, const char* key); - -extern JS_FRIEND_API(void) -JS_SetAccumulateTelemetryCallback(JSContext* cx, JSAccumulateTelemetryDataCallback callback); - extern JS_FRIEND_API(bool) JS_GetIsSecureContext(JSCompartment* compartment); @@ -1456,9 +1416,6 @@ struct MOZ_STACK_CLASS JS_FRIEND_API(ErrorReport) bool populateUncaughtExceptionReportUTF8(JSContext* cx, ...); bool populateUncaughtExceptionReportUTF8VA(JSContext* cx, va_list ap); - // Reports exceptions from add-on scopes to telementry. - void ReportAddonExceptionToTelementry(JSContext* cx); - // We may have a provided JSErrorReport, so need a way to represent that. JSErrorReport* reportp; diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index bcb0da80b..e624aa415 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -288,6 +288,12 @@ CallerGetterImpl(JSContext* cx, const CallArgs& args) return true; } + if (JS_IsDeadWrapper(callerObj)) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_DEAD_OBJECT); + return false; + } + JSFunction* callerFun = &callerObj->as<JSFunction>(); MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?"); @@ -314,54 +320,14 @@ CallerSetterImpl(JSContext* cx, const CallArgs& args) { MOZ_ASSERT(IsFunction(args.thisv())); - // Beware! This function can be invoked on *any* function! It can't - // assume it'll never be invoked on natives, strict mode functions, bound - // functions, or anything else that ordinarily has immutable .caller - // defined with [[ThrowTypeError]]. - RootedFunction fun(cx, &args.thisv().toObject().as<JSFunction>()); - if (!CallerRestrictions(cx, fun)) - return false; - - // Return |undefined| unless an error must be thrown. - args.rval().setUndefined(); - - // We can almost just return |undefined| here -- but if the caller function - // was strict mode code, we still have to throw a TypeError. This requires - // computing the caller, checking that no security boundaries are crossed, - // and throwing a TypeError if the resulting caller is strict. - - NonBuiltinScriptFrameIter iter(cx); - if (!AdvanceToActiveCallLinear(cx, iter, fun)) - return true; - - ++iter; - while (!iter.done() && iter.isEvalFrame()) - ++iter; - - if (iter.done() || !iter.isFunctionFrame()) - return true; - - RootedObject caller(cx, iter.callee(cx)); - if (!cx->compartment()->wrap(cx, &caller)) { - cx->clearPendingException(); - return true; - } - - // If we don't have full access to the caller, or the caller is not strict, - // return undefined. Otherwise throw a TypeError. - JSObject* callerObj = CheckedUnwrap(caller); - if (!callerObj) - return true; - - JSFunction* callerFun = &callerObj->as<JSFunction>(); - MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?"); - - if (callerFun->strict()) { - JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_ERROR, GetErrorMessage, nullptr, - JSMSG_CALLER_IS_STRICT); - return false; + // We just have to return |undefined|, but first we call CallerGetterImpl
+ // because we need the same strict-mode and security checks.
+
+ if (!CallerGetterImpl(cx, args)) { + return false;
} + args.rval().setUndefined(); return true; } @@ -690,7 +656,7 @@ js::fun_symbolHasInstance(JSContext* cx, unsigned argc, Value* vp) } /* - * ES6 (4-25-16) 7.3.19 OrdinaryHasInstance + * ES6 7.3.19 OrdinaryHasInstance */ bool JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool* bp) @@ -707,7 +673,7 @@ JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool* if (obj->is<JSFunction>() && obj->isBoundFunction()) { /* Steps 2a-b. */ obj = obj->as<JSFunction>().getBoundFunctionTarget(); - return InstanceOfOperator(cx, obj, v, bp); + return InstanceofOperator(cx, obj, v, bp); } /* Step 3. */ @@ -716,12 +682,12 @@ JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool* return true; } - /* Step 4. */ + /* Step 4-5. */ RootedValue pval(cx); if (!GetProperty(cx, obj, obj, cx->names().prototype, &pval)) return false; - /* Step 5. */ + /* Step 6. */ if (pval.isPrimitive()) { /* * Throw a runtime error if instanceof is called on a function that @@ -732,7 +698,7 @@ JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool* return false; } - /* Step 6. */ + /* Step 7. */ RootedObject pobj(cx, &pval.toObject()); bool isDelegate; if (!IsDelegate(cx, pobj, v, &isDelegate)) @@ -815,8 +781,10 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key) RootedFunction functionProto(cx, &functionProto_->as<JSFunction>()); - const char* rawSource = "() {\n}"; + const char* rawSource = "function () {\n}"; size_t sourceLen = strlen(rawSource); + size_t begin = 9; + MOZ_ASSERT(rawSource[begin] == '('); mozilla::UniquePtr<char16_t[], JS::FreePolicy> source(InflateString(cx, rawSource, &sourceLen)); if (!source) return nullptr; @@ -838,8 +806,9 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key) RootedScript script(cx, JSScript::Create(cx, options, sourceObject, - 0, - ss->length())); + begin, + ss->length(), + 0)); if (!script || !JSScript::initFunctionPrototype(cx, script, functionProto)) return nullptr; @@ -1019,53 +988,62 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint) } } - if (fun->isAsync()) { - if (!out.append("async ")) - return nullptr; - } - - bool funIsMethodOrNonArrowLambda = (fun->isLambda() && !fun->isArrow()) || fun->isMethod() || - fun->isGetter() || fun->isSetter(); + bool funIsNonArrowLambda = fun->isLambda() && !fun->isArrow(); bool haveSource = fun->isInterpreted() && !fun->isSelfHostedBuiltin(); - // If we're not in pretty mode, put parentheses around lambda functions and methods. - if (haveSource && !prettyPrint && funIsMethodOrNonArrowLambda) { + // If we're not in pretty mode, put parentheses around lambda functions + // so that eval returns lambda, not function statement. + if (haveSource && !prettyPrint && funIsNonArrowLambda) { if (!out.append("(")) return nullptr; } - if (!fun->isArrow()) { - bool ok; - if (fun->isStarGenerator() && !fun->isAsync()) - ok = out.append("function* "); - else - ok = out.append("function "); - if (!ok) - return nullptr; - } - if (fun->explicitName()) { - if (!out.append(fun->explicitName())) - return nullptr; - } if (haveSource && !script->scriptSource()->hasSourceData() && !JSScript::loadSource(cx, script->scriptSource(), &haveSource)) { return nullptr; } + + auto AppendPrelude = [&out, &fun]() { + if (fun->isAsync()) { + if (!out.append("async ")) + return false; + } + + if (!fun->isArrow()) { + if (!out.append("function")) + return false; + + if (fun->isStarGenerator()) { + if (!out.append('*')) + return false; + } + } + + if (fun->explicitName()) { + if (!out.append(' ')) + return false; + if (!out.append(fun->explicitName())) + return false; + } + return true; + }; + if (haveSource) { - Rooted<JSFlatString*> src(cx, script->sourceData(cx)); + Rooted<JSFlatString*> src(cx, script->sourceDataWithPrelude(cx)); if (!src) return nullptr; if (!out.append(src)) return nullptr; - if (!prettyPrint && funIsMethodOrNonArrowLambda) { + if (!prettyPrint && funIsNonArrowLambda) { if (!out.append(")")) return nullptr; } } else if (fun->isInterpreted() && !fun->isSelfHostedBuiltin()) { - if (!out.append("() {\n ") || + if (!AppendPrelude() || + !out.append("() {\n ") || !out.append("[sourceless code]") || !out.append("\n}")) { @@ -1076,13 +1054,15 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint) bool derived = fun->infallibleIsDefaultClassConstructor(cx); if (derived && fun->isDerivedClassConstructor()) { - if (!out.append("(...args) {\n ") || + if (!AppendPrelude() || + !out.append("(...args) {\n ") || !out.append("super(...args);\n}")) { return nullptr; } } else { - if (!out.append("() {\n ")) + if (!AppendPrelude() || + !out.append("() {\n ")) return nullptr; if (!derived) { @@ -1669,7 +1649,18 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator StringBuffer sb(cx); - if (!sb.append('(')) + if (isAsync) { + if (!sb.append("async ")) + return false; + } + if (!sb.append("function")) + return false; + if (isStarGenerator && !isAsync) { + if (!sb.append('*')) + return false; + } + + if (!sb.append(" anonymous(")) return false; if (args.length() > 1) { @@ -1690,12 +1681,15 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator if (i < args.length() - 2) { // Step 9.d.iii. - if (!sb.append(", ")) + if (!sb.append(",")) return false; } } } + if (!sb.append('\n')) + return false; + // Remember the position of ")". Maybe<uint32_t> parameterListEnd = Some(uint32_t(sb.length())); MOZ_ASSERT(FunctionConstructorMedialSigils[0] == ')'); diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 7da831aa2..1c7da57ec 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -460,6 +460,19 @@ class JSFunction : public js::NativeObject return nonLazyScript(); } + // If this is a scripted function, returns its canonical function (the + // original function allocated by the frontend). Note that lazy self-hosted + // builtins don't have a lazy script so in that case we also return nullptr. + JSFunction* maybeCanonicalFunction() const { + if (hasScript()) { + return nonLazyScript()->functionNonDelazifying(); + } + if (isInterpretedLazy() && !isSelfHostedBuiltin()) { + return lazyScript()->functionNonDelazifying(); + } + return nullptr; + } + // The state of a JSFunction whose script errored out during bytecode // compilation. Such JSFunctions are only reachable via GC iteration and // not from script. diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 3d4dae9bb..8cee9ec09 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2310,22 +2310,27 @@ GCRuntime::updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds, s // 2) typed object type descriptor objects // 3) all other objects // +// Also, there can be data races calling IsForwarded() on the new location of a +// cell that is being updated in parallel on another thread. This can be avoided +// by updating some kinds of cells in different phases. This is done for JSScripts +// and LazyScripts, and JSScripts and Scopes. +// // Since we want to minimize the number of phases, we put everything else into // the first phase and label it the 'misc' phase. static const AllocKinds UpdatePhaseMisc { AllocKind::SCRIPT, - AllocKind::LAZY_SCRIPT, AllocKind::BASE_SHAPE, AllocKind::SHAPE, AllocKind::ACCESSOR_SHAPE, AllocKind::OBJECT_GROUP, AllocKind::STRING, - AllocKind::JITCODE, - AllocKind::SCOPE + AllocKind::JITCODE }; static const AllocKinds UpdatePhaseObjects { + AllocKind::LAZY_SCRIPT, + AllocKind::SCOPE, AllocKind::FUNCTION, AllocKind::FUNCTION_EXTENDED, AllocKind::OBJECT0, diff --git a/js/src/jsnativestack.cpp b/js/src/jsnativestack.cpp index 166a5a4f7..98f8fc741 100644 --- a/js/src/jsnativestack.cpp +++ b/js/src/jsnativestack.cpp @@ -71,35 +71,6 @@ js::GetNativeStackBaseImpl() # endif } -#elif defined(SOLARIS) - -#include <ucontext.h> - -JS_STATIC_ASSERT(JS_STACK_GROWTH_DIRECTION < 0); - -void* -js::GetNativeStackBaseImpl() -{ - stack_t st; - stack_getbounds(&st); - return static_cast<char*>(st.ss_sp) + st.ss_size; -} - -#elif defined(AIX) - -#include <ucontext.h> - -JS_STATIC_ASSERT(JS_STACK_GROWTH_DIRECTION < 0); - -void* -js::GetNativeStackBaseImpl() -{ - ucontext_t context; - getcontext(&context); - return static_cast<char*>(context.uc_stack.ss_sp) + - context.uc_stack.ss_size; -} - #elif defined(XP_LINUX) && !defined(ANDROID) && defined(__GLIBC__) void* js::GetNativeStackBaseImpl() diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 9f914943e..33ae56d6f 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -235,6 +235,7 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri { uint32_t begin = script->sourceStart(); uint32_t end = script->sourceEnd(); + uint32_t preludeStart = script->preludeStart(); uint32_t lineno = script->lineno(); uint32_t column = script->column(); @@ -242,6 +243,7 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri packedFields = lazy->packedFields(); MOZ_ASSERT(begin == lazy->begin()); MOZ_ASSERT(end == lazy->end()); + MOZ_ASSERT(preludeStart == lazy->preludeStart()); MOZ_ASSERT(lineno == lazy->lineno()); MOZ_ASSERT(column == lazy->column()); // We can assert we have no inner functions because we don't @@ -255,7 +257,7 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri if (mode == XDR_DECODE) { lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, script, - packedFields, begin, end, lineno, column)); + packedFields, begin, end, preludeStart, lineno, column)); // As opposed to XDRLazyScript, we need to restore the runtime bits // of the script, as we are trying to match the fact this function @@ -517,7 +519,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>(); } - script = JSScript::Create(cx, options, sourceObject, 0, 0); + script = JSScript::Create(cx, options, sourceObject, 0, 0, 0); if (!script) return false; @@ -600,6 +602,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip return false; if (!xdr->codeUint32(&script->sourceEnd_)) return false; + if (!xdr->codeUint32(&script->preludeStart_)) + return false; if (!xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) || @@ -930,6 +934,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript { uint32_t begin; uint32_t end; + uint32_t preludeStart; uint32_t lineno; uint32_t column; uint64_t packedFields; @@ -943,12 +948,14 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript begin = lazy->begin(); end = lazy->end(); + preludeStart = lazy->preludeStart(); lineno = lazy->lineno(); column = lazy->column(); packedFields = lazy->packedFields(); } if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) || + !xdr->codeUint32(&preludeStart) || !xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) || !xdr->codeUint64(&packedFields)) { @@ -957,7 +964,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript if (mode == XDR_DECODE) { lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, enclosingScript, - packedFields, begin, end, lineno, column)); + packedFields, begin, end, preludeStart, lineno, column)); if (!lazy) return false; fun->initLazyScript(lazy); @@ -1430,6 +1437,13 @@ JSScript::sourceData(JSContext* cx) return scriptSource()->substring(cx, sourceStart(), sourceEnd()); } +JSFlatString* +JSScript::sourceDataWithPrelude(JSContext* cx) +{ + MOZ_ASSERT(scriptSource()->hasSourceData()); + return scriptSource()->substring(cx, preludeStart(), sourceEnd()); +} + UncompressedSourceCache::AutoHoldEntry::AutoHoldEntry() : cache_(nullptr), sourceChunk_() { @@ -2428,7 +2442,8 @@ JSScript::initCompartment(ExclusiveContext* cx) /* static */ JSScript* JSScript::Create(ExclusiveContext* cx, const ReadOnlyCompileOptions& options, - HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd) + HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd, + uint32_t preludeStart) { MOZ_ASSERT(bufStart <= bufEnd); @@ -2450,6 +2465,7 @@ JSScript::Create(ExclusiveContext* cx, const ReadOnlyCompileOptions& options, script->setSourceObject(sourceObject); script->sourceStart_ = bufStart; script->sourceEnd_ = bufEnd; + script->preludeStart_ = preludeStart; return script; } @@ -3382,7 +3398,8 @@ CreateEmptyScriptForClone(JSContext* cx, HandleScript src) .setNoScriptRval(src->noScriptRval()) .setVersion(src->getVersion()); - return JSScript::Create(cx, options, sourceObject, src->sourceStart(), src->sourceEnd()); + return JSScript::Create(cx, options, sourceObject, src->sourceStart(), src->sourceEnd(), + src->preludeStart()); } JSScript* @@ -3932,7 +3949,8 @@ JSScript::formalLivesInArgumentsObject(unsigned argSlot) } LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields, - uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) + uint32_t begin, uint32_t end, + uint32_t preludeStart, uint32_t lineno, uint32_t column) : script_(nullptr), function_(fun), enclosingScope_(nullptr), @@ -3941,6 +3959,7 @@ LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields, packedFields_(packedFields), begin_(begin), end_(end), + preludeStart_(preludeStart), lineno_(lineno), column_(column) { @@ -3990,7 +4009,7 @@ LazyScript::maybeForwardedScriptSource() const /* static */ LazyScript* LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun, uint64_t packedFields, uint32_t begin, uint32_t end, - uint32_t lineno, uint32_t column) + uint32_t preludeStart, uint32_t lineno, uint32_t column) { union { PackedView p; @@ -4018,7 +4037,8 @@ LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun, cx->compartment()->scheduleDelazificationForDebugger(); - return new (res) LazyScript(fun, table.forget(), packed, begin, end, lineno, column); + return new (res) LazyScript(fun, table.forget(), packed, begin, end, + preludeStart, lineno, column); } /* static */ LazyScript* @@ -4026,7 +4046,8 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun, const frontend::AtomVector& closedOverBindings, Handle<GCVector<JSFunction*, 8>> innerFunctions, JSVersion version, - uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) + uint32_t begin, uint32_t end, + uint32_t preludeStart, uint32_t lineno, uint32_t column) { union { PackedView p; @@ -4049,7 +4070,8 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun, p.isDerivedClassConstructor = false; p.needsHomeObject = false; - LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column); + LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, preludeStart, + lineno, column); if (!res) return nullptr; @@ -4070,7 +4092,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun, HandleScript script, HandleScope enclosingScope, HandleScript enclosingScript, uint64_t packedFields, uint32_t begin, uint32_t end, - uint32_t lineno, uint32_t column) + uint32_t preludeStart, uint32_t lineno, uint32_t column) { // Dummy atom which is not a valid property name. RootedAtom dummyAtom(cx, cx->names().comma); @@ -4079,7 +4101,8 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun, // holding this lazy script. HandleFunction dummyFun = fun; - LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column); + LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, preludeStart, + lineno, column); if (!res) return nullptr; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 87da79901..bb8635581 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -575,10 +575,6 @@ class ScriptSource introductionOffset_ = offset; hasIntroductionOffset_ = true; } - - uint32_t parameterListEnd() const { - return parameterListEnd_; - } }; class ScriptSourceHolder @@ -857,9 +853,19 @@ class JSScript : public js::gc::TenuredCell uint32_t bodyScopeIndex_; /* index into the scopes array of the body scope */ - /* Range of characters in scriptSource which contains this script's source. */ + // Range of characters in scriptSource which contains this script's source. + // each field points the following location. + // + // function * f(a, b) { return a + b; } + // ^ ^ ^ + // | | | + // | sourceStart_ sourceEnd_ + // | + // preludeStart_ + // uint32_t sourceStart_; uint32_t sourceEnd_; + uint32_t preludeStart_; // Number of times the script has been called or has had backedges taken. // When running in ion, also increased for any inlined scripts. Reset if @@ -1020,7 +1026,7 @@ class JSScript : public js::gc::TenuredCell // instead of private to suppress -Wunused-private-field compiler warnings. protected: #if JS_BITS_PER_WORD == 32 - // Currently no padding is needed. + uint32_t padding; #endif // @@ -1031,7 +1037,7 @@ class JSScript : public js::gc::TenuredCell static JSScript* Create(js::ExclusiveContext* cx, const JS::ReadOnlyCompileOptions& options, js::HandleObject sourceObject, uint32_t sourceStart, - uint32_t sourceEnd); + uint32_t sourceEnd, uint32_t preludeStart); void initCompartment(js::ExclusiveContext* cx); @@ -1178,6 +1184,10 @@ class JSScript : public js::gc::TenuredCell return sourceEnd_; } + size_t preludeStart() const { + return preludeStart_; + } + bool noScriptRval() const { return noScriptRval_; } @@ -1501,7 +1511,8 @@ class JSScript : public js::gc::TenuredCell bool mayReadFrameArgsDirectly(); JSFlatString* sourceData(JSContext* cx); - + JSFlatString* sourceDataWithPrelude(JSContext* cx); + static bool loadSource(JSContext* cx, js::ScriptSource* ss, bool* worked); void setSourceObject(JSObject* object); @@ -1920,7 +1931,8 @@ class LazyScript : public gc::TenuredCell // instead of private to suppress -Wunused-private-field compiler warnings. protected: #if JS_BITS_PER_WORD == 32 - uint32_t padding; + // uint32_t padding; + // Currently no padding is needed. #endif private: @@ -1960,20 +1972,25 @@ class LazyScript : public gc::TenuredCell }; // Source location for the script. + // See the comment in JSScript for the details. uint32_t begin_; uint32_t end_; + uint32_t preludeStart_; + // Line and column of |begin_| position, that is the position where we + // start parsing. uint32_t lineno_; uint32_t column_; LazyScript(JSFunction* fun, void* table, uint64_t packedFields, - uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column); + uint32_t begin, uint32_t end, uint32_t preludeStart, + uint32_t lineno, uint32_t column); // Create a LazyScript without initializing the closedOverBindings and the // innerFunctions. To be GC-safe, the caller must initialize both vectors // with valid atoms and functions. static LazyScript* CreateRaw(ExclusiveContext* cx, HandleFunction fun, uint64_t packedData, uint32_t begin, uint32_t end, - uint32_t lineno, uint32_t column); + uint32_t preludeStart, uint32_t lineno, uint32_t column); public: static const uint32_t NumClosedOverBindingsLimit = 1 << NumClosedOverBindingsBits; @@ -1985,7 +2002,7 @@ class LazyScript : public gc::TenuredCell const frontend::AtomVector& closedOverBindings, Handle<GCVector<JSFunction*, 8>> innerFunctions, JSVersion version, uint32_t begin, uint32_t end, - uint32_t lineno, uint32_t column); + uint32_t preludeStart, uint32_t lineno, uint32_t column); // Create a LazyScript and initialize the closedOverBindings and the // innerFunctions with dummy values to be replaced in a later initialization @@ -2000,7 +2017,7 @@ class LazyScript : public gc::TenuredCell HandleScript script, HandleScope enclosingScope, HandleScript enclosingScript, uint64_t packedData, uint32_t begin, uint32_t end, - uint32_t lineno, uint32_t column); + uint32_t preludeStart, uint32_t lineno, uint32_t column); void initRuntimeFields(uint64_t packedFields); @@ -2173,6 +2190,9 @@ class LazyScript : public gc::TenuredCell uint32_t end() const { return end_; } + uint32_t preludeStart() const { + return preludeStart_; + } uint32_t lineno() const { return lineno_; } @@ -2199,7 +2219,8 @@ class LazyScript : public gc::TenuredCell }; /* If this fails, add/remove padding within LazyScript. */ -JS_STATIC_ASSERT(sizeof(LazyScript) % js::gc::CellSize == 0); +static_assert(sizeof(LazyScript) % js::gc::CellSize == 0, + "Size of LazyScript must be an integral multiple of js::gc::CellSize"); struct ScriptAndCounts { diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 4151d012b..e3b5708ca 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -3070,8 +3070,11 @@ js::ValueToSource(JSContext* cx, HandleValue v) return ToString<CanGC>(cx, v); } - +#if JS_HAS_TOSOURCE return ObjectToSource(cx, obj); +#else + return ToString<CanGC>(cx, v); +#endif } JSString* diff --git a/js/src/jstypes.h b/js/src/jstypes.h index 75774e5b8..6cfb3d4ad 100644 --- a/js/src/jstypes.h +++ b/js/src/jstypes.h @@ -147,13 +147,7 @@ # define JS_64BIT # endif #elif defined(__GNUC__) -/* Additional GCC defines are when running on Solaris, AIX, and HPUX */ -# if defined(__x86_64__) || defined(__sparcv9) || \ - defined(__64BIT__) || defined(__LP64__) -# define JS_64BIT -# endif -#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) /* Sun Studio C/C++ */ -# if defined(__x86_64) || defined(__sparcv9) +# if defined(__x86_64__) || defined(__64BIT__) # define JS_64BIT # endif #elif defined(__xlc__) || defined(__xlC__) /* IBM XL C/C++ */ diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 3c73979f8..84ebe2732 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -270,6 +270,8 @@ class JS_FRIEND_API(OpaqueCrossCompartmentWrapper) : public CrossCompartmentWrap virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper, ESClass* cls) const override; virtual bool isArray(JSContext* cx, HandleObject obj, JS::IsArrayAnswer* answer) const override; + virtual bool hasInstance(JSContext* cx, HandleObject wrapper, + MutableHandleValue v, bool* bp) const override; virtual const char* className(JSContext* cx, HandleObject wrapper) const override; virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override; diff --git a/js/src/moz.build b/js/src/moz.build index a3283b5d6..888741138 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -722,14 +722,6 @@ if CONFIG['OS_ARCH'] == 'Linux': 'dl', ] -if CONFIG['OS_ARCH'] == 'SunOS': - OS_LIBS += [ - 'posix4', - 'dl', - 'nsl', - 'socket', - ] - OS_LIBS += CONFIG['REALTIME_LIBS'] CFLAGS += CONFIG['MOZ_ICU_CFLAGS'] diff --git a/js/src/old-configure.in b/js/src/old-configure.in index 8abea5956..45108ee59 100644 --- a/js/src/old-configure.in +++ b/js/src/old-configure.in @@ -206,10 +206,6 @@ case "$target" in # -Zc:sizedDealloc- disables C++14 global sized deallocation (see bug 1160146) CXXFLAGS="$CXXFLAGS -Zc:sizedDealloc-" - - # Disable C++11 thread-safe statics due to crashes on XP (bug 1204752) - # See https://connect.microsoft.com/VisualStudio/feedback/details/1789709/visual-c-2015-runtime-broken-on-windows-server-2003-c-11-magic-statics - CXXFLAGS="$CXXFLAGS -Zc:threadSafeInit-" ;; esac AC_SUBST(MSVS_VERSION) diff --git a/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp b/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp index ff3f4145c..02bf237ff 100644 --- a/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp +++ b/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp @@ -175,6 +175,14 @@ OpaqueCrossCompartmentWrapper::isArray(JSContext* cx, HandleObject obj, return true; } +bool OpaqueCrossCompartmentWrapper::hasInstance(JSContext* cx, + HandleObject wrapper, + MutableHandleValue v, + bool* bp) const { + *bp = false; + return true; +} + const char* OpaqueCrossCompartmentWrapper::className(JSContext* cx, HandleObject proxy) const diff --git a/js/src/proxy/ScriptedProxyHandler.cpp b/js/src/proxy/ScriptedProxyHandler.cpp index 776547337..0e25f470c 100644 --- a/js/src/proxy/ScriptedProxyHandler.cpp +++ b/js/src/proxy/ScriptedProxyHandler.cpp @@ -8,8 +8,6 @@ #include "jsapi.h" -#include "vm/Interpreter.h" // For InstanceOfOperator - #include "jsobjinlines.h" #include "vm/NativeObject-inl.h" @@ -1230,7 +1228,7 @@ bool ScriptedProxyHandler::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const { - return InstanceOfOperator(cx, proxy, v, bp); + return InstanceofOperator(cx, proxy, v, bp); } bool diff --git a/js/src/tests/ecma_2017/Function/Object-toSource.js b/js/src/tests/ecma_2017/Function/Object-toSource.js new file mode 100644 index 000000000..33b9e588e --- /dev/null +++ b/js/src/tests/ecma_2017/Function/Object-toSource.js @@ -0,0 +1,370 @@ +var BUGNUMBER = 1317400;
+var summary = "Function string representation in Object.prototype.toSource";
+
+print(BUGNUMBER + ": " + summary);
+
+// Methods.
+
+assertEq(({ foo(){} }).toSource(),
+ "({foo(){}})");
+assertEq(({ *foo(){} }).toSource(),
+ "({*foo(){}})");
+assertEq(({ async foo(){} }).toSource(),
+ "({async foo(){}})");
+
+assertEq(({ 1(){} }).toSource(),
+ "({1(){}})");
+
+// Methods with more spacing.
+// Spacing is kept.
+
+assertEq(({ foo (){} }).toSource(),
+ "({foo (){}})");
+assertEq(({ foo () {} }).toSource(),
+ "({foo () {}})");
+
+// Methods with computed name.
+// Method syntax is composed.
+
+let name = "foo";
+assertEq(({ [name](){} }).toSource(),
+ "({foo(){}})");
+assertEq(({ *[name](){} }).toSource(),
+ "({*foo(){}})");
+assertEq(({ async [name](){} }).toSource(),
+ "({async foo(){}})");
+
+assertEq(({ [ Symbol.iterator ](){} }).toSource(),
+ "({[Symbol.iterator](){}})");
+
+// Accessors.
+
+assertEq(({ get foo(){} }).toSource(),
+ "({get foo(){}})");
+assertEq(({ set foo(v){} }).toSource(),
+ "({set foo(v){}})");
+
+// Accessors with computed name.
+// Method syntax is composed.
+
+assertEq(({ get [name](){} }).toSource(),
+ "({get foo(){}})");
+assertEq(({ set [name](v){} }).toSource(),
+ "({set foo(v){}})");
+
+assertEq(({ get [ Symbol.iterator ](){} }).toSource(),
+ "({get [Symbol.iterator](){}})");
+assertEq(({ set [ Symbol.iterator ](v){} }).toSource(),
+ "({set [Symbol.iterator](v){}})");
+
+// Getter and setter with same name.
+// Getter always comes before setter.
+
+assertEq(({ get foo(){}, set foo(v){} }).toSource(),
+ "({get foo(){}, set foo(v){}})");
+assertEq(({ set foo(v){}, get foo(){} }).toSource(),
+ "({get foo(){}, set foo(v){}})");
+
+// Normal properties.
+
+assertEq(({ foo: function(){} }).toSource(),
+ "({foo:(function(){})})");
+assertEq(({ foo: function bar(){} }).toSource(),
+ "({foo:(function bar(){})})");
+assertEq(({ foo: function*(){} }).toSource(),
+ "({foo:(function*(){})})");
+assertEq(({ foo: async function(){} }).toSource(),
+ "({foo:(async function(){})})");
+
+// Normal properties with computed name.
+
+assertEq(({ [ Symbol.iterator ]: function(){} }).toSource(),
+ "({[Symbol.iterator]:(function(){})})");
+
+// Dynamically defined properties with function expression.
+// Never become a method syntax.
+
+let obj = {};
+obj.foo = function() {};
+assertEq(obj.toSource(),
+ "({foo:(function() {})})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: function() {}});
+assertEq(obj.toSource(),
+ "({})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({foo:(function() {})})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: function bar() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({foo:(function bar() {})})");
+
+obj = {};
+Object.defineProperty(obj, Symbol.iterator, {value: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({[Symbol.iterator]:(function() {})})");
+
+// Dynamically defined property with other object's method.
+// Method syntax is composed.
+
+let method = ({foo() {}}).foo;
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: method, enumerable: true});
+assertEq(obj.toSource(),
+ "({foo() {}})");
+
+obj = {};
+Object.defineProperty(obj, "bar", {value: method, enumerable: true});
+assertEq(obj.toSource(),
+ "({bar() {}})");
+
+method = ({*foo() {}}).foo;
+
+obj = {};
+Object.defineProperty(obj, "bar", {value: method, enumerable: true});
+assertEq(obj.toSource(),
+ "({*bar() {}})");
+
+method = ({async foo() {}}).foo;
+
+obj = {};
+Object.defineProperty(obj, "bar", {value: method, enumerable: true});
+assertEq(obj.toSource(),
+ "({async bar() {}})");
+
+// Dynamically defined accessors.
+// Accessor syntax is composed.
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+obj = {};
+Object.defineProperty(obj, Symbol.iterator, {get: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({get [Symbol.iterator]() {}})");
+
+obj = {};
+Object.defineProperty(obj, Symbol.iterator, {set: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({set [Symbol.iterator]() {}})");
+
+// Dynamically defined accessors with other object's accessors.
+// Accessor syntax is composed.
+
+let accessor = Object.getOwnPropertyDescriptor({ get foo() {} }, "foo").get;
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ get bar() {} }, "bar").get;
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ set foo(v) {} }, "foo").set;
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo(v) {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ set bar(v) {} }, "bar").set;
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo(v) {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ get foo() {} }, "foo").get;
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ get bar() {} }, "bar").get;
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ set foo(v) {} }, "foo").set;
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo(v) {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ set bar(v) {} }, "bar").set;
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo(v) {}})");
+
+// Methods with proxy.
+// Treated as normal property.
+
+method = ({foo() {}}).foo;
+let handler = {
+ get(that, name) {
+ if (name == "toSource") {
+ return function() {
+ return that.toSource();
+ };
+ }
+ return that[name];
+ }
+};
+let proxy = new Proxy(method, handler);
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: proxy, enumerable: true});
+assertEq(obj.toSource(),
+ "({foo:foo() {}})");
+
+// Accessors with proxy.
+// Accessor syntax is composed.
+
+accessor = Object.getOwnPropertyDescriptor({ get foo() {} }, "foo").get;
+proxy = new Proxy(accessor, handler);
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: proxy, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: proxy, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+// Methods from other global.
+// Treated as normal property.
+
+let g = newGlobal();
+
+method = g.eval("({ foo() {} }).foo");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: method, enumerable: true});
+assertEq(obj.toSource(),
+ "({foo:foo() {}})");
+
+// Accessors from other global.
+// Accessor syntax is composed.
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ get foo() {} }, 'foo').get");
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ get bar() {} }, 'bar').get");
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ set foo(v) {} }, 'foo').set");
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo(v) {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ set bar(v) {} }, 'bar').set");
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo(v) {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ get foo() {} }, 'foo').get");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ get bar() {} }, 'bar').get");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ set foo(v) {} }, 'foo').set");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo(v) {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ set bar(v) {} }, 'bar').set");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo(v) {}})");
+
+// **** Some weird cases ****
+
+// Accessors with generator or async.
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: function*() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: async function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+// Modified toSource.
+
+obj = { foo() {} };
+obj.foo.toSource = () => "hello";
+assertEq(obj.toSource(),
+ "({hello})");
+
+obj = { foo() {} };
+obj.foo.toSource = () => "bar() {}";
+assertEq(obj.toSource(),
+ "({bar() {}})");
+
+// Modified toSource with different method name.
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: function bar() {}, enumerable: true});
+obj.foo.toSource = () => "hello";
+assertEq(obj.toSource(),
+ "({foo:hello})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: function* bar() {}, enumerable: true});
+obj.foo.toSource = () => "hello";
+assertEq(obj.toSource(),
+ "({foo:hello})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: async function bar() {}, enumerable: true});
+obj.foo.toSource = () => "hello";
+assertEq(obj.toSource(),
+ "({foo:hello})");
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_2017/Function/browser.js b/js/src/tests/ecma_2017/Function/browser.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/js/src/tests/ecma_2017/Function/browser.js diff --git a/js/src/tests/ecma_2017/Function/shell.js b/js/src/tests/ecma_2017/Function/shell.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/js/src/tests/ecma_2017/Function/shell.js diff --git a/js/src/tests/ecma_6/Generators/runtime.js b/js/src/tests/ecma_6/Generators/runtime.js index c4d3bb6a6..7146eef9f 100644 --- a/js/src/tests/ecma_6/Generators/runtime.js +++ b/js/src/tests/ecma_6/Generators/runtime.js @@ -109,7 +109,7 @@ function TestGeneratorFunction() { // Doesn't matter particularly what string gets serialized, as long // as it contains "function*" and "yield 10". assertEq(GeneratorFunction('yield 10').toString(), - "function* anonymous() {\nyield 10\n}"); + "function* anonymous(\n) {\nyield 10\n}"); } TestGeneratorFunction(); diff --git a/js/src/tests/js1_5/Scope/regress-185485.js b/js/src/tests/js1_5/Scope/regress-185485.js index 19d190eea..a75bf885a 100644 --- a/js/src/tests/js1_5/Scope/regress-185485.js +++ b/js/src/tests/js1_5/Scope/regress-185485.js @@ -94,7 +94,7 @@ with (x) } status = inSection(5); actual = x.g.toString(); -expect = (function () {}).toString(); +expect = (function() {}).toString(); addThis(); diff --git a/js/src/tests/js1_7/extensions/regress-354945-01.js b/js/src/tests/js1_7/extensions/regress-354945-01.js index 76f1a3c82..1c57db0f7 100644 --- a/js/src/tests/js1_7/extensions/regress-354945-01.js +++ b/js/src/tests/js1_7/extensions/regress-354945-01.js @@ -6,7 +6,7 @@ //----------------------------------------------------------------------------- var BUGNUMBER = 354945; var summary = 'Do not crash with new Iterator'; -var expect = 'TypeError: trap __iterator__ for ({__iterator__:(function (){ })}) returned a primitive value'; +var expect = 'TypeError: trap __iterator__ for ({__iterator__:(function(){ })}) returned a primitive value'; var actual; diff --git a/js/src/tests/js1_7/extensions/regress-354945-02.js b/js/src/tests/js1_7/extensions/regress-354945-02.js index 261bf7de1..abef90f77 100644 --- a/js/src/tests/js1_7/extensions/regress-354945-02.js +++ b/js/src/tests/js1_7/extensions/regress-354945-02.js @@ -20,7 +20,7 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); - expect = 'TypeError: trap __iterator__ for ({__iterator__:(function (){ })}) returned a primitive value'; + expect = 'TypeError: trap __iterator__ for ({__iterator__:(function(){ })}) returned a primitive value'; var obj = {}; obj.__iterator__ = function(){ }; try diff --git a/js/src/tests/js1_7/geniter/regress-352197.js b/js/src/tests/js1_7/geniter/regress-352197.js index 7982e12ee..495717af7 100644 --- a/js/src/tests/js1_7/geniter/regress-352197.js +++ b/js/src/tests/js1_7/geniter/regress-352197.js @@ -20,7 +20,7 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); - expect = /TypeError: anonymous generator function returns a value/; + expect = /TypeError: can't use 'yield' in a function that can return a value/; try { var gen = eval('(function() { { return 5; } yield 3; })'); diff --git a/js/src/tests/js1_8/genexps/regress-683738.js b/js/src/tests/js1_8/genexps/regress-683738.js index de563645d..b0309a90e 100644 --- a/js/src/tests/js1_8/genexps/regress-683738.js +++ b/js/src/tests/js1_8/genexps/regress-683738.js @@ -20,7 +20,7 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); - expect = "generator function foo returns a value"; + expect = "can't use 'yield' in a function that can return a value"; try { actual = 'No Error'; @@ -32,7 +32,7 @@ function test() } reportCompare(expect, actual, summary + ": 1"); - expect = "generator function foo returns a value"; + expect = "generator function can't return a value"; try { actual = 'No Error'; @@ -44,7 +44,7 @@ function test() } reportCompare(expect, actual, summary + ": 2"); - expect = "generator function foo returns a value"; + expect = "can't use 'yield' in a function that can return a value"; try { actual = 'No Error'; @@ -56,7 +56,7 @@ function test() } reportCompare(expect, actual, summary + ": 3"); - expect = "generator function foo returns a value"; + expect = "generator function can't return a value"; try { actual = 'No Error'; diff --git a/js/src/tests/js1_8_5/regress/regress-584355.js b/js/src/tests/js1_8_5/regress/regress-584355.js index 4ddfe65d3..7d1b81a2e 100644 --- a/js/src/tests/js1_8_5/regress/regress-584355.js +++ b/js/src/tests/js1_8_5/regress/regress-584355.js @@ -1,5 +1,5 @@ var actual; -var expect = "function f() { ff (); }"; +var expect = "function f () { ff (); }"; function fun() { (new Function ("function ff () { actual = '' + ff. caller; } function f () { ff (); } f ();")) (); } diff --git a/js/src/tests/user.js b/js/src/tests/user.js index 732bbbd1a..e75593ab1 100755 --- a/js/src/tests/user.js +++ b/js/src/tests/user.js @@ -22,7 +22,6 @@ user_pref("javascript.options.strict", false); user_pref("javascript.options.werror", false); user_pref("toolkit.startup.max_resumed_crashes", -1); user_pref("security.turn_off_all_security_so_that_viruses_can_take_over_this_computer", true); -user_pref("toolkit.telemetry.enabled", false); user_pref("browser.safebrowsing.phishing.enabled", false); user_pref("browser.safebrowsing.malware.enabled", false); user_pref("browser.safebrowsing.forbiddenURIs.enabled", false); diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h index 6614f5220..e9c9bc0e0 100644 --- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -457,8 +457,8 @@ ClampDoubleToUint8(const double x); struct uint8_clamped { uint8_t val; - uint8_clamped() { } - uint8_clamped(const uint8_clamped& other) : val(other.val) { } + uint8_clamped() = default; + uint8_clamped(const uint8_clamped& other) = default; // invoke our assignment helpers for constructor conversion explicit uint8_clamped(uint8_t x) { *this = x; } @@ -469,10 +469,7 @@ struct uint8_clamped { explicit uint8_clamped(int32_t x) { *this = x; } explicit uint8_clamped(double x) { *this = x; } - uint8_clamped& operator=(const uint8_clamped& x) { - val = x.val; - return *this; - } + uint8_clamped& operator=(const uint8_clamped& x) = default; uint8_clamped& operator=(uint8_t x) { val = x; diff --git a/js/src/vm/Caches.h b/js/src/vm/Caches.h index 91a78bdc8..b11dd9dcb 100644 --- a/js/src/vm/Caches.h +++ b/js/src/vm/Caches.h @@ -7,6 +7,8 @@ #ifndef vm_Caches_h #define vm_Caches_h +#include <new> + #include "jsatom.h" #include "jsbytecode.h" #include "jsobj.h" @@ -191,14 +193,20 @@ class NewObjectCache char templateObject[MAX_OBJ_SIZE]; }; - Entry entries[41]; // TODO: reconsider size + using EntryArray = Entry[41]; // TODO: reconsider size; + EntryArray entries; public: - typedef int EntryIndex; + using EntryIndex = int; + + NewObjectCache() + : entries{} // zeroes out the array + {} - NewObjectCache() { mozilla::PodZero(this); } - void purge() { mozilla::PodZero(this); } + void purge() { + new (&entries) EntryArray{}; // zeroes out the array + } /* Remove any cached items keyed on moved objects. */ void clearNurseryObjects(JSRuntime* rt); diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index b747e4d7a..e6d6630c4 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -718,14 +718,14 @@ js::Execute(JSContext* cx, HandleScript script, JSObject& envChainArg, Value* rv } /* - * ES6 (4-25-16) 12.10.4 InstanceofOperator + * ES6 12.9.4 InstanceofOperator */ extern bool -js::InstanceOfOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp) +JS::InstanceofOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp) { /* Step 1. is handled by caller. */ - /* Step 2. */ + /* Step 2-3. */ RootedValue hasInstance(cx); RootedId id(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().hasInstance)); if (!GetProperty(cx, obj, obj, id, &hasInstance)) @@ -735,7 +735,7 @@ js::InstanceOfOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp) if (!IsCallable(hasInstance)) return ReportIsNotFunction(cx, hasInstance); - /* Step 3. */ + /* Step 4. */ RootedValue rval(cx); if (!Call(cx, hasInstance, obj, v, &rval)) return false; @@ -743,13 +743,13 @@ js::InstanceOfOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp) return true; } - /* Step 4. */ + /* Step 5. */ if (!obj->isCallable()) { RootedValue val(cx, ObjectValue(*obj)); return ReportIsNotFunction(cx, val); } - /* Step 5. */ + /* Step 6. */ return OrdinaryHasInstance(cx, obj, v, bp); } @@ -760,7 +760,7 @@ js::HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp) RootedValue local(cx, v); if (JSHasInstanceOp hasInstance = clasp->getHasInstance()) return hasInstance(cx, obj, &local, bp); - return js::InstanceOfOperator(cx, obj, local, bp); + return JS::InstanceofOperator(cx, obj, local, bp); } static inline bool diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index 330dbef5f..9fefd75cc 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -323,9 +323,6 @@ extern JSType TypeOfValue(const Value& v); extern bool -InstanceOfOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp); - -extern bool HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp); // Unwind environment chain and iterator to match the scope corresponding to diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index d6a8fcaa4..46159a972 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -495,13 +495,7 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp, if (associated->is<JSFunction>()) { // Canonicalize new functions to use the original one associated with its script. - JSFunction* fun = &associated->as<JSFunction>(); - if (fun->hasScript()) - associated = fun->nonLazyScript()->functionNonDelazifying(); - else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin()) - associated = fun->lazyScript()->functionNonDelazifying(); - else - associated = nullptr; + associated = associated->as<JSFunction>().maybeCanonicalFunction(); // If we have previously cleared the 'new' script information for this // function, don't try to construct another one. diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 174e23594..8eb997c71 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -147,7 +147,6 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime) updateChildRuntimeCount(parentRuntime), #endif interrupt_(false), - telemetryCallback(nullptr), handlingSegFault(false), handlingJitInterrupt_(false), interruptCallbackDisabled(false), @@ -452,19 +451,6 @@ JSRuntime::destroyRuntime() } void -JSRuntime::addTelemetry(int id, uint32_t sample, const char* key) -{ - if (telemetryCallback) - (*telemetryCallback)(id, sample, key); -} - -void -JSRuntime::setTelemetryCallback(JSRuntime* rt, JSAccumulateTelemetryDataCallback callback) -{ - rt->telemetryCallback = callback; -} - -void JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes* rtSizes) { // Several tables in the runtime enumerated below can be used off thread. diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 735adadf2..e60371e38 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -11,11 +11,11 @@ #include "mozilla/Attributes.h" #include "mozilla/LinkedList.h" #include "mozilla/MemoryReporting.h" -#include "mozilla/PodOperations.h" #include "mozilla/Scoped.h" #include "mozilla/ThreadLocal.h" #include "mozilla/Vector.h" +#include <algorithm> #include <setjmp.h> #include "jsatom.h" @@ -577,17 +577,7 @@ struct JSRuntime : public JS::shadow::Runtime, #endif mozilla::Atomic<uint32_t, mozilla::Relaxed> interrupt_; - - /* Call this to accumulate telemetry data. */ - JSAccumulateTelemetryDataCallback telemetryCallback; public: - // Accumulates data for Firefox telemetry. |id| is the ID of a JS_TELEMETRY_* - // histogram. |key| provides an additional key to identify the histogram. - // |sample| is the data to add to the histogram. - void addTelemetry(int id, uint32_t sample, const char* key = nullptr); - - void setTelemetryCallback(JSRuntime* rt, JSAccumulateTelemetryDataCallback callback); - enum InterruptMode { RequestInterruptUrgent, RequestInterruptCanWait @@ -1504,20 +1494,21 @@ PerThreadData::exclusiveThreadsPresent() static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(Value* vec, size_t len) { - mozilla::PodZero(vec, len); + // Don't PodZero here because JS::Value is non-trivial. + for (size_t i = 0; i < len; i++) + vec[i].setDouble(+0.0); } static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(Value* beg, Value* end) { - mozilla::PodZero(beg, end - beg); + MakeRangeGCSafe(beg, end - beg); } static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(jsid* beg, jsid* end) { - for (jsid* id = beg; id != end; ++id) - *id = INT_TO_JSID(0); + std::fill(beg, end, INT_TO_JSID(0)); } static MOZ_ALWAYS_INLINE void @@ -1529,13 +1520,13 @@ MakeRangeGCSafe(jsid* vec, size_t len) static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(Shape** beg, Shape** end) { - mozilla::PodZero(beg, end - beg); + std::fill(beg, end, nullptr); } static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(Shape** vec, size_t len) { - mozilla::PodZero(vec, len); + MakeRangeGCSafe(vec, vec + len); } static MOZ_ALWAYS_INLINE void diff --git a/js/src/vm/Scope.cpp b/js/src/vm/Scope.cpp index 112b34586..a71c03695 100644 --- a/js/src/vm/Scope.cpp +++ b/js/src/vm/Scope.cpp @@ -191,12 +191,12 @@ template <typename ConcreteScope> static UniquePtr<typename ConcreteScope::Data> NewEmptyScopeData(ExclusiveContext* cx, uint32_t length = 0) { - uint8_t* bytes = cx->zone()->pod_calloc<uint8_t>(ConcreteScope::sizeOfData(length)); + uint8_t* bytes = cx->zone()->pod_malloc<uint8_t>(ConcreteScope::sizeOfData(length)); if (!bytes) ReportOutOfMemory(cx); auto data = reinterpret_cast<typename ConcreteScope::Data*>(bytes); if (data) - new (data) typename ConcreteScope::Data(); + new (data) typename ConcreteScope::Data(length); return UniquePtr<typename ConcreteScope::Data>(data); } @@ -273,7 +273,7 @@ Scope::XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope, } for (uint32_t i = 0; i < length; i++) { - if (!XDRBindingName(xdr, &data->names[i])) { + if (!XDRBindingName(xdr, &data->trailingNames[i])) { if (mode == XDR_DECODE) { DeleteScopeData(data.get()); data.set(nullptr); @@ -1250,7 +1250,7 @@ BindingIter::init(LexicalScope::Data& data, uint32_t firstFrameSlot, uint8_t fla init(0, 0, 0, 0, 0, 0, CanHaveEnvironmentSlots | flags, firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_), - data.names, data.length); + data.trailingNames.start(), data.length); } else { // imports - [0, 0) // positional formals - [0, 0) @@ -1262,7 +1262,7 @@ BindingIter::init(LexicalScope::Data& data, uint32_t firstFrameSlot, uint8_t fla init(0, 0, 0, 0, 0, data.constStart, CanHaveFrameSlots | CanHaveEnvironmentSlots | flags, firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_), - data.names, data.length); + data.trailingNames.start(), data.length); } } @@ -1283,7 +1283,7 @@ BindingIter::init(FunctionScope::Data& data, uint8_t flags) init(0, data.nonPositionalFormalStart, data.varStart, data.varStart, data.length, data.length, flags, 0, JSSLOT_FREE(&CallObject::class_), - data.names, data.length); + data.trailingNames.start(), data.length); } void @@ -1299,7 +1299,7 @@ BindingIter::init(VarScope::Data& data, uint32_t firstFrameSlot) init(0, 0, 0, 0, data.length, data.length, CanHaveFrameSlots | CanHaveEnvironmentSlots, firstFrameSlot, JSSLOT_FREE(&VarEnvironmentObject::class_), - data.names, data.length); + data.trailingNames.start(), data.length); } void @@ -1315,7 +1315,7 @@ BindingIter::init(GlobalScope::Data& data) init(0, 0, 0, data.varStart, data.letStart, data.constStart, CannotHaveSlots, UINT32_MAX, UINT32_MAX, - data.names, data.length); + data.trailingNames.start(), data.length); } void @@ -1343,7 +1343,7 @@ BindingIter::init(EvalScope::Data& data, bool strict) // consts - [data.length, data.length) init(0, 0, 0, data.varStart, data.length, data.length, flags, firstFrameSlot, firstEnvironmentSlot, - data.names, data.length); + data.trailingNames.start(), data.length); } void @@ -1359,7 +1359,7 @@ BindingIter::init(ModuleScope::Data& data) init(data.varStart, data.varStart, data.varStart, data.varStart, data.letStart, data.constStart, CanHaveFrameSlots | CanHaveEnvironmentSlots, 0, JSSLOT_FREE(&ModuleEnvironmentObject::class_), - data.names, data.length); + data.trailingNames.start(), data.length); } PositionalFormalParameterIter::PositionalFormalParameterIter(JSScript* script) diff --git a/js/src/vm/Scope.h b/js/src/vm/Scope.h index 5304d6713..1d04fd9f6 100644 --- a/js/src/vm/Scope.h +++ b/js/src/vm/Scope.h @@ -12,6 +12,7 @@ #include "jsobj.h" #include "jsopcode.h" +#include "jsutil.h" #include "gc/Heap.h" #include "gc/Policy.h" @@ -111,6 +112,47 @@ class BindingName void trace(JSTracer* trc); }; +/** + * The various {Global,Module,...}Scope::Data classes consist of always-present + * bits, then a trailing array of BindingNames. The various Data classes all + * end in a TrailingNamesArray that contains sized/aligned space for *one* + * BindingName. Data instances that contain N BindingNames, are then allocated + * in sizeof(Data) + (space for (N - 1) BindingNames). Because this class's + * |data_| field is properly sized/aligned, the N-BindingName array can start + * at |data_|. + * + * This is concededly a very low-level representation, but we want to only + * allocate once for data+bindings both, and this does so approximately as + * elegantly as C++ allows. + */ +class TrailingNamesArray +{ + private: + alignas(BindingName) unsigned char data_[sizeof(BindingName)]; + + private: + // Some versions of GCC treat it as a -Wstrict-aliasing violation (ergo a + // -Werror compile error) to reinterpret_cast<> |data_| to |T*|, even + // through |void*|. Placing the latter cast in these separate functions + // breaks the chain such that affected GCC versions no longer warn/error. + void* ptr() { + return data_; + } + + public: + // Explicitly ensure no one accidentally allocates scope data without + // poisoning its trailing names. + TrailingNamesArray() = delete; + + explicit TrailingNamesArray(size_t nameCount) { + if (nameCount) + JS_POISON(&data_, 0xCC, sizeof(BindingName) * nameCount); + } + BindingName* start() { return reinterpret_cast<BindingName*>(ptr()); } + + BindingName& operator[](size_t i) { return start()[i]; } +}; + class BindingLocation { public: @@ -337,16 +379,19 @@ class LexicalScope : public Scope // // lets - [0, constStart) // consts - [constStart, length) - uint32_t constStart; - uint32_t length; + uint32_t constStart = 0; + uint32_t length = 0; // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. - uint32_t nextFrameSlot; + uint32_t nextFrameSlot = 0; // The array of tagged JSAtom* names, allocated beyond the end of the // struct. - BindingName names[1]; + TrailingNamesArray trailingNames; + + explicit Data(size_t nameCount) : trailingNames(nameCount) {} + Data() = delete; void trace(JSTracer* trc); }; @@ -433,11 +478,11 @@ class FunctionScope : public Scope // The canonical function of the scope, as during a scope walk we // often query properties of the JSFunction (e.g., is the function an // arrow). - GCPtrFunction canonicalFunction; + GCPtrFunction canonicalFunction = {}; // If parameter expressions are present, parameters act like lexical // bindings. - bool hasParameterExprs; + bool hasParameterExprs = false; // Bindings are sorted by kind in both frames and environments. // @@ -452,17 +497,20 @@ class FunctionScope : public Scope // positional formals - [0, nonPositionalFormalStart) // other formals - [nonPositionalParamStart, varStart) // vars - [varStart, length) - uint16_t nonPositionalFormalStart; - uint16_t varStart; - uint32_t length; + uint16_t nonPositionalFormalStart = 0; + uint16_t varStart = 0; + uint32_t length = 0; // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. - uint32_t nextFrameSlot; + uint32_t nextFrameSlot = 0; // The array of tagged JSAtom* names, allocated beyond the end of the // struct. - BindingName names[1]; + TrailingNamesArray trailingNames; + + explicit Data(size_t nameCount) : trailingNames(nameCount) {} + Data() = delete; void trace(JSTracer* trc); }; @@ -548,15 +596,18 @@ class VarScope : public Scope struct Data { // All bindings are vars. - uint32_t length; + uint32_t length = 0; // Frame slots [firstFrameSlot(), nextFrameSlot) are live when this is // the innermost scope. - uint32_t nextFrameSlot; + uint32_t nextFrameSlot = 0; // The array of tagged JSAtom* names, allocated beyond the end of the // struct. - BindingName names[1]; + TrailingNamesArray trailingNames; + + explicit Data(size_t nameCount) : trailingNames(nameCount) {} + Data() = delete; void trace(JSTracer* trc); }; @@ -638,14 +689,17 @@ class GlobalScope : public Scope // vars - [varStart, letStart) // lets - [letStart, constStart) // consts - [constStart, length) - uint32_t varStart; - uint32_t letStart; - uint32_t constStart; - uint32_t length; + uint32_t varStart = 0; + uint32_t letStart = 0; + uint32_t constStart = 0; + uint32_t length = 0; // The array of tagged JSAtom* names, allocated beyond the end of the // struct. - BindingName names[1]; + TrailingNamesArray trailingNames; + + explicit Data(size_t nameCount) : trailingNames(nameCount) {} + Data() = delete; void trace(JSTracer* trc); }; @@ -736,16 +790,19 @@ class EvalScope : public Scope // // top-level funcs - [0, varStart) // vars - [varStart, length) - uint32_t varStart; - uint32_t length; + uint32_t varStart = 0; + uint32_t length = 0; // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. - uint32_t nextFrameSlot; + uint32_t nextFrameSlot = 0; // The array of tagged JSAtom* names, allocated beyond the end of the // struct. - BindingName names[1]; + TrailingNamesArray trailingNames; + + explicit Data(size_t nameCount) : trailingNames(nameCount) {} + Data() = delete; void trace(JSTracer* trc); }; @@ -827,7 +884,7 @@ class ModuleScope : public Scope struct Data { // The module of the scope. - GCPtr<ModuleObject*> module; + GCPtr<ModuleObject*> module = {}; // Bindings are sorted by kind. // @@ -835,18 +892,21 @@ class ModuleScope : public Scope // vars - [varStart, letStart) // lets - [letStart, constStart) // consts - [constStart, length) - uint32_t varStart; - uint32_t letStart; - uint32_t constStart; - uint32_t length; + uint32_t varStart = 0; + uint32_t letStart = 0; + uint32_t constStart = 0; + uint32_t length = 0; // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. - uint32_t nextFrameSlot; + uint32_t nextFrameSlot = 0; // The array of tagged JSAtom* names, allocated beyond the end of the // struct. - BindingName names[1]; + TrailingNamesArray trailingNames; + + explicit Data(size_t nameCount) : trailingNames(nameCount) {} + Data() = delete; void trace(JSTracer* trc); }; diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 08670c833..328a960b6 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -1904,23 +1904,6 @@ intrinsic_RuntimeDefaultLocale(JSContext* cx, unsigned argc, Value* vp) } static bool -intrinsic_AddContentTelemetry(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - MOZ_ASSERT(args.length() == 2); - - int id = args[0].toInt32(); - MOZ_ASSERT(id < JS_TELEMETRY_END); - MOZ_ASSERT(id >= 0); - - if (!cx->compartment()->isProbablySystemOrAddonCode()) - cx->runtime()->addTelemetry(id, args[1].toInt32()); - - args.rval().setUndefined(); - return true; -} - -static bool intrinsic_ConstructFunction(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); @@ -2273,7 +2256,6 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0), JS_FN("_FinishBoundFunctionInit", intrinsic_FinishBoundFunctionInit, 3,0), JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0), - JS_FN("AddContentTelemetry", intrinsic_AddContentTelemetry, 2,0), JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing, 0,0, IntrinsicIsConstructing), diff --git a/js/src/vm/Stopwatch.cpp b/js/src/vm/Stopwatch.cpp index 28632c2a1..684846f00 100644 --- a/js/src/vm/Stopwatch.cpp +++ b/js/src/vm/Stopwatch.cpp @@ -20,6 +20,7 @@ #include "gc/Zone.h" #include "vm/Runtime.h" + namespace js { bool @@ -136,6 +137,9 @@ PerformanceMonitoring::start() bool PerformanceMonitoring::commit() { + // Maximal initialization size, in elements for the vector of groups. + static const size_t MAX_GROUPS_INIT_CAPACITY = 1024; + #if !defined(MOZ_HAVE_RDTSC) // The AutoStopwatch is only executed if `MOZ_HAVE_RDTSC`. return false; @@ -152,13 +156,24 @@ PerformanceMonitoring::commit() return true; } - PerformanceGroupVector recentGroups; - recentGroups_.swap(recentGroups); + // The move operation is generally constant time, unless + // `recentGroups_.length()` is very small, in which case + // it's fast just because it's small. + PerformanceGroupVector recentGroups(Move(recentGroups_)); + recentGroups_ = PerformanceGroupVector(); // Reconstruct after `Move`. bool success = true; if (stopwatchCommitCallback) success = stopwatchCommitCallback(iteration_, recentGroups, stopwatchCommitClosure); + // Heuristic: we expect to have roughly the same number of groups as in + // the previous iteration. + const size_t capacity = recentGroups.capacity() < MAX_GROUPS_INIT_CAPACITY ? + recentGroups.capacity() : + MAX_GROUPS_INIT_CAPACITY; + success = recentGroups_.reserve(capacity) + && success; + // Reset immediately, to make sure that we're not hit by the end // of a nested event loop (which would cause `commit` to be called // twice in succession). @@ -227,7 +242,7 @@ AutoStopwatch::AutoStopwatch(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IM MOZ_GUARD_OBJECT_NOTIFIER_INIT; JSCompartment* compartment = cx_->compartment(); - if (compartment->scheduledForDestruction) + if (MOZ_UNLIKELY(compartment->scheduledForDestruction)) return; JSRuntime* runtime = cx_->runtime(); @@ -266,11 +281,11 @@ AutoStopwatch::~AutoStopwatch() } JSCompartment* compartment = cx_->compartment(); - if (compartment->scheduledForDestruction) + if (MOZ_UNLIKELY(compartment->scheduledForDestruction)) return; JSRuntime* runtime = cx_->runtime(); - if (iteration_ != runtime->performanceMonitoring.iteration()) { + if (MOZ_UNLIKELY(iteration_ != runtime->performanceMonitoring.iteration())) { // We have entered a nested event loop at some point. // Any information we may have is obsolete. return; @@ -319,11 +334,6 @@ AutoStopwatch::exit() const uint64_t cyclesEnd = getCycles(runtime); cyclesDelta = cyclesEnd - cyclesStart_; // Always >= 0 by definition of `getCycles`. } -#if WINVER >= 0x600 - updateTelemetry(cpuStart_, cpuEnd); -#elif defined(__linux__) - updateTelemetry(cpuStart_, cpuEnd); -#endif // WINVER >= 0x600 || _linux__ } uint64_t CPOWTimeDelta = 0; @@ -335,17 +345,6 @@ AutoStopwatch::exit() return addToGroups(cyclesDelta, CPOWTimeDelta); } -void -AutoStopwatch::updateTelemetry(const cpuid_t& cpuStart_, const cpuid_t& cpuEnd) -{ - JSRuntime* runtime = cx_->runtime(); - - if (isSameCPU(cpuStart_, cpuEnd)) - runtime->performanceMonitoring.testCpuRescheduling.stayed += 1; - else - runtime->performanceMonitoring.testCpuRescheduling.moved += 1; -} - PerformanceGroup* AutoStopwatch::acquireGroup(PerformanceGroup* group) { @@ -638,13 +637,6 @@ GetStopwatchIsMonitoringCPOW(JSContext* cx) } JS_PUBLIC_API(void) -GetPerfMonitoringTestCpuRescheduling(JSContext* cx, uint64_t* stayed, uint64_t* moved) -{ - *stayed = cx->performanceMonitoring.testCpuRescheduling.stayed; - *moved = cx->performanceMonitoring.testCpuRescheduling.moved; -} - -JS_PUBLIC_API(void) AddCPOWPerformanceDelta(JSContext* cx, uint64_t delta) { cx->performanceMonitoring.totalCPOWTime += delta; diff --git a/js/src/vm/Stopwatch.h b/js/src/vm/Stopwatch.h index 38a3eb801..d7f299594 100644 --- a/js/src/vm/Stopwatch.h +++ b/js/src/vm/Stopwatch.h @@ -217,33 +217,6 @@ struct PerformanceMonitoring { */ uint64_t monotonicReadTimestampCounter(); - /** - * Data extracted by the AutoStopwatch to determine how often - * we reschedule the process to a different CPU during the - * execution of JS. - * - * Warning: These values are incremented *only* on platforms - * that offer a syscall/libcall to check on which CPU a - * process is currently executed. - */ - struct TestCpuRescheduling - { - // Incremented once we have finished executing code - // in a group, if the CPU on which we started - // execution is the same as the CPU on which - // we finished. - uint64_t stayed; - // Incremented once we have finished executing code - // in a group, if the CPU on which we started - // execution is different from the CPU on which - // we finished. - uint64_t moved; - TestCpuRescheduling() - : stayed(0), - moved(0) - { } - }; - TestCpuRescheduling testCpuRescheduling; private: PerformanceMonitoring(const PerformanceMonitoring&) = delete; PerformanceMonitoring& operator=(const PerformanceMonitoring&) = delete; @@ -375,9 +348,6 @@ class AutoStopwatch final { // Add recent changes to a single group. Mark the group as changed recently. bool addToGroup(JSRuntime* runtime, uint64_t cyclesDelta, uint64_t CPOWTimeDelta, PerformanceGroup* group); - // Update telemetry statistics. - void updateTelemetry(const cpuid_t& a, const cpuid_t& b); - // Perform a subtraction for a quantity that should be monotonic // but is not guaranteed to be so. // diff --git a/js/src/vm/String.h b/js/src/vm/String.h index 1a0c58575..514e2c205 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -8,7 +8,6 @@ #define vm_String_h #include "mozilla/MemoryReporting.h" -#include "mozilla/PodOperations.h" #include "mozilla/Range.h" #include "jsapi.h" @@ -1087,19 +1086,17 @@ class StaticStrings static const size_t SMALL_CHAR_LIMIT = 128U; static const size_t NUM_SMALL_CHARS = 64U; - JSAtom* length2StaticTable[NUM_SMALL_CHARS * NUM_SMALL_CHARS]; + JSAtom* length2StaticTable[NUM_SMALL_CHARS * NUM_SMALL_CHARS] = {}; // zeroes public: /* We keep these public for the JITs. */ static const size_t UNIT_STATIC_LIMIT = 256U; - JSAtom* unitStaticTable[UNIT_STATIC_LIMIT]; + JSAtom* unitStaticTable[UNIT_STATIC_LIMIT] = {}; // zeroes static const size_t INT_STATIC_LIMIT = 256U; - JSAtom* intStaticTable[INT_STATIC_LIMIT]; + JSAtom* intStaticTable[INT_STATIC_LIMIT] = {}; // zeroes - StaticStrings() { - mozilla::PodZero(this); - } + StaticStrings() = default; bool init(JSContext* cx); void trace(JSTracer* trc); diff --git a/js/src/vm/Time.cpp b/js/src/vm/Time.cpp index 69e2cc41d..87531c148 100644 --- a/js/src/vm/Time.cpp +++ b/js/src/vm/Time.cpp @@ -11,9 +11,6 @@ #include "mozilla/DebugOnly.h" #include "mozilla/MathAlgorithms.h" -#ifdef SOLARIS -#define _REENTRANT 1 -#endif #include <string.h> #include <time.h> @@ -33,10 +30,6 @@ #ifdef XP_UNIX -#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */ -extern int gettimeofday(struct timeval* tv); -#endif - #include <sys/time.h> #endif /* XP_UNIX */ @@ -49,11 +42,7 @@ PRMJ_Now() { struct timeval tv; -#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */ - gettimeofday(&tv); -#else gettimeofday(&tv, 0); -#endif /* _SVID_GETTOD */ return int64_t(tv.tv_sec) * PRMJ_USEC_PER_SEC + int64_t(tv.tv_usec); } diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index 3d09c7464..4775a2dea 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -12,6 +12,8 @@ #include "mozilla/SizePrintfMacros.h" #include "mozilla/Sprintf.h" +#include <new> + #include "jsapi.h" #include "jscntxt.h" #include "jsgc.h" @@ -859,10 +861,8 @@ TypeSet::IsTypeAboutToBeFinalized(TypeSet::Type* v) } bool -TypeSet::clone(LifoAlloc* alloc, TemporaryTypeSet* result) const +TypeSet::cloneIntoUninitialized(LifoAlloc* alloc, TemporaryTypeSet* result) const { - MOZ_ASSERT(result->empty()); - unsigned objectCount = baseObjectCount(); unsigned capacity = (objectCount >= 2) ? TypeHashSet::Capacity(objectCount) : 0; @@ -874,15 +874,15 @@ TypeSet::clone(LifoAlloc* alloc, TemporaryTypeSet* result) const PodCopy(newSet, objectSet, capacity); } - new(result) TemporaryTypeSet(flags, capacity ? newSet : objectSet); + new (result) TemporaryTypeSet(flags, capacity ? newSet : objectSet); return true; } TemporaryTypeSet* TypeSet::clone(LifoAlloc* alloc) const { - TemporaryTypeSet* res = alloc->new_<TemporaryTypeSet>(); - if (!res || !clone(alloc, res)) + TemporaryTypeSet* res = alloc->pod_malloc<TemporaryTypeSet>(); + if (!res || !cloneIntoUninitialized(alloc, res)) return nullptr; return res; } @@ -1150,10 +1150,9 @@ TypeScript::FreezeTypeSets(CompilerConstraintList* constraints, JSScript* script TemporaryTypeSet* types = alloc->newArrayUninitialized<TemporaryTypeSet>(count); if (!types) return false; - PodZero(types, count); for (size_t i = 0; i < count; i++) { - if (!existing[i].clone(alloc, &types[i])) + if (!existing[i].cloneIntoUninitialized(alloc, &types[i])) return false; } @@ -3604,6 +3603,10 @@ TypeNewScript::make(JSContext* cx, ObjectGroup* group, JSFunction* fun) MOZ_ASSERT(!group->newScript()); MOZ_ASSERT(!group->maybeUnboxedLayout()); + // rollbackPartiallyInitializedObjects expects function_ to be + // canonicalized. + MOZ_ASSERT(fun->maybeCanonicalFunction() == fun); + if (group->unknownProperties()) return true; @@ -3959,8 +3962,15 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* g oomUnsafe.crash("rollbackPartiallyInitializedObjects"); } - if (!iter.isConstructing() || !iter.matchCallee(cx, function)) + if (!iter.isConstructing()) { continue; + } + + MOZ_ASSERT(iter.calleeTemplate()->maybeCanonicalFunction()); + + if (iter.calleeTemplate()->maybeCanonicalFunction() != function) { + continue; + } // Derived class constructors initialize their this-binding later and // we shouldn't run the definite properties analysis on them. diff --git a/js/src/vm/TypeInference.h b/js/src/vm/TypeInference.h index 9ba1c3cc8..0f1cd4936 100644 --- a/js/src/vm/TypeInference.h +++ b/js/src/vm/TypeInference.h @@ -498,7 +498,10 @@ class TypeSet // Clone a type set into an arbitrary allocator. TemporaryTypeSet* clone(LifoAlloc* alloc) const; - bool clone(LifoAlloc* alloc, TemporaryTypeSet* result) const; + + // |*result| is not even partly initialized when this function is called: + // this function placement-new's its contents into existence. + bool cloneIntoUninitialized(LifoAlloc* alloc, TemporaryTypeSet* result) const; // Create a new TemporaryTypeSet where undefined and/or null has been filtered out. TemporaryTypeSet* filter(LifoAlloc* alloc, bool filterUndefined, bool filterNull) const; @@ -807,12 +810,10 @@ class PreliminaryObjectArray private: // All objects with the type which have been allocated. The pointers in // this array are weak. - JSObject* objects[COUNT]; + JSObject* objects[COUNT] = {}; // zeroes public: - PreliminaryObjectArray() { - mozilla::PodZero(this); - } + PreliminaryObjectArray() = default; void registerNewObject(JSObject* res); void unregisterObject(JSObject* obj); @@ -906,11 +907,11 @@ class TypeNewScript private: // Scripted function which this information was computed for. - HeapPtr<JSFunction*> function_; + HeapPtr<JSFunction*> function_ = {}; // Any preliminary objects with the type. The analyses are not performed // until this array is cleared. - PreliminaryObjectArray* preliminaryObjects; + PreliminaryObjectArray* preliminaryObjects = nullptr; // After the new script properties analyses have been performed, a template // object to use for newly constructed objects. The shape of this object @@ -918,7 +919,7 @@ class TypeNewScript // allocation kind to use. This is null if the new objects have an unboxed // layout, in which case the UnboxedLayout provides the initial structure // of the object. - HeapPtr<PlainObject*> templateObject_; + HeapPtr<PlainObject*> templateObject_ = {}; // Order in which definite properties become initialized. We need this in // case the definite properties are invalidated (such as by adding a setter @@ -928,21 +929,21 @@ class TypeNewScript // shape. Property assignments in inner frames are preceded by a series of // SETPROP_FRAME entries specifying the stack down to the frame containing // the write. - Initializer* initializerList; + Initializer* initializerList = nullptr; // If there are additional properties found by the acquired properties // analysis which were not found by the definite properties analysis, this // shape contains all such additional properties (plus the definite // properties). When an object of this group acquires this shape, it is // fully initialized and its group can be changed to initializedGroup. - HeapPtr<Shape*> initializedShape_; + HeapPtr<Shape*> initializedShape_ = {}; // Group with definite properties set for all properties found by // both the definite and acquired properties analyses. - HeapPtr<ObjectGroup*> initializedGroup_; + HeapPtr<ObjectGroup*> initializedGroup_ = {}; public: - TypeNewScript() { mozilla::PodZero(this); } + TypeNewScript() = default; ~TypeNewScript() { js_delete(preliminaryObjects); js_free(initializerList); diff --git a/js/src/vm/TypedArrayCommon.h b/js/src/vm/TypedArrayCommon.h index d29c93a65..f59419b28 100644 --- a/js/src/vm/TypedArrayCommon.h +++ b/js/src/vm/TypedArrayCommon.h @@ -11,7 +11,8 @@ #include "mozilla/Assertions.h" #include "mozilla/FloatingPoint.h" -#include "mozilla/PodOperations.h" + +#include <algorithm> #include "jsarray.h" #include "jscntxt.h" @@ -245,12 +246,24 @@ class UnsharedOps template<typename T> static void podCopy(SharedMem<T*> dest, SharedMem<T*> src, size_t nelem) { - mozilla::PodCopy(dest.unwrapUnshared(), src.unwrapUnshared(), nelem); + // std::copy_n better matches the argument values/types of this + // function, but as noted below it allows the input/output ranges to + // overlap. std::copy does not, so use it so the compiler has extra + // ability to optimize. + const auto* first = src.unwrapUnshared(); + const auto* last = first + nelem; + auto* result = dest.unwrapUnshared(); + std::copy(first, last, result); } template<typename T> - static void podMove(SharedMem<T*> dest, SharedMem<T*> src, size_t nelem) { - mozilla::PodMove(dest.unwrapUnshared(), src.unwrapUnshared(), nelem); + static void podMove(SharedMem<T*> dest, SharedMem<T*> src, size_t n) { + // std::copy_n copies from |src| to |dest| starting from |src|, so + // input/output ranges *may* permissibly overlap, as this function + // allows. + const auto* start = src.unwrapUnshared(); + auto* result = dest.unwrapUnshared(); + std::copy_n(start, n, result); } static SharedMem<void*> extract(TypedArrayObject* obj) { diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index 7fade24fb..a318d67a9 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -249,14 +249,14 @@ typedef Vector<AsmJSImport, 0, SystemAllocPolicy> AsmJSImportVector; // case the function is toString()ed. class AsmJSExport { - uint32_t funcIndex_; + uint32_t funcIndex_ = 0; // All fields are treated as cacheable POD: - uint32_t startOffsetInModule_; // Store module-start-relative offsets - uint32_t endOffsetInModule_; // so preserved by serialization. + uint32_t startOffsetInModule_ = 0; // Store module-start-relative offsets + uint32_t endOffsetInModule_ = 0; // so preserved by serialization. public: - AsmJSExport() { PodZero(this); } + AsmJSExport() = default; AsmJSExport(uint32_t funcIndex, uint32_t startOffsetInModule, uint32_t endOffsetInModule) : funcIndex_(funcIndex), startOffsetInModule_(startOffsetInModule), @@ -288,12 +288,12 @@ enum class CacheResult struct AsmJSMetadataCacheablePod { - uint32_t numFFIs; - uint32_t srcLength; - uint32_t srcLengthWithRightBrace; - bool usesSimd; + uint32_t numFFIs = 0; + uint32_t srcLength = 0; + uint32_t srcLengthWithRightBrace = 0; + bool usesSimd = false; - AsmJSMetadataCacheablePod() { PodZero(this); } + AsmJSMetadataCacheablePod() = default; }; struct js::AsmJSMetadata : Metadata, AsmJSMetadataCacheablePod @@ -318,6 +318,7 @@ struct js::AsmJSMetadata : Metadata, AsmJSMetadataCacheablePod // Function constructor, this will be the first character in the function // source. Otherwise, it will be the opening parenthesis of the arguments // list. + uint32_t preludeStart; uint32_t srcStart; uint32_t srcBodyStart; bool strict; @@ -1758,6 +1759,7 @@ class MOZ_STACK_CLASS ModuleValidator if (!asmJSMetadata_) return false; + asmJSMetadata_->preludeStart = moduleFunctionNode_->pn_funbox->preludeStart; asmJSMetadata_->srcStart = moduleFunctionNode_->pn_body->pn_pos.begin; asmJSMetadata_->srcBodyStart = parser_.tokenStream.currentToken().pos.end; asmJSMetadata_->strict = parser_.pc->sc()->strict() && @@ -7049,6 +7051,7 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line) TokenStream& tokenStream = m.tokenStream(); tokenStream.consumeKnownToken(TOK_FUNCTION, TokenStream::Operand); + uint32_t preludeStart = tokenStream.currentToken().pos.begin; *line = tokenStream.srcCoords.lineNum(tokenStream.currentToken().pos.end); TokenKind tk; @@ -7061,7 +7064,7 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line) if (!name) return false; - ParseNode* fn = m.parser().handler.newFunctionDefinition(); + ParseNode* fn = m.parser().handler.newFunctionStatement(); if (!fn) return false; @@ -7071,7 +7074,7 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line) ParseContext* outerpc = m.parser().pc; Directives directives(outerpc); - FunctionBox* funbox = m.parser().newFunctionBox(fn, fun, directives, NotGenerator, + FunctionBox* funbox = m.parser().newFunctionBox(fn, fun, preludeStart, directives, NotGenerator, SyncFunction, /* tryAnnexB = */ false); if (!funbox) return false; @@ -8054,7 +8057,7 @@ HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& me return false; } - uint32_t begin = metadata.srcStart; + uint32_t begin = metadata.preludeStart; uint32_t end = metadata.srcEndAfterCurly(); Rooted<JSFlatString*> src(cx, source->substringDontDeflate(cx, begin, end)); if (!src) @@ -8085,7 +8088,7 @@ HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& me SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller() ? SourceBufferHolder::GiveOwnership : SourceBufferHolder::NoOwnership; - SourceBufferHolder srcBuf(chars, stableChars.twoByteRange().length(), ownership); + SourceBufferHolder srcBuf(chars, end - begin, ownership); if (!frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, Nothing())) return false; @@ -8537,6 +8540,7 @@ LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, bool* loaded return true; // See AsmJSMetadata comment as well as ModuleValidator::init(). + asmJSMetadata->preludeStart = parser.pc->functionBox()->preludeStart; asmJSMetadata->srcStart = parser.pc->functionBox()->functionNode->pn_body->pn_pos.begin; asmJSMetadata->srcBodyStart = parser.tokenStream.currentToken().pos.end; asmJSMetadata->strict = parser.pc->sc()->strict() && !parser.pc->sc()->hasExplicitUseStrict(); @@ -8834,7 +8838,7 @@ js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda MOZ_ASSERT(IsAsmJSModule(fun)); const AsmJSMetadata& metadata = AsmJSModuleFunctionToModule(fun).metadata().asAsmJS(); - uint32_t begin = metadata.srcStart; + uint32_t begin = metadata.preludeStart; uint32_t end = metadata.srcEndAfterCurly(); ScriptSource* source = metadata.scriptSource.get(); @@ -8843,17 +8847,15 @@ js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda if (addParenToLambda && fun->isLambda() && !out.append("(")) return nullptr; - if (!out.append("function ")) - return nullptr; - - if (fun->explicitName() && !out.append(fun->explicitName())) - return nullptr; - bool haveSource = source->hasSourceData(); if (!haveSource && !JSScript::loadSource(cx, source, &haveSource)) return nullptr; if (!haveSource) { + if (!out.append("function ")) + return nullptr; + if (fun->explicitName() && !out.append(fun->explicitName())) + return nullptr; if (!out.append("() {\n [sourceless code]\n}")) return nullptr; } else { diff --git a/js/src/wasm/WasmBinaryConstants.h b/js/src/wasm/WasmBinaryConstants.h index fd3bd1264..9aa5091f6 100644 --- a/js/src/wasm/WasmBinaryConstants.h +++ b/js/src/wasm/WasmBinaryConstants.h @@ -434,15 +434,6 @@ enum class Op Limit }; -// Telemetry sample values for the JS_AOT_USAGE key, indicating whether asm.js -// or WebAssembly is used. - -enum class Telemetry -{ - ASMJS = 0, - WASM = 1 -}; - } // namespace wasm } // namespace js diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp index b24e01a40..f1ecd8620 100644 --- a/js/src/wasm/WasmModule.cpp +++ b/js/src/wasm/WasmModule.cpp @@ -1066,8 +1066,5 @@ Module::instantiate(JSContext* cx, return false; } - uint32_t mode = uint32_t(metadata().isAsmJS() ? Telemetry::ASMJS : Telemetry::WASM); - cx->runtime()->addTelemetry(JS_TELEMETRY_AOT_USAGE, mode); - return true; } diff --git a/js/src/wasm/WasmSignalHandlers.cpp b/js/src/wasm/WasmSignalHandlers.cpp index 78d21369d..c4733cc96 100644 --- a/js/src/wasm/WasmSignalHandlers.cpp +++ b/js/src/wasm/WasmSignalHandlers.cpp @@ -130,7 +130,7 @@ class AutoSetHandlingSegFault # define EPC_sig(p) ((p)->sc_pc) # define RFP_sig(p) ((p)->sc_regs[30]) # endif -#elif defined(__linux__) || defined(SOLARIS) +#elif defined(__linux__) # if defined(__linux__) # define XMM_sig(p,i) ((p)->uc_mcontext.fpregs->_xmm[i]) # define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_EIP]) |