diff options
Diffstat (limited to 'js/src')
33 files changed, 948 insertions, 295 deletions
diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index cd4ac122c9..389bb57dbb 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/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index 76afe80b13..b5be5f5acd 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 72e9676395..0bc1ab2abf 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 4eb7bf880c..c5e62ae340 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -7834,7 +7834,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; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 209265a584..1ba725a829 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -441,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), @@ -454,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)), @@ -738,7 +740,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) { @@ -753,8 +756,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; @@ -2214,6 +2218,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; @@ -2267,6 +2272,33 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun, { MOZ_ASSERT(checkOptionsCalled); + // 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.newFunctionDefinition(); if (!fn) return null(); @@ -2276,8 +2308,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); @@ -2295,7 +2327,6 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun, return null(); } - TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); if (tt != TOK_EOF) { @@ -2991,8 +3022,8 @@ Parser<ParseHandler>::checkFunctionDefinition(HandleAtom funAtom, Node pn, Funct 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 @@ -3001,7 +3032,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; @@ -3031,8 +3062,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"); } @@ -3108,7 +3139,8 @@ Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling) template <typename ParseHandler> typename ParseHandler::Node -Parser<ParseHandler>::functionDefinition(InHandling inHandling, YieldHandling yieldHandling, +Parser<ParseHandler>::functionDefinition(uint32_t preludeStart, InHandling inHandling, + YieldHandling yieldHandling, HandleAtom funName, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, InvokedPrediction invoked) @@ -3132,7 +3164,7 @@ Parser<ParseHandler>::functionDefinition(InHandling inHandling, YieldHandling yi // 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; } @@ -3165,8 +3197,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; } @@ -3193,6 +3226,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, @@ -3226,14 +3260,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 @@ -3259,13 +3294,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, @@ -3276,13 +3312,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) @@ -3306,6 +3343,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, @@ -3317,14 +3355,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> @@ -3359,8 +3397,8 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict 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(); @@ -3529,8 +3567,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)); @@ -3579,8 +3617,8 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling } YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind); - Node fun = functionDefinition(InAllowed, newYieldHandling, name, Statement, generatorKind, - asyncKind, PredictUninvoked); + Node fun = functionDefinition(preludeStart, InAllowed, newYieldHandling, name, Statement, + generatorKind, asyncKind, PredictUninvoked); if (!fun) return null(); @@ -3597,7 +3635,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)); @@ -3628,8 +3667,8 @@ Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind tokenStream.ungetToken(); } - return functionDefinition(InAllowed, yieldHandling, name, Expression, generatorKind, - asyncKind, invoked); + return functionDefinition(preludeStart, InAllowed, yieldHandling, name, Expression, + generatorKind, asyncKind, invoked); } /* @@ -5074,7 +5113,7 @@ Parser<FullParseHandler>::exportDeclaration() } case TOK_FUNCTION: - kid = functionStmt(YieldIsKeyword, NameRequired); + kid = functionStmt(pos().begin, YieldIsKeyword, NameRequired); if (!kid) return null(); @@ -5114,7 +5153,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; @@ -5131,7 +5170,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; @@ -5232,7 +5271,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); } } @@ -6182,7 +6221,7 @@ Parser<ParseHandler>::labeledItem(YieldHandling yieldHandling) return null(); } - return functionStmt(yieldHandling, NameRequired); + return functionStmt(pos().begin, yieldHandling, NameRequired); } tokenStream.ungetToken(); @@ -6649,6 +6688,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) @@ -6701,7 +6744,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(); @@ -7094,8 +7137,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); } } @@ -7174,7 +7218,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: @@ -7677,8 +7721,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; @@ -7702,7 +7748,7 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl } } - Node arrowFunc = functionDefinition(inHandling, yieldHandling, nullptr, + Node arrowFunc = functionDefinition(preludeStart, inHandling, yieldHandling, nullptr, Arrow, generatorKind, asyncKind); if (!arrowFunc) return null(); @@ -8054,8 +8100,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; @@ -8087,12 +8133,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) @@ -9158,6 +9206,8 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* if (tt == TOK_RC) break; + TokenPos namePos = pos(); + tokenStream.ungetToken(); PropertyType propType; @@ -9309,7 +9359,7 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* } } - Node fn = methodDefinition(propType, funName); + Node fn = methodDefinition(namePos.begin, propType, funName); if (!fn) return null(); @@ -9336,13 +9386,15 @@ 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); YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); - return functionDefinition(InAllowed, yieldHandling, funName, kind, generatorKind, asyncKind); + return functionDefinition(preludeStart, InAllowed, yieldHandling, funName, kind, + generatorKind, asyncKind); } template <typename ParseHandler> @@ -9404,7 +9456,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); @@ -9471,8 +9523,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); } } diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 12642fad82..090931f5b4 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -954,7 +954,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 +1035,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 +1090,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); @@ -1222,7 +1225,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,7 +1233,8 @@ 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, + InHandling inHandling, YieldHandling yieldHandling, HandleAtom name, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, InvokedPrediction invoked = PredictUninvoked); @@ -1321,14 +1325,16 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter 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); diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index a6ac542f67..b20417d5d3 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/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 179a7c2447..b040d29981 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); diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 5d6b4b795a..77eea3d81e 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -570,6 +570,13 @@ 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/jit-test/tests/asm.js/testSource.js b/js/src/jit-test/tests/asm.js/testSource.js index b44c52a6bc..d7ad428640 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 3893d3ff2c..782b2594bc 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 e1d1443640..9a8961fe2c 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 36c6d010e7..1804d38f2f 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 263c3eb8a3..9a2f391973 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 04dd4b220f..9689a6ebe4 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 30c3e8dbca..5ecb4556f2 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 2aa382b7b4..4546818e47 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 64cfce92a4..46e76015ec 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/latin1/assorted.js b/js/src/jit-test/tests/latin1/assorted.js index cef79cb96f..1389a1adae 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 a0dedf2516..07a76733a3 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/jsapi.cpp b/js/src/jsapi.cpp index 6114b81576..f78f94dc12 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4250,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) @@ -4261,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)); @@ -4285,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) { @@ -4298,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++) { @@ -4334,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) diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 98311be2f0..e624aa415b 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -781,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; @@ -804,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; @@ -985,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}")) { @@ -1042,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) { @@ -1635,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) { @@ -1656,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/jsscript.cpp b/js/src/jsscript.cpp index 9f914943ec..33ae56d6fc 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 87da79901d..bb86355810 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/tests/ecma_2017/Function/Object-toSource.js b/js/src/tests/ecma_2017/Function/Object-toSource.js new file mode 100644 index 0000000000..33b9e588ee --- /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 0000000000..e69de29bb2 --- /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 0000000000..e69de29bb2 --- /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 c4d3bb6a66..7146eef9ff 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 19d190eeaa..a75bf885a9 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 76f1a3c82f..1c57db0f7f 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 261bf7de17..abef90f77f 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_8_5/regress/regress-584355.js b/js/src/tests/js1_8_5/regress/regress-584355.js index 4ddfe65d35..7d1b81a2ec 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/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index 2237d1d7f0..2b66903637 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -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; @@ -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 { |