Skip to content

Commit

Permalink
Detect and optimize qsTr() and qsTrId() bindings
Browse files Browse the repository at this point in the history
As these two are frequently used with constants, we can detect them in
the compiler, and run the appropriate C++ functions directly in the VME.
This saves pointlessly creating and running bindings.

Change-Id: I148a150400c13fda7955949453405202f18b1a6b
Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
  • Loading branch information
Aaron Kennedy authored and Qt by Nokia committed Dec 21, 2011
1 parent 8249c72 commit b6c8497
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 40 deletions.
173 changes: 146 additions & 27 deletions src/declarative/qml/qdeclarativecompiler.cpp
Expand Up @@ -83,6 +83,7 @@ QT_BEGIN_NAMESPACE
DEFINE_BOOL_CONFIG_OPTION(compilerDump, QML_COMPILER_DUMP);
DEFINE_BOOL_CONFIG_OPTION(compilerStatDump, QML_COMPILER_STATS);

using namespace QDeclarativeJS;
using namespace QDeclarativeScript;
using namespace QDeclarativeCompilerTypes;

Expand All @@ -91,13 +92,15 @@ static QString on_string(QLatin1String("on"));
static QString Changed_string(QLatin1String("Changed"));
static QString Component_string(QLatin1String("Component"));
static QString Component_import_string(QLatin1String("QML/Component"));
static QString qsTr_string(QLatin1String("qsTr"));
static QString qsTrId_string(QLatin1String("qsTrId"));

/*!
Instantiate a new QDeclarativeCompiler.
*/
QDeclarativeCompiler::QDeclarativeCompiler(QDeclarativePool *pool)
: pool(pool), output(0), engine(0), unitRoot(0), unit(0), cachedComponentTypeRef(-1),
componentStats(0)
cachedTranslationContextIndex(-1), componentStats(0)
{
if (compilerStatDump())
componentStats = pool->New<ComponentStats>();
Expand Down Expand Up @@ -832,6 +835,7 @@ bool QDeclarativeCompiler::compile(QDeclarativeEngine *engine,
this->enginePrivate = 0;
this->unit = 0;
this->cachedComponentTypeRef = -1;
this->cachedTranslationContextIndex = -1;
this->unitRoot = 0;

return !isError();
Expand Down Expand Up @@ -1606,6 +1610,20 @@ int QDeclarativeCompiler::componentTypeRef()
return cachedComponentTypeRef;
}

int QDeclarativeCompiler::translationContextIndex()
{
if (cachedTranslationContextIndex == -1) {
// This code must match that in the qsTr() implementation
QString path = output->url.toString();
int lastSlash = path.lastIndexOf(QLatin1Char('/'));
QString context = (lastSlash > -1) ? path.mid(lastSlash + 1, path.length()-lastSlash-5) :
QString();
QByteArray contextUtf8 = context.toUtf8();
cachedTranslationContextIndex = output->indexForByteArray(contextUtf8);
}
return cachedTranslationContextIndex;
}

bool QDeclarativeCompiler::buildSignal(QDeclarativeScript::Property *prop, QDeclarativeScript::Object *obj,
const BindingContext &ctxt)
{
Expand Down Expand Up @@ -2028,7 +2046,7 @@ void QDeclarativeCompiler::addId(const QString &id, QDeclarativeScript::Object *
compileState->ids.append(obj);
}

void QDeclarativeCompiler::addBindingReference(BindingReference *ref)
void QDeclarativeCompiler::addBindingReference(JSBindingReference *ref)
{
Q_ASSERT(ref->value && !ref->value->bindingReference);
ref->value->bindingReference = ref;
Expand Down Expand Up @@ -2185,7 +2203,7 @@ bool QDeclarativeCompiler::buildValueTypeProperty(QObject *type,
if (isEnumAssignment) {
value->type = Value::Literal;
} else {
BindingReference *reference = pool->New<BindingReference>();
JSBindingReference *reference = pool->New<JSBindingReference>();
reference->expression = value->value;
reference->property = prop;
reference->value = value;
Expand Down Expand Up @@ -2456,7 +2474,9 @@ bool QDeclarativeCompiler::buildPropertyLiteralAssignment(QDeclarativeScript::Pr
}
}

COMPILE_CHECK(buildBinding(v, prop, ctxt));
// Test for other binding optimizations
if (!buildLiteralBinding(v, prop, ctxt))
COMPILE_CHECK(buildBinding(v, prop, ctxt));

v->type = Value::PropertyBinding;

Expand Down Expand Up @@ -3294,7 +3314,7 @@ bool QDeclarativeCompiler::buildBinding(QDeclarativeScript::Value *value,
if (!prop->core.isWritable() && !prop->core.isQList() && !prop->isReadOnlyDeclaration)
COMPILE_EXCEPTION(prop, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));

BindingReference *reference = pool->New<BindingReference>();
JSBindingReference *reference = pool->New<JSBindingReference>();
reference->expression = value->value;
reference->property = prop;
reference->value = value;
Expand All @@ -3304,6 +3324,79 @@ bool QDeclarativeCompiler::buildBinding(QDeclarativeScript::Value *value,
return true;
}

bool QDeclarativeCompiler::buildLiteralBinding(QDeclarativeScript::Value *v,
QDeclarativeScript::Property *prop,
const QDeclarativeCompilerTypes::BindingContext &)
{
Q_ASSERT(v->value.isScript());

if (!prop->core.isWritable())
return false;

AST::Node *binding = v->value.asAST();

if (prop->type == QVariant::String) {
if (AST::CallExpression *e = AST::cast<AST::CallExpression *>(binding)) {
if (AST::IdentifierExpression *i = AST::cast<AST::IdentifierExpression *>(e->base)) {
if (i->name == qsTrId_string) {
AST::ArgumentList *arg1 = e->arguments?e->arguments:0;
AST::ArgumentList *arg2 = arg1?arg1->next:0;

if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral &&
(!arg2 || arg2->expression->kind == AST::Node::Kind_NumericLiteral) &&
(!arg2 || !arg2->next)) {

QStringRef text;
int n = -1;

text = AST::cast<AST::StringLiteral *>(arg1->expression)->value;
if (arg2) n = (int)AST::cast<AST::NumericLiteral *>(arg2->expression)->value;

TrBindingReference *reference = pool->New<TrBindingReference>();
reference->dataType = BindingReference::TrId;
reference->text = text;
reference->n = n;
v->bindingReference = reference;
return true;
}

} else if (i->name == qsTr_string) {

AST::ArgumentList *arg1 = e->arguments?e->arguments:0;
AST::ArgumentList *arg2 = arg1?arg1->next:0;
AST::ArgumentList *arg3 = arg2?arg2->next:0;

if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral &&
(!arg2 || arg2->expression->kind == AST::Node::Kind_StringLiteral) &&
(!arg3 || arg3->expression->kind == AST::Node::Kind_NumericLiteral) &&
(!arg3 || !arg3->next)) {

QStringRef text;
QStringRef comment;
int n = -1;

text = AST::cast<AST::StringLiteral *>(arg1->expression)->value;
if (arg2) comment = AST::cast<AST::StringLiteral *>(arg2->expression)->value;
if (arg3) n = (int)AST::cast<AST::NumericLiteral *>(arg3->expression)->value;

TrBindingReference *reference = pool->New<TrBindingReference>();
reference->dataType = BindingReference::Tr;
reference->text = text;
reference->comment = comment;
reference->n = n;
v->bindingReference = reference;
return true;
}

}
}
}

}

return false;
}

void QDeclarativeCompiler::genBindingAssignment(QDeclarativeScript::Value *binding,
QDeclarativeScript::Property *prop,
QDeclarativeScript::Object *obj,
Expand All @@ -3313,11 +3406,31 @@ void QDeclarativeCompiler::genBindingAssignment(QDeclarativeScript::Value *bindi
Q_ASSERT(binding->bindingReference);

const BindingReference &ref = *binding->bindingReference;
if (ref.dataType == BindingReference::V4) {
if (ref.dataType == BindingReference::TrId) {
const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref);

Instruction::StoreTrIdString store;
store.propertyIndex = prop->core.coreIndex;
store.text = output->indexForByteArray(tr.text.toUtf8());
store.n = tr.n;
output->addInstruction(store);
} else if (ref.dataType == BindingReference::Tr) {
const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref);

Instruction::StoreTrString store;
store.propertyIndex = prop->core.coreIndex;
store.context = translationContextIndex();
store.text = output->indexForByteArray(tr.text.toUtf8());
store.comment = output->indexForByteArray(tr.comment.toUtf8());
store.n = tr.n;
output->addInstruction(store);
} else if (ref.dataType == BindingReference::V4) {
const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);

Instruction::StoreV4Binding store;
store.value = ref.compiledIndex;
store.context = ref.bindingContext.stack;
store.owner = ref.bindingContext.owner;
store.value = js.compiledIndex;
store.context = js.bindingContext.stack;
store.owner = js.bindingContext.owner;
if (valueTypeProperty) {
store.property = (valueTypeProperty->index & 0xFFFF) |
((valueTypeProperty->type & 0xFF)) << 16 |
Expand All @@ -3331,10 +3444,12 @@ void QDeclarativeCompiler::genBindingAssignment(QDeclarativeScript::Value *bindi
store.column = binding->location.start.column;
output->addInstruction(store);
} else if (ref.dataType == BindingReference::V8) {
const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);

Instruction::StoreV8Binding store;
store.value = ref.compiledIndex;
store.context = ref.bindingContext.stack;
store.owner = ref.bindingContext.owner;
store.value = js.compiledIndex;
store.context = js.bindingContext.stack;
store.owner = js.bindingContext.owner;
if (valueTypeProperty) {
store.isRoot = (compileState->root == valueTypeProperty->parent);
} else {
Expand All @@ -3343,20 +3458,22 @@ void QDeclarativeCompiler::genBindingAssignment(QDeclarativeScript::Value *bindi
store.line = binding->location.start.line;
store.column = binding->location.start.column;

Q_ASSERT(ref.bindingContext.owner == 0 ||
(ref.bindingContext.owner != 0 && valueTypeProperty));
if (ref.bindingContext.owner) {
Q_ASSERT(js.bindingContext.owner == 0 ||
(js.bindingContext.owner != 0 && valueTypeProperty));
if (js.bindingContext.owner) {
store.property = genValueTypeData(prop, valueTypeProperty);
} else {
store.property = prop->core;
}

output->addInstruction(store);
} else {
} else if (ref.dataType == BindingReference::QtScript) {
const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);

QDeclarativeInstruction store;
store.assignBinding.value = output->indexForString(ref.rewrittenExpression);
store.assignBinding.context = ref.bindingContext.stack;
store.assignBinding.owner = ref.bindingContext.owner;
store.assignBinding.value = output->indexForString(js.rewrittenExpression);
store.assignBinding.context = js.bindingContext.stack;
store.assignBinding.owner = js.bindingContext.owner;
store.assignBinding.line = binding->location.start.line;
store.assignBinding.column = binding->location.start.column;

Expand All @@ -3366,9 +3483,9 @@ void QDeclarativeCompiler::genBindingAssignment(QDeclarativeScript::Value *bindi
store.assignBinding.isRoot = (compileState->root == obj);
}

Q_ASSERT(ref.bindingContext.owner == 0 ||
(ref.bindingContext.owner != 0 && valueTypeProperty));
if (ref.bindingContext.owner) {
Q_ASSERT(js.bindingContext.owner == 0 ||
(js.bindingContext.owner != 0 && valueTypeProperty));
if (js.bindingContext.owner) {
store.assignBinding.property = genValueTypeData(prop, valueTypeProperty);
} else {
store.assignBinding.property = prop->core;
Expand All @@ -3377,6 +3494,8 @@ void QDeclarativeCompiler::genBindingAssignment(QDeclarativeScript::Value *bindi
!prop->isAlias ? QDeclarativeInstruction::StoreBinding
: QDeclarativeInstruction::StoreBindingOnAlias
, store);
} else {
Q_ASSERT(!"Unhandled BindingReference::DataType type");
}
}

Expand Down Expand Up @@ -3420,11 +3539,11 @@ bool QDeclarativeCompiler::completeComponentBuild()

QV4Compiler bindingCompiler;

QList<BindingReference*> sharedBindings;
QList<JSBindingReference*> sharedBindings;

for (BindingReference *b = compileState->bindings.first(); b; b = b->nextReference) {
for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) {

BindingReference &binding = *b;
JSBindingReference &binding = *b;

// ### We don't currently optimize for bindings on alias's - because
// of the solution to QTBUG-13719
Expand Down Expand Up @@ -3465,7 +3584,7 @@ bool QDeclarativeCompiler::completeComponentBuild()

if (!sharedBindings.isEmpty()) {
struct Sort {
static bool lt(const BindingReference *lhs, const BindingReference *rhs)
static bool lt(const JSBindingReference *lhs, const JSBindingReference *rhs)
{
return lhs->value->location.start.line < rhs->value->location.start.line;
}
Expand All @@ -3478,7 +3597,7 @@ bool QDeclarativeCompiler::completeComponentBuild()

QString functionArray(QLatin1String("["));
for (int ii = 0; ii < sharedBindings.count(); ++ii) {
BindingReference *reference = sharedBindings.at(ii);
JSBindingReference *reference = sharedBindings.at(ii);
QDeclarativeScript::Value *value = reference->value;
const QString &expression = reference->rewrittenExpression;

Expand Down
37 changes: 27 additions & 10 deletions src/declarative/qml/qdeclarativecompiler_p.h
Expand Up @@ -179,23 +179,36 @@ namespace QDeclarativeCompilerTypes {
QDeclarativeScript::Object *object;
};

struct BindingReference : public QDeclarativePool::Class
struct BindingReference
{
BindingReference() : nextReference(0) {}
enum DataType { QtScript, V4, V8,
Tr, TrId };
DataType dataType;
};

struct JSBindingReference : public QDeclarativePool::Class,
public BindingReference
{
JSBindingReference() : nextReference(0) {}

QDeclarativeScript::Variant expression;
QDeclarativeScript::Property *property;
QDeclarativeScript::Value *value;

enum DataType { QtScript, V4, V8 };
DataType dataType;

int compiledIndex;

QString rewrittenExpression;
BindingContext bindingContext;

BindingReference *nextReference;
JSBindingReference *nextReference;
};

struct TrBindingReference : public QDeclarativePool::POD,
public BindingReference
{
QStringRef text;
QStringRef comment;
int n;
};

struct IdList : public QFieldList<QDeclarativeScript::Object,
Expand Down Expand Up @@ -250,9 +263,9 @@ namespace QDeclarativeCompilerTypes {
DepthStack objectDepth;
DepthStack listDepth;

typedef QDeclarativeCompilerTypes::BindingReference B;
typedef QFieldList<B, &B::nextReference> BindingReferenceList;
BindingReferenceList bindings;
typedef QDeclarativeCompilerTypes::JSBindingReference B;
typedef QFieldList<B, &B::nextReference> JSBindingReferenceList;
JSBindingReferenceList bindings;
typedef QDeclarativeScript::Object O;
typedef QFieldList<O, &O::nextAliasingObject> AliasingObjectsList;
AliasingObjectsList aliasingObjects;
Expand Down Expand Up @@ -347,6 +360,8 @@ class Q_AUTOTEST_EXPORT QDeclarativeCompiler
bool checkDynamicMeta(QDeclarativeScript::Object *obj);
bool buildBinding(QDeclarativeScript::Value *, QDeclarativeScript::Property *prop,
const QDeclarativeCompilerTypes::BindingContext &ctxt);
bool buildLiteralBinding(QDeclarativeScript::Value *, QDeclarativeScript::Property *prop,
const QDeclarativeCompilerTypes::BindingContext &ctxt);
bool buildComponentFromRoot(QDeclarativeScript::Object *obj, const QDeclarativeCompilerTypes::BindingContext &);
bool compileAlias(QFastMetaBuilder &,
QByteArray &data,
Expand Down Expand Up @@ -378,6 +393,7 @@ class Q_AUTOTEST_EXPORT QDeclarativeCompiler
QDeclarativeScript::Property *valueTypeProp);

int componentTypeRef();
int translationContextIndex();

static QDeclarativeType *toQmlType(QDeclarativeScript::Object *from);
bool canCoerce(int to, QDeclarativeScript::Object *from);
Expand All @@ -399,7 +415,7 @@ class Q_AUTOTEST_EXPORT QDeclarativeCompiler

void dumpStats();

void addBindingReference(QDeclarativeCompilerTypes::BindingReference *);
void addBindingReference(QDeclarativeCompilerTypes::JSBindingReference *);

QDeclarativeCompilerTypes::ComponentCompileState *compileState;

Expand All @@ -415,6 +431,7 @@ class Q_AUTOTEST_EXPORT QDeclarativeCompiler
QDeclarativeScript::Object *unitRoot;
QDeclarativeTypeData *unit;
int cachedComponentTypeRef;
int cachedTranslationContextIndex;

// Compiler component statistics. Only collected if QML_COMPILER_STATS=1
struct ComponentStat
Expand Down

0 comments on commit b6c8497

Please sign in to comment.