summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_02.js4
-rw-r--r--devtools/server/tests/unit/test_listsources-04.js4
-rw-r--r--js/src/builtin/Object.cpp280
-rw-r--r--js/src/frontend/BytecodeCompiler.cpp12
-rw-r--r--js/src/frontend/BytecodeCompiler.h2
-rw-r--r--js/src/frontend/BytecodeEmitter.cpp3
-rw-r--r--js/src/frontend/Parser.cpp167
-rw-r--r--js/src/frontend/Parser.h28
-rw-r--r--js/src/frontend/SharedContext.h5
-rw-r--r--js/src/frontend/TokenStream.cpp6
-rw-r--r--js/src/frontend/TokenStream.h7
-rw-r--r--js/src/jit-test/tests/asm.js/testSource.js48
-rw-r--r--js/src/jit-test/tests/basic/function-tosource-bug779694.js2
-rw-r--r--js/src/jit-test/tests/basic/function-tosource-constructor.js16
-rw-r--r--js/src/jit-test/tests/basic/function-tosource-getset.js10
-rw-r--r--js/src/jit-test/tests/basic/testLet.js2
-rw-r--r--js/src/jit-test/tests/debug/Script-gc-02.js2
-rw-r--r--js/src/jit-test/tests/debug/Script-gc-03.js2
-rw-r--r--js/src/jit-test/tests/debug/Script-sourceStart-04.js4
-rw-r--r--js/src/jit-test/tests/debug/Source-text-02.js1
-rw-r--r--js/src/jit-test/tests/latin1/assorted.js6
-rw-r--r--js/src/jit-test/tests/latin1/function.js4
-rw-r--r--js/src/jsapi.cpp46
-rw-r--r--js/src/jsfun.cpp92
-rw-r--r--js/src/jsscript.cpp47
-rw-r--r--js/src/jsscript.h49
-rw-r--r--js/src/tests/ecma_2017/Function/Object-toSource.js370
-rw-r--r--js/src/tests/ecma_2017/Function/browser.js0
-rw-r--r--js/src/tests/ecma_2017/Function/shell.js0
-rw-r--r--js/src/tests/ecma_6/Generators/runtime.js2
-rw-r--r--js/src/tests/js1_5/Scope/regress-185485.js2
-rw-r--r--js/src/tests/js1_7/extensions/regress-354945-01.js2
-rw-r--r--js/src/tests/js1_7/extensions/regress-354945-02.js2
-rw-r--r--js/src/tests/js1_8_5/regress/regress-584355.js2
-rw-r--r--js/src/wasm/AsmJS.cpp22
-rw-r--r--testing/web-platform/tests/webstorage/storage_string_conversion.html6
-rw-r--r--toolkit/modules/tests/xpcshell/test_Log.js6
37 files changed, 958 insertions, 305 deletions
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_02.js b/devtools/client/webconsole/test/browser_webconsole_output_02.js
index 4c61cf041..837ec673c 100644
--- a/devtools/client/webconsole/test/browser_webconsole_output_02.js
+++ b/devtools/client/webconsole/test/browser_webconsole_output_02.js
@@ -24,7 +24,7 @@ var inputTests = [
{
input: "(function() { return 42; })",
output: "function ()",
- printOutput: "function () { return 42; }",
+ printOutput: "function() { return 42; }",
suppressClick: true
},
@@ -40,7 +40,7 @@ var inputTests = [
{
input: "testobj1.testfn2",
output: "function testfn2()",
- printOutput: "function () { return 42; }",
+ printOutput: "function() { return 42; }",
suppressClick: true
},
diff --git a/devtools/server/tests/unit/test_listsources-04.js b/devtools/server/tests/unit/test_listsources-04.js
index 6da99a6ce..8e1a57bf0 100644
--- a/devtools/server/tests/unit/test_listsources-04.js
+++ b/devtools/server/tests/unit/test_listsources-04.js
@@ -29,12 +29,12 @@ function run_test_with_server(server, cb) {
addSources(debuggee);
threadClient.getSources(Task.async(function* (res) {
- do_check_true(res.sources.length === 3, "3 sources exist");
+ do_check_eq(res.sources.length, 3, "3 sources exist");
yield threadClient.reconfigure({ useSourceMaps: false });
threadClient.getSources(function(res) {
- do_check_true(res.sources.length === 1, "1 source exist");
+ do_check_eq(res.sources.length, 1, "1 source exist");
client.close().then(cb);
});
}));
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/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 4eb7bf880..c5e62ae34 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 209265a58..1ba725a82 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 12642fad8..090931f5b 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 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/TokenStream.cpp b/js/src/frontend/TokenStream.cpp
index 179a7c244..b040d2998 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 5d6b4b795..77eea3d81 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 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/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/jsapi.cpp b/js/src/jsapi.cpp
index 37d023bd4..9ee29ffe4 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, &parameterListEnd))
+ if (!BuildFunctionString(isInvalidName ? nullptr : name, nameLen, nargs, argnames, srcBuf,
+ &funStr, &parameterListEnd)) {
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 bcb0da80b..be11bf436 100644
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -815,8 +815,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 +840,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 +1022,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 +1088,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 +1683,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 +1715,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 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/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_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/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp
index 2237d1d7f..2b6690363 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 {
diff --git a/testing/web-platform/tests/webstorage/storage_string_conversion.html b/testing/web-platform/tests/webstorage/storage_string_conversion.html
index 518b27521..c76eddfec 100644
--- a/testing/web-platform/tests/webstorage/storage_string_conversion.html
+++ b/testing/web-platform/tests/webstorage/storage_string_conversion.html
@@ -22,21 +22,21 @@
storage.b = 0;
assert_equals(storage.b, "0");
storage.c = function(){};
- assert_equals(storage.c, "function (){}");
+ assert_equals(storage.c, "function(){}");
storage.setItem('d', null);
assert_equals(storage.d, "null");
storage.setItem('e', 0);
assert_equals(storage.e, "0");
storage.setItem('f', function(){});
- assert_equals(storage.f, "function (){}");
+ assert_equals(storage.f, "function(){}");
storage['g'] = null;
assert_equals(storage.g, "null");
storage['h'] = 0;
assert_equals(storage.h, "0");
storage['i'] = function(){};
- assert_equals(storage.f, "function (){}");
+ assert_equals(storage.f, "function(){}");
}, name + " only stores strings");
});
diff --git a/toolkit/modules/tests/xpcshell/test_Log.js b/toolkit/modules/tests/xpcshell/test_Log.js
index 429bbcc50..6aee99c93 100644
--- a/toolkit/modules/tests/xpcshell/test_Log.js
+++ b/toolkit/modules/tests/xpcshell/test_Log.js
@@ -381,12 +381,12 @@ add_task(function* log_message_with_params() {
ob = function() {};
ob.toJSON = function() {throw "oh noes JSON"};
do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}),
- 'Fail is (function () {})');
+ 'Fail is (function() {})');
// Fall back to .toString if both .toJSON and .toSource fail.
ob.toSource = function() {throw "oh noes SOURCE"};
do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}),
- 'Fail is function () {}');
+ 'Fail is function() {}');
// Fall back to '[object]' if .toJSON, .toSource and .toString fail.
ob.toString = function() {throw "oh noes STRING"};
@@ -450,7 +450,7 @@ add_task(function* log_message_with_params() {
// doesn't cause the logger to fail.
let vOf = {a: 1, valueOf: function() {throw "oh noes valueOf"}};
do_check_eq(formatMessage("Broken valueOf ${}", vOf),
- 'Broken valueOf ({a:1, valueOf:(function () {throw "oh noes valueOf"})})');
+ 'Broken valueOf ({a:1, valueOf:(function() {throw "oh noes valueOf"})})');
// Test edge cases of bad data to formatter:
// If 'params' is not an object, format it as a basic type.