aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--tests/auto/qml/ecmascripttests/TestExpectations30
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