summaryrefslogtreecommitdiffstats
path: root/js
diff options
context:
space:
mode:
authorwolfbeast <mcwerewolf@gmail.com>2018-03-20 10:08:54 +0100
committerwolfbeast <mcwerewolf@gmail.com>2018-03-20 10:10:12 +0100
commit893a886ea38853a1a3e97bcf135ea3cb616cd69a (patch)
tree5188f8895ce513381917d37115b50f72fb4e64a9 /js
parent7197b308fb97cd8ab7a972df6a3a17a7a265b594 (diff)
parent6085bfdcecc2529c1037f813e70583c2a776677d (diff)
downloadUXP-893a886ea38853a1a3e97bcf135ea3cb616cd69a.tar
UXP-893a886ea38853a1a3e97bcf135ea3cb616cd69a.tar.gz
UXP-893a886ea38853a1a3e97bcf135ea3cb616cd69a.tar.lz
UXP-893a886ea38853a1a3e97bcf135ea3cb616cd69a.tar.xz
UXP-893a886ea38853a1a3e97bcf135ea3cb616cd69a.zip
Add support for the function `name` property.
This resolves #78. Merged remote-tracking branch 'janek/js_function_name_1'
Diffstat (limited to 'js')
-rw-r--r--js/src/builtin/ModuleObject.cpp2
-rw-r--r--js/src/builtin/ReflectParse.cpp4
-rw-r--r--js/src/builtin/TypedObject.cpp2
-rw-r--r--js/src/frontend/BytecodeCompiler.cpp27
-rw-r--r--js/src/frontend/BytecodeEmitter.cpp141
-rw-r--r--js/src/frontend/BytecodeEmitter.h16
-rw-r--r--js/src/frontend/FullParseHandler.h7
-rw-r--r--js/src/frontend/ParseNode-inl.h2
-rw-r--r--js/src/frontend/ParseNode.cpp18
-rw-r--r--js/src/frontend/ParseNode.h15
-rw-r--r--js/src/frontend/Parser.cpp45
-rw-r--r--js/src/frontend/SharedContext.h8
-rw-r--r--js/src/frontend/SyntaxParseHandler.h3
-rw-r--r--js/src/jit-test/tests/basic/constructor-name.js2
-rw-r--r--js/src/jit-test/tests/basic/functionnames.js32
-rw-r--r--js/src/jit-test/tests/debug/Memory-drainAllocationsLog-16.js2
-rw-r--r--js/src/jit-test/tests/saved-stacks/function-display-name.js5
-rw-r--r--js/src/jit-test/tests/self-test/assertDeepEq.js3
-rw-r--r--js/src/jit/BaselineCompiler.cpp25
-rw-r--r--js/src/jit/BaselineCompiler.h1
-rw-r--r--js/src/jit/CodeGenerator.cpp14
-rw-r--r--js/src/jit/CodeGenerator.h1
-rw-r--r--js/src/jit/IonBuilder.cpp18
-rw-r--r--js/src/jit/IonBuilder.h1
-rw-r--r--js/src/jit/Lowering.cpp12
-rw-r--r--js/src/jit/Lowering.h1
-rw-r--r--js/src/jit/MIR.h28
-rw-r--r--js/src/jit/MOpcodes.h1
-rw-r--r--js/src/jit/shared/LIR-shared.h19
-rw-r--r--js/src/jit/shared/LOpcodes-shared.h1
-rw-r--r--js/src/jsapi.cpp11
-rw-r--r--js/src/jsapi.h6
-rw-r--r--js/src/jsatom.cpp8
-rw-r--r--js/src/jscntxt.cpp2
-rw-r--r--js/src/jsfun.cpp120
-rw-r--r--js/src/jsfun.h61
-rw-r--r--js/src/jsfuninlines.h2
-rw-r--r--js/src/jsscript.cpp11
-rw-r--r--js/src/jsscript.h17
-rw-r--r--js/src/jsstr.cpp2
-rw-r--r--js/src/tests/ecma_5/extensions/error-tostring-function.js4
-rw-r--r--js/src/tests/ecma_6/Class/className.js20
-rw-r--r--js/src/tests/ecma_6/Function/function-name-assignment.js139
-rw-r--r--js/src/tests/ecma_6/Function/function-name-binding.js54
-rw-r--r--js/src/tests/ecma_6/Function/function-name-class.js32
-rw-r--r--js/src/tests/ecma_6/Function/function-name-for.js31
-rw-r--r--js/src/tests/ecma_6/Function/function-name-method.js70
-rw-r--r--js/src/tests/ecma_6/Function/function-name-property.js58
-rw-r--r--js/src/tests/ecma_6/Object/accessor-name.js9
-rw-r--r--js/src/tests/ecma_6/RegExp/compile-symbol.js14
-rw-r--r--js/src/tests/ecma_6/RegExp/constructor-symbol.js14
-rw-r--r--js/src/vm/AsyncFunction.cpp5
-rw-r--r--js/src/vm/Debugger.cpp2
-rw-r--r--js/src/vm/GlobalObject.cpp4
-rw-r--r--js/src/vm/Interpreter.cpp24
-rw-r--r--js/src/vm/Opcodes.h11
-rw-r--r--js/src/vm/SelfHosting.cpp10
-rw-r--r--js/src/vm/Stack.cpp2
-rw-r--r--js/src/vm/TypeInference.cpp2
-rw-r--r--js/src/vm/TypedArrayObject.cpp2
-rw-r--r--js/src/wasm/AsmJS.cpp14
61 files changed, 1033 insertions, 184 deletions
diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp
index 3bfc8f60b..40fd008b9 100644
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -1205,7 +1205,7 @@ ModuleBuilder::processExport(frontend::ParseNode* pn)
case PNK_FUNCTION: {
RootedFunction func(cx_, kid->pn_funbox->function());
if (!func->isArrow()) {
- RootedAtom localName(cx_, func->name());
+ RootedAtom localName(cx_, func->explicitName());
RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get());
MOZ_ASSERT_IF(isDefault, localName);
if (!appendExportEntry(exportName, localName))
diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp
index 748ff7351..eef6fd7ec 100644
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -3415,7 +3415,7 @@ ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst)
#endif
RootedValue id(cx);
- RootedAtom funcAtom(cx, func->name());
+ RootedAtom funcAtom(cx, func->explicitName());
if (!optIdentifier(funcAtom, nullptr, &id))
return false;
@@ -3423,7 +3423,7 @@ ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst)
NodeVector defaults(cx);
RootedValue body(cx), rest(cx);
- if (func->hasRest())
+ if (pn->pn_funbox->hasRest())
rest.setUndefined();
else
rest.setNull();
diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp
index b7297c894..ae74f01bf 100644
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -230,7 +230,7 @@ const Class js::ScalarTypeDescr::class_ = {
const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
- JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, JSFUN_HAS_REST),
+ JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
JS_FS_END
};
diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp
index 3fbfdaa1a..76afe80b1 100644
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -336,10 +336,10 @@ BytecodeCompiler::compileScript(HandleObject environment, SharedContext* sc)
if (!deoptimizeArgumentsInEnclosingScripts(cx->asJSContext(), environment))
return nullptr;
}
- if (!NameFunctions(cx, pn))
- return nullptr;
if (!emitter->emitScript(pn))
return nullptr;
+ if (!NameFunctions(cx, pn))
+ return nullptr;
parser->handler.freeTree(pn);
break;
@@ -397,15 +397,15 @@ BytecodeCompiler::compileModule()
if (!pn)
return nullptr;
- if (!NameFunctions(cx, pn))
- return nullptr;
-
Maybe<BytecodeEmitter> emitter;
if (!emplaceEmitter(emitter, &modulesc))
return nullptr;
if (!emitter->emitScript(pn->pn_body))
return nullptr;
+ if (!NameFunctions(cx, pn))
+ return nullptr;
+
parser->handler.freeTree(pn);
if (!builder.initModule())
@@ -453,9 +453,6 @@ BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun,
return false;
} while (!fn);
- if (!NameFunctions(cx, fn))
- return false;
-
if (fn->pn_funbox->function()->isInterpreted()) {
MOZ_ASSERT(fun == fn->pn_funbox->function());
@@ -472,6 +469,9 @@ BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun,
MOZ_ASSERT(IsAsmJSModule(fun));
}
+ if (!NameFunctions(cx, fn))
+ return false;
+
if (!maybeCompleteCompressSource())
return false;
@@ -646,9 +646,6 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
if (!pn)
return false;
- if (!NameFunctions(cx, pn))
- return false;
-
RootedScriptSource sourceObject(cx, lazy->sourceObject());
MOZ_ASSERT(sourceObject);
@@ -667,7 +664,13 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
if (!bce.init())
return false;
- return bce.emitFunctionScript(pn->pn_body);
+ if (!bce.emitFunctionScript(pn->pn_body))
+ return false;
+
+ if (!NameFunctions(cx, pn))
+ return false;
+
+ return true;
}
bool
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index 1e9d8f224..acf734794 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1053,7 +1053,7 @@ BytecodeEmitter::EmitterScope::enterFunction(BytecodeEmitter* bce, FunctionBox*
if (p) {
MOZ_ASSERT(bi.kind() == BindingKind::FormalParameter);
MOZ_ASSERT(!funbox->hasDestructuringArgs);
- MOZ_ASSERT(!funbox->function()->hasRest());
+ MOZ_ASSERT(!funbox->hasRest());
p->value() = loc;
continue;
}
@@ -4070,7 +4070,7 @@ BytecodeEmitter::isRunOnceLambda()
FunctionBox* funbox = sc->asFunctionBox();
return !funbox->argumentsHasLocalBinding() &&
!funbox->isGenerator() &&
- !funbox->function()->name();
+ !funbox->function()->explicitName();
}
bool
@@ -4463,7 +4463,7 @@ BytecodeEmitter::emitDestructuringLHS(ParseNode* target, DestructuringFlavor fla
}
bool
-BytecodeEmitter::emitConditionallyExecutedDestructuringLHS(ParseNode* target, DestructuringFlavor flav)
+BytecodeEmitter::emitDestructuringLHSInBranch(ParseNode* target, DestructuringFlavor flav)
{
TDZCheckCache tdzCache(this);
return emitDestructuringLHS(target, flav);
@@ -4491,7 +4491,7 @@ BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted)
}
bool
-BytecodeEmitter::emitDefault(ParseNode* defaultExpr)
+BytecodeEmitter::emitDefault(ParseNode* defaultExpr, ParseNode* pattern)
{
if (!emit1(JSOP_DUP)) // VALUE VALUE
return false;
@@ -4507,13 +4507,77 @@ BytecodeEmitter::emitDefault(ParseNode* defaultExpr)
return false;
if (!emit1(JSOP_POP)) // .
return false;
- if (!emitConditionallyExecutedTree(defaultExpr)) // DEFAULTVALUE
+ if (!emitInitializerInBranch(defaultExpr, pattern)) // DEFAULTVALUE
return false;
if (!emitJumpTargetAndPatch(jump))
return false;
return true;
}
+bool
+BytecodeEmitter::setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name,
+ FunctionPrefixKind prefixKind)
+{
+ if (maybeFun->isKind(PNK_FUNCTION)) {
+ // Function doesn't have 'name' property at this point.
+ // Set function's name at compile time.
+ RootedFunction fun(cx, maybeFun->pn_funbox->function());
+
+ // Single node can be emitted multiple times if it appears in
+ // array destructuring default. If function already has a name,
+ // just return.
+ if (fun->hasCompileTimeName()) {
+#ifdef DEBUG
+ RootedAtom funName(cx, NameToFunctionName(cx, name, prefixKind));
+ if (!funName)
+ return false;
+ MOZ_ASSERT(funName == maybeFun->pn_funbox->function()->compileTimeName());
+#endif
+ return true;
+ }
+
+ RootedAtom funName(cx, NameToFunctionName(cx, name, prefixKind));
+ if (!funName)
+ return false;
+ fun->setCompileTimeName(name);
+ return true;
+ }
+
+ uint32_t nameIndex;
+ if (!makeAtomIndex(name, &nameIndex))
+ return false;
+ if (!emitIndexOp(JSOP_STRING, nameIndex)) // FUN NAME
+ return false;
+ uint8_t kind = uint8_t(prefixKind);
+ if (!emit2(JSOP_SETFUNNAME, kind)) // FUN
+ return false;
+ return true;
+}
+
+bool
+BytecodeEmitter::emitInitializer(ParseNode* initializer, ParseNode* pattern)
+{
+ if (!emitTree(initializer))
+ return false;
+
+ if (!pattern->isInParens() && pattern->isKind(PNK_NAME) &&
+ initializer->isDirectRHSAnonFunction())
+ {
+ RootedAtom name(cx, pattern->name());
+ if (!setOrEmitSetFunName(initializer, name, FunctionPrefixKind::None))
+ return false;
+ }
+
+ return true;
+}
+
+bool
+BytecodeEmitter::emitInitializerInBranch(ParseNode* initializer, ParseNode* pattern)
+{
+ TDZCheckCache tdzCache(this);
+ return emitInitializer(initializer, pattern);
+}
+
class MOZ_STACK_CLASS IfThenElseEmitter
{
BytecodeEmitter* bce_;
@@ -4765,7 +4829,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
return false;
if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ? ARRAY
return false;
- if (!emitConditionallyExecutedDestructuringLHS(member, flav)) // ... OBJ?
+ if (!emitDestructuringLHSInBranch(member, flav)) // ... OBJ?
return false;
if (!ifThenElse.emitElse()) // ... OBJ? ITER
@@ -4782,7 +4846,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
return false;
if (!emit1(JSOP_POP)) // ... OBJ? ARRAY
return false;
- if (!emitConditionallyExecutedDestructuringLHS(member, flav)) // ... OBJ?
+ if (!emitDestructuringLHSInBranch(member, flav)) // ... OBJ?
return false;
if (!isHead) {
@@ -4834,7 +4898,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
if (pndefault) {
// Emit only pndefault tree here, as undefined check in emitDefault
// should always be true.
- if (!emitConditionallyExecutedTree(pndefault)) // ... OBJ? ITER VALUE
+ if (!emitInitializerInBranch(pndefault, subpattern)) // ... OBJ? ITER VALUE
return false;
} else {
if (!isElision) {
@@ -4845,7 +4909,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
}
}
if (!isElision) {
- if (!emitConditionallyExecutedDestructuringLHS(subpattern, flav)) // ... OBJ? ITER
+ if (!emitDestructuringLHSInBranch(subpattern, flav)) // ... OBJ? ITER
return false;
} else if (pndefault) {
if (!emit1(JSOP_POP)) // ... OBJ? ITER
@@ -4872,12 +4936,12 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
return false;
if (pndefault) {
- if (!emitDefault(pndefault)) // ... OBJ? ITER VALUE
+ if (!emitDefault(pndefault, subpattern)) // ... OBJ? ITER VALUE
return false;
}
if (!isElision) {
- if (!emitConditionallyExecutedDestructuringLHS(subpattern, flav)) // ... OBJ? ITER
+ if (!emitDestructuringLHSInBranch(subpattern, flav)) // ... OBJ? ITER
return false;
} else {
if (!emit1(JSOP_POP)) // ... OBJ? ITER
@@ -4980,7 +5044,7 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla
return false;
if (subpattern->isKind(PNK_ASSIGN)) {
- if (!emitDefault(subpattern->pn_right))
+ if (!emitDefault(subpattern->pn_right, subpattern->pn_left))
return false;
subpattern = subpattern->pn_left;
}
@@ -5094,7 +5158,7 @@ BytecodeEmitter::emitSingleDeclaration(ParseNode* declList, ParseNode* decl,
if (!initializer && declList->isKind(PNK_VAR))
return true;
- auto emitRhs = [initializer, declList](BytecodeEmitter* bce, const NameLocation&, bool) {
+ auto emitRhs = [initializer, declList, decl](BytecodeEmitter* bce, const NameLocation&, bool) {
if (!initializer) {
// Lexical declarations are initialized to undefined without an
// initializer.
@@ -5105,7 +5169,7 @@ BytecodeEmitter::emitSingleDeclaration(ParseNode* declList, ParseNode* decl,
}
MOZ_ASSERT(initializer);
- return bce->emitTree(initializer);
+ return bce->emitInitializer(initializer, decl);
};
if (!emitInitializeName(decl, emitRhs))
@@ -5164,6 +5228,12 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs)
if (!EmitAssignmentRhs(bce, rhs, emittedBindOp ? 2 : 1))
return false;
+ if (!lhs->isInParens() && op == JSOP_NOP && rhs && rhs->isDirectRHSAnonFunction()) {
+ RootedAtom name(bce->cx, lhs->name());
+ if (!bce->setOrEmitSetFunName(rhs, name, FunctionPrefixKind::None))
+ return false;
+ }
+
// Emit the compound assignment op if there is one.
if (op != JSOP_NOP && !bce->emit1(op))
return false;
@@ -5788,7 +5858,7 @@ BytecodeEmitter::emitIf(ParseNode* pn)
if_again:
/* Emit code for the condition before pushing stmtInfo. */
- if (!emitConditionallyExecutedTree(pn->pn_kid1))
+ if (!emitTreeInBranch(pn->pn_kid1))
return false;
ParseNode* elseNode = pn->pn_kid3;
@@ -5801,7 +5871,7 @@ BytecodeEmitter::emitIf(ParseNode* pn)
}
/* Emit code for the then part. */
- if (!emitConditionallyExecutedTree(pn->pn_kid2))
+ if (!emitTreeInBranch(pn->pn_kid2))
return false;
if (elseNode) {
@@ -5814,7 +5884,7 @@ BytecodeEmitter::emitIf(ParseNode* pn)
}
/* Emit code for the else part. */
- if (!emitConditionallyExecutedTree(elseNode))
+ if (!emitTreeInBranch(elseNode))
return false;
}
@@ -6283,8 +6353,8 @@ BytecodeEmitter::emitForIn(ParseNode* forInLoop, EmitterScope* headLexicalEmitte
if (!updateSourceCoordNotes(decl->pn_pos.begin))
return false;
- auto emitRhs = [initializer](BytecodeEmitter* bce, const NameLocation&, bool) {
- return bce->emitTree(initializer);
+ auto emitRhs = [decl, initializer](BytecodeEmitter* bce, const NameLocation&, bool) {
+ return bce->emitInitializer(initializer, decl);
};
if (!emitInitializeName(decl, emitRhs))
@@ -6504,7 +6574,7 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn, EmitterScope* headLexicalEmitterSc
if (jmp.offset == -1 && !emitLoopEntry(forBody, jmp))
return false;
- if (!emitConditionallyExecutedTree(forBody))
+ if (!emitTreeInBranch(forBody))
return false;
// Set loop and enclosing "update" offsets, for continue. Note that we
@@ -6903,7 +6973,7 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
{
FunctionBox* funbox = pn->pn_funbox;
RootedFunction fun(cx, funbox->function());
- RootedAtom name(cx, fun->name());
+ RootedAtom name(cx, fun->explicitName());
MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
MOZ_ASSERT_IF(pn->isOp(JSOP_FUNWITHPROTO), needsProto);
@@ -7284,7 +7354,7 @@ BytecodeEmitter::emitWhile(ParseNode* pn)
if (!emitLoopHead(pn->pn_right, &top))
return false;
- if (!emitConditionallyExecutedTree(pn->pn_right))
+ if (!emitTreeInBranch(pn->pn_right))
return false;
if (!emitLoopEntry(pn->pn_left, jmp))
@@ -8017,7 +8087,7 @@ BytecodeEmitter::isRestParameter(ParseNode* pn, bool* result)
FunctionBox* funbox = sc->asFunctionBox();
RootedFunction fun(cx, funbox->function());
- if (!fun->hasRest()) {
+ if (!funbox->hasRest()) {
*result = false;
return true;
}
@@ -8449,13 +8519,13 @@ BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional)
if (!ifThenElse.emitCond())
return false;
- if (!emitConditionallyExecutedTree(&conditional.thenExpression()))
+ if (!emitTreeInBranch(&conditional.thenExpression()))
return false;
if (!ifThenElse.emitElse())
return false;
- if (!emitConditionallyExecutedTree(&conditional.elseExpression()))
+ if (!emitTreeInBranch(&conditional.elseExpression()))
return false;
if (!ifThenElse.emitEnd())
@@ -8532,6 +8602,10 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
op == JSOP_INITPROP_GETTER ||
op == JSOP_INITPROP_SETTER);
+ FunctionPrefixKind prefixKind = op == JSOP_INITPROP_GETTER ? FunctionPrefixKind::Get
+ : op == JSOP_INITPROP_SETTER ? FunctionPrefixKind::Set
+ : FunctionPrefixKind::None;
+
if (op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER)
objp.set(nullptr);
@@ -8573,6 +8647,12 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
case JSOP_INITHIDDENPROP_SETTER: op = JSOP_INITHIDDENELEM_SETTER; break;
default: MOZ_CRASH("Invalid op");
}
+ if (propdef->pn_right->isDirectRHSAnonFunction()) {
+ if (!emitDupAt(1))
+ return false;
+ if (!emit2(JSOP_SETFUNNAME, uint8_t(prefixKind)))
+ return false;
+ }
if (!emit1(op))
return false;
} else {
@@ -8597,6 +8677,11 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
objp.set(nullptr);
}
+ if (propdef->pn_right->isDirectRHSAnonFunction()) {
+ RootedAtom keyName(cx, key->pn_atom);
+ if (!setOrEmitSetFunName(propdef->pn_right, keyName, prefixKind))
+ return false;
+ }
if (!emitIndex32(op, index))
return false;
}
@@ -8960,7 +9045,7 @@ BytecodeEmitter::emitFunctionFormalParameters(ParseNode* pn)
EmitterScope* funScope = innermostEmitterScope;
bool hasParameterExprs = funbox->hasParameterExprs;
- bool hasRest = funbox->function()->hasRest();
+ bool hasRest = funbox->hasRest();
uint16_t argSlot = 0;
for (ParseNode* arg = pn->pn_head; arg != funBody; arg = arg->pn_next, argSlot++) {
@@ -9017,7 +9102,7 @@ BytecodeEmitter::emitFunctionFormalParameters(ParseNode* pn)
return false;
if (!emit1(JSOP_POP))
return false;
- if (!emitConditionallyExecutedTree(initializer))
+ if (!emitInitializerInBranch(initializer, bindingElement))
return false;
if (!emitJumpTargetAndPatch(jump))
return false;
@@ -9728,7 +9813,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
}
bool
-BytecodeEmitter::emitConditionallyExecutedTree(ParseNode* pn)
+BytecodeEmitter::emitTreeInBranch(ParseNode* pn)
{
// Code that may be conditionally executed always need their own TDZ
// cache.
diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
index 1bb4191ee..9a2ddb568 100644
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -435,7 +435,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote = EMIT_LINENOTE);
// Emit code for the tree rooted at pn with its own TDZ cache.
- MOZ_MUST_USE bool emitConditionallyExecutedTree(ParseNode* pn);
+ MOZ_MUST_USE bool emitTreeInBranch(ParseNode* pn);
// Emit global, eval, or module code for tree rooted at body. Always
// encompasses the entire source.
@@ -648,8 +648,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
// the stack and emits code to destructure a single lhs expression (either a
// name or a compound []/{} expression).
MOZ_MUST_USE bool emitDestructuringLHS(ParseNode* target, DestructuringFlavor flav);
- MOZ_MUST_USE bool emitConditionallyExecutedDestructuringLHS(ParseNode* target,
- DestructuringFlavor flav);
+ MOZ_MUST_USE bool emitDestructuringLHSInBranch(ParseNode* target, DestructuringFlavor flav);
// emitDestructuringOps assumes the to-be-destructured value has been
// pushed on the stack and emits code to destructure each part of a [] or
@@ -678,7 +677,16 @@ struct MOZ_STACK_CLASS BytecodeEmitter
// Check if the value on top of the stack is "undefined". If so, replace
// that value on the stack with the value defined by |defaultExpr|.
- MOZ_MUST_USE bool emitDefault(ParseNode* defaultExpr);
+ // |pattern| is a lhs node of the default expression. If it's an
+ // identifier and |defaultExpr| is an anonymous function, |SetFunctionName|
+ // is called at compile time.
+ MOZ_MUST_USE bool emitDefault(ParseNode* defaultExpr, ParseNode* pattern);
+
+ MOZ_MUST_USE bool setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name,
+ FunctionPrefixKind prefixKind);
+
+ MOZ_MUST_USE bool emitInitializer(ParseNode* initializer, ParseNode* pattern);
+ MOZ_MUST_USE bool emitInitializerInBranch(ParseNode* initializer, ParseNode* pattern);
MOZ_MUST_USE bool emitCallSiteObject(ParseNode* pn);
MOZ_MUST_USE bool emitTemplateString(ParseNode* pn);
diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
index add881900..0fd137796 100644
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -665,6 +665,11 @@ class FullParseHandler
ParseNode* pn);
inline void setLastFunctionFormalParameterDestructuring(ParseNode* funcpn, ParseNode* pn);
+ void checkAndSetIsDirectRHSAnonFunction(ParseNode* pn) {
+ if (IsAnonymousFunctionDefinition(pn))
+ pn->setDirectRHSAnonFunction(true);
+ }
+
ParseNode* newFunctionDefinition() {
return new_<CodeNode>(PNK_FUNCTION, pos());
}
@@ -942,6 +947,8 @@ FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn, Parse
if (!pn)
return false;
+ checkAndSetIsDirectRHSAnonFunction(defaultValue);
+
funcpn->pn_body->pn_pos.end = pn->pn_pos.end;
ParseNode* pnchild = funcpn->pn_body->pn_head;
ParseNode* pnlast = funcpn->pn_body->last();
diff --git a/js/src/frontend/ParseNode-inl.h b/js/src/frontend/ParseNode-inl.h
index 395d09b5b..21bd83766 100644
--- a/js/src/frontend/ParseNode-inl.h
+++ b/js/src/frontend/ParseNode-inl.h
@@ -18,7 +18,7 @@ inline PropertyName*
ParseNode::name() const
{
MOZ_ASSERT(isKind(PNK_FUNCTION) || isKind(PNK_NAME));
- JSAtom* atom = isKind(PNK_FUNCTION) ? pn_funbox->function()->name() : pn_atom;
+ JSAtom* atom = isKind(PNK_FUNCTION) ? pn_funbox->function()->explicitName() : pn_atom;
return atom->asPropertyName();
}
diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp
index f79baba9e..ece3a45df 100644
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -902,3 +902,21 @@ FunctionBox::trace(JSTracer* trc)
if (enclosingScope_)
TraceRoot(trc, &enclosingScope_, "funbox-enclosingScope");
}
+
+bool
+js::frontend::IsAnonymousFunctionDefinition(ParseNode* pn)
+{
+ // ES 2017 draft
+ // 12.15.2 (ArrowFunction, AsyncArrowFunction).
+ // 14.1.12 (FunctionExpression).
+ // 14.4.8 (GeneratorExpression).
+ // 14.6.8 (AsyncFunctionExpression)
+ if (pn->isKind(PNK_FUNCTION) && !pn->pn_funbox->function()->explicitName())
+ return true;
+
+ // 14.5.8 (ClassExpression)
+ if (pn->is<ClassNode>() && !pn->as<ClassNode>().names())
+ return true;
+
+ return false;
+}
diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
index d37aaaae0..ff26279af 100644
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -450,6 +450,9 @@ class ParseNode
uint8_t pn_op; /* see JSOp enum and jsopcode.tbl */
uint8_t pn_arity:4; /* see ParseNodeArity enum */
bool pn_parens:1; /* this expr was enclosed in parens */
+ bool pn_rhs_anon_fun:1; /* this expr is anonymous function or class that
+ * is a direct RHS of PNK_ASSIGN or PNK_COLON of
+ * property, that needs SetFunctionName. */
ParseNode(const ParseNode& other) = delete;
void operator=(const ParseNode& other) = delete;
@@ -460,6 +463,7 @@ class ParseNode
pn_op(op),
pn_arity(arity),
pn_parens(false),
+ pn_rhs_anon_fun(false),
pn_pos(0, 0),
pn_next(nullptr)
{
@@ -472,6 +476,7 @@ class ParseNode
pn_op(op),
pn_arity(arity),
pn_parens(false),
+ pn_rhs_anon_fun(false),
pn_pos(pos),
pn_next(nullptr)
{
@@ -512,6 +517,13 @@ class ParseNode
bool isLikelyIIFE() const { return isInParens(); }
void setInParens(bool enabled) { pn_parens = enabled; }
+ bool isDirectRHSAnonFunction() const {
+ return pn_rhs_anon_fun;
+ }
+ void setDirectRHSAnonFunction(bool enabled) {
+ pn_rhs_anon_fun = enabled;
+ }
+
TokenPos pn_pos; /* two 16-bit pairs here, for 64 bits */
ParseNode* pn_next; /* intrinsic link in parent PN_LIST */
@@ -1444,6 +1456,9 @@ FunctionFormalParametersList(ParseNode* fn, unsigned* numFormals)
return argsBody->pn_head;
}
+bool
+IsAnonymousFunctionDefinition(ParseNode* pn);
+
} /* namespace frontend */
} /* namespace js */
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index f42546eb5..f4c02720a 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -326,10 +326,14 @@ ParseContext::init()
if (fun->isNamedLambda()) {
if (!namedLambdaScope_->init(this))
return false;
- AddDeclaredNamePtr p = namedLambdaScope_->lookupDeclaredNameForAdd(fun->name());
+ AddDeclaredNamePtr p =
+ namedLambdaScope_->lookupDeclaredNameForAdd(fun->explicitName());
MOZ_ASSERT(!p);
- if (!namedLambdaScope_->addDeclaredName(this, p, fun->name(), DeclarationKind::Const))
+ if (!namedLambdaScope_->addDeclaredName(this, p, fun->explicitName(),
+ DeclarationKind::Const))
+ {
return false;
+ }
}
if (!functionScope_->init(this))
@@ -367,7 +371,7 @@ ParseContext::removeInnerFunctionBoxesForAnnexB(JSAtom* name)
{
for (uint32_t i = 0; i < innerFunctionBoxesForAnnexB_->length(); i++) {
if (FunctionBox* funbox = innerFunctionBoxesForAnnexB_[i]) {
- if (funbox->function()->name() == name)
+ if (funbox->function()->explicitName() == name)
innerFunctionBoxesForAnnexB_[i] = nullptr;
}
}
@@ -465,6 +469,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* trac
usesApply(false),
usesThis(false),
usesReturn(false),
+ hasRest_(false),
funCxFlags()
{
// Functions created at parse time may be set singleton after parsing and
@@ -477,7 +482,6 @@ void
FunctionBox::initFromLazyFunction()
{
JSFunction* fun = function();
- length = fun->nargs() - fun->hasRest();
if (fun->lazyScript()->isDerivedClassConstructor())
setDerivedClassConstructor();
if (fun->lazyScript()->needsHomeObject())
@@ -492,8 +496,6 @@ FunctionBox::initStandaloneFunction(Scope* enclosingScope)
// Standalone functions are Function or Generator constructors and are
// always scoped to the global.
MOZ_ASSERT(enclosingScope->is<GlobalScope>());
- JSFunction* fun = function();
- length = fun->nargs() - fun->hasRest();
enclosingScope_ = enclosingScope;
allowNewTarget_ = true;
thisBinding_ = ThisBinding::Function;
@@ -836,7 +838,7 @@ Parser<ParseHandler>::reportBadReturn(Node pn, ParseReportKind kind,
unsigned errnum, unsigned anonerrnum)
{
JSAutoByteString name;
- if (JSAtom* atom = pc->functionBox()->function()->name()) {
+ if (JSAtom* atom = pc->functionBox()->function()->explicitName()) {
if (!AtomToPrintableString(context, atom, &name))
return false;
} else {
@@ -2214,6 +2216,8 @@ Parser<SyntaxParseHandler>::finishFunction()
lazy->setStrict();
lazy->setGeneratorKind(funbox->generatorKind());
lazy->setAsyncKind(funbox->asyncKind());
+ if (funbox->hasRest())
+ lazy->setHasRest();
if (funbox->isLikelyConstructorWrapper())
lazy->setLikelyConstructorWrapper();
if (funbox->isDerivedClassConstructor())
@@ -2757,7 +2761,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
}
hasRest = true;
- funbox->function()->setHasRest();
+ funbox->setHasRest();
if (!tokenStream.getToken(&tt))
return false;
@@ -3477,8 +3481,8 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
if (!body)
return false;
- if ((kind != Method && !IsConstructorKind(kind)) && fun->name()) {
- RootedPropertyName propertyName(context, fun->name()->asPropertyName());
+ if ((kind != Method && !IsConstructorKind(kind)) && fun->explicitName()) {
+ RootedPropertyName propertyName(context, fun->explicitName()->asPropertyName());
if (!checkStrictBinding(propertyName, handler.getPosition(pn)))
return false;
}
@@ -4337,6 +4341,8 @@ Parser<ParseHandler>::declarationPattern(Node decl, DeclarationKind declKind, To
if (!init)
return null();
+ handler.checkAndSetIsDirectRHSAnonFunction(init);
+
if (forHeadKind) {
// For for(;;) declarations, consistency with |for (;| parsing requires
// that the ';' first be examined as Operand, even though absence of a
@@ -4366,6 +4372,8 @@ Parser<ParseHandler>::initializerInNameDeclaration(Node decl, Node binding,
if (!initializer)
return false;
+ handler.checkAndSetIsDirectRHSAnonFunction(initializer);
+
if (forHeadKind) {
if (initialDeclaration) {
bool isForIn, isForOf;
@@ -5063,7 +5071,7 @@ Parser<FullParseHandler>::exportDeclaration()
if (!kid)
return null();
- if (!checkExportedName(kid->pn_funbox->function()->name()))
+ if (!checkExportedName(kid->pn_funbox->function()->explicitName()))
return null();
break;
@@ -6670,8 +6678,6 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
return null();
}
- // FIXME: Implement ES6 function "name" property semantics
- // (bug 883377).
RootedAtom funName(context);
switch (propType) {
case PropertyType::GetterNoExpressionClosure:
@@ -6694,6 +6700,8 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
if (!fn)
return null();
+ handler.checkAndSetIsDirectRHSAnonFunction(fn);
+
JSOp op = JSOpFromPropertyType(propType);
if (!handler.addClassMethodDefinition(classMethods, propName, fn, op, isStatic))
return null();
@@ -7753,6 +7761,9 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
return null();
}
+ if (kind == PNK_ASSIGN)
+ handler.checkAndSetIsDirectRHSAnonFunction(rhs);
+
return handler.newAssignment(kind, lhs, rhs, op);
}
@@ -9155,6 +9166,8 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
if (!propExpr)
return null();
+ handler.checkAndSetIsDirectRHSAnonFunction(propExpr);
+
if (foldConstants && !FoldConstants(context, &propExpr, this))
return null();
@@ -9268,6 +9281,8 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
return null();
}
+ handler.checkAndSetIsDirectRHSAnonFunction(rhs);
+
Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP);
if (!propExpr)
return null();
@@ -9278,8 +9293,6 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
if (!abortIfSyntaxParser())
return null();
} else {
- // FIXME: Implement ES6 function "name" property semantics
- // (bug 883377).
RootedAtom funName(context);
if (!tokenStream.isCurrentTokenType(TOK_RB)) {
funName = propAtom;
@@ -9295,6 +9308,8 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
if (!fn)
return null();
+ handler.checkAndSetIsDirectRHSAnonFunction(fn);
+
JSOp op = JSOpFromPropertyType(propType);
if (!handler.addObjectMethodDefinition(literal, propName, fn, op))
return null();
diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h
index 39df47c20..a6ac542f6 100644
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -471,6 +471,7 @@ class FunctionBox : public ObjectBox, public SharedContext
bool usesApply:1; /* contains an f.apply() call */
bool usesThis:1; /* contains 'this' */
bool usesReturn:1; /* contains a 'return' statement */
+ bool hasRest_:1; /* has rest parameter */
FunctionContextFlags funCxFlags;
@@ -539,6 +540,11 @@ class FunctionBox : public ObjectBox, public SharedContext
bool isAsync() const { return asyncKind() == AsyncFunction; }
bool isArrow() const { return function()->isArrow(); }
+ bool hasRest() const { return hasRest_; }
+ void setHasRest() {
+ hasRest_ = true;
+ }
+
void setGeneratorKind(GeneratorKind kind) {
// A generator kind can be set at initialization, or when "yield" is
// first seen. In both cases the transition can only happen from
@@ -567,7 +573,7 @@ class FunctionBox : public ObjectBox, public SharedContext
void setHasInnerFunctions() { funCxFlags.hasInnerFunctions = true; }
bool hasSimpleParameterList() const {
- return !function()->hasRest() && !hasParameterExprs && !hasDestructuringArgs;
+ return !hasRest() && !hasParameterExprs && !hasDestructuringArgs;
}
bool hasMappedArgsObj() const {
diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h
index 75c7e3333..b7f00605b 100644
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -339,6 +339,9 @@ class SyntaxParseHandler
Node catchGuard, Node catchBody) { return true; }
MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(Node funcpn, Node pn) { return true; }
+
+ void checkAndSetIsDirectRHSAnonFunction(Node pn) {}
+
Node newFunctionDefinition() { return NodeFunctionDefinition; }
bool setComprehensionLambdaBody(Node pn, Node body) { return true; }
void setFunctionFormalParametersAndBody(Node pn, Node kid) {}
diff --git a/js/src/jit-test/tests/basic/constructor-name.js b/js/src/jit-test/tests/basic/constructor-name.js
index 4bc6a61ea..2dd4c073f 100644
--- a/js/src/jit-test/tests/basic/constructor-name.js
+++ b/js/src/jit-test/tests/basic/constructor-name.js
@@ -17,7 +17,7 @@ function makeObject() {
let tests = [
{ name: "Ctor", object: new Ctor },
{ name: "nested.Ctor", object: new nested.Ctor },
- { name: "makeInstance/LexicalCtor", object: makeInstance() },
+ { name: "LexicalCtor", object: makeInstance() },
{ name: null, object: {} },
{ name: null, object: nested.object },
{ name: null, object: makeObject() },
diff --git a/js/src/jit-test/tests/basic/functionnames.js b/js/src/jit-test/tests/basic/functionnames.js
index 935292ee3..7fef872fc 100644
--- a/js/src/jit-test/tests/basic/functionnames.js
+++ b/js/src/jit-test/tests/basic/functionnames.js
@@ -40,7 +40,7 @@ assertName(Foo, 'Foo</<');
/* various properties and such */
var x = {fox: { bax: function(){} } };
-assertName(x.fox.bax, 'x.fox.bax');
+assertName(x.fox.bax, 'bax');
var foo = {foo: {foo: {}}};
foo.foo.foo = function(){};
assertName(foo.foo.foo, 'foo.foo.foo');
@@ -48,20 +48,20 @@ var z = {
foz: function() {
var baz = function() {
var y = {bay: function() {}};
- assertName(y.bay, 'z.foz/baz/y.bay');
+ assertName(y.bay, 'bay');
};
- assertName(baz, 'z.foz/baz');
+ assertName(baz, 'baz');
baz();
}
};
-assertName(z.foz, 'z.foz');
+assertName(z.foz, 'foz');
z.foz();
var outer = function() {
x.fox.bax.nx = function(){};
var w = {fow: { baw: function(){} } };
assertName(x.fox.bax.nx, 'outer/x.fox.bax.nx')
- assertName(w.fow.baw, 'outer/w.fow.baw');
+ assertName(w.fow.baw, 'baw');
};
assertName(outer, 'outer');
outer();
@@ -69,7 +69,7 @@ function Fuz(){};
Fuz.prototype = {
add: function() {}
}
-assertName(Fuz.prototype.add, 'Fuz.prototype.add');
+assertName(Fuz.prototype.add, 'add');
var x = 1;
x = function(){};
@@ -94,7 +94,7 @@ a.b = function() {
assertName(arguments.callee, 'a.b<');
return { a: function() {} }
}();
-assertName(a.b.a, 'a.b</<.a');
+assertName(a.b.a, 'a');
a = {
b: function(a) {
@@ -104,9 +104,9 @@ a = {
return function() {};
}
};
-assertName(a.b, 'a.b');
-assertName(a.b(true), 'a.b/<')
-assertName(a.b(false), 'a.b/<')
+assertName(a.b, 'b');
+assertName(a.b(true), 'b/<')
+assertName(a.b(false), 'b/<')
function f(g) {
assertName(g, 'x<');
@@ -116,7 +116,7 @@ var x = f(function () { return function() {}; });
assertName(x, 'x</<');
var a = {'b': function(){}};
-assertName(a.b, 'a.b');
+assertName(a.b, 'b');
function g(f) {
assertName(f, '');
@@ -138,15 +138,15 @@ a = {
"\"\'quotes\'\"": function(){},
"!@#$%": function(){}
};
-assertName(a["embedded spaces"], 'a["embedded spaces"]');
-assertName(a["dots.look.like.property.references"], 'a["dots.look.like.property.references"]');
-assertName(a["\"\'quotes\'\""], 'a["\\\"\'quotes\'\\\""]');
-assertName(a["!@#$%"], 'a["!@#$%"]');
+assertName(a["embedded spaces"], 'embedded spaces');
+assertName(a["dots.look.like.property.references"], 'dots.look.like.property.references');
+assertName(a["\"\'quotes\'\""], '"\'quotes\'"');
+assertName(a["!@#$%"], '!@#$%');
a.b = {};
a.b.c = {};
a.b["c"]["d e"] = { f: { 1: { "g": { "h i": function() {} } } } };
-assertName(a.b.c["d e"].f[1].g["h i"], 'a.b.c["d e"].f[1].g["h i"]');
+assertName(a.b.c["d e"].f[1].g["h i"], 'h i');
this.m = function () {};
assertName(m, "this.m");
diff --git a/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-16.js b/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-16.js
index b147d6ded..5c73a1ad3 100644
--- a/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-16.js
+++ b/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-16.js
@@ -24,7 +24,7 @@ root.eval(
this.tests = [
{ name: "Ctor", fn: () => new Ctor },
{ name: "nested.Ctor", fn: () => new nested.Ctor },
- { name: "makeInstance/LexicalCtor", fn: () => makeInstance() },
+ { name: "LexicalCtor", fn: () => makeInstance() },
{ name: null, fn: () => ({}) },
{ name: null, fn: () => (nested.object = {}) },
{ name: null, fn: () => makeObject() },
diff --git a/js/src/jit-test/tests/saved-stacks/function-display-name.js b/js/src/jit-test/tests/saved-stacks/function-display-name.js
index cfe175758..f10b7de6b 100644
--- a/js/src/jit-test/tests/saved-stacks/function-display-name.js
+++ b/js/src/jit-test/tests/saved-stacks/function-display-name.js
@@ -2,9 +2,8 @@
function uno() { return dos(); }
const dos = () => tres.quattro();
-const tres = {
- quattro: () => saveStack()
-};
+let tres = {};
+tres.quattro = () => saveStack()
const frame = uno();
diff --git a/js/src/jit-test/tests/self-test/assertDeepEq.js b/js/src/jit-test/tests/self-test/assertDeepEq.js
index b2a949abc..9c1b37e8e 100644
--- a/js/src/jit-test/tests/self-test/assertDeepEq.js
+++ b/js/src/jit-test/tests/self-test/assertDeepEq.js
@@ -77,7 +77,8 @@ assertDeepEq(q, p);
assertNotDeepEq(() => 1, () => 2);
assertNotDeepEq((...x) => 1, x => 1);
assertNotDeepEq(function f(){}, function g(){});
-var f1 = function () {}, f2 = function () {};
+// Avoid setting name property.
+var [f1, f2] = [function () {}, function () {}];
assertDeepEq(f1, f1);
assertDeepEq(f1, f2); // same text, close enough
f1.prop = 1;
diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp
index c58367aa3..4dcc10b61 100644
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -9,6 +9,8 @@
#include "mozilla/Casting.h"
#include "mozilla/SizePrintfMacros.h"
+#include "jsfun.h"
+
#include "jit/BaselineIC.h"
#include "jit/BaselineJIT.h"
#include "jit/FixedList.h"
@@ -1681,6 +1683,29 @@ BaselineCompiler::emit_JSOP_LAMBDA_ARROW()
return true;
}
+typedef bool (*SetFunNameFn)(JSContext*, HandleFunction, HandleValue, FunctionPrefixKind);
+static const VMFunction SetFunNameInfo =
+ FunctionInfo<SetFunNameFn>(js::SetFunctionNameIfNoOwnName, "SetFunName");
+
+bool
+BaselineCompiler::emit_JSOP_SETFUNNAME()
+{
+ frame.popRegsAndSync(2);
+
+ frame.push(R0);
+ frame.syncStack(0);
+
+ FunctionPrefixKind prefixKind = FunctionPrefixKind(GET_UINT8(pc));
+ masm.unboxObject(R0, R0.scratchReg());
+
+ prepareVMCall();
+
+ pushArg(Imm32(int32_t(prefixKind)));
+ pushArg(R1);
+ pushArg(R0.scratchReg());
+ return callVM(SetFunNameInfo);
+}
+
void
BaselineCompiler::storeValue(const StackValue* source, const Address& dest,
const ValueOperand& scratch)
diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h
index 9adf65c27..77f4dd005 100644
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -71,6 +71,7 @@ namespace jit {
_(JSOP_REGEXP) \
_(JSOP_LAMBDA) \
_(JSOP_LAMBDA_ARROW) \
+ _(JSOP_SETFUNNAME) \
_(JSOP_BITOR) \
_(JSOP_BITXOR) \
_(JSOP_BITAND) \
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
index ce97363be..3b5ec6baa 100644
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2548,6 +2548,20 @@ CodeGenerator::emitLambdaInit(Register output, Register envChain,
masm.storePtr(ImmGCPtr(info.fun->displayAtom()), Address(output, JSFunction::offsetOfAtom()));
}
+typedef bool (*SetFunNameFn)(JSContext*, HandleFunction, HandleValue, FunctionPrefixKind);
+static const VMFunction SetFunNameInfo =
+ FunctionInfo<SetFunNameFn>(js::SetFunctionNameIfNoOwnName, "SetFunName");
+
+void
+CodeGenerator::visitSetFunName(LSetFunName* lir)
+{
+ pushArg(Imm32(lir->mir()->prefixKind()));
+ pushArg(ToValue(lir, LSetFunName::NameValue));
+ pushArg(ToRegister(lir->fun()));
+
+ callVM(SetFunNameInfo, lir);
+}
+
void
CodeGenerator::visitOsiPoint(LOsiPoint* lir)
{
diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h
index 8f4bcc813..b69e919a3 100644
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -134,6 +134,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitOutOfLineLambdaArrow(OutOfLineLambdaArrow* ool);
void visitLambdaArrow(LLambdaArrow* lir);
void visitLambdaForSingleton(LLambdaForSingleton* lir);
+ void visitSetFunName(LSetFunName* lir);
void visitPointer(LPointer* lir);
void visitKeepAliveObject(LKeepAliveObject* lir);
void visitSlots(LSlots* lir);
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index 1488d7d34..2e7784ff4 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -2122,6 +2122,9 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_LAMBDA_ARROW:
return jsop_lambda_arrow(info().getFunction(pc));
+ case JSOP_SETFUNNAME:
+ return jsop_setfunname(GET_UINT8(pc));
+
case JSOP_ITER:
return jsop_iter(GET_INT8(pc));
@@ -13340,6 +13343,21 @@ IonBuilder::jsop_lambda_arrow(JSFunction* fun)
}
bool
+IonBuilder::jsop_setfunname(uint8_t prefixKind)
+{
+ MDefinition* name = current->pop();
+ MDefinition* fun = current->pop();
+ MOZ_ASSERT(fun->type() == MIRType::Object);
+
+ MSetFunName* ins = MSetFunName::New(alloc(), fun, name, prefixKind);
+
+ current->add(ins);
+ current->push(fun);
+
+ return resumeAfter(ins);
+}
+
+bool
IonBuilder::jsop_setarg(uint32_t arg)
{
// To handle this case, we should spill the arguments to the space where
diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h
index 38647a88f..0d1bdb1e3 100644
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -766,6 +766,7 @@ class IonBuilder
MOZ_MUST_USE bool jsop_object(JSObject* obj);
MOZ_MUST_USE bool jsop_lambda(JSFunction* fun);
MOZ_MUST_USE bool jsop_lambda_arrow(JSFunction* fun);
+ MOZ_MUST_USE bool jsop_setfunname(uint8_t prefixKind);
MOZ_MUST_USE bool jsop_functionthis();
MOZ_MUST_USE bool jsop_globalthis();
MOZ_MUST_USE bool jsop_typeof();
diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp
index 13e50820e..a21a529be 100644
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2460,6 +2460,18 @@ LIRGenerator::visitLambdaArrow(MLambdaArrow* ins)
}
void
+LIRGenerator::visitSetFunName(MSetFunName* ins)
+{
+ MOZ_ASSERT(ins->fun()->type() == MIRType::Object);
+ MOZ_ASSERT(ins->name()->type() == MIRType::Value);
+
+ LSetFunName* lir = new(alloc()) LSetFunName(useRegisterAtStart(ins->fun()),
+ useBoxAtStart(ins->name()));
+ add(lir, ins);
+ assignSafepoint(lir, ins);
+}
+
+void
LIRGenerator::visitKeepAliveObject(MKeepAliveObject* ins)
{
MDefinition* obj = ins->object();
diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h
index 0f66a3c24..4062c0960 100644
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -185,6 +185,7 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitNullarySharedStub(MNullarySharedStub* ins);
void visitLambda(MLambda* ins);
void visitLambdaArrow(MLambdaArrow* ins);
+ void visitSetFunName(MSetFunName* ins);
void visitKeepAliveObject(MKeepAliveObject* ins);
void visitSlots(MSlots* ins);
void visitElements(MElements* ins);
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index dcb08c317..3caa7e357 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8464,6 +8464,34 @@ class MLambdaArrow
}
};
+class MSetFunName
+ : public MAryInstruction<2>,
+ public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
+{
+ uint8_t prefixKind_;
+
+ explicit MSetFunName(MDefinition* fun, MDefinition* name, uint8_t prefixKind)
+ : prefixKind_(prefixKind)
+ {
+ initOperand(0, fun);
+ initOperand(1, name);
+ setResultType(MIRType::None);
+ }
+
+ public:
+ INSTRUCTION_HEADER(SetFunName)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, fun), (1, name))
+
+ uint8_t prefixKind() const {
+ return prefixKind_;
+ }
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+};
+
// Returns obj->slots.
class MSlots
: public MUnaryInstruction,
diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h
index 74594cb35..1a6911247 100644
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -161,6 +161,7 @@ namespace jit {
_(StringReplace) \
_(Lambda) \
_(LambdaArrow) \
+ _(SetFunName) \
_(KeepAliveObject) \
_(Slots) \
_(Elements) \
diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h
index a352f5d8a..f8e0ce9cc 100644
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -4995,6 +4995,25 @@ class LLambdaArrow : public LInstructionHelper<1, 1 + BOX_PIECES, 0>
}
};
+class LSetFunName : public LCallInstructionHelper<1, 1 + BOX_PIECES, 0>
+{
+ public:
+ LIR_HEADER(SetFunName)
+
+ static const size_t NameValue = 1;
+
+ LSetFunName(const LAllocation& fun, const LBoxAllocation& name) {
+ setOperand(0, fun);
+ setBoxOperand(NameValue, name);
+ }
+ const LAllocation* fun() {
+ return getOperand(0);
+ }
+ const MSetFunName* mir() const {
+ return mir_->toSetFunName();
+ }
+};
+
class LKeepAliveObject : public LInstructionHelper<0, 1, 0>
{
public:
diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h
index bb04553a6..e57751437 100644
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -242,6 +242,7 @@
_(Lambda) \
_(LambdaArrow) \
_(LambdaForSingleton) \
+ _(SetFunName) \
_(KeepAliveObject) \
_(Slots) \
_(Elements) \
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index e6fc1f98b..85a38bba4 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2095,7 +2095,7 @@ DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue val
getter != JS_PropertyStub && setter != JS_StrictPropertyStub)
{
if (getter && !(attrs & JSPROP_GETTER)) {
- RootedAtom atom(cx, IdToFunctionName(cx, id, "get"));
+ RootedAtom atom(cx, IdToFunctionName(cx, id, FunctionPrefixKind::Get));
if (!atom)
return false;
JSFunction* getobj = NewNativeFunction(cx, (Native) getter, 0, atom);
@@ -2111,7 +2111,7 @@ DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue val
if (setter && !(attrs & JSPROP_SETTER)) {
// Root just the getter, since the setter is not yet a JSObject.
AutoRooterGetterSetter getRoot(cx, JSPROP_GETTER, &getter, nullptr);
- RootedAtom atom(cx, IdToFunctionName(cx, id, "set"));
+ RootedAtom atom(cx, IdToFunctionName(cx, id, FunctionPrefixKind::Set));
if (!atom)
return false;
JSFunction* setobj = NewNativeFunction(cx, (Native) setter, 1, atom);
@@ -3435,10 +3435,7 @@ JS::NewFunctionFromSpec(JSContext* cx, const JSFunctionSpec* fs, HandleId id)
{
return nullptr;
}
- JSFunction* fun = &funVal.toObject().as<JSFunction>();
- if (fs->flags & JSFUN_HAS_REST)
- fun->setHasRest();
- return fun;
+ return &funVal.toObject().as<JSFunction>();
}
RootedAtom atom(cx, IdToFunctionName(cx, id));
@@ -3618,7 +3615,7 @@ JS_GetFunctionObject(JSFunction* fun)
JS_PUBLIC_API(JSString*)
JS_GetFunctionId(JSFunction* fun)
{
- return fun->name();
+ return fun->explicitName();
}
JS_PUBLIC_API(JSString*)
diff --git a/js/src/jsapi.h b/js/src/jsapi.h
index 332ce8562..cbef0f8fb 100644
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -875,11 +875,7 @@ class MOZ_STACK_CLASS SourceBufferHolder final
#define JSFUN_CONSTRUCTOR 0x400 /* native that can be called as a ctor */
-// 0x800 /* Unused */
-
-#define JSFUN_HAS_REST 0x1000 /* function has ...rest parameter. */
-
-#define JSFUN_FLAGS_MASK 0x1e00 /* | of all the JSFUN_* flags */
+#define JSFUN_FLAGS_MASK 0x600 /* | of all the JSFUN_* flags */
/*
* If set, will allow redefining a non-configurable property, but only on a
diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp
index 3f8e8d8f8..2a3c58638 100644
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -510,6 +510,14 @@ ToAtomSlow(ExclusiveContext* cx, typename MaybeRooted<Value, allowGC>::HandleTyp
return v.toBoolean() ? cx->names().true_ : cx->names().false_;
if (v.isNull())
return cx->names().null;
+ if (v.isSymbol()) {
+ if (cx->shouldBeJSContext() && allowGC) {
+ JS_ReportErrorNumberASCII(cx->asJSContext(), GetErrorMessage, nullptr,
+ JSMSG_SYMBOL_TO_STRING);
+ }
+ return nullptr;
+ }
+ MOZ_ASSERT(v.isUndefined());
return cx->names().undefined;
}
diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp
index 3ffd9ad7b..31d62332d 100644
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -803,7 +803,7 @@ js::ReportMissingArg(JSContext* cx, HandleValue v, unsigned arg)
SprintfLiteral(argbuf, "%u", arg);
if (IsFunctionObject(v)) {
- RootedAtom name(cx, v.toObject().as<JSFunction>().name());
+ RootedAtom name(cx, v.toObject().as<JSFunction>().explicitName());
bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, name);
if (!bytes)
return;
diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp
index 1e1b76d5d..bcb0da80b 100644
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -562,7 +562,7 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
return false;
}
- if (fun->name() || fun->hasGuessedAtom())
+ if (fun->explicitName() || fun->hasCompileTimeName() || fun->hasGuessedAtom())
firstword |= HasAtom;
if (fun->isStarGenerator())
@@ -1042,8 +1042,8 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
if (!ok)
return nullptr;
}
- if (fun->name()) {
- if (!out.append(fun->name()))
+ if (fun->explicitName()) {
+ if (!out.append(fun->explicitName()))
return nullptr;
}
@@ -1329,8 +1329,7 @@ JSFunction::getLength(JSContext* cx, uint16_t* length)
if (self->isInterpretedLazy() && !self->getOrCreateScript(cx))
return false;
- *length = self->hasScript() ? self->nonLazyScript()->funLength()
- : (self->nargs() - self->hasRest());
+ *length = self->isNative() ? self->nargs() : self->nonLazyScript()->funLength();
return true;
}
@@ -1365,14 +1364,14 @@ JSFunction::getUnresolvedName(JSContext* cx)
if (isClassConstructor()) {
// It's impossible to have an empty named class expression. We use
// empty as a sentinel when creating default class constructors.
- MOZ_ASSERT(name() != cx->names().empty);
+ MOZ_ASSERT(explicitOrCompileTimeName() != cx->names().empty);
// Unnamed class expressions should not get a .name property at all.
- return name();
+ return explicitOrCompileTimeName();
}
- // Returns the empty string for unnamed functions (FIXME: bug 883377).
- return name() != nullptr ? name() : cx->names().empty;
+ return explicitOrCompileTimeName() != nullptr ? explicitOrCompileTimeName()
+ : cx->names().empty;
}
static const js::Value&
@@ -1620,7 +1619,7 @@ const JSFunctionSpec js::function_methods[] = {
JS_FN(js_apply_str, fun_apply, 2,0),
JS_FN(js_call_str, fun_call, 1,0),
JS_FN("isGenerator", fun_isGenerator,0,0),
- JS_SELF_HOSTED_FN("bind", "FunctionBind", 2, JSFUN_HAS_REST),
+ JS_SELF_HOSTED_FN("bind", "FunctionBind", 2, 0),
JS_SYM_FN(hasInstance, fun_symbolHasInstance, 1, JSPROP_READONLY | JSPROP_PERMANENT),
JS_FS_END
};
@@ -2132,34 +2131,115 @@ js::CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject enclo
*
* Function names are always strings. If id is the well-known @@iterator
* symbol, this returns "[Symbol.iterator]". If a prefix is supplied the final
- * name is |prefix + " " + name|.
+ * name is |prefix + " " + name|. A prefix cannot be supplied if id is a
+ * symbol value.
*
- * Implements step 4 and 5 of SetFunctionName in ES 2016 draft Dec 20, 2015.
+ * Implements steps 3-5 of 9.2.11 SetFunctionName in ES2016.
*/
JSAtom*
-js::IdToFunctionName(JSContext* cx, HandleId id, const char* prefix /* = nullptr */)
+js::IdToFunctionName(JSContext* cx, HandleId id,
+ FunctionPrefixKind prefixKind /* = FunctionPrefixKind::None */)
{
- if (JSID_IS_ATOM(id) && !prefix)
+ // No prefix fastpath.
+ if (JSID_IS_ATOM(id) && prefixKind == FunctionPrefixKind::None)
return JSID_TO_ATOM(id);
- if (JSID_IS_SYMBOL(id) && !prefix) {
+ // Step 3 (implicit).
+
+ // Step 4.
+ if (JSID_IS_SYMBOL(id)) {
+ // Step 4.a.
RootedAtom desc(cx, JSID_TO_SYMBOL(id)->description());
+
+ // Step 4.b, no prefix fastpath.
+ if (!desc && prefixKind == FunctionPrefixKind::None)
+ return cx->names().empty;
+
+ // Step 5 (reordered).
StringBuffer sb(cx);
- if (!sb.append('[') || !sb.append(desc) || !sb.append(']'))
- return nullptr;
+ if (prefixKind == FunctionPrefixKind::Get) {
+ if (!sb.append("get "))
+ return nullptr;
+ } else if (prefixKind == FunctionPrefixKind::Set) {
+ if (!sb.append("set "))
+ return nullptr;
+ }
+
+ // Step 4.b.
+ if (desc) {
+ // Step 4.c.
+ if (!sb.append('[') || !sb.append(desc) || !sb.append(']'))
+ return nullptr;
+ }
return sb.finishAtom();
}
RootedValue idv(cx, IdToValue(id));
- if (!prefix)
- return ToAtom<CanGC>(cx, idv);
+ RootedAtom name(cx, ToAtom<CanGC>(cx, idv));
+ if (!name)
+ return nullptr;
+
+ // Step 5.
+ return NameToFunctionName(cx, name, prefixKind);
+}
+
+JSAtom*
+js::NameToFunctionName(ExclusiveContext* cx, HandleAtom name,
+ FunctionPrefixKind prefixKind /* = FunctionPrefixKind::None */)
+{
+ if (prefixKind == FunctionPrefixKind::None)
+ return name;
StringBuffer sb(cx);
- if (!sb.append(prefix, strlen(prefix)) || !sb.append(' ') || !sb.append(ToAtom<CanGC>(cx, idv)))
+ if (prefixKind == FunctionPrefixKind::Get) {
+ if (!sb.append("get "))
+ return nullptr;
+ } else {
+ if (!sb.append("set "))
+ return nullptr;
+ }
+ if (!sb.append(name))
return nullptr;
return sb.finishAtom();
}
+bool
+js::SetFunctionNameIfNoOwnName(JSContext* cx, HandleFunction fun, HandleValue name,
+ FunctionPrefixKind prefixKind)
+{
+ MOZ_ASSERT(name.isString() || name.isSymbol() || name.isNumber());
+
+ if (fun->isClassConstructor()) {
+ // A class may have static 'name' method or accessor.
+ RootedId nameId(cx, NameToId(cx->names().name));
+ bool result;
+ if (!HasOwnProperty(cx, fun, nameId, &result))
+ return false;
+
+ if (result)
+ return true;
+ } else {
+ // Anonymous function shouldn't have own 'name' property at this point.
+ MOZ_ASSERT(!fun->containsPure(cx->names().name));
+ }
+
+ RootedId id(cx);
+ if (!ValueToId<CanGC>(cx, name, &id))
+ return false;
+
+ RootedAtom funNameAtom(cx, IdToFunctionName(cx, id, prefixKind));
+ if (!funNameAtom)
+ return false;
+
+ RootedValue funNameVal(cx, StringValue(funNameAtom));
+ if (!NativeDefineProperty(cx, fun, cx->names().name, funNameVal, nullptr, nullptr,
+ JSPROP_READONLY))
+ {
+ return false;
+ }
+ return true;
+}
+
JSFunction*
js::DefineFunction(JSContext* cx, HandleObject obj, HandleId id, Native native,
unsigned nargs, unsigned flags, AllocKind allocKind /* = AllocKind::FUNCTION */)
diff --git a/js/src/jsfun.h b/js/src/jsfun.h
index 88af5c22d..d45d112a5 100644
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -31,6 +31,12 @@ static const uint32_t JSSLOT_BOUND_FUNCTION_ARGS = 4;
static const char FunctionConstructorMedialSigils[] = ") {\n";
static const char FunctionConstructorFinalBrace[] = "\n}";
+enum class FunctionPrefixKind {
+ None,
+ Get,
+ Set
+};
+
class JSFunction : public js::NativeObject
{
public:
@@ -61,7 +67,9 @@ class JSFunction : public js::NativeObject
function-statement) */
SELF_HOSTED = 0x0080, /* function is self-hosted builtin and must not be
decompilable nor constructible. */
- HAS_REST = 0x0100, /* function has a rest (...) parameter */
+ HAS_COMPILE_TIME_NAME = 0x0100, /* function had no explicit name, but a
+ name was set by SetFunctionName
+ at compile time */
INTERPRETED_LAZY = 0x0200, /* function is interpreted but doesn't have a script yet */
RESOLVED_LENGTH = 0x0400, /* f.length has been resolved (see fun_resolve). */
RESOLVED_NAME = 0x0800, /* f.name has been resolved (see fun_resolve). */
@@ -95,7 +103,7 @@ class JSFunction : public js::NativeObject
NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME,
STABLE_ACROSS_CLONES = CONSTRUCTOR | EXPR_BODY | HAS_GUESSED_ATOM | LAMBDA |
- SELF_HOSTED | HAS_REST | FUNCTION_KIND_MASK
+ SELF_HOSTED | HAS_COMPILE_TIME_NAME | FUNCTION_KIND_MASK
};
static_assert((INTERPRETED | INTERPRETED_LAZY) == js::JS_FUNCTION_INTERPRETED_BITS,
@@ -180,10 +188,10 @@ class JSFunction : public js::NativeObject
/* Possible attributes of an interpreted function: */
bool isExprBody() const { return flags() & EXPR_BODY; }
+ bool hasCompileTimeName() const { return flags() & HAS_COMPILE_TIME_NAME; }
bool hasGuessedAtom() const { return flags() & HAS_GUESSED_ATOM; }
bool isLambda() const { return flags() & LAMBDA; }
bool isBoundFunction() const { return flags() & BOUND_FUN; }
- bool hasRest() const { return flags() & HAS_REST; }
bool isInterpretedLazy() const { return flags() & INTERPRETED_LAZY; }
bool hasScript() const { return flags() & INTERPRETED; }
@@ -222,7 +230,7 @@ class JSFunction : public js::NativeObject
}
bool isNamedLambda() const {
- return isLambda() && displayAtom() && !hasGuessedAtom();
+ return isLambda() && displayAtom() && !hasCompileTimeName() && !hasGuessedAtom();
}
bool hasLexicalThis() const {
@@ -264,11 +272,6 @@ class JSFunction : public js::NativeObject
this->nargs_ = nargs;
}
- // Can be called multiple times by the parser.
- void setHasRest() {
- flags_ |= HAS_REST;
- }
-
void setIsBoundFunction() {
MOZ_ASSERT(!isBoundFunction());
flags_ |= BOUND_FUN;
@@ -315,14 +318,12 @@ class JSFunction : public js::NativeObject
JSAtom* getUnresolvedName(JSContext* cx);
- JSAtom* name() const { return hasGuessedAtom() ? nullptr : atom_.get(); }
-
- // Because display names (see Debugger.Object.displayName) are already stored
- // on functions and will always contain a valid es6 function name, as described
- // in "ECMA-262 (2016-02-27) 9.2.11 SetFunctionName," we have opted to save
- // memory by parsing the existing display name when a function's name property
- // is accessed.
- JSAtom* functionName(JSContext* cx) const;
+ JSAtom* explicitName() const {
+ return (hasCompileTimeName() || hasGuessedAtom()) ? nullptr : atom_.get();
+ }
+ JSAtom* explicitOrCompileTimeName() const {
+ return hasGuessedAtom() ? nullptr : atom_.get();
+ }
void initAtom(JSAtom* atom) { atom_.init(atom); }
@@ -332,9 +333,24 @@ class JSFunction : public js::NativeObject
return atom_;
}
+ void setCompileTimeName(JSAtom* atom) {
+ MOZ_ASSERT(!atom_);
+ MOZ_ASSERT(atom);
+ MOZ_ASSERT(!hasGuessedAtom());
+ MOZ_ASSERT(!isClassConstructor());
+ atom_ = atom;
+ flags_ |= HAS_COMPILE_TIME_NAME;
+ }
+ JSAtom* compileTimeName() const {
+ MOZ_ASSERT(hasCompileTimeName());
+ MOZ_ASSERT(atom_);
+ return atom_;
+ }
+
void setGuessedAtom(JSAtom* atom) {
MOZ_ASSERT(!atom_);
MOZ_ASSERT(atom);
+ MOZ_ASSERT(!hasCompileTimeName());
MOZ_ASSERT(!hasGuessedAtom());
atom_ = atom;
flags_ |= HAS_GUESSED_ATOM;
@@ -679,7 +695,16 @@ NewFunctionWithProto(ExclusiveContext* cx, JSNative native, unsigned nargs,
NewFunctionProtoHandling protoHandling = NewFunctionClassProto);
extern JSAtom*
-IdToFunctionName(JSContext* cx, HandleId id, const char* prefix = nullptr);
+IdToFunctionName(JSContext* cx, HandleId id,
+ FunctionPrefixKind prefixKind = FunctionPrefixKind::None);
+
+extern JSAtom*
+NameToFunctionName(ExclusiveContext* cx, HandleAtom name,
+ FunctionPrefixKind prefixKind = FunctionPrefixKind::None);
+
+extern bool
+SetFunctionNameIfNoOwnName(JSContext* cx, HandleFunction fun, HandleValue name,
+ FunctionPrefixKind prefixKind);
extern JSFunction*
DefineFunction(JSContext* cx, HandleObject obj, HandleId id, JSNative native,
diff --git a/js/src/jsfuninlines.h b/js/src/jsfuninlines.h
index 8286a7d6a..e134def61 100644
--- a/js/src/jsfuninlines.h
+++ b/js/src/jsfuninlines.h
@@ -16,7 +16,7 @@ namespace js {
inline const char*
GetFunctionNameBytes(JSContext* cx, JSFunction* fun, JSAutoByteString* bytes)
{
- if (JSAtom* name = fun->name())
+ if (JSAtom* name = fun->explicitName())
return bytes->encodeLatin1(cx, name);
return js_anonymous_str;
}
diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp
index b568b4b30..929251d8b 100644
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -316,6 +316,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
IsLegacyGenerator,
IsStarGenerator,
IsAsync,
+ HasRest,
OwnSource,
ExplicitUseStrict,
SelfHosted,
@@ -431,6 +432,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
scriptBits |= (1 << IsStarGenerator);
if (script->asyncKind() == AsyncFunction)
scriptBits |= (1 << IsAsync);
+ if (script->hasRest())
+ scriptBits |= (1 << HasRest);
if (script->hasSingletons())
scriptBits |= (1 << HasSingleton);
if (script->treatAsRunOnce())
@@ -582,6 +585,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
if (scriptBits & (1 << IsAsync))
script->setAsyncKind(AsyncFunction);
+ if (scriptBits & (1 << HasRest))
+ script->setHasRest();
}
JS_STATIC_ASSERT(sizeof(jsbytecode) == 1);
@@ -2637,6 +2642,8 @@ JSScript::initFromFunctionBox(ExclusiveContext* cx, HandleScript script,
script->isGeneratorExp_ = funbox->isGenexpLambda;
script->setGeneratorKind(funbox->generatorKind());
script->setAsyncKind(funbox->asyncKind());
+ if (funbox->hasRest())
+ script->setHasRest();
PositionalFormalParameterIter fi(script);
while (fi && !fi.closedOver())
@@ -3295,6 +3302,7 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
dst->needsHomeObject_ = src->needsHomeObject();
dst->isDefaultClassConstructor_ = src->isDefaultClassConstructor();
dst->isAsync_ = src->asyncKind() == AsyncFunction;
+ dst->hasRest_ = src->hasRest_;
if (nconsts != 0) {
GCPtrValue* vector = Rebase<GCPtrValue>(dst, src, src->consts()->vector);
@@ -4028,6 +4036,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
p.shouldDeclareArguments = false;
p.hasThisBinding = false;
p.isAsync = false;
+ p.hasRest = false;
p.numClosedOverBindings = closedOverBindings.length();
p.numInnerFunctions = innerFunctions.length();
p.generatorKindBits = GeneratorKindAsBits(NotGenerator);
@@ -4169,7 +4178,7 @@ JSScript::hasLoops()
bool
JSScript::mayReadFrameArgsDirectly()
{
- return argumentsHasVarBinding() || (function() && function()->hasRest());
+ return argumentsHasVarBinding() || hasRest();
}
static inline void
diff --git a/js/src/jsscript.h b/js/src/jsscript.h
index ffd4b1e2e..690bc225d 100644
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -1012,6 +1012,8 @@ class JSScript : public js::gc::TenuredCell
bool isAsync_:1;
+ bool hasRest_:1;
+
// Add padding so JSScript is gc::Cell aligned. Make padding protected
// instead of private to suppress -Wunused-private-field compiler warnings.
protected:
@@ -1308,6 +1310,13 @@ class JSScript : public js::gc::TenuredCell
isAsync_ = kind == js::AsyncFunction;
}
+ bool hasRest() const {
+ return hasRest_;
+ }
+ void setHasRest() {
+ hasRest_ = true;
+ }
+
void setNeedsHomeObject() {
needsHomeObject_ = true;
}
@@ -1940,6 +1949,7 @@ class LazyScript : public gc::TenuredCell
uint32_t treatAsRunOnce : 1;
uint32_t isDerivedClassConstructor : 1;
uint32_t needsHomeObject : 1;
+ uint32_t hasRest : 1;
};
union {
@@ -2068,6 +2078,13 @@ class LazyScript : public gc::TenuredCell
p_.isAsync = kind == AsyncFunction;
}
+ bool hasRest() const {
+ return p_.hasRest;
+ }
+ void setHasRest() {
+ p_.hasRest = true;
+ }
+
bool strict() const {
return p_.strict;
}
diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp
index 32a85dc13..7adeed620 100644
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2894,7 +2894,7 @@ static const JSFunctionSpec string_static_methods[] = {
JS_INLINABLE_FN("fromCharCode", js::str_fromCharCode, 1, 0, StringFromCharCode),
JS_INLINABLE_FN("fromCodePoint", js::str_fromCodePoint, 1, 0, StringFromCodePoint),
- JS_SELF_HOSTED_FN("raw", "String_static_raw", 2,JSFUN_HAS_REST),
+ JS_SELF_HOSTED_FN("raw", "String_static_raw", 2,0),
JS_SELF_HOSTED_FN("substring", "String_static_substring", 3,0),
JS_SELF_HOSTED_FN("substr", "String_static_substr", 3,0),
JS_SELF_HOSTED_FN("slice", "String_static_slice", 3,0),
diff --git a/js/src/tests/ecma_5/extensions/error-tostring-function.js b/js/src/tests/ecma_5/extensions/error-tostring-function.js
index 5e92f1075..86751c39d 100644
--- a/js/src/tests/ecma_5/extensions/error-tostring-function.js
+++ b/js/src/tests/ecma_5/extensions/error-tostring-function.js
@@ -27,7 +27,7 @@ assertEq(ErrorToString(function(){}), "");
var fn1 = function() {};
fn1.message = "ohai";
-assertEq(ErrorToString(fn1), "ohai");
+assertEq(ErrorToString(fn1), "fn1: ohai");
var fn2 = function blerch() {};
fn2.message = "fnord";
@@ -35,7 +35,7 @@ assertEq(ErrorToString(fn2), "blerch: fnord");
var fn3 = function() {};
fn3.message = "";
-assertEq(ErrorToString(fn3), "");
+assertEq(ErrorToString(fn3), "fn3");
/******************************************************************************/
diff --git a/js/src/tests/ecma_6/Class/className.js b/js/src/tests/ecma_6/Class/className.js
index a33397a8a..ad3920c15 100644
--- a/js/src/tests/ecma_6/Class/className.js
+++ b/js/src/tests/ecma_6/Class/className.js
@@ -174,27 +174,29 @@ testName(ExtendedExpr3, "base", false, false, false);
// Anonymous class expressions don't get name properties unless specified in a
// static manner.
-let Anon = class {
+// Use property assignment to avoid setting name property.
+let tmp = {};
+let Anon = tmp.value = class {
constructor() {}
};
testName(Anon, "", false, false, false);
-let AnonDefault = class { };
+let AnonDefault = tmp.value = class { };
testName(AnonDefault, "", false, false, false);
-let AnonWithGetter = class {
+let AnonWithGetter = tmp.value = class {
constructor() {}
static get name() { return "base"; }
};
testName(AnonWithGetter, "base", false, true, false);
-let AnonWithSetter = class {
+let AnonWithSetter = tmp.value = class {
constructor() {}
static set name(v) {}
};
testName(AnonWithSetter, undefined, false, false, true);
-let AnonWithGetterSetter = class {
+let AnonWithGetterSetter = tmp.value = class {
constructor() {}
static get name() { return "base"; }
static set name(v) {}
@@ -202,15 +204,15 @@ let AnonWithGetterSetter = class {
testName(AnonWithGetterSetter, "base", false, true, true);
-let ExtendedAnon1 = class extends Anon {
+let ExtendedAnon1 = tmp.value = class extends Anon {
constructor() {}
};
testName(ExtendedAnon1, "", false, false, false);
-let ExtendedAnonDefault = class extends Anon { };
+let ExtendedAnonDefault = tmp.value = class extends Anon { };
testName(ExtendedAnonDefault, "", false, false, false);
-let ExtendedAnon2 = class extends AnonWithGetterSetter {
+let ExtendedAnon2 = tmp.value = class extends AnonWithGetterSetter {
constructor() {}
static get name() { return "extend"; }
};
@@ -218,7 +220,7 @@ testName(ExtendedAnon2, "extend", false, true, false);
delete ExtendedAnon2.name;
testName(ExtendedAnon2, "base", false, false, false);
-let ExtendedAnon3 = class extends AnonWithGetterSetter {
+let ExtendedAnon3 = tmp.value = class extends AnonWithGetterSetter {
constructor() {}
static set name(v) {}
};
diff --git a/js/src/tests/ecma_6/Function/function-name-assignment.js b/js/src/tests/ecma_6/Function/function-name-assignment.js
new file mode 100644
index 000000000..5e4d1c004
--- /dev/null
+++ b/js/src/tests/ecma_6/Function/function-name-assignment.js
@@ -0,0 +1,139 @@
+var BUGNUMBER = 883377;
+var summary = "Anonymous function name should be set based on assignment";
+
+print(BUGNUMBER + ": " + summary);
+
+var fooSymbol = Symbol("foo");
+var emptySymbol = Symbol("");
+var undefSymbol = Symbol();
+var globalVar;
+
+var exprs = [
+ ["function() {}", false],
+ ["function named() {}", true],
+ ["function*() {}", false],
+ ["function* named() {}", true],
+ ["async function() {}", false],
+ ["async function named() {}", true],
+ ["() => {}", false],
+ ["async () => {}", false],
+ ["class {}", false],
+ ["class named {}", true],
+];
+
+function testAssignmentExpression(expr, named) {
+ eval(`
+ var assignment;
+ assignment = ${expr};
+ assertEq(assignment.name, named ? "named" : "assignment");
+
+ globalVar = ${expr};
+ assertEq(globalVar.name, named ? "named" : "globalVar");
+
+ var obj = { dynamic: null };
+ with (obj) {
+ dynamic = ${expr};
+ }
+ assertEq(obj.dynamic.name, named ? "named" : "dynamic");
+
+ (function namedLambda(param1, param2) {
+ var assignedToNamedLambda;
+ assignedToNamedLambda = namedLambda = ${expr};
+ assertEq(namedLambda.name, "namedLambda");
+ assertEq(assignedToNamedLambda.name, named ? "named" : "namedLambda");
+
+ param1 = ${expr};
+ assertEq(param1.name, named ? "named" : "param1");
+
+ {
+ let param1 = ${expr};
+ assertEq(param1.name, named ? "named" : "param1");
+
+ param2 = ${expr};
+ assertEq(param2.name, named ? "named" : "param2");
+ }
+ })();
+
+ {
+ let nextedLexical1, nextedLexical2;
+ {
+ let nextedLexical1 = ${expr};
+ assertEq(nextedLexical1.name, named ? "named" : "nextedLexical1");
+
+ nextedLexical2 = ${expr};
+ assertEq(nextedLexical2.name, named ? "named" : "nextedLexical2");
+ }
+ }
+ `);
+
+ // Not applicable cases: not IsIdentifierRef.
+ eval(`
+ var inParen;
+ (inParen) = ${expr};
+ assertEq(inParen.name, named ? "named" : "");
+ `);
+
+ // Not applicable cases: not direct RHS.
+ if (!expr.includes("=>")) {
+ eval(`
+ var a = true && ${expr};
+ assertEq(a.name, named ? "named" : "");
+ `);
+ } else {
+ // Arrow function cannot be RHS of &&.
+ eval(`
+ var a = true && (${expr});
+ assertEq(a.name, named ? "named" : "");
+ `);
+ }
+
+ // Not applicable cases: property.
+ eval(`
+ var obj = {};
+
+ obj.prop = ${expr};
+ assertEq(obj.prop.name, named ? "named" : "");
+
+ obj["literal"] = ${expr};
+ assertEq(obj["literal"].name, named ? "named" : "");
+ `);
+
+ // Not applicable cases: assigned again.
+ eval(`
+ var tmp = [${expr}];
+ assertEq(tmp[0].name, named ? "named" : "");
+
+ var assignment;
+ assignment = tmp[0];
+ assertEq(assignment.name, named ? "named" : "");
+ `);
+}
+for (var [expr, named] of exprs) {
+ testAssignmentExpression(expr, named);
+}
+
+function testVariableDeclaration(expr, named) {
+ eval(`
+ var varDecl = ${expr};
+ assertEq(varDecl.name, named ? "named" : "varDecl");
+ `);
+}
+for (var [expr, named] of exprs) {
+ testVariableDeclaration(expr, named);
+}
+
+function testLexicalBinding(expr, named) {
+ eval(`
+ let lexical = ${expr};
+ assertEq(lexical.name, named ? "named" : "lexical");
+
+ const constLexical = ${expr};
+ assertEq(constLexical.name, named ? "named" : "constLexical");
+ `);
+}
+for (var [expr, named] of exprs) {
+ testLexicalBinding(expr, named);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/ecma_6/Function/function-name-binding.js b/js/src/tests/ecma_6/Function/function-name-binding.js
new file mode 100644
index 000000000..bdd6c131c
--- /dev/null
+++ b/js/src/tests/ecma_6/Function/function-name-binding.js
@@ -0,0 +1,54 @@
+var BUGNUMBER = 883377;
+var summary = "Anonymous function name should be set based on binding pattern";
+
+print(BUGNUMBER + ": " + summary);
+
+var exprs = [
+ ["function() {}", false],
+ ["function named() {}", true],
+ ["function*() {}", false],
+ ["function* named() {}", true],
+ ["async function() {}", false],
+ ["async function named() {}", true],
+ ["() => {}", false],
+ ["async () => {}", false],
+ ["class {}", false],
+ ["class named {}", true],
+];
+
+function testAssignmentProperty(expr, named) {
+ var f = eval(`(function({ prop1 = ${expr} }) { return prop1; })`);
+ assertEq(f({}).name, named ? "named" : "prop1");
+
+ eval(`
+ var { prop1 = ${expr} } = {};
+ assertEq(prop1.name, named ? "named" : "prop1");
+ `);
+}
+for (var [expr, named] of exprs) {
+ testAssignmentProperty(expr, named);
+}
+
+function testAssignmentElement(expr, named) {
+ var f = eval(`(function([elem1 = ${expr}]) { return elem1; })`);
+ assertEq(f([]).name, named ? "named" : "elem1");
+
+ eval(`
+ var [elem1 = ${expr}] = [];
+ assertEq(elem1.name, named ? "named" : "elem1");
+ `);
+}
+for (var [expr, named] of exprs) {
+ testAssignmentElement(expr, named);
+}
+
+function testSingleNameBinding(expr, named) {
+ var f = eval(`(function(param1 = ${expr}) { return param1; })`);
+ assertEq(f().name, named ? "named" : "param1");
+}
+for (var [expr, named] of exprs) {
+ testSingleNameBinding(expr, named);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/ecma_6/Function/function-name-class.js b/js/src/tests/ecma_6/Function/function-name-class.js
new file mode 100644
index 000000000..edde69055
--- /dev/null
+++ b/js/src/tests/ecma_6/Function/function-name-class.js
@@ -0,0 +1,32 @@
+var BUGNUMBER = 883377;
+var summary = "Anonymous class with name method shouldn't be affected by assignment";
+
+print(BUGNUMBER + ": " + summary);
+
+var classWithStaticNameMethod = class { static name() {} };
+assertEq(typeof classWithStaticNameMethod.name, "function");
+
+var classWithStaticNameGetter = class { static get name() { return "static name"; } };
+assertEq(typeof Object.getOwnPropertyDescriptor(classWithStaticNameGetter, "name").get, "function");
+assertEq(classWithStaticNameGetter.name, "static name");
+
+var classWithStaticNameSetter = class { static set name(v) {} };
+assertEq(typeof Object.getOwnPropertyDescriptor(classWithStaticNameSetter, "name").set, "function");
+
+var n = "NAME".toLowerCase();
+var classWithStaticNameMethodComputed = class { static [n]() {} };
+assertEq(typeof classWithStaticNameMethodComputed.name, "function");
+
+// It doesn't apply for non-static method.
+
+var classWithNameMethod = class { name() {} };
+assertEq(classWithNameMethod.name, "classWithNameMethod");
+
+var classWithNameGetter = class { get name() { return "name"; } };
+assertEq(classWithNameGetter.name, "classWithNameGetter");
+
+var classWithNameSetter = class { set name(v) {} };
+assertEq(classWithNameSetter.name, "classWithNameSetter");
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/ecma_6/Function/function-name-for.js b/js/src/tests/ecma_6/Function/function-name-for.js
new file mode 100644
index 000000000..2f04a5fa8
--- /dev/null
+++ b/js/src/tests/ecma_6/Function/function-name-for.js
@@ -0,0 +1,31 @@
+var BUGNUMBER = 883377;
+var summary = "Anonymous function name should be set based on for-in initializer";
+
+print(BUGNUMBER + ": " + summary);
+
+var exprs = [
+ ["function() {}", false],
+ ["function named() {}", true],
+ ["function*() {}", false],
+ ["function* named() {}", true],
+ ["async function() {}", false],
+ ["async function named() {}", true],
+ ["() => {}", false],
+ ["async () => {}", false],
+ ["class {}", false],
+ ["class named {}", true],
+];
+
+function testForInHead(expr, named) {
+ eval(`
+ for (var forInHead = ${expr} in {}) {
+ }
+ `);
+ assertEq(forInHead.name, named ? "named" : "forInHead");
+}
+for (var [expr, named] of exprs) {
+ testForInHead(expr, named);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/ecma_6/Function/function-name-method.js b/js/src/tests/ecma_6/Function/function-name-method.js
new file mode 100644
index 000000000..3b2eeee79
--- /dev/null
+++ b/js/src/tests/ecma_6/Function/function-name-method.js
@@ -0,0 +1,70 @@
+var BUGNUMBER = 883377;
+var summary = "Anonymous function name should be set based on method definition";
+
+print(BUGNUMBER + ": " + summary);
+
+var fooSymbol = Symbol("foo");
+var emptySymbol = Symbol("");
+var undefSymbol = Symbol();
+
+function testMethod(prefix, classPrefix="", prototype=false) {
+ var param = (prefix == "set" || prefix == "static set") ? "v" : "";
+ var sep = classPrefix ? "" : ",";
+ var objOrClass = eval(`(${classPrefix}{
+ ${prefix} prop(${param}) {} ${sep}
+ ${prefix} "literal"(${param}) {} ${sep}
+ ${prefix} ""(${param}) {} ${sep}
+ ${prefix} 5(${param}) {} ${sep}
+ ${prefix} [Symbol.iterator](${param}) {} ${sep}
+ ${prefix} [fooSymbol](${param}) {} ${sep}
+ ${prefix} [emptySymbol](${param}) {} ${sep}
+ ${prefix} [undefSymbol](${param}) {} ${sep}
+ ${prefix} [/a/](${param}) {} ${sep}
+ })`);
+
+ var target = prototype ? objOrClass.prototype : objOrClass;
+
+ function testOne(methodName, expectedName) {
+ var f;
+ if (prefix == "get" || prefix == "static get") {
+ f = Object.getOwnPropertyDescriptor(target, methodName).get;
+ expectedName = "get " + expectedName;
+ } else if (prefix == "set" || prefix == "static set") {
+ f = Object.getOwnPropertyDescriptor(target, methodName).set;
+ expectedName = "set " + expectedName;
+ } else {
+ f = Object.getOwnPropertyDescriptor(target, methodName).value;
+ }
+
+ assertEq(f.name, expectedName);
+ }
+ testOne("prop", "prop");
+ testOne("literal", "literal");
+ testOne("", "");
+ testOne(5, "5");
+ testOne(Symbol.iterator, "[Symbol.iterator]");
+ testOne(fooSymbol, "[foo]");
+ testOne(emptySymbol, "[]");
+ testOne(undefSymbol, "");
+ testOne(/a/, "/a/");
+}
+testMethod("");
+testMethod("*");
+testMethod("async");
+testMethod("get");
+testMethod("set");
+
+testMethod("", "class", true);
+testMethod("*", "class", true);
+testMethod("async", "class", true);
+testMethod("get", "class", true);
+testMethod("set", "class", true);
+
+testMethod("static", "class");
+testMethod("static *", "class");
+testMethod("static async", "class");
+testMethod("static get", "class");
+testMethod("static set", "class");
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/ecma_6/Function/function-name-property.js b/js/src/tests/ecma_6/Function/function-name-property.js
new file mode 100644
index 000000000..7ad174b10
--- /dev/null
+++ b/js/src/tests/ecma_6/Function/function-name-property.js
@@ -0,0 +1,58 @@
+var BUGNUMBER = 883377;
+var summary = "Anonymous function name should be set based on property name";
+
+print(BUGNUMBER + ": " + summary);
+
+var fooSymbol = Symbol("foo");
+var emptySymbol = Symbol("");
+var undefSymbol = Symbol();
+
+var exprs = [
+ ["function() {}", false],
+ ["function named() {}", true],
+ ["function*() {}", false],
+ ["function* named() {}", true],
+ ["async function() {}", false],
+ ["async function named() {}", true],
+ ["() => {}", false],
+ ["async () => {}", false],
+ ["class {}", false],
+ ["class named {}", true],
+];
+
+function testPropertyDefinition(expr, named) {
+ var obj = eval(`({
+ prop: ${expr},
+ "literal": ${expr},
+ "": ${expr},
+ 5: ${expr},
+ 0.4: ${expr},
+ [Symbol.iterator]: ${expr},
+ [fooSymbol]: ${expr},
+ [emptySymbol]: ${expr},
+ [undefSymbol]: ${expr},
+ [/a/]: ${expr},
+ })`);
+ assertEq(obj.prop.name, named ? "named" : "prop");
+ assertEq(obj["literal"].name, named ? "named" : "literal");
+ assertEq(obj[""].name, named ? "named" : "");
+ assertEq(obj[5].name, named ? "named" : "5");
+ assertEq(obj[0.4].name, named ? "named" : "0.4");
+ assertEq(obj[Symbol.iterator].name, named ? "named" : "[Symbol.iterator]");
+ assertEq(obj[fooSymbol].name, named ? "named" : "[foo]");
+ assertEq(obj[emptySymbol].name, named ? "named" : "[]");
+ assertEq(obj[undefSymbol].name, named ? "named" : "");
+ assertEq(obj[/a/].name, named ? "named" : "/a/");
+
+ // Not applicable cases: __proto__.
+ obj = {
+ __proto__: function() {}
+ };
+ assertEq(obj.__proto__.name, "");
+}
+for (var [expr, named] of exprs) {
+ testPropertyDefinition(expr, named);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/ecma_6/Object/accessor-name.js b/js/src/tests/ecma_6/Object/accessor-name.js
index 1b5268e07..f238a2aef 100644
--- a/js/src/tests/ecma_6/Object/accessor-name.js
+++ b/js/src/tests/ecma_6/Object/accessor-name.js
@@ -27,10 +27,9 @@ o = {get case() { }, set case(v) {}}
assertEq(name(o, "case", true), "get case");
assertEq(name(o, "case", false), "set case");
-// Congratulations on implementing these!
-assertEq(name({get ["a"]() {}}, "a", true), "");
-assertEq(name({get [123]() {}}, "123", true), "");
-assertEq(name({set ["a"](v) {}}, "a", false), "");
-assertEq(name({set [123](v) {}}, "123", false), "");
+assertEq(name({get ["a"]() {}}, "a", true), "get a");
+assertEq(name({get [123]() {}}, "123", true), "get 123");
+assertEq(name({set ["a"](v) {}}, "a", false), "set a");
+assertEq(name({set [123](v) {}}, "123", false), "set 123");
reportCompare(true, true);
diff --git a/js/src/tests/ecma_6/RegExp/compile-symbol.js b/js/src/tests/ecma_6/RegExp/compile-symbol.js
new file mode 100644
index 000000000..9eea1124c
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/compile-symbol.js
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+for (let sym of [Symbol.iterator, Symbol(), Symbol("description")]) {
+ let re = /a/;
+
+ assertEq(re.source, "a");
+ assertThrowsInstanceOf(() => re.compile(sym), TypeError);
+ assertEq(re.source, "a");
+}
+
+if (typeof reportCompare === 'function')
+ reportCompare(0, 0);
diff --git a/js/src/tests/ecma_6/RegExp/constructor-symbol.js b/js/src/tests/ecma_6/RegExp/constructor-symbol.js
new file mode 100644
index 000000000..503d7e5a8
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/constructor-symbol.js
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+for (let sym of [Symbol.iterator, Symbol(), Symbol("description")]) {
+ assertThrowsInstanceOf(() => RegExp(sym), TypeError);
+ assertThrowsInstanceOf(() => new RegExp(sym), TypeError);
+
+ assertThrowsInstanceOf(() => RegExp(sym, "g"), TypeError);
+ assertThrowsInstanceOf(() => new RegExp(sym, "g"), TypeError);
+}
+
+if (typeof reportCompare === 'function')
+ reportCompare(0, 0);
diff --git a/js/src/vm/AsyncFunction.cpp b/js/src/vm/AsyncFunction.cpp
index 1e0c7d7c2..f50c87114 100644
--- a/js/src/vm/AsyncFunction.cpp
+++ b/js/src/vm/AsyncFunction.cpp
@@ -116,7 +116,7 @@ js::WrapAsyncFunctionWithProto(JSContext* cx, HandleFunction unwrapped, HandleOb
// Create a new function with AsyncFunctionPrototype, reusing the name and
// the length of `unwrapped`.
- RootedAtom funName(cx, unwrapped->name());
+ RootedAtom funName(cx, unwrapped->explicitName());
uint16_t length;
if (!unwrapped->getLength(cx, &length))
return nullptr;
@@ -130,6 +130,9 @@ js::WrapAsyncFunctionWithProto(JSContext* cx, HandleFunction unwrapped, HandleOb
if (!wrapped)
return nullptr;
+ if (unwrapped->hasCompileTimeName())
+ wrapped->setCompileTimeName(unwrapped->compileTimeName());
+
// Link them to each other to make GetWrappedAsyncFunction and
// GetUnwrappedAsyncFunction work.
unwrapped->setExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT, ObjectValue(*wrapped));
diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp
index 4d181545f..d16781326 100644
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -9509,7 +9509,7 @@ DebuggerObject::name() const
{
MOZ_ASSERT(isFunction());
- return referent()->as<JSFunction>().name();
+ return referent()->as<JSFunction>().explicitName();
}
JSAtom*
diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp
index 039be2e32..280548cd6 100644
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -802,10 +802,10 @@ GlobalObject::getSelfHostedFunction(JSContext* cx, Handle<GlobalObject*> global,
return false;
if (exists) {
RootedFunction fun(cx, &funVal.toObject().as<JSFunction>());
- if (fun->name() == name)
+ if (fun->explicitName() == name)
return true;
- if (fun->name() == selfHostedName) {
+ if (fun->explicitName() == selfHostedName) {
// This function was initially cloned because it was called by
// other self-hosted code, so the clone kept its self-hosted name,
// instead of getting the name it's intended to have in content
diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
index fbf526ae5..9cba1f4dc 100644
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -282,8 +282,6 @@ MakeDefaultConstructor(JSContext* cx, JSOp op, JSAtom* atom, HandleObject proto)
ctor->setIsConstructor();
ctor->setIsClassConstructor();
- if (derived)
- ctor->setHasRest();
MOZ_ASSERT(ctor->infallibleIsDefaultClassConstructor(cx));
@@ -1862,7 +1860,6 @@ CASE(EnableInterruptsPseudoOpcode)
/* Various 1-byte no-ops. */
CASE(JSOP_NOP)
CASE(JSOP_NOP_DESTRUCTURING)
-CASE(JSOP_UNUSED182)
CASE(JSOP_UNUSED183)
CASE(JSOP_UNUSED187)
CASE(JSOP_UNUSED192)
@@ -3484,6 +3481,19 @@ CASE(JSOP_TOASYNC)
}
END_CASE(JSOP_TOASYNC)
+CASE(JSOP_SETFUNNAME)
+{
+ MOZ_ASSERT(REGS.stackDepth() >= 2);
+ FunctionPrefixKind prefixKind = FunctionPrefixKind(GET_UINT8(REGS.pc));
+ ReservedRooted<Value> name(&rootValue0, REGS.sp[-1]);
+ ReservedRooted<JSFunction*> fun(&rootFunction0, &REGS.sp[-2].toObject().as<JSFunction>());
+ if (!SetFunctionNameIfNoOwnName(cx, fun, name, prefixKind))
+ goto error;
+
+ REGS.sp--;
+}
+END_CASE(JSOP_SETFUNNAME)
+
CASE(JSOP_CALLEE)
MOZ_ASSERT(REGS.fp()->isFunctionFrame());
PUSH_COPY(REGS.fp()->calleev());
@@ -4345,7 +4355,7 @@ js::DefFunOperation(JSContext* cx, HandleScript script, HandleObject envChain,
parent = parent->enclosingEnvironment();
/* ES5 10.5 (NB: with subsequent errata). */
- RootedPropertyName name(cx, fun->name()->asPropertyName());
+ RootedPropertyName name(cx, fun->explicitName()->asPropertyName());
RootedShape shape(cx);
RootedObject pobj(cx);
@@ -4993,7 +5003,7 @@ js::ReportRuntimeLexicalError(JSContext* cx, unsigned errorNumber,
RootedPropertyName name(cx);
if (op == JSOP_THROWSETCALLEE) {
- name = script->functionNonDelazifying()->name()->asPropertyName();
+ name = script->functionNonDelazifying()->explicitName()->asPropertyName();
} else if (IsLocalOp(op)) {
name = FrameSlotName(script, pc)->asPropertyName();
} else if (IsAtomOp(op)) {
@@ -5061,8 +5071,8 @@ js::ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame)
if (fun->isDerivedClassConstructor()) {
const char* name = "anonymous";
JSAutoByteString str;
- if (fun->name()) {
- if (!AtomToPrintableString(cx, fun->name(), &str))
+ if (fun->explicitName()) {
+ if (!AtomToPrintableString(cx, fun->explicitName(), &str))
return false;
name = str.ptr();
}
diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h
index 18ae6f073..f6636004d 100644
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1870,7 +1870,16 @@
* Stack: =>
*/ \
macro(JSOP_POPVARENV, 181, "popvarenv", NULL, 1, 0, 0, JOF_BYTE) \
- macro(JSOP_UNUSED182, 182,"unused182", NULL, 1, 0, 0, JOF_BYTE) \
+ /*
+ * Pops the top two values on the stack as 'name' and 'fun', defines the
+ * name of 'fun' to 'name' with prefix if any, and pushes 'fun' back onto
+ * the stack.
+ * Category: Statements
+ * Type: Function
+ * Operands: uint8_t prefixKind
+ * Stack: fun, name => fun
+ */ \
+ macro(JSOP_SETFUNNAME, 182,"setfunname", NULL, 2, 2, 1, JOF_UINT8) \
macro(JSOP_UNUSED183, 183,"unused183", NULL, 1, 0, 0, JOF_BYTE) \
\
/*
diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
index 6737e774c..bf49f2268 100644
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2878,7 +2878,7 @@ CloneObject(JSContext* cx, HandleNativeObject selfHostedObject)
RootedObject clone(cx);
if (selfHostedObject->is<JSFunction>()) {
RootedFunction selfHostedFunction(cx, &selfHostedObject->as<JSFunction>());
- bool hasName = selfHostedFunction->name() != nullptr;
+ bool hasName = selfHostedFunction->explicitName() != nullptr;
// Arrow functions use the first extended slot for their lexical |this| value.
MOZ_ASSERT(!selfHostedFunction->isArrow());
@@ -2894,7 +2894,7 @@ CloneObject(JSContext* cx, HandleNativeObject selfHostedObject)
// self-hosting compartment has to be stored on the clone.
if (clone && hasName) {
clone->as<JSFunction>().setExtendedSlot(LAZY_FUNCTION_NAME_SLOT,
- StringValue(selfHostedFunction->name()));
+ StringValue(selfHostedFunction->explicitName()));
}
} else if (selfHostedObject->is<RegExpObject>()) {
RegExpObject& reobj = selfHostedObject->as<RegExpObject>();
@@ -2977,10 +2977,10 @@ JSRuntime::createLazySelfHostedFunctionClone(JSContext* cx, HandlePropertyName s
return false;
if (!selfHostedFun->isClassConstructor() && !selfHostedFun->hasGuessedAtom() &&
- selfHostedFun->name() != selfHostedName)
+ selfHostedFun->explicitName() != selfHostedName)
{
MOZ_ASSERT(selfHostedFun->getExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT).toBoolean());
- funName = selfHostedFun->name();
+ funName = selfHostedFun->explicitName();
}
fun.set(NewScriptedFunction(cx, nargs, JSFunction::INTERPRETED_LAZY,
@@ -3022,7 +3022,7 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, HandlePropertyName name,
MOZ_ASSERT(!targetFun->isInterpretedLazy());
MOZ_ASSERT(sourceFun->nargs() == targetFun->nargs());
- MOZ_ASSERT(sourceFun->hasRest() == targetFun->hasRest());
+ MOZ_ASSERT(sourceScript->hasRest() == targetFun->nonLazyScript()->hasRest());
// The target function might have been relazified after its flags changed.
targetFun->setFlags(targetFun->flags() | sourceFun->flags());
diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp
index 439bb1ed4..87e95c893 100644
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -85,7 +85,7 @@ InterpreterFrame::isNonGlobalEvalFrame() const
JSObject*
InterpreterFrame::createRestParameter(JSContext* cx)
{
- MOZ_ASSERT(callee().hasRest());
+ MOZ_ASSERT(script()->hasRest());
unsigned nformal = callee().nargs() - 1, nactual = numActualArgs();
unsigned nrest = (nactual > nformal) ? nactual - nformal : 0;
Value* restvp = argv() + nformal;
diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp
index 2a7762e4f..3d09c7464 100644
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -4541,7 +4541,7 @@ TypeScript::printTypes(JSContext* cx, HandleScript script) const
uintptr_t(script.get()), script->filename(), script->lineno());
if (script->functionNonDelazifying()) {
- if (JSAtom* name = script->functionNonDelazifying()->name())
+ if (JSAtom* name = script->functionNonDelazifying()->explicitName())
name->dumpCharsNoNewline();
}
diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp
index 9d4ee94c6..ae97be0de 100644
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -2870,7 +2870,7 @@ bool
DataViewObject::defineGetter(JSContext* cx, PropertyName* name, HandleNativeObject proto)
{
RootedId id(cx, NameToId(name));
- RootedAtom atom(cx, IdToFunctionName(cx, id, "get"));
+ RootedAtom atom(cx, IdToFunctionName(cx, id, FunctionPrefixKind::Get));
if (!atom)
return false;
unsigned attrs = JSPROP_SHARED | JSPROP_GETTER;
diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp
index 4dbc9b387..b4f41c3d5 100644
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -679,7 +679,7 @@ FunctionObject(ParseNode* fn)
static inline PropertyName*
FunctionName(ParseNode* fn)
{
- if (JSAtom* name = FunctionObject(fn)->name())
+ if (JSAtom* name = FunctionObject(fn)->explicitName())
return name->asPropertyName();
return nullptr;
}
@@ -3249,7 +3249,7 @@ static bool
CheckFunctionHead(ModuleValidator& m, ParseNode* fn)
{
JSFunction* fun = FunctionObject(fn);
- if (fun->hasRest())
+ if (fn->pn_funbox->hasRest())
return m.fail(fn, "rest args not allowed");
if (fun->isExprBody())
return m.fail(fn, "expression closures not allowed");
@@ -8037,7 +8037,7 @@ TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata
static bool
HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& metadata)
{
- RootedAtom name(cx, args.callee().as<JSFunction>().name());
+ RootedAtom name(cx, args.callee().as<JSFunction>().explicitName());
if (cx->isExceptionPending())
return false;
@@ -8128,7 +8128,7 @@ InstantiateAsmJS(JSContext* cx, unsigned argc, JS::Value* vp)
static JSFunction*
NewAsmJSModuleFunction(ExclusiveContext* cx, JSFunction* origFun, HandleObject moduleObj)
{
- RootedAtom name(cx, origFun->name());
+ RootedAtom name(cx, origFun->explicitName());
JSFunction::Flags flags = origFun->isLambda() ? JSFunction::ASMJS_LAMBDA_CTOR
: JSFunction::ASMJS_CTOR;
@@ -8846,7 +8846,7 @@ js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda
if (!out.append("function "))
return nullptr;
- if (fun->name() && !out.append(fun->name()))
+ if (fun->explicitName() && !out.append(fun->explicitName()))
return nullptr;
bool haveSource = source->hasSourceData();
@@ -8894,8 +8894,8 @@ js::AsmJSFunctionToString(JSContext* cx, HandleFunction fun)
if (!haveSource) {
// asm.js functions can't be anonymous
- MOZ_ASSERT(fun->name());
- if (!out.append(fun->name()))
+ MOZ_ASSERT(fun->explicitName());
+ if (!out.append(fun->explicitName()))
return nullptr;
if (!out.append("() {\n [sourceless code]\n}"))
return nullptr;