aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-10-15 10:22:47 +0200
committerLars Knoll <lars.knoll@qt.io>2018-11-05 21:15:39 +0000
commitb1f07986c30978ed6636457e8d3f4a65c5db38a3 (patch)
tree6dd48a2901219b4268a95b9d5acd08a3c49988c0 /src/qml/compiler
parent0fce92af2cab51d03f33230718ab5ae35149b9e1 (diff)
Create proper template objects for tagged templates
If a tagged template gets evaluated multiple times, the underlying template object is shared. Change-Id: Ie2f476fbc93d5991322ce1087c42719a8d8333ae Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml/compiler')
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp3
-rw-r--r--src/qml/compiler/qv4codegen.cpp78
-rw-r--r--src/qml/compiler/qv4codegen_p.h2
-rw-r--r--src/qml/compiler/qv4compileddata.cpp34
-rw-r--r--src/qml/compiler/qv4compileddata_p.h42
-rw-r--r--src/qml/compiler/qv4compiler.cpp53
-rw-r--r--src/qml/compiler/qv4compiler_p.h2
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h9
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp4
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h2
10 files changed, 160 insertions, 69 deletions
diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp
index 9cf96d27f3..af86b70014 100644
--- a/src/qml/compiler/qv4bytecodehandler.cpp
+++ b/src/qml/compiler/qv4bytecodehandler.cpp
@@ -537,6 +537,9 @@ std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint
COLLECTOR_BEGIN_INSTR(ThrowOnNullOrUndefined)
COLLECTOR_END_INSTR(ThrowOnNullOrUndefined)
+ COLLECTOR_BEGIN_INSTR(GetTemplateObject)
+ COLLECTOR_END_INSTR(GetTemplateObject)
+
COLLECTOR_BEGIN_INSTR(LoadQmlContext)
COLLECTOR_END_INSTR(LoadQmlContext)
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index ac596e2b5b..09c0e60f4d 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -2243,85 +2243,35 @@ bool Codegen::visit(TaggedTemplate *ast)
break;
}
- int arrayTemp = createTemplateArray(ast->templateLiteral);
- Q_UNUSED(arrayTemp);
+ createTemplateObject(ast->templateLiteral);
+ int templateObjectTemp = Reference::fromAccumulator(this).storeOnStack().stackSlot();
+ Q_UNUSED(templateObjectTemp);
auto calldata = pushTemplateArgs(ast->templateLiteral);
if (hasError)
return false;
++calldata.argc;
- Q_ASSERT(calldata.argv == arrayTemp + 1);
+ Q_ASSERT(calldata.argv == templateObjectTemp + 1);
--calldata.argv;
handleCall(base, calldata, functionObject, thisObject);
return false;
}
-int Codegen::createTemplateArray(TemplateLiteral *t)
+void Codegen::createTemplateObject(TemplateLiteral *t)
{
- int arrayTemp = bytecodeGenerator->newRegister();
+ TemplateObject obj;
- int argc = 0;
- int args = -1;
- auto push = [this, &argc, &args](const QStringRef &arg) {
- int temp = bytecodeGenerator->newRegister();
- if (args == -1)
- args = temp;
- Instruction::LoadRuntimeString instr;
- instr.stringId = registerString(arg.toString());
- bytecodeGenerator->addInstruction(instr);
- Instruction::StoreReg store;
- store.reg = temp;
- bytecodeGenerator->addInstruction(store);
-
- ++argc;
- };
-
- {
- RegisterScope scope(this);
-
- for (TemplateLiteral *it = t; it; it = it->next)
- push(it->value);
-
- if (args == -1) {
- Q_ASSERT(argc == 0);
- args = 0;
- }
-
- Instruction::DefineArray call;
- call.argc = argc;
- call.args = Moth::StackSlot::createRegister(args);
- bytecodeGenerator->addInstruction(call);
-
- Instruction::StoreReg store;
- store.reg = arrayTemp;
- bytecodeGenerator->addInstruction(store);
+ for (TemplateLiteral *it = t; it; it = it->next) {
+ obj.strings.append(registerString(it->value.toString()));
+ obj.rawStrings.append(registerString(it->rawValue.toString()));
}
- {
- RegisterScope scope(this);
-
- argc = 0;
- args = -1;
-
- for (TemplateLiteral *it = t; it; it = it->next)
- push(it->rawValue);
-
- if (args == -1) {
- Q_ASSERT(argc == 0);
- args = 0;
- }
-
- Instruction::DefineArray call;
- call.argc = argc;
- call.args = Moth::StackSlot::createRegister(args);
- bytecodeGenerator->addInstruction(call);
-
- Reference a = Reference::fromStackSlot(this, arrayTemp);
- Reference m = Reference::fromMember(a, QStringLiteral("raw"));
- m.storeConsumeAccumulator();
- }
+ int index = _module->templateObjects.size();
+ _module->templateObjects.append(obj);
- return arrayTemp;
+ Instruction::GetTemplateObject getTemplateObject;
+ getTemplateObject.index = index;
+ bytecodeGenerator->addInstruction(getTemplateObject);
}
bool Codegen::visit(FunctionExpression *ast)
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index e6e7d2e9fb..0bc04750f7 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -682,7 +682,7 @@ public:
void handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject);
Arguments pushTemplateArgs(AST::TemplateLiteral *args);
- int createTemplateArray(AST::TemplateLiteral *t);
+ void createTemplateObject(AST::TemplateLiteral *t);
void setUseFastLookups(bool b) { useFastLookups = b; }
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index 244e762faf..3b2d6e0a48 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -225,6 +225,36 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
return nullptr;
}
+Heap::Object *CompilationUnit::templateObjectAt(int index) const
+{
+ Q_ASSERT(index < int(data->templateObjectTableSize));
+ if (!templateObjects.size())
+ templateObjects.resize(data->templateObjectTableSize);
+ Heap::Object *o = templateObjects.at(index);
+ if (o)
+ return o;
+
+ // create the template object
+ Scope scope(engine);
+ const CompiledData::TemplateObject *t = data->templateObjectAt(index);
+ Scoped<ArrayObject> a(scope, engine->newArrayObject(t->size));
+ Scoped<ArrayObject> raw(scope, engine->newArrayObject(t->size));
+ ScopedValue s(scope);
+ for (uint i = 0; i < t->size; ++i) {
+ s = runtimeStrings[t->stringIndexAt(i)];
+ a->arraySet(i, s);
+ s = runtimeStrings[t->rawStringIndexAt(i)];
+ raw->arraySet(i, s);
+ }
+
+ ObjectPrototype::method_freeze(engine->functionCtor(), nullptr, raw, 1);
+ a->defineReadonlyProperty(QStringLiteral("raw"), raw);
+ ObjectPrototype::method_freeze(engine->functionCtor(), nullptr, a, 1);
+
+ templateObjects[index] = a->objectValue()->d();
+ return templateObjects.at(index);
+}
+
void CompilationUnit::unlink()
{
if (engine)
@@ -284,6 +314,10 @@ void CompilationUnit::markObjects(QV4::MarkStack *markStack)
if (c)
c->mark(markStack);
+ for (QV4::Heap::Object *o : qAsConst(templateObjects))
+ if (o)
+ o->mark(markStack);
+
if (runtimeLookups) {
for (uint i = 0; i < data->lookupTableSize; ++i)
runtimeLookups[i].markObjects(markStack);
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index b36b1a91ea..d0785c6883 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -373,6 +373,34 @@ struct Class
};
static_assert(sizeof(Class) == 24, "Class structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+struct TemplateObject
+{
+ quint32_le size;
+
+ static int calculateSize(int size) {
+ int trailingData = 2 * size * sizeof(quint32_le);
+ size_t s = align(sizeof(TemplateObject) + trailingData);
+ Q_ASSERT(s < INT_MAX);
+ return int(s);
+ }
+
+ static size_t align(size_t a) {
+ return (a + 7) & ~size_t(7);
+ }
+
+ const quint32_le *stringTable() const {
+ return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this + 1));
+ }
+
+ uint stringIndexAt(uint i) const {
+ return stringTable()[i];
+ }
+ uint rawStringIndexAt(uint i) const {
+ return stringTable()[size + i];
+ }
+};
+static_assert(sizeof(TemplateObject) == 4, "Template object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
struct ExportEntry
{
quint32_le exportName;
@@ -841,6 +869,8 @@ struct Unit
quint32_le offsetToFunctionTable;
quint32_le classTableSize;
quint32_le offsetToClassTable;
+ quint32_le templateObjectTableSize;
+ quint32_le offsetToTemplateObjectTable;
quint32_le blockTableSize;
quint32_le offsetToBlockTable;
quint32_le lookupTableSize;
@@ -908,6 +938,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 *templateObjectOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToTemplateObjectTable); }
const quint32_le *blockOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToBlockTable); }
const Function *functionAt(int idx) const {
@@ -922,6 +953,12 @@ struct Unit
return reinterpret_cast<const Class *>(reinterpret_cast<const char *>(this) + offset);
}
+ const TemplateObject *templateObjectAt(int idx) const {
+ const quint32_le *offsetTable = templateObjectOffsetTable();
+ const quint32_le offset = offsetTable[idx];
+ return reinterpret_cast<const TemplateObject *>(reinterpret_cast<const char *>(this) + offset);
+ }
+
const Block *blockAt(int idx) const {
const quint32_le *offsetTable = blockOffsetTable();
const quint32_le offset = offsetTable[idx];
@@ -957,7 +994,7 @@ struct Unit
const quint32_le *moduleRequestTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToModuleRequestTable); }
};
-static_assert(sizeof(Unit) == 240, "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) == 248, "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
{
@@ -1110,6 +1147,7 @@ public:
QV4::Lookup *runtimeLookups = nullptr;
QVector<QV4::Function *> runtimeFunctions;
QVector<QV4::Heap::InternalClass *> runtimeBlocks;
+ mutable QVector<QV4::Heap::Object *> templateObjects;
mutable QQmlNullableValue<QUrl> m_url;
mutable QQmlNullableValue<QUrl> m_finalUrl;
@@ -1160,6 +1198,8 @@ public:
return data->stringAtInternal(index);
}
+ Heap::Object *templateObjectAt(int index) const;
+
struct FunctionIterator
{
FunctionIterator(const Unit *unit, const Object *object, int index) : unit(unit), object(object), index(index) {}
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 7636baa1e6..3076c6b526 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -269,7 +269,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
registerString(request);
}
- Q_ALLOCA_VAR(quint32_le, blockClassAndFunctionOffsets, (module->functions.size() + module->classes.size() + module->blocks.size()) * sizeof(quint32_le));
+ Q_ALLOCA_VAR(quint32_le, blockClassAndFunctionOffsets, (module->functions.size() + module->classes.size() + module->templateObjects.size() + module->blocks.size()) * sizeof(quint32_le));
uint jsClassDataOffset = 0;
char *dataPtr;
@@ -284,7 +284,8 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
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));
+ memcpy(dataPtr + unit->offsetToTemplateObjectTable, blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize, unit->templateObjectTableSize * sizeof(quint32_le));
+ memcpy(dataPtr + unit->offsetToBlockTable, blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize + unit->templateObjectTableSize, unit->blockTableSize * sizeof(quint32_le));
for (int i = 0; i < module->functions.size(); ++i) {
Context *function = module->functions.at(i);
@@ -300,10 +301,16 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
writeClass(dataPtr + blockClassAndFunctionOffsets[i + module->functions.size()], c);
}
+ for (int i = 0; i < module->templateObjects.size(); ++i) {
+ const TemplateObject &t = module->templateObjects.at(i);
+
+ writeTemplateObject(dataPtr + blockClassAndFunctionOffsets[i + module->functions.size() + module->classes.size()], t);
+ }
+
for (int i = 0; i < module->blocks.size(); ++i) {
Context *block = module->blocks.at(i);
- writeBlock(dataPtr + blockClassAndFunctionOffsets[i + module->classes.size() + module->functions.size()], block);
+ writeBlock(dataPtr + blockClassAndFunctionOffsets[i + module->classes.size() + module->templateObjects.size() + module->functions.size()], block);
}
CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable);
@@ -532,6 +539,34 @@ void QV4::Compiler::JSUnitGenerator::writeClass(char *b, const QV4::Compiler::Cl
}
}
+void QV4::Compiler::JSUnitGenerator::writeTemplateObject(char *b, const QV4::Compiler::TemplateObject &t)
+{
+ QV4::CompiledData::TemplateObject *tmpl = reinterpret_cast<QV4::CompiledData::TemplateObject *>(b);
+ tmpl->size = t.strings.size();
+
+ quint32 currentOffset = sizeof(QV4::CompiledData::TemplateObject);
+
+ quint32_le *strings = reinterpret_cast<quint32_le *>(b + currentOffset);
+
+ // write methods
+ for (int i = 0; i < t.strings.size(); ++i)
+ strings[i] = t.strings.at(i);
+ strings += t.strings.size();
+
+ for (int i = 0; i < t.rawStrings.size(); ++i)
+ strings[i] = t.rawStrings.at(i);
+
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
+ if (showCode) {
+ qDebug() << "=== TemplateObject size" << tmpl->size;
+ for (uint i = 0; i < tmpl->size; ++i) {
+ qDebug() << " " << i << stringForIndex(tmpl->stringIndexAt(i));
+ qDebug() << " raw: " << stringForIndex(tmpl->rawStringIndexAt(i));
+ }
+ qDebug();
+ }
+}
+
void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context *irBlock) const
{
QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b);
@@ -580,6 +615,10 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.offsetToClassTable = nextOffset;
nextOffset += unit.classTableSize * sizeof(uint);
+ unit.templateObjectTableSize = module->templateObjects.size();
+ unit.offsetToTemplateObjectTable = nextOffset;
+ nextOffset += unit.templateObjectTableSize * sizeof(uint);
+
unit.blockTableSize = module->blocks.size();
unit.offsetToBlockTable = nextOffset;
nextOffset += unit.blockTableSize * sizeof(uint);
@@ -658,6 +697,14 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
}
blockAndFunctionOffsets += module->classes.size();
+ for (int i = 0; i < module->templateObjects.size(); ++i) {
+ const TemplateObject &t = module->templateObjects.at(i);
+ blockAndFunctionOffsets[i] = nextOffset;
+
+ nextOffset += QV4::CompiledData::TemplateObject::calculateSize(t.strings.size());
+ }
+ blockAndFunctionOffsets += module->templateObjects.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 c4c886ffad..2f5889ab53 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -73,6 +73,7 @@ struct JSClassMember;
namespace Compiler {
struct Class;
+struct TemplateObject;
struct Q_QML_PRIVATE_EXPORT StringTableGenerator {
StringTableGenerator();
@@ -137,6 +138,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 writeTemplateObject(char *f, const TemplateObject &o);
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 d36ef0f447..328715da07 100644
--- a/src/qml/compiler/qv4compilercontext_p.h
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -99,6 +99,14 @@ struct Class {
QVector<Method> methods;
};
+struct TemplateObject {
+ QVector<uint> strings;
+ QVector<uint> rawStrings;
+ bool operator==(const TemplateObject &other) {
+ return strings == other.strings && rawStrings == other.rawStrings;
+ }
+};
+
struct ExportEntry
{
QString exportName;
@@ -133,6 +141,7 @@ struct Module {
QList<Context *> functions;
QList<Context *> blocks;
QVector<Class> classes;
+ QVector<TemplateObject> templateObjects;
Context *rootContext;
QString fileName;
QString finalUrl;
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index eed8ffe6b8..6edf5a4ae7 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -712,6 +712,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_BEGIN_INSTR(ThrowOnNullOrUndefined)
MOTH_END_INSTR(ThrowOnNullOrUndefined)
+ MOTH_BEGIN_INSTR(GetTemplateObject)
+ d << index;
+ MOTH_END_INSTR(GetTemplateObject)
+
MOTH_BEGIN_INSTR(LoadQmlContext)
d << dumpRegister(result, nFormals);
MOTH_END_INSTR(LoadQmlContext)
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index 2b1660ee58..2ca8f692b8 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -198,6 +198,7 @@ QT_BEGIN_NAMESPACE
#define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result)
#define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count)
#define INSTR_ThrowOnNullOrUndefined(op) INSTRUCTION(op, ThrowOnNullOrUndefined, 0)
+#define INSTR_GetTemplateObject(op) INSTRUCTION(op, GetTemplateObject, 1, index)
#define INSTR_TailCall(op) INSTRUCTION(op, TailCall, 4, func, thisObject, argc, argv)
#define FOR_EACH_MOTH_INSTR_ALL(F) \
@@ -339,6 +340,7 @@ QT_BEGIN_NAMESPACE
F(PopScriptContext) \
F(InitializeBlockDeadTemporalZone) \
F(ThrowOnNullOrUndefined) \
+ F(GetTemplateObject) \
F(TailCall) \
F(Debug) \