diff options
| author | Yulong Bai <yulong.bai@qt.io> | 2018-06-15 14:44:19 +0200 |
|---|---|---|
| committer | Lars Knoll <lars.knoll@qt.io> | 2018-06-26 10:03:56 +0000 |
| commit | 74f4065caa7254b0e30afa955aa59092024decdb (patch) | |
| tree | b9b5555807ebfb2e7b6a80a56aec8785b4066ae1 /src/qml/compiler/qv4codegen.cpp | |
| parent | c9fa5b8071f4a32afd05cc9fa5cd8ab28e60dc55 (diff) | |
Add basic support for EcmaScript classes
Most of the class creation is done inside the runtime
in the CreateClass method. Added a corresponding
instruction to the interpreter and jit.
The compiled data now contains an array of classes
containing the compile time generated layout of the class.
Currently, classes without an explicit constructor and
classes with inheritance are not supported.
Done-with: Yulong Bai <yulong.bai@qt.io>
Change-Id: I0185dcc1e3b0b8f44deff74e44a8262fc646aa9e
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml/compiler/qv4codegen.cpp')
| -rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 127 |
1 files changed, 114 insertions, 13 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 9c00d660b0..e88f01fdca 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -545,6 +545,23 @@ void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, con } } +Codegen::Reference Codegen::referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name) +{ + AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(name); + Reference property; + if (cname) { + Reference computedName = expression(cname->expression); + if (hasError) + return Reference(); + computedName = computedName.storeOnStack(); + property = Reference::fromSubscript(object, computedName).asLValue(); + } else { + QString propertyName = name->asString(); + property = Reference::fromMember(object, propertyName); + } + return property; +} + void Codegen::destructurePropertyList(const Codegen::Reference &object, PatternPropertyList *bindingList) { RegisterScope scope(this); @@ -552,18 +569,9 @@ void Codegen::destructurePropertyList(const Codegen::Reference &object, PatternP for (PatternPropertyList *it = bindingList; it; it = it->next) { PatternProperty *p = it->property; RegisterScope scope(this); - AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(p->name); - Reference property; - if (cname) { - Reference computedName = expression(cname->expression); - if (hasError) - return; - computedName = computedName.storeOnStack(); - property = Reference::fromSubscript(object, computedName).asLValue(); - } else { - QString propertyName = p->name->asString(); - property = Reference::fromMember(object, propertyName); - } + Reference property = referenceForPropertyName(object, p->name); + if (hasError) + return; initializeAndDestructureBindingElement(p, property); if (hasError) return; @@ -812,7 +820,100 @@ bool Codegen::visit(VariableDeclarationList *) bool Codegen::visit(ClassExpression *ast) { - throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Support for 'class' is unimplemented.")); + Compiler::Class jsClass; + jsClass.name = ast->name.toString(); + registerString(jsClass.name); + + Reference outerVar = referenceForName(ast->name.toString(), true); + + ClassElementList *constructor = nullptr; + int nComputedNames = 0; + int nStaticComputedNames = 0; + + RegisterScope scope(this); + ControlFlowBlock controlFlow(this, ast); + + for (auto *member = ast->elements; member; member = member->next) { + PatternProperty *p = member->property; + FunctionExpression *f = p->initializer->asFunctionDefinition(); + Q_ASSERT(f); + AST::ComputedPropertyName *cname = AST::cast<ComputedPropertyName *>(p->name); + if (cname) { + ++nComputedNames; + if (member->isStatic) + ++nStaticComputedNames; + } + QString name = p->name->asString(); + Compiler::Class::Method::Type type = Compiler::Class::Method::Regular; + if (p->type == PatternProperty::Getter) + type = Compiler::Class::Method::Getter; + else if (p->type == PatternProperty::Setter) + type = Compiler::Class::Method::Setter; + Compiler::Class::Method m{ name, type, defineFunction(name, f, f->formals, f->body)}; + + if (!member->isStatic && name == QStringLiteral("constructor")) { + if (constructor) { + throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a multiple constructors in a class.")); + return false; + } + if (m.type != Compiler::Class::Method::Regular) { + throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a getter or setter named 'constructor'.")); + return false; + } + constructor = member; + jsClass.constructorIndex = m.functionIndex; + continue; + } + + if (member->isStatic) + jsClass.staticMethods << m; + else + jsClass.methods << m; + } + + int classIndex = _module->classes.size(); + _module->classes.append(jsClass); + + Reference heritage = Reference::fromStackSlot(this); + if (ast->heritage) { + bytecodeGenerator->setLocation(ast->heritage->firstSourceLocation()); + Reference r = expression(ast->heritage); + if (hasError) + return false; + r.storeOnStack(heritage.stackSlot()); + } else { + Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()).loadInAccumulator(); + heritage.storeConsumeAccumulator(); + } + + int computedNames = nComputedNames ? bytecodeGenerator->newRegisterArray(nComputedNames) : 0; + int currentStaticName = computedNames; + int currentNonStaticName = computedNames + nStaticComputedNames; + + for (auto *member = ast->elements; member; member = member->next) { + AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(member->property->name); + if (!cname) + continue; + RegisterScope scope(this); + bytecodeGenerator->setLocation(cname->firstSourceLocation()); + Reference computedName = expression(cname->expression); + if (hasError) + return false; + computedName.storeOnStack(member->isStatic ? currentStaticName++ : currentNonStaticName++); + } + + Instruction::CreateClass createClass; + createClass.classIndex = classIndex; + createClass.heritage = heritage.stackSlot(); + createClass.computedNames = computedNames; + + bytecodeGenerator->addInstruction(createClass); + + Reference ctor = referenceForName(ast->name.toString(), true); + (void) ctor.storeRetainAccumulator(); + (void) outerVar.storeRetainAccumulator(); + + _expr.setResult(Reference::fromAccumulator(this)); return false; } |
