diff options
21 files changed, 377 insertions, 51 deletions
diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp index 8d1ac0bfd9..3e683cc8bf 100644 --- a/src/qml/compiler/qv4bytecodehandler.cpp +++ b/src/qml/compiler/qv4bytecodehandler.cpp @@ -349,6 +349,9 @@ std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint COLLECTOR_BEGIN_INSTR(DefineObjectLiteral) COLLECTOR_END_INSTR(DefineObjectLiteral) + COLLECTOR_BEGIN_INSTR(CreateClass) + COLLECTOR_END_INSTR(CreateClass) + COLLECTOR_BEGIN_INSTR(CreateMappedArgumentsObject) COLLECTOR_END_INSTR(CreateMappedArgumentsObject) 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; } diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 83b8731179..5ddb762c8b 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -525,6 +525,8 @@ protected: void destructureElementList(const Reference &array, AST::PatternElementList *bindingList); void destructurePattern(AST::Pattern *p, const Reference &rhs); + Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name); + // Hook provided to implement QML lookup semantics virtual Reference fallbackNameLookup(const QString &name); virtual void beginFunctionBodyHook() {} diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 08170e1c3b..6d81db23e0 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -317,6 +317,33 @@ struct Function }; static_assert(sizeof(Function) == 80, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +struct Class +{ + quint32_le nameIndex; + quint32_le scopeIndex; + quint32_le constructorFunction; + quint32_le nStaticMethods; + quint32_le nMethods; + quint32_le nameTableOffset; + quint32_le methodTableOffset; + + const quint32_le *nameTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + nameTableOffset); } + const quint32_le *methodTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + methodTableOffset); } + + static int calculateSize(int nStaticMethods, int nMethods) { + int trailingData = (nStaticMethods + nMethods) * 2 * sizeof(quint32); + size_t size = align(sizeof(Class) + trailingData); + Q_ASSERT(size < INT_MAX); + return int(size); + } + + static size_t align(size_t a) { + return (a + 7) & ~size_t(7); + } +}; +static_assert(sizeof(Class) == 28, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + + // Qml data structures struct Q_QML_EXPORT TranslationData { @@ -747,6 +774,8 @@ struct Unit quint32_le offsetToStringTable; quint32_le functionTableSize; quint32_le offsetToFunctionTable; + quint32_le classTableSize; + quint32_le offsetToClassTable; quint32_le blockTableSize; quint32_le offsetToBlockTable; quint32_le lookupTableSize; @@ -810,6 +839,7 @@ struct Unit } const quint32_le *functionOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); } + const quint32_le *classOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToClassTable); } const quint32_le *blockOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToBlockTable); } const Function *functionAt(int idx) const { @@ -818,6 +848,12 @@ struct Unit return reinterpret_cast<const Function*>(reinterpret_cast<const char *>(this) + offset); } + const Class *classAt(int idx) const { + const quint32_le *offsetTable = classOffsetTable(); + const quint32_le offset = offsetTable[idx]; + return reinterpret_cast<const Class *>(reinterpret_cast<const char *>(this) + offset); + } + const Block *blockAt(int idx) const { const quint32_le *offsetTable = blockOffsetTable(); const quint32_le offset = offsetTable[idx]; @@ -842,7 +878,7 @@ struct Unit } }; -static_assert(sizeof(Unit) == 200, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Unit) == 208, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct TypeReference { diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index d5a02a3ac8..eb38ba564d 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -227,34 +227,41 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO registerString(c->locals.at(i)); } - Q_ALLOCA_VAR(quint32_le, blockAndFunctionOffsets, (module->functions.size() + module->blocks.size()) * sizeof(quint32_le)); + Q_ALLOCA_VAR(quint32_le, blockClassAndFunctionOffsets, (module->functions.size() + module->classes.size() + module->blocks.size()) * sizeof(quint32_le)); uint jsClassDataOffset = 0; char *dataPtr; CompiledData::Unit *unit; { - QV4::CompiledData::Unit tempHeader = generateHeader(option, blockAndFunctionOffsets, &jsClassDataOffset); + QV4::CompiledData::Unit tempHeader = generateHeader(option, blockClassAndFunctionOffsets, &jsClassDataOffset); dataPtr = reinterpret_cast<char *>(malloc(tempHeader.unitSize)); memset(dataPtr, 0, tempHeader.unitSize); memcpy(&unit, &dataPtr, sizeof(CompiledData::Unit*)); memcpy(unit, &tempHeader, sizeof(tempHeader)); } - memcpy(dataPtr + unit->offsetToFunctionTable, blockAndFunctionOffsets, unit->functionTableSize * sizeof(quint32_le)); - memcpy(dataPtr + unit->offsetToBlockTable, blockAndFunctionOffsets + unit->functionTableSize, unit->blockTableSize * sizeof(quint32_le)); + memcpy(dataPtr + unit->offsetToFunctionTable, blockClassAndFunctionOffsets, unit->functionTableSize * sizeof(quint32_le)); + memcpy(dataPtr + unit->offsetToClassTable, blockClassAndFunctionOffsets + unit->functionTableSize, unit->classTableSize * sizeof(quint32_le)); + memcpy(dataPtr + unit->offsetToBlockTable, blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize, unit->blockTableSize * sizeof(quint32_le)); for (int i = 0; i < module->functions.size(); ++i) { Context *function = module->functions.at(i); if (function == module->rootContext) unit->indexOfRootFunction = i; - writeFunction(dataPtr + blockAndFunctionOffsets[i], function); + writeFunction(dataPtr + blockClassAndFunctionOffsets[i], function); + } + + for (int i = 0; i < module->classes.size(); ++i) { + const Class &c = module->classes.at(i); + + writeClass(dataPtr + blockClassAndFunctionOffsets[i + module->functions.size()], c); } for (int i = 0; i < module->blocks.size(); ++i) { Context *block = module->blocks.at(i); - writeBlock(dataPtr + blockAndFunctionOffsets[i + module->functions.size()], block); + writeBlock(dataPtr + blockClassAndFunctionOffsets[i + module->classes.size() + module->functions.size()], block); } CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable); @@ -389,6 +396,45 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte memcpy(f + function->codeOffset, irFunction->code.constData(), irFunction->code.size()); } +void QV4::Compiler::JSUnitGenerator::writeClass(char *b, const QV4::Compiler::Class &c) +{ + QV4::CompiledData::Class *cls = reinterpret_cast<QV4::CompiledData::Class *>(b); + + quint32 currentOffset = sizeof(QV4::CompiledData::Class); + + QVector<Class::Method> allMethods = c.staticMethods; + allMethods += c.methods; + + cls->constructorFunction = c.constructorIndex; + cls->nameIndex = getStringId(c.name); + cls->nMethods = c.methods.size(); + cls->nStaticMethods = c.staticMethods.size(); + cls->nameTableOffset = currentOffset; + quint32_le *names = reinterpret_cast<quint32_le *>(b + currentOffset); + currentOffset += allMethods.size() * sizeof(quint32); + cls->methodTableOffset = currentOffset; + quint32_le *methods = reinterpret_cast<quint32_le *>(b + currentOffset); + currentOffset += cls->nMethods * sizeof(quint32); + + // write methods + for (int i = 0; i < allMethods.size(); ++i) { + names[i] = getStringId(allMethods.at(i).name); + methods[i] = allMethods.at(i).functionIndex; + // ### fix getter and setter methods + } + + static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); + if (showCode) { + qDebug() << "=== Class " << stringForIndex(cls->nameIndex) << "static methods" << cls->nStaticMethods << "methods" << cls->nMethods; + qDebug() << " constructor:" << cls->constructorFunction; + for (uint i = 0; i < cls->nStaticMethods; ++i) + qDebug() << " " << i << ": static" << stringForIndex(cls->nameTable()[i]); + for (uint i = 0; i < cls->nMethods; ++i) + qDebug() << " " << i << ": " << stringForIndex(cls->nameTable()[cls->nStaticMethods + i]); + qDebug(); + } +} + void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context *irBlock) const { QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b); @@ -433,6 +479,10 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.offsetToFunctionTable = nextOffset; nextOffset += unit.functionTableSize * sizeof(uint); + unit.classTableSize = module->classes.size(); + unit.offsetToClassTable = nextOffset; + nextOffset += unit.classTableSize * sizeof(uint); + unit.blockTableSize = module->blocks.size(); unit.offsetToBlockTable = nextOffset; nextOffset += unit.blockTableSize * sizeof(uint); @@ -472,6 +522,14 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp } blockAndFunctionOffsets += module->functions.size(); + for (int i = 0; i < module->classes.size(); ++i) { + const Class &c = module->classes.at(i); + blockAndFunctionOffsets[i] = nextOffset; + + nextOffset += QV4::CompiledData::Class::calculateSize(c.staticMethods.size(), c.methods.size()); + } + blockAndFunctionOffsets += module->classes.size(); + for (int i = 0; i < module->blocks.size(); ++i) { Context *c = module->blocks.at(i); blockAndFunctionOffsets[i] = nextOffset; diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index c40d49213a..944c44b1ff 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -72,6 +72,8 @@ struct JSClassMember; namespace Compiler { +struct Class; + struct Q_QML_PRIVATE_EXPORT StringTableGenerator { StringTableGenerator(); @@ -125,6 +127,7 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable); void writeFunction(char *f, Context *irFunction) const; + void writeClass(char *f, const Class &c); void writeBlock(char *f, Context *irBlock) const; StringTableGenerator stringTable; diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 070be7573a..e72704f4b1 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -79,6 +79,24 @@ enum class ContextType { struct Context; +struct Class { + struct Method { + enum Type { + Regular, + Getter, + Setter + }; + QString name; + Type type; + int functionIndex; + }; + + QString name; + int constructorIndex = -1; + QVector<Method> staticMethods; + QVector<Method> methods; +}; + struct Module { Module(bool debugMode) : debugMode(debugMode) @@ -92,6 +110,7 @@ struct Module { QHash<QQmlJS::AST::Node *, Context *> contextMap; QList<Context *> functions; QList<Context *> blocks; + QVector<Class> classes; Context *rootContext; QString fileName; QString finalUrl; diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index f3c1694b79..eda0102844 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -224,6 +224,24 @@ bool ScanFunctions::visit(FunctionExpression *ast) return enterFunction(ast, /*enterName*/ false); } +bool ScanFunctions::visit(ClassExpression *ast) +{ + if (!ast->name.isEmpty()) + _context->addLocalVar(ast->name.toString(), Context::VariableDeclaration, AST::VariableScope::Let); + + enterEnvironment(ast, ContextType::Block, QStringLiteral("%Class")); + _context->isStrict = true; + _context->hasNestedFunctions = true; + if (!ast->name.isEmpty()) + _context->addLocalVar(ast->name.toString(), Context::VariableDeclaration, AST::VariableScope::Let); + return true; +} + +void ScanFunctions::endVisit(ClassExpression *) +{ + leaveEnvironment(); +} + bool ScanFunctions::visit(TemplateLiteral *ast) { while (ast) { @@ -590,12 +608,12 @@ void ScanFunctions::calcEscapingVariables() if (showEscapingVars) { qDebug() << "==== escaping variables ===="; for (Context *c : qAsConst(m->contextMap)) { - qDebug() << "Context" << c << c->name << "requiresExecutionContext" << c->requiresExecutionContext; + qDebug() << "Context" << c << c->name << "requiresExecutionContext" << c->requiresExecutionContext << "isStrict" << c->isStrict; qDebug() << " parent:" << c->parent; if (c->argumentsCanEscape) qDebug() << " Arguments escape"; for (auto it = c->members.constBegin(); it != c->members.constEnd(); ++it) { - qDebug() << " " << it.key() << it.value().canEscape << "isLexicallyScoped:" << it.value().isLexicallyScoped(); + qDebug() << " " << it.key() << it.value().index << it.value().canEscape << "isLexicallyScoped:" << it.value().isLexicallyScoped(); } } } diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h index 3120ce917c..ffa1409da9 100644 --- a/src/qml/compiler/qv4compilerscanfunctions_p.h +++ b/src/qml/compiler/qv4compilerscanfunctions_p.h @@ -121,6 +121,9 @@ protected: bool visit(AST::FunctionDeclaration *ast) override; void endVisit(AST::FunctionDeclaration *) override; + bool visit(AST::ClassExpression *ast) override; + void endVisit(AST::ClassExpression *) override; + bool visit(AST::DoWhileStatement *ast) override; bool visit(AST::ForStatement *ast) override; void endVisit(AST::ForStatement *) override; diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index df5dd5610c..d8a1fa2dde 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -482,6 +482,12 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st << ", " << dumpRegister(args, nFormals); MOTH_END_INSTR(DefineObjectLiteral) + MOTH_BEGIN_INSTR(CreateClass) + d << classIndex + << ", " << dumpRegister(heritage, nFormals) + << ", " << dumpRegister(computedNames, nFormals); + MOTH_END_INSTR(CreateClass) + MOTH_BEGIN_INSTR(CreateMappedArgumentsObject) MOTH_END_INSTR(CreateMappedArgumentsObject) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 85b30507a1..3bd60288c3 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -137,6 +137,7 @@ QT_BEGIN_NAMESPACE #define INSTR_DeclareVar(op) INSTRUCTION(op, DeclareVar, 2, varName, isDeletable) #define INSTR_DefineArray(op) INSTRUCTION(op, DefineArray, 2, argc, args) #define INSTR_DefineObjectLiteral(op) INSTRUCTION(op, DefineObjectLiteral, 3, internalClassId, argc, args) +#define INSTR_CreateClass(op) INSTRUCTION(op, CreateClass, 3, classIndex, heritage, computedNames) #define INSTR_CreateMappedArgumentsObject(op) INSTRUCTION(op, CreateMappedArgumentsObject, 0) #define INSTR_CreateUnmappedArgumentsObject(op) INSTRUCTION(op, CreateUnmappedArgumentsObject, 0) #define INSTR_CreateRestParameter(op) INSTRUCTION(op, CreateRestParameter, 1, argIndex) @@ -265,6 +266,7 @@ QT_BEGIN_NAMESPACE F(DeclareVar) \ F(DefineArray) \ F(DefineObjectLiteral) \ + F(CreateClass) \ F(CreateMappedArgumentsObject) \ F(CreateUnmappedArgumentsObject) \ F(CreateRestParameter) \ diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp index 3e8991122f..87b6478e0d 100644 --- a/src/qml/jit/qv4baselinejit.cpp +++ b/src/qml/jit/qv4baselinejit.cpp @@ -817,6 +817,17 @@ void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int argc, in as->passEngineAsArg(0); JIT_GENERATE_RUNTIME_CALL(Runtime::method_objectLiteral, Assembler::ResultInAccumulator); } + +void BaselineJIT::generate_CreateClass(int classIndex, int heritage, int computedNames) +{ + as->prepareCallWithArgCount(4); + as->passRegAsArg(computedNames, 3); + as->passRegAsArg(heritage, 2); + as->passInt32AsArg(classIndex, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_createClass, Assembler::ResultInAccumulator); +} + void BaselineJIT::generate_CreateMappedArgumentsObject() { as->prepareCallWithArgCount(1); diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h index 11548219af..9fa4aa2cda 100644 --- a/src/qml/jit/qv4baselinejit_p.h +++ b/src/qml/jit/qv4baselinejit_p.h @@ -154,6 +154,7 @@ public: void generate_DeclareVar(int varName, int isDeletable) override; void generate_DefineArray(int argc, int args) override; void generate_DefineObjectLiteral(int internalClassId, int argc, int args) override; + void generate_CreateClass(int classIndex, int heritage, int computedNames) override; void generate_CreateMappedArgumentsObject() override; void generate_CreateUnmappedArgumentsObject() override; void generate_CreateRestParameter(int argIndex) override; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 7f26d1d16b..657c267f7c 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -341,6 +341,8 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) ic = ic->addMember(id_length()->identifier(), Attr_ReadOnly_ButConfigurable, &index); Q_ASSERT(index == Heap::ScriptFunction::Index_Length); classes[Class_ScriptFunction] = ic->d(); + ic = ic->changeVTable(ConstructorFunction::staticVTable()); + classes[Class_ConstructorFunction] = ic->d(); ic = ic->changeVTable(GeneratorFunction::staticVTable()); classes[Class_GeneratorFunction] = ic->d(); classes[Class_ObjectProto] = classes[Class_Object]->addMember(id_constructor()->identifier(), Attr_NotEnumerable, &index); diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index 7701cf37bc..3e7aaca6a7 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -105,6 +105,7 @@ struct Q_QML_EXPORT EngineBase { Class_StringObject, Class_SymbolObject, Class_ScriptFunction, + Class_ConstructorFunction, Class_ObjectProto, Class_RegExp, Class_RegExpObject, diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index a9aaa344cb..7fcb1c3536 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -179,6 +179,11 @@ Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *sco return scope->engine()->memoryManager->allocate<ScriptFunction>(scope, function); } +Heap::FunctionObject *FunctionObject::createConstructorFunction(ExecutionContext *scope, Function *function) +{ + return scope->engine()->memoryManager->allocate<ConstructorFunction>(scope, function); +} + Heap::FunctionObject *FunctionObject::createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, jsCallFunction code, int argumentCount) { Scope scope(engine); @@ -490,6 +495,13 @@ Heap::InternalClass *ScriptFunction::classForConstructor() const return ic->d(); } +DEFINE_OBJECT_VTABLE(ConstructorFunction); + +ReturnedValue ConstructorFunction::call(const FunctionObject *f, const Value *, const Value *, int) +{ + return f->engine()->throwTypeError(QStringLiteral("Cannot call a class constructor without |new|")); +} + DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); DEFINE_OBJECT_VTABLE(BoundFunction); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index e8bd574161..1e7df2c27e 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -120,6 +120,10 @@ DECLARE_HEAP_OBJECT(ScriptFunction, FunctionObject) { void init(QV4::ExecutionContext *scope, Function *function); }; +struct ConstructorFunction : ScriptFunction +{ +}; + #define BoundFunctionMembers(class, Member) \ Member(class, Pointer, FunctionObject *, target) \ Member(class, HeapValue, HeapValue, boundThis) \ @@ -165,6 +169,7 @@ struct Q_QML_EXPORT FunctionObject: Object { static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); + static Heap::FunctionObject *createConstructorFunction(ExecutionContext *scope, Function *function); static Heap::FunctionObject *createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, jsCallFunction code, int argumentCount); bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } @@ -232,6 +237,11 @@ struct ScriptFunction : FunctionObject { Heap::InternalClass *classForConstructor() const; }; +struct ConstructorFunction : ScriptFunction { + V4_OBJECT2(ConstructorFunction, ScriptFunction) + V4_INTERNALCLASS(ConstructorFunction) + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; struct BoundFunction: FunctionObject { V4_OBJECT2(BoundFunction, FunctionObject) diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 64efaa9a86..bae8a9d332 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1482,6 +1482,67 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId return o.asReturnedValue(); } +ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classIndex, const Value &heritage, const Value *computedNames) +{ + const CompiledData::CompilationUnit *unit = engine->currentStackFrame->v4Function->compilationUnit; + const QV4::CompiledData::Class *cls = unit->data->classAt(classIndex); + + if (!heritage.isEmpty()) { + // #### + return engine->throwTypeError(QStringLiteral("classes with heritage not yet supported.")); + } + + Scope scope(engine); + ScopedString name(scope, unit->runtimeStrings[cls->nameIndex]); + // ### fix heritage + ScopedObject protoParent(scope, engine->objectPrototype()); + ScopedObject constructorParent(scope, engine->functionPrototype()); + + ScopedObject proto(scope, engine->newObject()); + proto->setPrototypeUnchecked(protoParent); + ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); + + ScopedFunctionObject constructor(scope); + if (cls->constructorFunction != UINT_MAX) { + QV4::Function *f = unit->runtimeFunctions[cls->constructorFunction]; + Q_ASSERT(f); + constructor = FunctionObject::createConstructorFunction(current, f)->asReturnedValue(); + } else { + // #### + return engine->throwTypeError(QStringLiteral("default constructor not implemented")); + } + constructor->setPrototypeUnchecked(constructorParent); + constructor->defineDefaultProperty(engine->id_prototype(), proto); + proto->defineDefaultProperty(engine->id_constructor(), constructor); + + + ScopedFunctionObject function(scope); + for (uint i = 0; i < cls->nStaticMethods; ++i) { + name = unit->runtimeStrings[cls->nameTable()[i]]; + QV4::Function *f = unit->runtimeFunctions[cls->methodTable()[i]]; + Q_ASSERT(f); + function = FunctionObject::createScriptFunction(current, f)->asReturnedValue(); + constructor->defineDefaultProperty(name, function); + } + + for (uint i = 0; i < cls->nMethods; ++i) { + name = unit->runtimeStrings[cls->nameTable()[i + cls->nStaticMethods]]; + name->makeIdentifier(); + if (name->identifier() == engine->id_empty()->identifier()) { + name = computedNames->toPropertyKey(engine); + if (engine->hasException) + return Encode::undefined(); + ++computedNames; + } + QV4::Function *f = unit->runtimeFunctions[cls->methodTable()[i + cls->nStaticMethods]]; + Q_ASSERT(f); + function = FunctionObject::createScriptFunction(current, f)->asReturnedValue(); + proto->defineDefaultProperty(name, function); + } + + return constructor->asReturnedValue(); +} + QV4::ReturnedValue Runtime::method_createMappedArgumentsObject(ExecutionEngine *engine) { Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext); diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 09f6a65dde..824d2fb0ea 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -143,6 +143,7 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { /* literals */ \ F(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length)) \ F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, int classId, int argc, const Value *args)) \ + F(ReturnedValue, createClass, (ExecutionEngine *engine, int classIndex, const Value &heritage, const Value *computedNames)) \ \ /* for-in, for-of and array destructuring */ \ F(ReturnedValue, getIterator, (ExecutionEngine *engine, const Value &in, int iterator)) \ diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index f625368b62..f19e1b1988 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -964,6 +964,10 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code) acc = Runtime::method_objectLiteral(engine, internalClassId, argc, arguments); MOTH_END_INSTR(DefineObjectLiteral) + MOTH_BEGIN_INSTR(CreateClass) + acc = Runtime::method_createClass(engine, classIndex, STACK_VALUE(heritage), stack + computedNames); + MOTH_END_INSTR(CreateClass) + MOTH_BEGIN_INSTR(CreateMappedArgumentsObject) acc = Runtime::method_createMappedArgumentsObject(engine); MOTH_END_INSTR(CreateMappedArgumentsObject) diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations index 9922103702..56944b928a 100644 --- a/tests/auto/qml/ecmascripttests/TestExpectations +++ b/tests/auto/qml/ecmascripttests/TestExpectations @@ -39,7 +39,6 @@ built-ins/Array/prototype/concat/Array.prototype.concat_array-like.js fails built-ins/Array/prototype/concat/Array.prototype.concat_holey-sloppy-arguments.js fails built-ins/Array/prototype/concat/Array.prototype.concat_large-typed-array.js fails built-ins/Array/prototype/concat/Array.prototype.concat_length-throws.js fails -built-ins/Array/prototype/concat/Array.prototype.concat_non-array.js fails built-ins/Array/prototype/concat/Array.prototype.concat_sloppy-arguments-throws.js fails built-ins/Array/prototype/concat/Array.prototype.concat_sloppy-arguments-with-dupes.js sloppyFails built-ins/Array/prototype/concat/Array.prototype.concat_sloppy-arguments.js fails @@ -1977,8 +1976,6 @@ language/computed-property-names/class/accessor/setter.js fails language/computed-property-names/class/method/constructor-can-be-generator.js fails language/computed-property-names/class/method/constructor-can-be-getter.js fails language/computed-property-names/class/method/constructor-can-be-setter.js fails -language/computed-property-names/class/method/constructor-duplicate-1.js fails -language/computed-property-names/class/method/constructor-duplicate-2.js fails language/computed-property-names/class/method/constructor-duplicate-3.js fails language/computed-property-names/class/method/constructor.js fails language/computed-property-names/class/method/generator.js fails @@ -1986,16 +1983,12 @@ language/computed-property-names/class/method/number.js fails language/computed-property-names/class/method/string.js fails language/computed-property-names/class/method/symbol.js fails language/computed-property-names/class/static/generator-constructor.js fails -language/computed-property-names/class/static/generator-prototype.js fails language/computed-property-names/class/static/getter-constructor.js fails -language/computed-property-names/class/static/getter-prototype.js fails language/computed-property-names/class/static/method-constructor.js fails language/computed-property-names/class/static/method-number.js fails -language/computed-property-names/class/static/method-prototype.js fails language/computed-property-names/class/static/method-string.js fails language/computed-property-names/class/static/method-symbol.js fails language/computed-property-names/class/static/setter-constructor.js fails -language/computed-property-names/class/static/setter-prototype.js fails language/computed-property-names/object/accessor/getter-super.js fails language/computed-property-names/object/accessor/setter-super.js fails language/computed-property-names/object/method/super.js fails @@ -2133,9 +2126,6 @@ language/expressions/call/tco-non-eval-function-dynamic.js sloppyFails language/expressions/call/tco-non-eval-function.js sloppyFails language/expressions/call/tco-non-eval-global.js sloppyFails language/expressions/call/tco-non-eval-with.js sloppyFails -language/expressions/class/accessor-name-inst-computed-err-evaluation.js fails -language/expressions/class/accessor-name-inst-computed-err-to-prop-key.js fails -language/expressions/class/accessor-name-inst-computed-err-unresolvable.js fails language/expressions/class/accessor-name-inst-computed-in.js fails language/expressions/class/accessor-name-inst-computed-yield-expr.js fails language/expressions/class/accessor-name-inst-computed.js fails @@ -2153,9 +2143,6 @@ language/expressions/class/accessor-name-inst-literal-string-hex-escape.js fails language/expressions/class/accessor-name-inst-literal-string-line-continuation.js fails language/expressions/class/accessor-name-inst-literal-string-single-quote.js fails language/expressions/class/accessor-name-inst-literal-string-unicode-escape.js fails -language/expressions/class/accessor-name-static-computed-err-evaluation.js fails -language/expressions/class/accessor-name-static-computed-err-to-prop-key.js fails -language/expressions/class/accessor-name-static-computed-err-unresolvable.js fails language/expressions/class/accessor-name-static-computed-in.js fails language/expressions/class/accessor-name-static-computed-yield-expr.js fails language/expressions/class/accessor-name-static-computed.js fails @@ -3345,9 +3332,6 @@ language/statements/async-function/evaluation-body.js fails language/statements/async-function/syntax-declaration-line-terminators-allowed.js fails language/statements/block/tco-stmt-list.js strictFails language/statements/block/tco-stmt.js strictFails -language/statements/class/accessor-name-inst-computed-err-evaluation.js fails -language/statements/class/accessor-name-inst-computed-err-to-prop-key.js fails -language/statements/class/accessor-name-inst-computed-err-unresolvable.js fails language/statements/class/accessor-name-inst-computed-yield-expr.js fails language/statements/class/accessor-name-inst-computed.js fails language/statements/class/accessor-name-inst-literal-numeric-binary.js fails @@ -3364,9 +3348,6 @@ language/statements/class/accessor-name-inst-literal-string-hex-escape.js fails language/statements/class/accessor-name-inst-literal-string-line-continuation.js fails language/statements/class/accessor-name-inst-literal-string-single-quote.js fails language/statements/class/accessor-name-inst-literal-string-unicode-escape.js fails -language/statements/class/accessor-name-static-computed-err-evaluation.js fails -language/statements/class/accessor-name-static-computed-err-to-prop-key.js fails -language/statements/class/accessor-name-static-computed-err-unresolvable.js fails language/statements/class/accessor-name-static-computed-yield-expr.js fails language/statements/class/accessor-name-static-computed.js fails language/statements/class/accessor-name-static-literal-numeric-binary.js fails @@ -3390,20 +3371,17 @@ language/statements/class/cptn-decl.js fails language/statements/class/definition/accessors.js fails language/statements/class/definition/basics.js fails language/statements/class/definition/class-method-returns-promise.js fails -language/statements/class/definition/constructable-but-no-prototype.js fails language/statements/class/definition/constructor-property.js fails -language/statements/class/definition/constructor-strict-by-default.js fails language/statements/class/definition/constructor.js fails language/statements/class/definition/fn-name-accessor-get.js fails language/statements/class/definition/fn-name-accessor-set.js fails language/statements/class/definition/fn-name-gen-method.js fails language/statements/class/definition/fn-name-method.js fails language/statements/class/definition/fn-name-static-precedence.js fails -language/statements/class/definition/getters-non-configurable-err.js fails language/statements/class/definition/getters-prop-desc.js fails language/statements/class/definition/getters-restricted-ids.js fails language/statements/class/definition/implicit-constructor.js fails -language/statements/class/definition/invalid-extends.js fails +language/statements/class/definition/invalid-extends.js strictFails language/statements/class/definition/methods-gen-no-yield.js fails language/statements/class/definition/methods-gen-return.js fails language/statements/class/definition/methods-gen-yield-as-expression-with-rhs.js fails @@ -3423,7 +3401,6 @@ language/statements/class/definition/prototype-getter.js fails language/statements/class/definition/prototype-property.js fails language/statements/class/definition/prototype-setter.js fails language/statements/class/definition/prototype-wiring.js fails -language/statements/class/definition/setters-non-configurable-err.js fails language/statements/class/definition/setters-prop-desc.js fails language/statements/class/definition/setters-restricted-ids.js fails language/statements/class/definition/side-effects-in-extends.js fails @@ -4153,9 +4130,7 @@ language/statements/class/scope-static-meth-paramsbody-var-open.js fails language/statements/class/scope-static-setter-paramsbody-var-close.js fails language/statements/class/scope-static-setter-paramsbody-var-open.js fails language/statements/class/setter-length-dflt.js fails -language/statements/class/static-method-gen-non-configurable-err.js fails language/statements/class/static-method-length-dflt.js fails -language/statements/class/static-method-non-configurable-err.js fails language/statements/class/strict-mode/arguments-callee.js fails language/statements/class/subclass/binding.js fails language/statements/class/subclass/bound-function.js fails @@ -4212,7 +4187,6 @@ language/statements/class/subclass/builtin-objects/Object/regular-subclassing.js language/statements/class/subclass/builtin-objects/Object/replacing-prototype.js fails language/statements/class/subclass/builtin-objects/Promise/regular-subclassing.js fails language/statements/class/subclass/builtin-objects/Promise/super-must-be-called.js fails -language/statements/class/subclass/builtin-objects/Proxy/no-prototype-throws.js fails language/statements/class/subclass/builtin-objects/RegExp/lastIndex.js fails language/statements/class/subclass/builtin-objects/RegExp/regular-subclassing.js fails language/statements/class/subclass/builtin-objects/RegExp/super-must-be-called.js fails @@ -4269,8 +4243,6 @@ language/statements/class/syntax/class-declaration-heritage-identifier-reference language/statements/class/syntax/class-expression-binding-identifier-opt-class-element-list.js fails language/statements/class/syntax/class-expression-heritage-identifier-reference.js fails language/statements/class/syntax/class-expression.js fails -language/statements/class/syntax/class-method-propname-constructor.js fails -language/statements/class/syntax/early-errors/class-body-constructor-empty-missing-class-heritage.js fails language/statements/const/block-local-closure-get-before-initialization.js fails language/statements/const/block-local-use-before-initialization-in-declaration-statement.js fails language/statements/const/block-local-use-before-initialization-in-prior-statement.js fails |