aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-09-04 14:49:49 +0200
committerLars Knoll <lars.knoll@qt.io>2018-09-07 10:31:45 +0000
commitee3d935a8b45576a237c74fd453fb0810f30f574 (patch)
tree4de5e0829d6159f9020f8abd549f59fce94a6236
parentd71df8db162ff8b4d6bd1833c088024b8870b02c (diff)
Fix new.target access from eval()
Change-Id: I1855eb303225d1784b019f8eebab0ad8bf2cdf5e Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r--src/qml/compiler/qv4codegen.cpp9
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp7
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h1
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp45
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h1
-rw-r--r--tests/auto/qml/ecmascripttests/TestExpectations3
6 files changed, 56 insertions, 10 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index d61f79f0fc..cbe935c5cd 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -2143,10 +2143,15 @@ bool Codegen::visit(FieldMemberExpression *ast)
if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(ast->base)) {
if (id->name == QLatin1String("new")) {
// new.target
- if (ast->name != QLatin1String("target")) {
- throwSyntaxError(ast->identifierToken, QLatin1String("Expected 'target' after 'new.'."));
+ Q_ASSERT(ast->name == QLatin1String("target"));
+
+ if (_context->isArrowFunction || _context->contextType == ContextType::Eval) {
+ Reference r = referenceForName(QStringLiteral("new.target"), false);
+ r.isReadonly = true;
+ _expr.setResult(r);
return false;
}
+
Reference r = Reference::fromStackSlot(this, CallData::NewTarget);
_expr.setResult(r);
return false;
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
index 1b4c084ab4..551467f95c 100644
--- a/src/qml/compiler/qv4compilercontext.cpp
+++ b/src/qml/compiler/qv4compilercontext.cpp
@@ -248,6 +248,13 @@ void Context::emitBlockHeader(Codegen *codegen)
Codegen::Reference r = codegen->referenceForName(QStringLiteral("this"), true);
r.storeConsumeAccumulator();
}
+ if (innerFunctionAccessesNewTarget) {
+ Instruction::LoadReg load;
+ load.reg = CallData::NewTarget;
+ bytecodeGenerator->addInstruction(load);
+ Codegen::Reference r = codegen->referenceForName(QStringLiteral("new.target"), true);
+ r.storeConsumeAccumulator();
+ }
if (contextType == ContextType::Global || (contextType == ContextType::Eval && !isStrict)) {
// variables in global code are properties of the global context object, not locals as with other functions.
diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h
index 8713b0a188..51b0595d0e 100644
--- a/src/qml/compiler/qv4compilercontext_p.h
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -202,6 +202,7 @@ struct Context {
bool isGenerator = false;
bool usesThis = false;
bool innerFunctionAccessesThis = false;
+ bool innerFunctionAccessesNewTarget = false;
bool hasTry = false;
bool returnsClosure = false;
mutable bool argumentsCanEscape = false;
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
index dafb1c360d..4e18af2227 100644
--- a/src/qml/compiler/qv4compilerscanfunctions.cpp
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -417,14 +417,42 @@ bool ScanFunctions::visit(TemplateLiteral *ast)
bool ScanFunctions::visit(SuperLiteral *)
{
Context *c = _context;
- while (c && (c->contextType != ContextType::Function || c->isArrowFunction))
+ bool needContext = false;
+ while (c && (c->contextType == ContextType::Block || c->isArrowFunction)) {
+ needContext |= c->isArrowFunction;
c = c->parent;
+ }
- if (c)
- c->requiresExecutionContext = true;
+ c->requiresExecutionContext |= needContext;
return false;
}
+
+bool ScanFunctions::visit(FieldMemberExpression *ast)
+{
+ if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(ast->base)) {
+ if (id->name == QLatin1String("new")) {
+ // new.target
+ if (ast->name != QLatin1String("target")) {
+ _cg->throwSyntaxError(ast->identifierToken, QLatin1String("Expected 'target' after 'new.'."));
+ return false;
+ }
+ Context *c = _context;
+ bool needContext = false;
+ while (c->contextType == ContextType::Block || c->isArrowFunction) {
+ needContext |= c->isArrowFunction;
+ c = c->parent;
+ }
+ c->requiresExecutionContext |= needContext;
+ c->innerFunctionAccessesNewTarget |= needContext;
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
bool ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName)
{
if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
@@ -735,6 +763,7 @@ void ScanFunctions::calcEscapingVariables()
}
if (inner->hasDirectEval) {
inner->hasDirectEval = false;
+ inner->innerFunctionAccessesNewTarget = true;
if (!inner->isStrict) {
Context *c = inner;
while (c->contextType == ContextType::Block) {
@@ -742,8 +771,7 @@ void ScanFunctions::calcEscapingVariables()
}
Q_ASSERT(c);
c->hasDirectEval = true;
- if (!c->isStrict)
- c->innerFunctionAccessesThis = true;
+ c->innerFunctionAccessesThis = true;
}
Context *c = inner;
while (c) {
@@ -773,6 +801,13 @@ void ScanFunctions::calcEscapingVariables()
auto m = c->members.find(QStringLiteral("this"));
m->canEscape = true;
}
+ if (c->innerFunctionAccessesNewTarget) {
+ // add an escaping 'new.target' variable
+ c->addLocalVar(QStringLiteral("new.target"), Context::VariableDefinition, VariableScope::Let);
+ c->requiresExecutionContext = true;
+ auto m = c->members.find(QStringLiteral("new.target"));
+ m->canEscape = true;
+ }
if (c->allVarsEscape && c->contextType == ContextType::Block && c->members.isEmpty())
c->allVarsEscape = false;
if (c->contextType == ContextType::Global || (!c->isStrict && c->contextType == ContextType::Eval) || m->debugMode)
diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h
index 2e016b25b1..bb07540ec9 100644
--- a/src/qml/compiler/qv4compilerscanfunctions_p.h
+++ b/src/qml/compiler/qv4compilerscanfunctions_p.h
@@ -116,6 +116,7 @@ protected:
bool visit(AST::FunctionExpression *ast) override;
bool visit(AST::TemplateLiteral *ast) override;
bool visit(AST::SuperLiteral *) override;
+ bool visit(AST::FieldMemberExpression *) override;
bool enterFunction(AST::FunctionExpression *ast, bool enterName);
diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations
index fe2e9835e1..f0bde2132b 100644
--- a/tests/auto/qml/ecmascripttests/TestExpectations
+++ b/tests/auto/qml/ecmascripttests/TestExpectations
@@ -578,7 +578,6 @@ language/computed-property-names/class/static/method-string.js fails
language/computed-property-names/class/static/method-symbol.js fails
language/eval-code/direct/new.target.js fails
language/eval-code/direct/new.target-arrow.js fails
-language/eval-code/direct/new.target-fn.js fails
language/eval-code/direct/non-definable-function-with-function.js sloppyFails
language/eval-code/direct/non-definable-function-with-variable.js sloppyFails
language/eval-code/direct/non-definable-global-function.js sloppyFails
@@ -617,8 +616,6 @@ language/eval-code/indirect/var-env-var-init-global-new.js strictFails
language/eval-code/indirect/var-env-var-non-strict.js strictFails
language/expressions/arrow-function/dflt-params-ref-later.js fails
language/expressions/arrow-function/dflt-params-ref-self.js fails
-language/expressions/arrow-function/lexical-new.target-closure-returned.js fails
-language/expressions/arrow-function/lexical-new.target.js fails
language/expressions/arrow-function/lexical-super-call-from-within-constructor.js fails
language/expressions/arrow-function/lexical-supercall-from-immediately-invoked-arrow.js fails
language/expressions/arrow-function/scope-body-lex-distinct.js sloppyFails