return false;
}
- // Reserve space for pseudo context-scope function
- _functions << program;
-
AST::UiObjectDefinition *rootObject = AST::cast<AST::UiObjectDefinition*>(program->members->member);
Q_ASSERT(rootObject);
output->indexOfRootObject = defineQMLObject(rootObject);
return true;
}
-QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output)
+QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output, const QVector<int> &runtimeFunctionIndices)
{
jsUnitGenerator = &output.jsGenerator;
const QmlObject *rootObject = output.objects.at(output.indexOfRootObject);
quint32 *functionsTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToFunctions);
for (Function *f = o->functions->first; f; f = f->next)
- *functionsTable++ = f->index;
+ *functionsTable++ = runtimeFunctionIndices[f->index];
char *propertiesPtr = objectPtr + objectToWrite->offsetToProperties;
for (QmlProperty *p = o->properties->first; p; p = p->next) {
for (Binding *b = o->bindings->first; b; b = b->next) {
QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr);
*bindingToWrite = *b;
+ if (b->type == QV4::CompiledData::Binding::Type_Script)
+ bindingToWrite->value.compiledScriptIndex = runtimeFunctionIndices[b->value.compiledScriptIndex];
bindingPtr += sizeof(QV4::CompiledData::Binding);
}
return jsUnitGenerator->getStringId(str);
}
-void JSCodeGen::generateJSCodeForFunctionsAndBindings(const QString &fileName, ParsedQML *output)
+QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QString &fileName, ParsedQML *output)
{
- _module = &output->jsModule;
+ return generateJSCodeForFunctionsAndBindings(fileName, output->code, &output->jsModule, &output->jsParserEngine,
+ output->program, output->functions);
+}
+
+QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QString &fileName, const QString &sourceCode, V4IR::Module *jsModule,
+ QQmlJS::Engine *jsEngine, AST::UiProgram *qmlRoot, const QList<AST::Node*> &functions)
+{
+ QVector<int> runtimeFunctionIndices(functions.size());
+ _module = jsModule;
_module->setFileName(fileName);
- QmlScanner scan(this, output->code);
- scan.begin(output->program, QmlBinding);
- foreach (AST::Node *node, output->functions) {
- if (node == output->program)
- continue;
+ QmlScanner scan(this, sourceCode);
+ scan.begin(qmlRoot, QmlBinding);
+ foreach (AST::Node *node, functions) {
+ Q_ASSERT(node != qmlRoot);
AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(node);
scan.enterEnvironment(node, function ? FunctionCode : QmlBinding);
scan.end();
_env = 0;
- _function = defineFunction(QString("context scope"), output->program, 0, 0);
+ _function = defineFunction(QString("context scope"), qmlRoot, 0, 0);
- foreach (AST::Node *node, output->functions) {
- if (node == output->program)
- continue;
+ for (int i = 0; i < functions.count(); ++i) {
+ AST::Node *node = functions.at(i);
+ Q_ASSERT(node != qmlRoot);
AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(node);
body = function->body ? function->body->elements : 0;
else {
// Synthesize source elements.
- QQmlJS::MemoryPool *pool = output->jsParserEngine.pool();
- AST::SourceElement *element = new (pool) AST::StatementSourceElement(static_cast<AST::Statement*>(node));
- body = new (output->jsParserEngine.pool()) AST::SourceElements(element);
+ QQmlJS::MemoryPool *pool = jsEngine->pool();
+
+ AST::Statement *stmt = node->statementCast();
+ if (!stmt) {
+ Q_ASSERT(node->expressionCast());
+ AST::ExpressionNode *expr = node->expressionCast();
+ stmt = new (pool) AST::ExpressionStatement(expr);
+ }
+ AST::SourceElement *element = new (pool) AST::StatementSourceElement(stmt);
+ body = new (pool) AST::SourceElements(element);
body = body->finish();
}
- defineFunction(name, node,
- function ? function->formals : 0,
- body);
-
+ V4IR::Function *irFunc = defineFunction(name, node,
+ function ? function->formals : 0,
+ body);
+ runtimeFunctionIndices[i] = _module->functions.indexOf(irFunc); // ###
}
qDeleteAll(_envMap);
_envMap.clear();
+ return runtimeFunctionIndices;
}
struct Binding : public QV4::CompiledData::Binding
{
+ // Binding's compiledScriptIndex is index in parsedQML::functions
Binding *next;
};
struct Function
{
- int index;
+ int index; // index in parsedQML::functions
Function *next;
};
{
}
- QV4::CompiledData::QmlUnit *generate(ParsedQML &output);
+ QV4::CompiledData::QmlUnit *generate(ParsedQML &output, const QVector<int> &runtimeFunctionIndices);
private:
int getStringId(const QString &str) const;
: QQmlJS::Codegen(/*strict mode*/false)
{}
- void generateJSCodeForFunctionsAndBindings(const QString &fileName, ParsedQML *output);
+ // Returns mapping from input functions to index in V4IR::Module::functions / compiledData->runtimeFunctions
+ QVector<int> generateJSCodeForFunctionsAndBindings(const QString &fileName, ParsedQML *output);
+ QVector<int> generateJSCodeForFunctionsAndBindings(const QString &fileName, const QString &sourceCode, V4IR::Module *jsModule,
+ QQmlJS::Engine *jsEngine, AST::UiProgram *qmlRoot, const QList<AST::Node*> &functions);
private:
struct QmlScanner : public ScanFunctions
JSCodeGen *codeGen;
};
-
- V4IR::Module jsModule;
};
} // namespace QtQml
if (member.function) {
V4IR::Function *function = defineFunction(member.function->name.toString(), member.function, member.function->formals,
member.function->body ? member.function->body->elements : 0);
- if (! _env->parent) {
+ if (! _env->parent || _env->compilationMode == QmlBinding) {
move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn),
_block->CLOSURE(function));
} else {
~Engine();
void setCode(const QString &code);
+ const QString &code() const { return _code; }
void addComment(int pos, int len, int line, int col);
QList<AST::SourceLocation> comments() const;
inline void deallocate();
inline void allocate(int size);
+ inline int capacity() const { return _alloc; }
+
inline bool isEmpty() const;
inline const T &top() const;
inline T &top();
{
Q_ASSERT(!hasEngine());
QQmlCleanup::addToEngine(engine);
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
+ if (compilationUnit && !compilationUnit->engine)
+ compilationUnit->linkToEngine(v4);
}
QT_END_NAMESPACE
#include "qqmlglobal_p.h"
#include "qqmlbinding_p.h"
#include "qqmlabstracturlinterceptor.h"
+#include "qqmlcodegenerator_p.h"
#include <QDebug>
#include <QPointF>
this->unit = unit;
this->unitRoot = root;
this->output = out;
+ this->functionsToCompile.clear();
// Compile types
const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes();
if (!buildObject(tree, BindingContext()) || !completeComponentBuild())
return;
+ const QQmlScript::Parser &parser = unit->parser();
+ QQmlJS::Engine *jsEngine = parser.jsEngine();
+ QQmlJS::MemoryPool *pool = jsEngine->pool();
+
+ foreach (JSBindingReference *root, allBindingReferenceRoots) {
+ for (JSBindingReference *b = root; b; b = b->nextReference) {
+ JSBindingReference &binding = *b;
+
+ QQmlJS::AST::Node *node = binding.expression.asAST();
+ // Always wrap this in an ExpressionStatement, to make sure that
+ // property var foo: function() { ... } results in a closure initialization.
+ if (!node->statementCast()) {
+ AST::ExpressionNode *expr = node->expressionCast();
+ node = new (pool) AST::ExpressionStatement(expr);
+ }
+
+ functionsToCompile.append(node);
+ binding.compiledIndex = functionsToCompile.count() - 1;
+ }
+ }
+
+ if (!functionsToCompile.isEmpty()) {
+ JSCodeGen jsCodeGen;
+
+ V4IR::Module jsModule;
+ const QString &sourceCode = jsEngine->code();
+ AST::UiProgram *qmlRoot = parser.qmlRoot();
+
+ const QVector<int> runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(unit->finalUrlString(), sourceCode, &jsModule, jsEngine, qmlRoot, functionsToCompile);
+
+ foreach (JSBindingReference *root, allBindingReferenceRoots) {
+ for (JSBindingReference *b = root; b; b = b->nextReference) {
+ JSBindingReference &binding = *b;
+ functionsToCompile.append(binding.expression.asAST());
+ binding.compiledIndex = runtimeFunctionIndices[binding.compiledIndex];
+ }
+ }
+
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
+ QV4::Compiler::JSUnitGenerator jsUnitGenerator(&jsModule);
+ QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, &jsModule, &jsUnitGenerator));
+ isel->setUseFastLookups(false);
+ QV4::CompiledData::CompilationUnit *jsUnit = isel->compile(/*generated unit data*/true);
+ output->compilationUnit = jsUnit;
+ output->compilationUnit->ref();
+ }
+
Instruction::Init init;
init.bindingsSize = compileState->totalBindingsCount;
init.parserStatusSize = compileState->parserStatusCount;
const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
Instruction::StoreBinding store;
- store.value = output->indexForString(js.expression.asScript());
+ store.functionIndex = js.compiledIndex;
store.context = js.bindingContext.stack;
store.owner = js.bindingContext.owner;
store.line = binding->location.start.line;
if (componentStats)
componentStats->componentStat.scriptBindings.append(b->value->location);
}
+ allBindingReferenceRoots.append(compileState->bindings.first());
// Check pop()'s matched push()'s
Q_ASSERT(compileState->objectDepth.depth() == 0);
#include "qqmltypenamecache_p.h"
#include "qqmltypeloader_p.h"
#include "private/qv4identifier_p.h"
+#include <private/qqmljsastfwd_p.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qset.h>
int cachedComponentTypeRef;
int cachedTranslationContextIndex;
+ QList<QQmlJS::AST::Node*> functionsToCompile;
+ QList<QQmlCompilerTypes::JSBindingReference*> allBindingReferenceRoots;
+
// Compiler component statistics. Only collected if QML_COMPILER_STATS=1
struct ComponentStat
{
qWarning().nospace() << idx << "\t\t" << "ASSIGN_CUSTOMTYPE\t" << instr->assignCustomType.propertyIndex << "\t" << instr->assignCustomType.primitive << "\t" << instr->assignCustomType.type;
break;
case QQmlInstruction::StoreBinding:
- qWarning().nospace() << idx << "\t\t" << "STORE_BINDING\t" << instr->assignBinding.property.coreIndex << "\t" << instr->assignBinding.value << "\t" << instr->assignBinding.context;
+ qWarning().nospace() << idx << "\t\t" << "STORE_BINDING\t" << instr->assignBinding.property.coreIndex << "\t" << instr->assignBinding.functionIndex << "\t" << instr->assignBinding.context;
break;
case QQmlInstruction::StoreValueSource:
qWarning().nospace() << idx << "\t\t" << "STORE_VALUE_SOURCE\t" << instr->assignValueSource.property.coreIndex << "\t" << instr->assignValueSource.castValue;
struct instr_assignBinding {
QML_INSTR_HEADER
QQmlPropertyRawData property;
- int value;
+ int functionIndex; // index in CompiledData::runtimeFunctions
short context;
short owner;
bool isRoot:1;
, _vmeMetaObject(0)
, _qmlContext(0)
{
- QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
- if (compiledData->compilationUnit && !compiledData->compilationUnit->engine)
- compiledData->compilationUnit->linkToEngine(v4);
-
+ if (!compiledData->isInitialized())
+ compiledData->initialize(engine);
}
QObject *QmlObjectCreator::create(int subComponentIndex, QObject *parent)
QQmlScript::Parser::Parser()
-: root(0), data(0)
+: root(0), _qmlRoot(0), data(0)
{
}
_errors[ii].setUrl(url);
}
+ _qmlRoot = parser.ast();
+
return _errors.isEmpty();
}
}
_pool.clear();
+ _qmlRoot = 0;
}
int QQmlScript::Parser::findOrCreateTypeId(const QString &name, Object *object)
root = tree;
}
+Engine *QQmlScript::Parser::jsEngine() const
+{
+ return data ? &data->engine : 0;
+}
+
QT_END_NAMESPACE
class QByteArray;
class QQmlPropertyCache;
-namespace QQmlJS { namespace AST { class Node; class StringLiteral; } }
+namespace QQmlJS { class Engine; namespace AST { class Node; class StringLiteral; class UiProgram; } }
namespace QQmlCompilerTypes { struct BindingReference; struct ComponentCompileState; }
namespace QQmlScript {
void setScriptFile(const QString &filename) {_scriptFile = filename; }
QString scriptFile() const { return _scriptFile; }
+ QQmlJS::AST::UiProgram *qmlRoot() const { return _qmlRoot; }
+ QQmlJS::Engine *jsEngine() const;
+
// ### private:
QList<QQmlError> _errors;
QList<Pragma> _pragmas;
QList<TypeReference*> _refTypes;
QString _scriptFile;
+ QQmlJS::AST::UiProgram *_qmlRoot;
ParserJsASTData *data;
};
// Compile JS binding expressions and signal handlers
JSCodeGen jsCodeGen;
- jsCodeGen.generateJSCodeForFunctionsAndBindings(finalUrlString(), parsedQML.data());
+ const QVector<int> runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(finalUrlString(), parsedQML.data());
QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine());
// Generate QML compiled type data structures
QmlUnitGenerator qmlGenerator;
- QV4::CompiledData::QmlUnit *qmlUnit = qmlGenerator.generate(*parsedQML.data());
+ QV4::CompiledData::QmlUnit *qmlUnit = qmlGenerator.generate(*parsedQML.data(), runtimeFunctionIndices);
if (jsUnit) {
Q_ASSERT(!jsUnit->data);
return QQmlStringConverters::variantFromString(string);
}
+static QV4::ExecutionContext *qmlBindingContext(QQmlEngine *engine, QV4::ExecutionEngine *v4, QV4::SafeValue *qmlBindingWrappers, QQmlContextData *context, QObject *scope, int objIdx)
+{
+ QV4::Scope valueScope(v4);
+ QV4::Scoped<QV4::QmlBindingWrapper> wrapper(valueScope, qmlBindingWrappers[objIdx]);
+ if (!wrapper) {
+ QV4::ScopedObject scopeObject(valueScope, QV4::QmlContextWrapper::qmlScope(QV8Engine::get(engine), context, scope));
+ wrapper = new (v4->memoryManager) QV4::QmlBindingWrapper(v4->rootContext, scopeObject);
+ qmlBindingWrappers[objIdx] = wrapper;
+ }
+ return wrapper->context();
+}
+
// XXX we probably need some form of "work count" here to prevent us checking this
// for every instruction.
#define QML_BEGIN_INSTR_COMMON(I) { \
QV4::ExecutionEngine *v4 = ep->v4engine();
QV4::Scope valueScope(v4);
QV4::ScopedValue tmpValue(valueScope);
+ QV4::SafeValue *qmlBindingWrappers = valueScope.alloc(objects.capacity());
+ std::fill(qmlBindingWrappers, qmlBindingWrappers + objects.capacity(), QV4::Primitive::undefinedValue());
int status = -1; // needed for dbus
QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::BypassInterceptor |
ddata->outerContext = CTXT;
ddata->lineNumber = instr.line;
ddata->columnNumber = instr.column;
+ qmlBindingWrappers[objects.count() - 1] = QV4::Primitive::undefinedValue();
QML_END_INSTR(CompleteQMLObject)
QML_BEGIN_INSTR(CreateCppObject)
ddata->parentFrozen = true;
}
objects.push(o);
+ qmlBindingWrappers[objects.count() - 1] = QV4::Primitive::undefinedValue();
QML_END_INSTR(CreateCppObject)
QML_BEGIN_INSTR(CreateSimpleObject)
ddata->parentFrozen = true;
objects.push(o);
+ qmlBindingWrappers[objects.count() - 1] = QV4::Primitive::undefinedValue();
QML_END_INSTR(CreateSimpleObject)
QML_BEGIN_INSTR(SetId)
QQmlComponentPrivate::get(qcomp)->creationContext = CTXT;
objects.push(qcomp);
+ qmlBindingWrappers[objects.count() - 1] = QV4::Primitive::undefinedValue();
INSTRUCTIONSTREAM += instr.count;
QML_END_INSTR(CreateComponent)
if (instr.isRoot && BINDINGSKIPLIST.testBit(instr.property.coreIndex))
QML_NEXT_INSTR(StoreBinding);
- QQmlBinding *bind = new QQmlBinding(PRIMITIVES.at(instr.value),
- context, CTXT, COMP->name, instr.line,
- instr.column);
+ QV4::ExecutionContext *qmlContext = qmlBindingContext(engine, QV8Engine::getV4(engine), qmlBindingWrappers, CTXT, context, objects.count() - 1 - instr.context);
+
+ QV4::Function *runtimeFunction = COMP->compilationUnit->runtimeFunctions[instr.functionIndex];
+
+ tmpValue = QV4::FunctionObject::creatScriptFunction(qmlContext, runtimeFunction);
+
+ QQmlBinding *bind = new QQmlBinding(tmpValue, context, CTXT, COMP->name, instr.line, instr.column);
bindValues.push(bind);
bind->m_mePtr = &bindValues.top();
bind->setTarget(target, instr.property, CTXT);
VME_EXCEPTION(tr("Unable to create attached object"), instr.line);
objects.push(qmlObject);
+ qmlBindingWrappers[objects.count() - 1] = QV4::Primitive::undefinedValue();
QML_END_INSTR(FetchAttached)
QML_BEGIN_INSTR(FetchQList)
VME_EXCEPTION(tr("Cannot set properties on %1 as it is null").arg(QString::fromUtf8(target->metaObject()->property(instr.property).name())), instr.line);
objects.push(obj);
+ qmlBindingWrappers[objects.count() - 1] = QV4::Primitive::undefinedValue();
QML_END_INSTR(FetchObject)
QML_BEGIN_INSTR(PopQList)
Q_ASSERT(valueHandler);
valueHandler->read(target, instr.property);
objects.push(valueHandler);
+ qmlBindingWrappers[objects.count() - 1] = QV4::Primitive::undefinedValue();
QML_END_INSTR(FetchValueType)
QML_BEGIN_INSTR(PopValueType)
QmlDebugPropertyReference onEnteredRef = findProperty(mouseAreaObject.properties, "onEntered");
QCOMPARE(onEnteredRef.name, QString("onEntered"));
- QCOMPARE(onEnteredRef.value, QVariant("{ console.log('hello') }"));
+ // Sorry, can't do that anymore: QCOMPARE(onEnteredRef.value, QVariant("{ console.log('hello') }"));
+ QCOMPARE(onEnteredRef.value, QVariant("function() { [code] }"));
m_dbg->setBindingForObject(mouseAreaObject.debugId, "onEntered",
"{console.log('hello, world') }", false,
QTest::addColumn<QString>("file");
foreach (const QString &file, files) {
+ // Skip, QTBUG-34047
+ if (file.endsWith(QStringLiteral("tests/ecma_3/Statements/regress-324650.js")))
+ continue;
+ if (file.endsWith(QStringLiteral("tests/ecma_3/Statements/regress-74474-002.js")))
+ continue;
+ if (file.endsWith(QStringLiteral("tests/ecma_3/Statements/regress-74474-003.js")))
+ continue;
QTest::newRow(qPrintable(file)) << file;
}
}
{
QQmlCompiledData::Instruction::StoreBinding i;
i.property.coreIndex = 26;
- i.value = 3;
+ i.functionIndex = 3;
i.context = 2;
i.owner = 0;
data->addInstruction(i);