aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorYulong Bai <yulong.bai@qt.io>2018-06-15 14:44:19 +0200
committerLars Knoll <lars.knoll@qt.io>2018-06-26 10:03:56 +0000
commit74f4065caa7254b0e30afa955aa59092024decdb (patch)
treeb9b5555807ebfb2e7b6a80a56aec8785b4066ae1 /src/qml
parentc9fa5b8071f4a32afd05cc9fa5cd8ab28e60dc55 (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')
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp3
-rw-r--r--src/qml/compiler/qv4codegen.cpp127
-rw-r--r--src/qml/compiler/qv4codegen_p.h2
-rw-r--r--src/qml/compiler/qv4compileddata_p.h38
-rw-r--r--src/qml/compiler/qv4compiler.cpp70
-rw-r--r--src/qml/compiler/qv4compiler_p.h3
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h19
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp22
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h3
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp6
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h2
-rw-r--r--src/qml/jit/qv4baselinejit.cpp11
-rw-r--r--src/qml/jit/qv4baselinejit_p.h1
-rw-r--r--src/qml/jsruntime/qv4engine.cpp2
-rw-r--r--src/qml/jsruntime/qv4enginebase_p.h1
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp12
-rw-r--r--src/qml/jsruntime/qv4functionobject_p.h10
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp61
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h1
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp4
20 files changed, 376 insertions, 22 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)