mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1141865 - Part 3: Parse new.target, add Reflect support, and tests. (r=jorendorff, r=shu)
This commit is contained in:
parent
2a004af5c4
commit
2efa486971
@ -7636,6 +7636,11 @@ BytecodeEmitter::emitTree(ParseNode* pn)
|
||||
ok = emitClass(pn);
|
||||
break;
|
||||
|
||||
case PNK_NEWTARGET:
|
||||
if (!emit1(JSOP_NEWTARGET))
|
||||
return false;
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT(0);
|
||||
}
|
||||
|
@ -402,6 +402,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
||||
case PNK_CLASSNAMES:
|
||||
case PNK_SUPERPROP:
|
||||
case PNK_SUPERELEM:
|
||||
case PNK_NEWTARGET:
|
||||
MOZ_CRASH("ContainsHoistedDeclaration should have indicated false on "
|
||||
"some parent node without recurring to test this node");
|
||||
|
||||
|
@ -299,6 +299,9 @@ class FullParseHandler
|
||||
ParseNode* newSuperElement(ParseNode* expr, const TokenPos& pos) {
|
||||
return new_<SuperElement>(expr, pos);
|
||||
}
|
||||
ParseNode* newNewTarget(const TokenPos& pos) {
|
||||
return new_<NullaryNode>(PNK_NEWTARGET, pos);
|
||||
}
|
||||
|
||||
bool addPrototypeMutation(ParseNode* literal, uint32_t begin, ParseNode* expr) {
|
||||
// Object literals with mutated [[Prototype]] are non-constant so that
|
||||
@ -671,6 +674,9 @@ class FullParseHandler
|
||||
MOZ_ASSERT(kind != PNK_VAR);
|
||||
return new_<ListNode>(kind, op, pos());
|
||||
}
|
||||
ParseNode* newList(ParseNodeKind kind, uint32_t begin, JSOp op = JSOP_NOP) {
|
||||
return new_<ListNode>(kind, op, TokenPos(begin, begin + 1));
|
||||
}
|
||||
ParseNode* newDeclarationList(ParseNodeKind kind, JSOp op = JSOP_NOP) {
|
||||
MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET ||
|
||||
kind == PNK_GLOBALCONST);
|
||||
|
@ -374,6 +374,7 @@ class NameResolver
|
||||
case PNK_FRESHENBLOCK:
|
||||
case PNK_SUPERPROP:
|
||||
case PNK_OBJECT_PROPERTY_NAME:
|
||||
case PNK_NEWTARGET:
|
||||
MOZ_ASSERT(cur->isArity(PN_NULLARY));
|
||||
break;
|
||||
|
||||
|
@ -215,6 +215,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
||||
case PNK_OBJECT_PROPERTY_NAME:
|
||||
case PNK_FRESHENBLOCK:
|
||||
case PNK_SUPERPROP:
|
||||
case PNK_NEWTARGET:
|
||||
MOZ_ASSERT(pn->isArity(PN_NULLARY));
|
||||
MOZ_ASSERT(!pn->isUsed(), "handle non-trivial cases separately");
|
||||
MOZ_ASSERT(!pn->isDefn(), "handle non-trivial cases separately");
|
||||
|
@ -155,6 +155,7 @@ class UpvarCookie
|
||||
F(CLASSNAMES) \
|
||||
F(SUPERPROP) \
|
||||
F(SUPERELEM) \
|
||||
F(NEWTARGET) \
|
||||
\
|
||||
/* Unary operators. */ \
|
||||
F(TYPEOF) \
|
||||
|
@ -7864,27 +7864,36 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TokenKind tt, bool
|
||||
|
||||
/* Check for new expression first. */
|
||||
if (tt == TOK_NEW) {
|
||||
lhs = handler.newList(PNK_NEW, JSOP_NEW);
|
||||
if (!lhs)
|
||||
uint32_t newBegin = pos().begin;
|
||||
// Make sure this wasn't a |new.target| in disguise.
|
||||
Node newTarget;
|
||||
if (!tryNewTarget(newTarget))
|
||||
return null();
|
||||
|
||||
if (!tokenStream.getToken(&tt, TokenStream::Operand))
|
||||
return null();
|
||||
Node ctorExpr = memberExpr(yieldHandling, tt, false, PredictInvoked);
|
||||
if (!ctorExpr)
|
||||
return null();
|
||||
|
||||
handler.addList(lhs, ctorExpr);
|
||||
|
||||
bool matched;
|
||||
if (!tokenStream.matchToken(&matched, TOK_LP))
|
||||
return null();
|
||||
if (matched) {
|
||||
bool isSpread = false;
|
||||
if (!argumentList(yieldHandling, lhs, &isSpread))
|
||||
if (newTarget) {
|
||||
lhs = newTarget;
|
||||
} else {
|
||||
lhs = handler.newList(PNK_NEW, newBegin, JSOP_NEW);
|
||||
if (!lhs)
|
||||
return null();
|
||||
if (isSpread)
|
||||
handler.setOp(lhs, JSOP_SPREADNEW);
|
||||
|
||||
// Gotten by tryNewTarget
|
||||
tt = tokenStream.currentToken().type;
|
||||
Node ctorExpr = memberExpr(yieldHandling, tt, false, PredictInvoked);
|
||||
if (!ctorExpr)
|
||||
return null();
|
||||
|
||||
handler.addList(lhs, ctorExpr);
|
||||
|
||||
bool matched;
|
||||
if (!tokenStream.matchToken(&matched, TOK_LP))
|
||||
return null();
|
||||
if (matched) {
|
||||
bool isSpread = false;
|
||||
if (!argumentList(yieldHandling, lhs, &isSpread))
|
||||
return null();
|
||||
if (isSpread)
|
||||
handler.setOp(lhs, JSOP_SPREADNEW);
|
||||
}
|
||||
}
|
||||
} else if (tt == TOK_SUPER) {
|
||||
lhs = null();
|
||||
@ -8603,6 +8612,44 @@ Parser<ParseHandler>::methodDefinition(YieldHandling yieldHandling, PropListType
|
||||
return handler.addObjectMethodDefinition(propList, propname, fn, op);
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::tryNewTarget(Node &newTarget)
|
||||
{
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NEW));
|
||||
|
||||
uint32_t begin = pos().begin;
|
||||
newTarget = null();
|
||||
|
||||
// |new| expects to look for an operand, so we will honor that.
|
||||
TokenKind next;
|
||||
if (!tokenStream.getToken(&next, TokenStream::Operand))
|
||||
return false;
|
||||
|
||||
// Don't unget the token, since lookahead cannot handle someone calling
|
||||
// getToken() with a different modifier. Callers should inspect currentToken().
|
||||
if (next != TOK_DOT)
|
||||
return true;
|
||||
|
||||
if (!tokenStream.getToken(&next))
|
||||
return false;
|
||||
if (next != TOK_NAME || tokenStream.currentName() != context->names().target) {
|
||||
report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
|
||||
"target", TokenKindToDesc(next));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pc->sc->isFunctionBox() || pc->sc->asFunctionBox()->isGenerator() ||
|
||||
pc->sc->asFunctionBox()->function()->isArrow())
|
||||
{
|
||||
reportWithOffset(ParseError, false, begin, JSMSG_BAD_NEWTARGET);
|
||||
return false;
|
||||
}
|
||||
|
||||
newTarget = handler.newNewTarget(TokenPos(begin, pos().end));
|
||||
return !!newTarget;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TokenKind tt,
|
||||
|
@ -590,6 +590,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
Node parenExprOrGeneratorComprehension(YieldHandling yieldHandling);
|
||||
Node exprInParens(InHandling inHandling, YieldHandling yieldHandling);
|
||||
|
||||
bool tryNewTarget(Node& newTarget);
|
||||
bool checkAndMarkSuperScope();
|
||||
|
||||
bool methodDefinition(YieldHandling yieldHandling, PropListType listType, Node propList,
|
||||
|
@ -193,6 +193,7 @@ class SyntaxParseHandler
|
||||
Node newClassMethodList(uint32_t begin) { return NodeGeneric; }
|
||||
Node newSuperProperty(JSAtom* atom, const TokenPos& pos) { return NodeGeneric; }
|
||||
Node newSuperElement(Node expr, const TokenPos& pos) { return NodeGeneric; }
|
||||
Node newNewTarget(const TokenPos& pos) { return NodeGeneric; }
|
||||
bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
|
||||
bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
|
||||
bool addShorthand(Node literal, Node name, Node expr) { return true; }
|
||||
@ -281,6 +282,9 @@ class SyntaxParseHandler
|
||||
MOZ_ASSERT(kind != PNK_VAR);
|
||||
return NodeGeneric;
|
||||
}
|
||||
Node newList(ParseNodeKind kind, uint32_t begin, JSOp op = JSOP_NOP) {
|
||||
return NodeGeneric;
|
||||
}
|
||||
Node newDeclarationList(ParseNodeKind kind, JSOp op = JSOP_NOP) {
|
||||
MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET ||
|
||||
kind == PNK_GLOBALCONST);
|
||||
|
6
js/src/jit-test/tests/basic/newTargetOSR.js
Normal file
6
js/src/jit-test/tests/basic/newTargetOSR.js
Normal file
@ -0,0 +1,6 @@
|
||||
function testOSRNewTarget(expected) {
|
||||
for (let i = 0; i < 1100; i++)
|
||||
assertEq(new.target, expected);
|
||||
}
|
||||
|
||||
new testOSRNewTarget(testOSRNewTarget);
|
@ -2678,6 +2678,43 @@ BaselineCompiler::emit_JSOP_SETARG()
|
||||
return emitFormalArgAccess(arg, /* get = */ false);
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_NEWTARGET()
|
||||
{
|
||||
MOZ_ASSERT(function());
|
||||
frame.syncStack(0);
|
||||
|
||||
// if (!isConstructing()) push(undefined)
|
||||
Label constructing, done;
|
||||
masm.branchTestPtr(Assembler::NonZero, frame.addressOfCalleeToken(),
|
||||
Imm32(CalleeToken_FunctionConstructing), &constructing);
|
||||
masm.moveValue(UndefinedValue(), R0);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&constructing);
|
||||
|
||||
// else push(argv[Max(numActualArgs, numFormalArgs)])
|
||||
Register argvLen = R0.scratchReg();
|
||||
|
||||
Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs());
|
||||
masm.loadPtr(actualArgs, argvLen);
|
||||
|
||||
Label actualArgsSufficient;
|
||||
|
||||
masm.branchPtr(Assembler::AboveOrEqual, argvLen, Imm32(function()->nargs()),
|
||||
&actualArgsSufficient);
|
||||
masm.move32(Imm32(function()->nargs()), argvLen);
|
||||
masm.bind(&actualArgsSufficient);
|
||||
|
||||
BaseValueIndex newTarget(BaselineFrameReg, argvLen, BaselineFrame::offsetOfArg(0));
|
||||
masm.loadValue(newTarget, R0);
|
||||
|
||||
masm.bind(&done);
|
||||
frame.push(R0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*ThrowUninitializedLexicalFn)(JSContext* cx);
|
||||
static const VMFunction ThrowUninitializedLexicalInfo =
|
||||
FunctionInfo<ThrowUninitializedLexicalFn>(jit::ThrowUninitializedLexical);
|
||||
|
@ -195,7 +195,8 @@ namespace jit {
|
||||
_(JSOP_CALLEE) \
|
||||
_(JSOP_SETRVAL) \
|
||||
_(JSOP_RETRVAL) \
|
||||
_(JSOP_RETURN)
|
||||
_(JSOP_RETURN) \
|
||||
_(JSOP_NEWTARGET)
|
||||
|
||||
class BaselineCompiler : public BaselineCompilerSpecific
|
||||
{
|
||||
|
@ -10074,5 +10074,40 @@ CodeGenerator::visitDebugger(LDebugger* ins)
|
||||
bailoutFrom(&bail, ins->snapshot());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitNewTarget(LNewTarget *ins)
|
||||
{
|
||||
ValueOperand output = GetValueOutput(ins);
|
||||
|
||||
// if (!isConstructing()) output = undefined
|
||||
Label constructing, done;
|
||||
Address calleeToken(masm.getStackPointer(), frameSize() + JitFrameLayout::offsetOfCalleeToken());
|
||||
masm.branchTestPtr(Assembler::NonZero, calleeToken,
|
||||
Imm32(CalleeToken_FunctionConstructing), &constructing);
|
||||
masm.moveValue(UndefinedValue(), output);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&constructing);
|
||||
|
||||
// else output = argv[Max(numActualArgs, numFormalArgs)]
|
||||
Register argvLen = output.scratchReg();
|
||||
|
||||
Address actualArgsPtr(masm.getStackPointer(), frameSize() + JitFrameLayout::offsetOfNumActualArgs());
|
||||
masm.loadPtr(actualArgsPtr, argvLen);
|
||||
|
||||
Label actualArgsSufficient;
|
||||
|
||||
size_t numFormalArgs = ins->mirRaw()->block()->info().funMaybeLazy()->nargs();
|
||||
masm.branchPtr(Assembler::AboveOrEqual, argvLen, Imm32(numFormalArgs),
|
||||
&actualArgsSufficient);
|
||||
masm.move32(Imm32(numFormalArgs), argvLen);
|
||||
masm.bind(&actualArgsSufficient);
|
||||
|
||||
BaseValueIndex newTarget(masm.getStackPointer(), argvLen, frameSize() + JitFrameLayout::offsetOfActualArgs());
|
||||
masm.loadValue(newTarget, output);
|
||||
|
||||
masm.bind(&done);
|
||||
}
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
@ -325,6 +325,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
void visitLexicalCheck(LLexicalCheck* ins);
|
||||
void visitThrowUninitializedLexical(LThrowUninitializedLexical* ins);
|
||||
void visitDebugger(LDebugger* ins);
|
||||
void visitNewTarget(LNewTarget* ins);
|
||||
|
||||
void visitCheckOverRecursed(LCheckOverRecursed* lir);
|
||||
void visitCheckOverRecursedFailure(CheckOverRecursedFailure* ool);
|
||||
|
@ -2007,6 +2007,9 @@ IonBuilder::inspectOpcode(JSOp op)
|
||||
// Just fall through to the unsupported bytecode case.
|
||||
break;
|
||||
|
||||
case JSOP_NEWTARGET:
|
||||
return jsop_newtarget();
|
||||
|
||||
#ifdef DEBUG
|
||||
case JSOP_PUSHBLOCKSCOPE:
|
||||
case JSOP_FRESHENBLOCKSCOPE:
|
||||
@ -9425,6 +9428,26 @@ IonBuilder::jsop_arguments()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_newtarget()
|
||||
{
|
||||
MOZ_ASSERT(info().funMaybeLazy());
|
||||
if (inliningDepth_ == 0) {
|
||||
MNewTarget* newTarget = MNewTarget::New(alloc());
|
||||
current->add(newTarget);
|
||||
current->push(newTarget);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!info().constructing()) {
|
||||
pushConstant(UndefinedValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
current->push(inlineCallInfo_->getNewTarget());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_rest()
|
||||
{
|
||||
|
@ -707,6 +707,7 @@ class IonBuilder
|
||||
bool jsop_getaliasedvar(ScopeCoordinate sc);
|
||||
bool jsop_setaliasedvar(ScopeCoordinate sc);
|
||||
bool jsop_debugger();
|
||||
bool jsop_newtarget();
|
||||
|
||||
/* Inlining. */
|
||||
|
||||
|
@ -1533,7 +1533,7 @@ class LJSCallInstructionHelper : public LCallInstructionHelper<Defs, Operands, T
|
||||
uint32_t numActualArgs() const {
|
||||
return mir()->numActualArgs();
|
||||
}
|
||||
|
||||
|
||||
bool isConstructing() const {
|
||||
return mir()->isConstructing();
|
||||
}
|
||||
@ -7053,6 +7053,12 @@ class LDebugger : public LCallInstructionHelper<0, 0, 2>
|
||||
}
|
||||
};
|
||||
|
||||
class LNewTarget : public LInstructionHelper<BOX_PIECES, 0, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(NewTarget)
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
@ -350,7 +350,8 @@
|
||||
_(LexicalCheck) \
|
||||
_(ThrowUninitializedLexical) \
|
||||
_(NurseryObject) \
|
||||
_(Debugger)
|
||||
_(Debugger) \
|
||||
_(NewTarget)
|
||||
|
||||
#if defined(JS_CODEGEN_X86)
|
||||
# include "jit/x86/LOpcodes-x86.h"
|
||||
|
@ -3560,6 +3560,13 @@ LIRGenerator::visitGetFrameArgument(MGetFrameArgument* ins)
|
||||
defineBox(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitNewTarget(MNewTarget* ins)
|
||||
{
|
||||
LNewTarget* lir = new(alloc()) LNewTarget();
|
||||
defineBox(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitSetFrameArgument(MSetFrameArgument* ins)
|
||||
{
|
||||
|
@ -297,6 +297,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
void visitThrowUninitializedLexical(MThrowUninitializedLexical* ins);
|
||||
void visitDebugger(MDebugger* ins);
|
||||
void visitNurseryObject(MNurseryObject* ins);
|
||||
void visitNewTarget(MNewTarget* ins);
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
|
@ -12027,6 +12027,28 @@ class MGetFrameArgument
|
||||
}
|
||||
};
|
||||
|
||||
class MNewTarget : public MNullaryInstruction
|
||||
{
|
||||
MNewTarget() : MNullaryInstruction() {
|
||||
setResultType(MIRType_Value);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(NewTarget)
|
||||
|
||||
static MNewTarget* New(TempAllocator& alloc) {
|
||||
return new(alloc) MNewTarget();
|
||||
}
|
||||
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
// This MIR instruction is used to set an argument value in the frame.
|
||||
class MSetFrameArgument
|
||||
: public MUnaryInstruction,
|
||||
|
@ -268,7 +268,8 @@ namespace jit {
|
||||
_(UnknownValue) \
|
||||
_(LexicalCheck) \
|
||||
_(ThrowUninitializedLexical) \
|
||||
_(Debugger)
|
||||
_(Debugger) \
|
||||
_(NewTarget)
|
||||
|
||||
// Forward declarations of MIR types.
|
||||
#define FORWARD_DECLARE(op) class M##op;
|
||||
|
@ -339,6 +339,7 @@ MSG_DEF(JSMSG_YIELD_IN_DEFAULT, 0, JSEXN_SYNTAXERR, "yield in default exp
|
||||
MSG_DEF(JSMSG_BAD_COLUMN_NUMBER, 0, JSEXN_RANGEERR, "column number out of range")
|
||||
MSG_DEF(JSMSG_COMPUTED_NAME_IN_PATTERN,0, JSEXN_SYNTAXERR, "computed property names aren't supported in this destructuring declaration")
|
||||
MSG_DEF(JSMSG_DEFAULT_IN_PATTERN, 0, JSEXN_SYNTAXERR, "destructuring defaults aren't supported in this destructuring declaration")
|
||||
MSG_DEF(JSMSG_BAD_NEWTARGET, 0, JSEXN_SYNTAXERR, "new.target only allowed in non-exotic functions")
|
||||
|
||||
// asm.js
|
||||
MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL, 1, JSEXN_TYPEERR, "asm.js type error: {0}")
|
||||
|
@ -55,6 +55,7 @@ UNIFIED_SOURCES += [
|
||||
'testMappedArrayBuffer.cpp',
|
||||
'testMutedErrors.cpp',
|
||||
'testNewObject.cpp',
|
||||
'testNewTargetInvokeConstructor.cpp',
|
||||
'testNullRoot.cpp',
|
||||
'testObjectEmulatingUndefined.cpp',
|
||||
'testOOM.cpp',
|
||||
|
25
js/src/jsapi-tests/testNewTargetInvokeConstructor.cpp
Normal file
25
js/src/jsapi-tests/testNewTargetInvokeConstructor.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
#include "jsapi-tests/tests.h"
|
||||
|
||||
BEGIN_TEST(testNewTargetInvokeConstructor)
|
||||
{
|
||||
JS::RootedValue func(cx);
|
||||
|
||||
EVAL("(function(expected) { if (expected !== new.target) throw new Error('whoops'); })",
|
||||
&func);
|
||||
|
||||
JS::AutoValueArray<1> args(cx);
|
||||
args[0].set(func);
|
||||
|
||||
JS::RootedValue rval(cx);
|
||||
|
||||
CHECK(JS::Construct(cx, func, args, &rval));
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testNewTargetInvokeConstructor)
|
@ -38,6 +38,7 @@ ASTDEF(AST_COMP_EXPR, "ComprehensionExpression", "comprehensi
|
||||
ASTDEF(AST_GENERATOR_EXPR, "GeneratorExpression", "generatorExpression")
|
||||
ASTDEF(AST_YIELD_EXPR, "YieldExpression", "yieldExpression")
|
||||
ASTDEF(AST_CLASS_EXPR, "ClassExpression", "classExpression")
|
||||
ASTDEF(AST_NEWTARGET_EXPR, "NewTargetExpression", "newTargetExpression")
|
||||
|
||||
ASTDEF(AST_EMPTY_STMT, "EmptyStatement", "emptyStatement")
|
||||
ASTDEF(AST_BLOCK_STMT, "BlockStatement", "blockStatement")
|
||||
|
@ -1274,6 +1274,8 @@ ExpressionDecompiler::decompilePC(jsbytecode* pc)
|
||||
// |this| could convert to a very long object initialiser, so cite it by
|
||||
// its keyword name.
|
||||
return write(js_this_str);
|
||||
case JSOP_NEWTARGET:
|
||||
return write("new.target");
|
||||
case JSOP_CALL:
|
||||
case JSOP_FUNCALL:
|
||||
return decompilePCForStackOperand(pc, -int32_t(GET_ARGC(pc) + 2)) &&
|
||||
|
@ -682,6 +682,8 @@ class NodeBuilder
|
||||
bool generatorExpression(HandleValue body, NodeVector& blocks, HandleValue filter,
|
||||
bool isLegacy, TokenPos* pos, MutableHandleValue dst);
|
||||
|
||||
bool newTargetExpression(TokenPos* pos, MutableHandleValue dst);
|
||||
|
||||
/*
|
||||
* declarations
|
||||
*/
|
||||
@ -1749,6 +1751,16 @@ NodeBuilder::classDefinition(bool expr, HandleValue name, HandleValue heritage,
|
||||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::newTargetExpression(TokenPos* pos, MutableHandleValue dst)
|
||||
{
|
||||
RootedValue cb(cx, callbacks[AST_NEWTARGET_EXPR]);
|
||||
if (!cb.isNull())
|
||||
return callback(cb, pos, dst);
|
||||
|
||||
return newNode(AST_NEWTARGET_EXPR, pos, dst);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
@ -3167,6 +3179,9 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
||||
case PNK_CLASS:
|
||||
return classDefinition(pn, true, dst);
|
||||
|
||||
case PNK_NEWTARGET:
|
||||
return builder.newTargetExpression(&pn->pn_pos, dst);
|
||||
|
||||
default:
|
||||
LOCAL_NOT_REACHED("unexpected expression type");
|
||||
}
|
||||
|
44
js/src/tests/ecma_6/Class/newTargetArgumentsIntact.js
Normal file
44
js/src/tests/ecma_6/Class/newTargetArgumentsIntact.js
Normal file
@ -0,0 +1,44 @@
|
||||
// Since we put new.target at the end of the arguments vector, ensrue that it
|
||||
// doesn't interact with the arguments object
|
||||
|
||||
var argsContent;
|
||||
|
||||
function argsWithNewTarget(foo) {
|
||||
assertEq(arguments.length, argsContent.length);
|
||||
for (let i = 0; i < arguments.length; i++)
|
||||
assertEq(arguments[i], argsContent[i]);
|
||||
let nt = new.target;
|
||||
|
||||
// Assigning to the arguments object shouldn't infect new.target, either
|
||||
arguments[arguments.length] = 42;
|
||||
assertEq(new.target, nt);
|
||||
}
|
||||
|
||||
// Test constructing invocations, with under and overflow
|
||||
argsContent = [];
|
||||
for (let i = 0; i < 100; i++)
|
||||
new argsWithNewTarget();
|
||||
|
||||
argsContent = [1];
|
||||
for (let i = 0; i < 100; i++)
|
||||
new argsWithNewTarget(1);
|
||||
|
||||
argsContent = [1,2,3];
|
||||
for (let i = 0; i < 100; i++)
|
||||
new argsWithNewTarget(1, 2, 3);
|
||||
|
||||
// Test spreadnew as well.
|
||||
argsContent = [];
|
||||
for (let i = 0; i < 100; i++)
|
||||
new argsWithNewTarget(...[]);
|
||||
|
||||
argsContent = [1];
|
||||
for (let i = 0; i < 100; i++)
|
||||
new argsWithNewTarget(...[1]);
|
||||
|
||||
argsContent = [1,2,3];
|
||||
for (let i = 0; i < 100; i++)
|
||||
new argsWithNewTarget(...[1,2,3]);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0,0,"OK");
|
16
js/src/tests/ecma_6/Class/newTargetBound.js
Normal file
16
js/src/tests/ecma_6/Class/newTargetBound.js
Normal file
@ -0,0 +1,16 @@
|
||||
function boundTarget(expected) {
|
||||
assertEq(new.target, expected);
|
||||
}
|
||||
|
||||
let bound = boundTarget.bind(undefined);
|
||||
|
||||
const TEST_ITERATIONS = 550;
|
||||
|
||||
for (let i = 0; i < TEST_ITERATIONS; i++)
|
||||
bound(undefined);
|
||||
|
||||
for (let i = 0; i < TEST_ITERATIONS; i++)
|
||||
new bound(boundTarget);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0,0,"OK");
|
11
js/src/tests/ecma_6/Class/newTargetContext.js
Normal file
11
js/src/tests/ecma_6/Class/newTargetContext.js
Normal file
@ -0,0 +1,11 @@
|
||||
// new.target is valid inside Function() invocations
|
||||
var func = new Function("new.target");
|
||||
|
||||
// new.target is invalid inside eval, even (for now!) eval inside a function.
|
||||
assertThrowsInstanceOf(() => eval('new.target'), SyntaxError);
|
||||
|
||||
function evalInFunction() { eval('new.target'); }
|
||||
assertThrowsInstanceOf(() => evalInFunction(), SyntaxError);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0,0,"OK");
|
7
js/src/tests/ecma_6/Class/newTargetDVG.js
Normal file
7
js/src/tests/ecma_6/Class/newTargetDVG.js
Normal file
@ -0,0 +1,7 @@
|
||||
function thunk() {
|
||||
new.target();
|
||||
}
|
||||
assertThrownErrorContains(thunk, "new.target");
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0, "OK");
|
47
js/src/tests/ecma_6/Class/newTargetDirectInvoke.js
Normal file
47
js/src/tests/ecma_6/Class/newTargetDirectInvoke.js
Normal file
@ -0,0 +1,47 @@
|
||||
// Note that this will also test new.target in ion inlines. When the toplevel
|
||||
// script is compiled, assertNewTarget will be inlined.
|
||||
function assertNewTarget(expected, unused) { assertEq(new.target, expected); }
|
||||
|
||||
// Test non-constructing invocations, with arg underflow, overflow, and correct
|
||||
// numbers
|
||||
for (let i = 0; i < 100; i++)
|
||||
assertNewTarget(undefined, null);
|
||||
|
||||
for (let i = 0; i < 100; i++)
|
||||
assertNewTarget(undefined);
|
||||
|
||||
for (let i = 0; i < 100; i++)
|
||||
assertNewTarget(undefined, null, 1);
|
||||
|
||||
// Test spread-call
|
||||
for (let i = 0; i < 100; i++)
|
||||
assertNewTarget(...[undefined]);
|
||||
|
||||
for (let i = 0; i < 100; i++)
|
||||
assertNewTarget(...[undefined, null]);
|
||||
|
||||
for (let i = 0; i < 100; i++)
|
||||
assertNewTarget(...[undefined, null, 1]);
|
||||
|
||||
// Test constructing invocations, again with under and overflow
|
||||
for (let i = 0; i < 100; i++)
|
||||
new assertNewTarget(assertNewTarget, null);
|
||||
|
||||
for (let i = 0; i < 100; i++)
|
||||
new assertNewTarget(assertNewTarget);
|
||||
|
||||
for (let i = 0; i < 100; i++)
|
||||
new assertNewTarget(assertNewTarget, null, 1);
|
||||
|
||||
// Test spreadnew as well.
|
||||
for (let i = 0; i < 100; i++)
|
||||
new assertNewTarget(...[assertNewTarget]);
|
||||
|
||||
for (let i = 0; i < 100; i++)
|
||||
new assertNewTarget(...[assertNewTarget, null]);
|
||||
|
||||
for (let i = 0; i < 100; i++)
|
||||
new assertNewTarget(...[assertNewTarget, null, 1]);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0,0,"OK");
|
52
js/src/tests/ecma_6/Class/newTargetMethods.js
Normal file
52
js/src/tests/ecma_6/Class/newTargetMethods.js
Normal file
@ -0,0 +1,52 @@
|
||||
// Just like newTargetDirectInvoke, except to prove it works in functions
|
||||
// defined with method syntax as well. Note that methods, getters, and setters
|
||||
// are not constructible.
|
||||
|
||||
let ol = {
|
||||
olTest(arg) { assertEq(arg, 4); assertEq(new.target, undefined); },
|
||||
get ol() { assertEq(new.target, undefined); },
|
||||
set ol(arg) { assertEq(arg, 4); assertEq(new.target, undefined); }
|
||||
}
|
||||
|
||||
class cl {
|
||||
constructor() { assertEq(new.target, cl); }
|
||||
clTest(arg) { assertEq(arg, 4); assertEq(new.target, undefined); }
|
||||
get cl() { assertEq(new.target, undefined); }
|
||||
set cl(arg) { assertEq(arg, 4); assertEq(new.target, undefined); }
|
||||
|
||||
static staticclTest(arg) { assertEq(arg, 4); assertEq(new.target, undefined); }
|
||||
static get staticcl() { assertEq(new.target, undefined); }
|
||||
static set staticcl(arg) { assertEq(arg, 4); assertEq(new.target, undefined); }
|
||||
}
|
||||
|
||||
const TEST_ITERATIONS = 150;
|
||||
|
||||
for (let i = 0; i < TEST_ITERATIONS; i++)
|
||||
ol.olTest(4);
|
||||
for (let i = 0; i < TEST_ITERATIONS; i++)
|
||||
ol.ol;
|
||||
for (let i = 0; i < TEST_ITERATIONS; i++)
|
||||
ol.ol = 4;
|
||||
|
||||
for (let i = 0; i < TEST_ITERATIONS; i++)
|
||||
cl.staticclTest(4);
|
||||
for (let i = 0; i < TEST_ITERATIONS; i++)
|
||||
cl.staticcl;
|
||||
for (let i = 0; i < TEST_ITERATIONS; i++)
|
||||
cl.staticcl = 4;
|
||||
|
||||
for (let i = 0; i < TEST_ITERATIONS; i++)
|
||||
new cl();
|
||||
|
||||
let clInst = new cl();
|
||||
|
||||
for (let i = 0; i < TEST_ITERATIONS; i++)
|
||||
clInst.clTest(4);
|
||||
for (let i = 0; i < TEST_ITERATIONS; i++)
|
||||
clInst.cl;
|
||||
for (let i = 0; i < TEST_ITERATIONS; i++)
|
||||
clInst.cl = 4;
|
||||
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0,0,"OK");
|
@ -12,3 +12,14 @@ function classesEnabled() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function assertThrownErrorContains(thunk, substr) {
|
||||
try {
|
||||
thunk();
|
||||
} catch (e) {
|
||||
if (e.message.indexOf(substr) !== -1)
|
||||
return;
|
||||
throw new Error("Expected error containing " + substr + ", got " + e);
|
||||
}
|
||||
throw new Error("Expected error containing " + substr + ", no exception thrown");
|
||||
}
|
||||
|
@ -2,17 +2,6 @@
|
||||
|
||||
var test = `
|
||||
|
||||
function assertThrownErrorContains(thunk, substr) {
|
||||
try {
|
||||
thunk();
|
||||
throw new Error("Expected error containing " + substr + ", no exception thrown");
|
||||
} catch (e) {
|
||||
if (e.message.indexOf(substr) !== -1)
|
||||
return;
|
||||
throw new Error("Expected error containing " + substr + ", got " + e);
|
||||
}
|
||||
}
|
||||
|
||||
class testNonExistent {
|
||||
constructor() {
|
||||
super["prop"]();
|
||||
|
@ -66,6 +66,11 @@ function assertStmt(src, patt) {
|
||||
assertBlockStmt(src, patt);
|
||||
}
|
||||
|
||||
function assertInFunctionExpr(src, patt) {
|
||||
assertLocalExpr(src, patt);
|
||||
assertBlockExpr(src, patt);
|
||||
}
|
||||
|
||||
function assertExpr(src, patt) {
|
||||
assertLocalExpr(src, patt);
|
||||
assertGlobalExpr(src, patt);
|
||||
|
@ -170,6 +170,9 @@ function arrowExpr(args, body) {
|
||||
body: body });
|
||||
}
|
||||
|
||||
function newTarget() {
|
||||
return Pattern({ type: "NewTargetExpression" });
|
||||
}
|
||||
function unExpr(op, arg) {
|
||||
return Pattern({ type: "UnaryExpression", operator: op, argument: arg });
|
||||
}
|
||||
|
41
js/src/tests/js1_8_5/reflect-parse/newTarget.js
Normal file
41
js/src/tests/js1_8_5/reflect-parse/newTarget.js
Normal file
@ -0,0 +1,41 @@
|
||||
// |reftest| skip-if(!xulRuntime.shell)
|
||||
function testNewTarget() {
|
||||
|
||||
// new.target is currently valid inside any non-arrow, non-generator function
|
||||
assertInFunctionExpr("new.target", newTarget());
|
||||
|
||||
// even with gratuitous whitespace.
|
||||
assertInFunctionExpr(`new.
|
||||
target`, newTarget());
|
||||
|
||||
// invalid in top-level scripts
|
||||
assertError("new.target", SyntaxError);
|
||||
|
||||
// invalid (for now!) in any arrow function
|
||||
assertError("function foo() { (() => new.target) }", SyntaxError);
|
||||
assertError("(() => new.target))", SyntaxError);
|
||||
|
||||
// invalid (for now!) in generators
|
||||
assertError("function *foo() { new.target; }", SyntaxError);
|
||||
|
||||
// new.target is a member expression. You should be able to call, invoke, or
|
||||
// access properties of it.
|
||||
assertInFunctionExpr("new.target.foo", dotExpr(newTarget(), ident("foo")));
|
||||
assertInFunctionExpr("new.target[\"foo\"]", memExpr(newTarget(), literal("foo")));
|
||||
|
||||
assertInFunctionExpr("new.target()", callExpr(newTarget(), []));
|
||||
assertInFunctionExpr("new new.target()", newExpr(newTarget(), []));
|
||||
|
||||
// assignment to newTarget is an error
|
||||
assertError("new.target = 4", SyntaxError);
|
||||
|
||||
// only new.target is a valid production, no shorn names or other names
|
||||
assertError("new.", SyntaxError);
|
||||
assertError("new.foo", SyntaxError);
|
||||
assertError("new.targe", SyntaxError);
|
||||
|
||||
// obj.new.target is still a member expression
|
||||
assertExpr("obj.new.target", dotExpr(dotExpr(ident("obj"), ident("new")), ident("target")));
|
||||
}
|
||||
|
||||
runtest(testNewTarget);
|
@ -196,6 +196,7 @@
|
||||
macro(StructType, StructType, "StructType") \
|
||||
macro(style, style, "style") \
|
||||
macro(super, super, "super") \
|
||||
macro(target, target, "target") \
|
||||
macro(test, test, "test") \
|
||||
macro(throw, throw_, "throw") \
|
||||
macro(timestamp, timestamp, "timestamp") \
|
||||
|
@ -1950,7 +1950,6 @@ CASE(EnableInterruptsPseudoOpcode)
|
||||
/* Various 1-byte no-ops. */
|
||||
CASE(JSOP_NOP)
|
||||
CASE(JSOP_UNUSED2)
|
||||
CASE(JSOP_UNUSED148)
|
||||
CASE(JSOP_BACKPATCH)
|
||||
CASE(JSOP_UNUSED150)
|
||||
CASE(JSOP_UNUSED161)
|
||||
@ -3963,6 +3962,10 @@ CASE(JSOP_SUPERBASE)
|
||||
}
|
||||
END_CASE(JSOP_SUPERBASE)
|
||||
|
||||
CASE(JSOP_NEWTARGET)
|
||||
PUSH_COPY(REGS.fp()->newTarget());
|
||||
END_CASE(JSOP_NEWTARGET)
|
||||
|
||||
DEFAULT()
|
||||
{
|
||||
char numBuf[12];
|
||||
|
@ -1514,8 +1514,15 @@
|
||||
* Stack: obj, val => obj
|
||||
*/ \
|
||||
macro(JSOP_INITHIDDENPROP, 147,"inithiddenprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
|
||||
/* Unused. */ \
|
||||
macro(JSOP_UNUSED148, 148,"unused148", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
/*
|
||||
* Push "new.target"
|
||||
*
|
||||
* Category: Variables and Scopes
|
||||
* Type: Arguments
|
||||
* Operands:
|
||||
* Stack: => new.target
|
||||
*/ \
|
||||
macro(JSOP_NEWTARGET, 148, "newtarget", NULL, 1, 0, 1, JOF_BYTE) \
|
||||
\
|
||||
/*
|
||||
* Placeholder opcode used during bytecode generation. This never
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "jsfun.h"
|
||||
#include "jsscript.h"
|
||||
#include "jsutil.h"
|
||||
|
||||
#include "asmjs/AsmJSFrameIterator.h"
|
||||
#include "jit/JitFrameIterator.h"
|
||||
@ -739,6 +740,23 @@ class InterpreterFrame
|
||||
return CallReceiverFromArgv(argv());
|
||||
}
|
||||
|
||||
/*
|
||||
* New Target
|
||||
*
|
||||
* Only function frames have a meaningful newTarget. An eval frame in a
|
||||
* function will have a copy of the newTarget of the enclosing function
|
||||
* frame.
|
||||
*/
|
||||
Value newTarget() const {
|
||||
// new.target in eval() NYI.
|
||||
MOZ_ASSERT(isNonEvalFunctionFrame());
|
||||
if (isConstructing()) {
|
||||
unsigned pushedArgs = Max(numFormalArgs(), numActualArgs());
|
||||
return argv()[pushedArgs];
|
||||
}
|
||||
return UndefinedValue();
|
||||
}
|
||||
|
||||
/*
|
||||
* Frame compartment
|
||||
*
|
||||
|
@ -29,11 +29,11 @@ namespace js {
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 288;
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 289;
|
||||
static const uint32_t XDR_BYTECODE_VERSION =
|
||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||
|
||||
static_assert(JSErr_Limit == 398,
|
||||
static_assert(JSErr_Limit == 399,
|
||||
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
|
||||
"removed MSG_DEFs from js.msg, you should increment "
|
||||
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
|
||||
|
Loading…
Reference in New Issue
Block a user