Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (187107 => 187108)
--- trunk/Source/_javascript_Core/ChangeLog 2015-07-21 17:14:00 UTC (rev 187107)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-07-21 18:18:42 UTC (rev 187108)
@@ -1,3 +1,40 @@
+2015-07-21 Keith Miller <keith_mil...@apple.com>
+
+ Add support for the new.target syntax.
+ https://bugs.webkit.org/show_bug.cgi?id=147051
+
+ Reviewed by Yusuke Suzuki.
+
+ Add support for new.target. Essentially the implementation is, before constructor calls,
+ the target of a "new" is placed where "this" noramlly goes in the calling convention.
+ Then in the constructor before object is initialized we move the target of the "new"
+ into a local variable.
+
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::BytecodeGenerator):
+ * bytecompiler/NodesCodegen.cpp:
+ (JSC::NewTargetNode::emitBytecode):
+ * parser/ASTBuilder.h:
+ (JSC::ASTBuilder::newTargetExpr):
+ * parser/NodeConstructors.h:
+ (JSC::NewTargetNode::NewTargetNode):
+ * parser/Nodes.h:
+ * parser/Parser.cpp:
+ (JSC::Parser<LexerType>::parseMemberExpression):
+ * parser/SyntaxChecker.h:
+ (JSC::SyntaxChecker::newTargetExpr):
+ * runtime/CommonIdentifiers.h:
+ * tests/stress/new-target.js: Added.
+ (test):
+ (call):
+ (Constructor.subCall):
+ (Constructor.SubConstructor):
+ (Constructor):
+ (noAssign):
+ (doWeirdThings):
+ (SuperClass):
+ (SubClass):
+
2015-07-20 Saam barati <saambara...@gmail.com>
"let" scoping introduced incoherent story about symbol table cloning
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (187107 => 187108)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2015-07-21 17:14:00 UTC (rev 187107)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2015-07-21 18:18:42 UTC (rev 187108)
@@ -500,10 +500,10 @@
}
}
+ m_newTargetRegister = addVar();
if (isConstructor()) {
+ emitMove(m_newTargetRegister, &m_thisRegister);
if (constructorKind() == ConstructorKind::Derived) {
- m_newTargetRegister = addVar();
- emitMove(m_newTargetRegister, &m_thisRegister);
emitMoveEmptyValue(&m_thisRegister);
} else
emitCreateThis(&m_thisRegister);
Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (187107 => 187108)
--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2015-07-21 17:14:00 UTC (rev 187107)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2015-07-21 18:18:42 UTC (rev 187108)
@@ -185,6 +185,16 @@
return generator.emitGetById(generator.newTemporary(), homeObject.get(), generator.propertyNames().underscoreProto);
}
+// ------------------------------ NewTargetNode ----------------------------------
+
+RegisterID* NewTargetNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
+{
+ if (dst == generator.ignoredResult())
+ return nullptr;
+
+ return generator.moveToDestinationIfNeeded(dst, generator.newTarget());
+}
+
// ------------------------------ ResolveNode ----------------------------------
bool ResolveNode::isPure(BytecodeGenerator& generator) const
Modified: trunk/Source/_javascript_Core/parser/ASTBuilder.h (187107 => 187108)
--- trunk/Source/_javascript_Core/parser/ASTBuilder.h 2015-07-21 17:14:00 UTC (rev 187107)
+++ trunk/Source/_javascript_Core/parser/ASTBuilder.h 2015-07-21 18:18:42 UTC (rev 187108)
@@ -177,6 +177,10 @@
{
return new (m_parserArena) SuperNode(location);
}
+ ExpressionNode* newTargetExpr(const JSTokenLocation location)
+ {
+ return new (m_parserArena) NewTargetNode(location);
+ }
ExpressionNode* createResolve(const JSTokenLocation& location, const Identifier* ident, const JSTextPosition& start)
{
if (m_vm->propertyNames->arguments == *ident)
Modified: trunk/Source/_javascript_Core/parser/NodeConstructors.h (187107 => 187108)
--- trunk/Source/_javascript_Core/parser/NodeConstructors.h 2015-07-21 17:14:00 UTC (rev 187107)
+++ trunk/Source/_javascript_Core/parser/NodeConstructors.h 2015-07-21 18:18:42 UTC (rev 187108)
@@ -170,6 +170,11 @@
{
}
+ inline NewTargetNode::NewTargetNode(const JSTokenLocation& location)
+ : ExpressionNode(location)
+ {
+ }
+
inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifier& ident, const JSTextPosition& start)
: ExpressionNode(location)
, m_ident(ident)
Modified: trunk/Source/_javascript_Core/parser/Nodes.h (187107 => 187108)
--- trunk/Source/_javascript_Core/parser/Nodes.h 2015-07-21 17:14:00 UTC (rev 187107)
+++ trunk/Source/_javascript_Core/parser/Nodes.h 2015-07-21 18:18:42 UTC (rev 187108)
@@ -547,6 +547,14 @@
virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
};
+ class NewTargetNode final : public ExpressionNode {
+ public:
+ NewTargetNode(const JSTokenLocation&);
+
+ private:
+ virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+ };
+
class ResolveNode : public ExpressionNode {
public:
ResolveNode(const JSTokenLocation&, const Identifier&, const JSTextPosition& start);
Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (187107 => 187108)
--- trunk/Source/_javascript_Core/parser/Parser.cpp 2015-07-21 17:14:00 UTC (rev 187107)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp 2015-07-21 18:18:42 UTC (rev 187108)
@@ -2883,11 +2883,28 @@
bool baseIsSuper = false;
#endif
+ bool baseIsNewTarget = false;
+ if (newCount && match(DOT)) {
+ next();
+ if (match(IDENT)) {
+ const Identifier* ident = m_token.m_data.ident;
+ if (m_vm->propertyNames->target == *ident) {
+ semanticFailIfFalse(currentScope()->isFunction(), "new.target is only valid inside functions");
+ baseIsNewTarget = true;
+ base = context.newTargetExpr(location);
+ newCount--;
+ next();
+ } else
+ failWithMessage("\"new.\" can only followed with target");
+ } else
+ failDueToUnexpectedToken();
+ }
+
if (baseIsSuper) {
base = context.superExpr(location);
next();
currentScope()->setNeedsSuperBinding();
- } else
+ } else if (!baseIsNewTarget)
base = parsePrimaryExpression(context);
failIfFalse(base, "Cannot parse base _expression_");
Modified: trunk/Source/_javascript_Core/parser/SyntaxChecker.h (187107 => 187108)
--- trunk/Source/_javascript_Core/parser/SyntaxChecker.h 2015-07-21 17:14:00 UTC (rev 187107)
+++ trunk/Source/_javascript_Core/parser/SyntaxChecker.h 2015-07-21 18:18:42 UTC (rev 187108)
@@ -74,7 +74,7 @@
ThisExpr, NullExpr, BoolExpr, RegExpExpr, ObjectLiteralExpr,
FunctionExpr, ClassExpr, SuperExpr, BracketExpr, DotExpr, CallExpr,
NewExpr, PreExpr, PostExpr, UnaryExpr, BinaryExpr,
- ConditionalExpr, AssignmentExpr, TypeofExpr,
+ ConditionalExpr, AssignmentExpr, TypeofExpr, NewTargetExpr,
DeleteExpr, ArrayLiteralExpr, BindingDestructuring,
ArrayDestructuring, ObjectDestructuring, SourceElementsResult,
FunctionBodyResult, SpreadExpr, ArgumentsResult,
@@ -154,6 +154,7 @@
ExpressionType createVoid(const JSTokenLocation&, ExpressionType) { return UnaryExpr; }
ExpressionType thisExpr(const JSTokenLocation&, ThisTDZMode) { return ThisExpr; }
ExpressionType superExpr(const JSTokenLocation&) { return SuperExpr; }
+ ExpressionType newTargetExpr(const JSTokenLocation&) { return NewTargetExpr; }
ExpressionType createResolve(const JSTokenLocation&, const Identifier*, int) { return ResolveExpr; }
ExpressionType createObjectLiteral(const JSTokenLocation&) { return ObjectLiteralExpr; }
ExpressionType createObjectLiteral(const JSTokenLocation&, int) { return ObjectLiteralExpr; }
Modified: trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h (187107 => 187108)
--- trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h 2015-07-21 17:14:00 UTC (rev 187107)
+++ trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h 2015-07-21 18:18:42 UTC (rev 187108)
@@ -166,6 +166,7 @@
macro(sourceCode) \
macro(stack) \
macro(subarray) \
+ macro(target) \
macro(test) \
macro(then) \
macro(toExponential) \
Added: trunk/Source/_javascript_Core/tests/stress/new-target.js (0 => 187108)
--- trunk/Source/_javascript_Core/tests/stress/new-target.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/new-target.js 2015-07-21 18:18:42 UTC (rev 187108)
@@ -0,0 +1,79 @@
+var passed = true;
+try {
+ eval("new.target;");
+ passed = false;
+} catch(e) {}
+
+test(passed, true, "new.target cannot be called in global scope");
+
+// Test without class syntax
+
+function test(result, expected, message) {
+ if (result !== expected)
+ throw "Error: " + message + ". was: " + result + " wanted: " + expected;
+}
+
+function call() {
+ test(new.target, undefined, "new.target should be undefined in a function call");
+}
+call();
+
+function Constructor() {
+ test(new.target, Constructor, "new.target should be the same as constructor");
+ function subCall() {
+ test(new.target, undefined, "new.target should be undefined in a sub function call");
+ }
+ subCall();
+
+ function SubConstructor() {
+ test(new.target, SubConstructor, "new.target should be subConstructor");
+ }
+ new SubConstructor();
+
+}
+new Constructor();
+
+function noAssign() {
+ new.target = 1;
+}
+
+try {
+ new noAssign();
+ passed = false;
+} catch(e) { }
+try {
+ noAssign();
+ passed = false;
+} catch(e) { }
+
+test(passed, true, "new.target should not be a reference");
+
+// This is mostly to test that calling new on new.target deos the right thing.
+function doWeirdThings(arg) {
+ if (new.target) {
+ if (arg)
+ this.value = new.target(1);
+ else
+ this.value = new new.target(true);
+ } else
+ return arg;
+}
+
+test(new doWeirdThings(false).value.value, 1, "calling new on new.target did something weird");
+
+// Test with class syntax
+
+class SuperClass {
+ constructor() {
+ this.target = new.target;
+ }
+}
+
+class SubClass extends SuperClass {
+ constructor() {
+ super();
+ }
+}
+
+test(new SuperClass().target, SuperClass, "new.target should be the same as the class constructor");
+test(new SubClass().target, SubClass, "new.target should not change when passed through super()");