diff options
author | Lars Knoll <lars.knoll@qt.io> | 2018-06-22 00:09:31 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2018-06-26 10:04:08 +0000 |
commit | 046d1c5db44f409c67244bd70b13077cc03219b2 (patch) | |
tree | fae07993dd3c6be0c4b020d20bf49c01c35cb25e /src/qml/compiler | |
parent | 5faf2e9a693d10e1e689c42deec911083a35ddb2 (diff) |
throw a type error when trying to write to a const variable
This makes them really const. The codegen needs some smaller changes
to ensure that writing to the variable when it's being defined is
allowed.
Change-Id: I781b4bc9c0e0397b9d00cad3daf758a062c17600
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml/compiler')
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 71 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen_p.h | 9 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilercontext.cpp | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilercontext_p.h | 1 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilerscanfunctions.cpp | 4 |
5 files changed, 55 insertions, 33 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index a7517fcde9..2a7c491bd6 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -152,7 +152,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) return _expr.result(); #ifndef V4_BOOTSTRAP - if (expr.isConst()) { + if (expr.isConstant()) { auto v = Value::fromReturnedValue(expr.constant); if (v.isNumber()) { switch (op) { @@ -459,7 +459,7 @@ void Codegen::variableDeclaration(PatternElement *ast) if (!ast->initializer) return; - initializeAndDestructureBindingElement(ast, Reference()); + initializeAndDestructureBindingElement(ast, Reference(), /*isDefinition*/ true); } void Codegen::variableDeclarationList(VariableDeclarationList *ast) @@ -482,12 +482,14 @@ Codegen::Reference Codegen::targetForPatternElement(AST::PatternElement *p) return lhs; } -void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &base) +void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &base, bool isDefinition) { Q_ASSERT(e->type == AST::PatternElement::Binding || e->type == AST::PatternElement::RestElement); RegisterScope scope(this); Reference baseRef = (base.isAccumulator()) ? base.storeOnStack() : base; Reference varToStore = targetForPatternElement(e); + if (isDefinition) + varToStore.isReferenceToConst = false; if (hasError) return; @@ -533,9 +535,9 @@ void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, con if (!varToStore.isStackSlot()) varToStore = varToStore.storeOnStack(); if (PatternElementList *l = e->elementList()) { - destructureElementList(varToStore, l); + destructureElementList(varToStore, l, isDefinition); } else if (PatternPropertyList *p = e->propertyList()) { - destructurePropertyList(varToStore, p); + destructurePropertyList(varToStore, p, isDefinition); } else if (e->bindingTarget) { // empty binding pattern. For spec compatibility, try to coerce the argument to an object varToStore.loadInAccumulator(); @@ -562,7 +564,7 @@ Codegen::Reference Codegen::referenceForPropertyName(const Codegen::Reference &o return property; } -void Codegen::destructurePropertyList(const Codegen::Reference &object, PatternPropertyList *bindingList) +void Codegen::destructurePropertyList(const Codegen::Reference &object, PatternPropertyList *bindingList, bool isDefinition) { RegisterScope scope(this); @@ -572,13 +574,13 @@ void Codegen::destructurePropertyList(const Codegen::Reference &object, PatternP Reference property = referenceForPropertyName(object, p->name); if (hasError) return; - initializeAndDestructureBindingElement(p, property); + initializeAndDestructureBindingElement(p, property, isDefinition); if (hasError) return; } } -void Codegen::destructureElementList(const Codegen::Reference &array, PatternElementList *bindingList) +void Codegen::destructureElementList(const Codegen::Reference &array, PatternElementList *bindingList, bool isDefinition) { RegisterScope scope(this); @@ -619,7 +621,7 @@ void Codegen::destructureElementList(const Codegen::Reference &array, PatternEle if (e->type == PatternElement::RestElement) { bytecodeGenerator->addInstruction(Instruction::DestructureRestElement()); - initializeAndDestructureBindingElement(e, Reference::fromAccumulator(this)); + initializeAndDestructureBindingElement(e, Reference::fromAccumulator(this), isDefinition); hasRest = true; } else { Instruction::IteratorNext next; @@ -628,7 +630,7 @@ void Codegen::destructureElementList(const Codegen::Reference &array, PatternEle bool last = !p->next || (!p->next->elision && !p->next->element); if (last) iteratorDone.storeConsumeAccumulator(); - initializeAndDestructureBindingElement(e, iteratorValue); + initializeAndDestructureBindingElement(e, iteratorValue, isDefinition); if (hasError) { end.link(); return; @@ -824,8 +826,6 @@ bool Codegen::visit(ClassExpression *ast) jsClass.name = ast->name.toString(); registerString(jsClass.name); - Reference outerVar = referenceForName(ast->name.toString(), true); - ClassElementList *constructor = nullptr; int nComputedNames = 0; int nStaticComputedNames = 0; @@ -915,6 +915,7 @@ bool Codegen::visit(ClassExpression *ast) bytecodeGenerator->addInstruction(createClass); Reference ctor = referenceForName(ast->name.toString(), true); + ctor.isReferenceToConst = false; // this is the definition (void) ctor.storeRetainAccumulator(); _expr.setResult(Reference::fromAccumulator(this)); @@ -1285,7 +1286,7 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::BitAnd: case QSOperator::BitOr: case QSOperator::BitXor: - if (left.isConst()) { + if (left.isConstant()) { Reference right = expression(ast->right); if (hasError) return false; @@ -1346,7 +1347,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; } case QSOperator::Sub: { - if (right.isConst() && right.constant == Encode(int(1))) { + if (right.isConstant() && right.constant == Encode(int(1))) { left.loadInAccumulator(); bytecodeGenerator->addInstruction(Instruction::Decrement()); } else { @@ -1391,9 +1392,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; } case QSOperator::BitAnd: - if (right.isConst()) { + if (right.isConstant()) { int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32(); - if (left.isConst()) { + if (left.isConstant()) { int result = Primitive::fromReturnedValue(left.constant).toInt32() & rightAsInt; return Reference::fromConst(this, Encode(result)); } @@ -1409,9 +1410,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re } break; case QSOperator::BitOr: - if (right.isConst()) { + if (right.isConstant()) { int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32(); - if (left.isConst()) { + if (left.isConstant()) { int result = Primitive::fromReturnedValue(left.constant).toInt32() | rightAsInt; return Reference::fromConst(this, Encode(result)); } @@ -1427,9 +1428,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re } break; case QSOperator::BitXor: - if (right.isConst()) { + if (right.isConstant()) { int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32(); - if (left.isConst()) { + if (left.isConstant()) { int result = Primitive::fromReturnedValue(left.constant).toInt32() ^ rightAsInt; return Reference::fromConst(this, Encode(result)); } @@ -1445,7 +1446,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re } break; case QSOperator::URShift: - if (right.isConst()) { + if (right.isConstant()) { left.loadInAccumulator(); Instruction::UShrConst ushr; ushr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f; @@ -1458,7 +1459,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re } break; case QSOperator::RShift: - if (right.isConst()) { + if (right.isConstant()) { left.loadInAccumulator(); Instruction::ShrConst shr; shr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f; @@ -1471,7 +1472,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re } break; case QSOperator::LShift: - if (right.isConst()) { + if (right.isConstant()) { left.loadInAccumulator(); Instruction::ShlConst shl; shl.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f; @@ -1610,12 +1611,12 @@ static QSOperator::Op operatorForSwappedOperands(QSOperator::Op oper) Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Reference &right) { - if (left.isConst()) { + if (left.isConstant()) { oper = operatorForSwappedOperands(oper); qSwap(left, right); } - if (right.isConst() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) { + if (right.isConstant() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) { Value c = Primitive::fromReturnedValue(right.constant); if (c.isNull() || c.isUndefined()) { left.loadInAccumulator(); @@ -2155,6 +2156,7 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs) if (r.isStackSlot() && _volatileMemoryLocations.isVolatile(name)) r.isVolatile = true; r.isArgOrEval = resolved.isArgOrEval; + r.isReferenceToConst = resolved.isConst; return r; } @@ -3018,7 +3020,7 @@ bool Codegen::visit(ForEachStatement *ast) lhs.storeConsumeAccumulator(); } } else if (PatternElement *p = AST::cast<PatternElement *>(ast->lhs)) { - initializeAndDestructureBindingElement(p, lhsValue); + initializeAndDestructureBindingElement(p, lhsValue, /*isDefinition =*/ true); if (hasError) goto error; } else { @@ -3711,6 +3713,7 @@ Codegen::Reference &Codegen::Reference::operator =(const Reference &other) isArgOrEval = other.isArgOrEval; codegen = other.codegen; isReadonly = other.isReadonly; + isReferenceToConst = other.isReferenceToConst; stackSlotIsLocalOrArgument = other.stackSlotIsLocalOrArgument; isVolatile = other.isVolatile; global = other.global; @@ -3836,7 +3839,7 @@ Codegen::Reference Codegen::Reference::doStoreOnStack(int slotIndex) const } Reference slot = Reference::fromStackSlot(codegen, slotIndex); - if (isConst()) { + if (isConstant()) { Instruction::MoveConst move; move.constIndex = codegen->registerConstant(constant); move.destTemp = slot.stackSlot(); @@ -3886,6 +3889,20 @@ bool Codegen::Reference::storeWipesAccumulator() const void Codegen::Reference::storeAccumulator() const { + if (isReferenceToConst) { + // throw a type error + RegisterScope scope(codegen); + Reference r = codegen->referenceForName(QStringLiteral("TypeError"), false); + r = r.storeOnStack(); + Instruction::Construct construct; + construct.func = r.stackSlot(); + construct.argc = 0; + construct.argv = 0; + codegen->bytecodeGenerator->addInstruction(construct); + Instruction::ThrowException throwException; + codegen->bytecodeGenerator->addInstruction(throwException); + return; + } switch (type) { case StackSlot: { Instruction::StoreReg store; diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 5232624f0f..fb69e46e42 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -212,7 +212,7 @@ public: return false; } } - bool isConst() const { return type == Const; } + bool isConstant() const { return type == Const; } bool isAccumulator() const { return type == Accumulator; } bool isStackSlot() const { return type == StackSlot; } bool isRegister() const { @@ -366,6 +366,7 @@ public: QString name; mutable bool isArgOrEval = false; bool isReadonly = false; + bool isReferenceToConst = false; bool stackSlotIsLocalOrArgument = false; bool isVolatile = false; bool global = false; @@ -520,9 +521,9 @@ protected: void variableDeclarationList(AST::VariableDeclarationList *ast); Reference targetForPatternElement(AST::PatternElement *p); - void initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &baseRef = Reference()); - void destructurePropertyList(const Reference &object, AST::PatternPropertyList *bindingList); - void destructureElementList(const Reference &array, AST::PatternElementList *bindingList); + void initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &baseRef = Reference(), bool isDefinition = false); + void destructurePropertyList(const Reference &object, AST::PatternPropertyList *bindingList, bool isDefinition = false); + void destructureElementList(const Reference &array, AST::PatternElementList *bindingList, bool isDefinition = false); void destructurePattern(AST::Pattern *p, const Reference &rhs); Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name); diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index bd9d19f491..9dfe3be7e0 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -125,6 +125,7 @@ Context::ResolvedName Context::resolveName(const QString &name) result.type = m.canEscape ? ResolvedName::Local : ResolvedName::Stack; result.scope = scope; result.index = m.index; + result.isConst = (m.scope == VariableScope::Const); if (c->isStrict && (name == QLatin1String("arguments") || name == QLatin1String("eval"))) result.isArgOrEval = true; return result; @@ -135,11 +136,13 @@ Context::ResolvedName Context::resolveName(const QString &name) result.index = argIdx + c->locals.size(); result.scope = scope; result.type = ResolvedName::Local; + result.isConst = false; return result; } else { result.index = argIdx + sizeof(CallData)/sizeof(Value) - 1; result.scope = 0; result.type = ResolvedName::Stack; + result.isConst = false; return result; } } diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index e72704f4b1..9444173840 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -293,6 +293,7 @@ struct Context { }; Type type = Unresolved; bool isArgOrEval = false; + bool isConst = false; int scope = -1; int index = -1; bool isValid() const { return type != Unresolved; } diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index 8b8c385b84..9be55c6ad0 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -230,7 +230,7 @@ bool ScanFunctions::visit(ClassExpression *ast) _context->isStrict = true; _context->hasNestedFunctions = true; if (!ast->name.isEmpty()) - _context->addLocalVar(ast->name.toString(), Context::VariableDeclaration, AST::VariableScope::Let); + _context->addLocalVar(ast->name.toString(), Context::VariableDefinition, AST::VariableScope::Const); return true; } @@ -248,7 +248,7 @@ bool ScanFunctions::visit(ClassDeclaration *ast) _context->isStrict = true; _context->hasNestedFunctions = true; if (!ast->name.isEmpty()) - _context->addLocalVar(ast->name.toString(), Context::VariableDeclaration, AST::VariableScope::Let); + _context->addLocalVar(ast->name.toString(), Context::VariableDefinition, AST::VariableScope::Const); return true; } |