aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/v4/qmljs_environment.cpp7
-rw-r--r--src/v4/qmljs_environment.h2
-rw-r--r--src/v4/qmljs_runtime.cpp92
-rw-r--r--src/v4/qmljs_runtime.h5
-rw-r--r--src/v4/qv4functionobject.cpp3
-rw-r--r--src/v4/qv4functionobject.h13
-rw-r--r--src/v4/qv4globalobject.cpp8
-rw-r--r--src/v4/qv4isel_masm.cpp52
-rw-r--r--src/v4/qv4isel_masm_p.h3
-rw-r--r--src/v4/qv4isel_p.cpp1
-rw-r--r--src/v4/qv4isel_p.h4
-rw-r--r--src/v4/qv4object.cpp25
-rw-r--r--src/v4/qv4object.h3
-rw-r--r--tests/fact.2.js2
-rw-r--r--tests/property_lookup.js9
-rw-r--r--tools/v4/main.cpp1
16 files changed, 217 insertions, 13 deletions
diff --git a/src/v4/qmljs_environment.cpp b/src/v4/qmljs_environment.cpp
index 76d08337c8..b1339b6a23 100644
--- a/src/v4/qmljs_environment.cpp
+++ b/src/v4/qmljs_environment.cpp
@@ -217,6 +217,8 @@ void ExecutionContext::init(ExecutionEngine *eng)
thisObject = eng->globalObject;
function = 0;
+ lookups = 0;
+
arguments = 0;
argumentCount = 0;
locals = 0;
@@ -235,6 +237,8 @@ void ExecutionContext::init(ExecutionContext *p, Object *with)
thisObject = p->thisObject;
function = 0;
+ lookups = parent->lookups;
+
arguments = 0;
argumentCount = 0;
locals = 0;
@@ -504,6 +508,9 @@ void ExecutionContext::initCallContext(ExecutionContext *parent)
strictMode = function->strictMode;
+ if (function->function)
+ lookups = function->function->lookups;
+
if (function->varCount) {
locals = reinterpret_cast<Value *>(this + 1);
std::fill(locals, locals + function->varCount, Value::undefinedValue());
diff --git a/src/v4/qmljs_environment.h b/src/v4/qmljs_environment.h
index 5dc4a56609..3c4e1fb690 100644
--- a/src/v4/qmljs_environment.h
+++ b/src/v4/qmljs_environment.h
@@ -54,6 +54,7 @@ struct Object;
struct ExecutionEngine;
struct ExecutionContext;
struct DeclarativeEnvironment;
+struct Lookup;
struct Q_V4_EXPORT DiagnosticMessage
{
@@ -81,6 +82,7 @@ struct ExecutionContext
Value thisObject;
FunctionObject *function;
+ Lookup *lookups;
Value *arguments;
unsigned int argumentCount;
diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp
index 1fcdf02d4f..4c32b85921 100644
--- a/src/v4/qmljs_runtime.cpp
+++ b/src/v4/qmljs_runtime.cpp
@@ -676,6 +676,61 @@ Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name)
return ctx->getProperty(name);
}
+Value __qmljs_get_property_lookup(ExecutionContext *ctx, Value object, int lookupIndex)
+{
+ Lookup *l = ctx->lookups + lookupIndex;
+ if (Object *o = object.asObject()) {
+ if (o->internalClass == l->internalClass)
+ return o->getValue(ctx, o->memberData + l->index);
+
+ uint idx = o->internalClass->find(l->name);
+ if (idx < UINT_MAX) {
+ l->internalClass = o->internalClass;
+ l->index = idx;
+ return o->getValue(ctx, o->memberData + idx);
+ }
+
+ return object.objectValue()->__get__(ctx, l->name);
+ } else if (object.isString() && l->name->isEqualTo(ctx->engine->id_length)) {
+ return Value::fromInt32(object.stringValue()->toQString().length());
+ } else {
+ object = __qmljs_to_object(object, ctx);
+
+ if (object.isObject()) {
+ return object.objectValue()->__get__(ctx, l->name);
+ } else {
+ ctx->throwTypeError();
+ return Value::undefinedValue();
+ }
+ }
+}
+
+void __qmljs_set_property_lookup(ExecutionContext *ctx, Value object, int lookupIndex, Value value)
+{
+ if (! object.isObject())
+ object = __qmljs_to_object(object, ctx);
+
+ Object *o = object.objectValue();
+ Lookup *l = ctx->lookups + lookupIndex;
+
+ if (l->index != ArrayObject::LengthPropertyIndex || !o->isArrayObject()) {
+ if (o->internalClass == l->internalClass) {
+ o->putValue(ctx, o->memberData + l->index, value);
+ return;
+ }
+
+ uint idx = o->internalClass->find(l->name);
+ if (idx < UINT_MAX) {
+ l->internalClass = o->internalClass;
+ l->index = idx;
+ return o->putValue(ctx, o->memberData + idx, value);
+ }
+ }
+
+ o->__put__(ctx, l->name, value);
+}
+
+
Value __qmljs_get_thisObject(ExecutionContext *ctx)
{
return ctx->thisObject;
@@ -770,6 +825,43 @@ Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String
return o->call(context, thisObject, args, argc);
}
+Value __qmljs_call_property_lookup(ExecutionContext *context, Value thisObject, uint index, Value *args, int argc)
+{
+ Lookup *l = context->lookups + index;
+
+ Object *baseObject;
+ if (thisObject.isString()) {
+ baseObject = context->engine->stringPrototype;
+ } else {
+ if (!thisObject.isObject())
+ thisObject = __qmljs_to_object(thisObject, context);
+
+ assert(thisObject.isObject());
+ baseObject = thisObject.objectValue();
+ }
+
+
+ Value func;
+
+ if (baseObject->internalClass == l->internalClass) {
+ func = baseObject->getValue(context, baseObject->memberData + l->index);
+ } else {
+ uint idx = baseObject->internalClass->find(l->name);
+ if (idx < UINT_MAX) {
+ l->internalClass = baseObject->internalClass;
+ l->index = idx;
+ func = baseObject->getValue(context, baseObject->memberData + idx);
+ } else {
+ func = baseObject->__get__(context, l->name);
+ }
+ }
+ FunctionObject *o = func.asFunctionObject();
+ if (!o)
+ context->throwTypeError();
+
+ return o->call(context, thisObject, args, argc);
+}
+
Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, Value *args, int argc)
{
Value thisObject = that;
diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h
index 2742692c51..b2efc15e1d 100644
--- a/src/v4/qmljs_runtime.h
+++ b/src/v4/qmljs_runtime.h
@@ -93,6 +93,7 @@ extern "C" {
// context
Value __qmljs_call_activation_property(ExecutionContext *, String *name, Value *args, int argc);
Value __qmljs_call_property(ExecutionContext *context, Value that, String *name, Value *args, int argc);
+Value __qmljs_call_property_lookup(ExecutionContext *context, Value thisObject, uint index, Value *args, int argc);
Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, Value *args, int argc);
Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc);
@@ -168,6 +169,10 @@ void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Val
Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name);
Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name);
+Value __qmljs_get_property_lookup(ExecutionContext *ctx, Value object, int lookupIndex);
+void __qmljs_set_property_lookup(ExecutionContext *ctx, Value object, int lookupIndex, Value value);
+
+
Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index);
void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value value);
diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp
index 67ba4870bf..94f923a47e 100644
--- a/src/v4/qv4functionobject.cpp
+++ b/src/v4/qv4functionobject.cpp
@@ -92,6 +92,7 @@ FunctionObject::FunctionObject(ExecutionContext *scope)
, varList(0)
, formalParameterCount(0)
, varCount(0)
+ , function(0)
{
prototype = scope->engine->functionPrototype;
@@ -339,8 +340,8 @@ Value FunctionPrototype::method_bind(ExecutionContext *ctx)
ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function)
: FunctionObject(scope)
- , function(function)
{
+ this->function = function;
assert(function);
assert(function->code);
diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h
index 87fb696411..9a4967488a 100644
--- a/src/v4/qv4functionobject.h
+++ b/src/v4/qv4functionobject.h
@@ -100,6 +100,13 @@ struct ReferenceErrorPrototype;
struct SyntaxErrorPrototype;
struct TypeErrorPrototype;
struct URIErrorPrototype;
+struct InternalClass;
+
+struct Lookup {
+ InternalClass *internalClass;
+ uint index;
+ String *name;
+};
struct Function {
String *name;
@@ -113,6 +120,8 @@ struct Function {
QVector<Value> generatedValues;
QVector<String *> identifiers;
+ Lookup *lookups;
+
bool hasNestedFunctions : 1;
bool hasDirectEval : 1;
bool usesArgumentsObject : 1;
@@ -122,6 +131,7 @@ struct Function {
: name(name)
, code(0)
, codeData(0)
+ , lookups(0)
, hasNestedFunctions(0)
, hasDirectEval(false)
, usesArgumentsObject(false)
@@ -141,6 +151,7 @@ struct Q_V4_EXPORT FunctionObject: Object {
String * const *varList;
unsigned int formalParameterCount;
unsigned int varCount;
+ VM::Function *function;
FunctionObject(ExecutionContext *scope);
@@ -196,8 +207,6 @@ struct BuiltinFunction: FunctionObject {
};
struct ScriptFunction: FunctionObject {
- VM::Function *function;
-
ScriptFunction(ExecutionContext *scope, VM::Function *function);
virtual ~ScriptFunction();
diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp
index 2d7bb71e10..8a0f779c51 100644
--- a/src/v4/qv4globalobject.cpp
+++ b/src/v4/qv4globalobject.cpp
@@ -375,15 +375,15 @@ Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Va
bool strict = f->isStrict || (directCall && context->strictMode);
- uint size = requiredMemoryForExecutionContect(this, argc);
+ uint size = requiredMemoryForExecutionContect(this, 0);
ExecutionContext *k = static_cast<ExecutionContext *>(alloca(size));
if (strict) {
ctx = k;
ctx->thisObject = directCall ? context->thisObject : context->engine->globalObject;
ctx->function = this;
- ctx->arguments = args;
- ctx->argumentCount = argc;
+ ctx->arguments = 0;
+ ctx->argumentCount = 0;
ctx->initCallContext(context);
}
@@ -472,6 +472,8 @@ QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct
Codegen cg(ctx, strictMode);
IR::Function *globalIRCode = cg(fileName, program, &module, mode, inheritedLocals);
QScopedPointer<EvalInstructionSelection> isel(ctx->engine->iselFactory->create(vm, &module));
+ if (inheritContext)
+ isel->setUseFastLookups(false);
if (globalIRCode)
globalCode = isel->vmFunction(globalIRCode);
}
diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp
index 24a51a94a3..7e74e0734a 100644
--- a/src/v4/qv4isel_masm.cpp
+++ b/src/v4/qv4isel_masm.cpp
@@ -383,8 +383,10 @@ InstructionSelection::~InstructionSelection()
void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function)
{
+ QVector<Lookup> lookups;
qSwap(_function, function);
qSwap(_vmFunction, vmFunction);
+ qSwap(_lookups, lookups);
Assembler* oldAssembler = _as;
_as = new Assembler(_function);
@@ -428,8 +430,14 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function)
_as->link(_vmFunction);
+ if (_lookups.size()) {
+ _vmFunction->lookups = new Lookup[_lookups.size()];
+ memcpy(_vmFunction->lookups, _lookups.constData(), _lookups.size()*sizeof(Lookup));
+ }
+
qSwap(_vmFunction, vmFunction);
qSwap(_function, function);
+ qSwap(_lookups, lookups);
delete _as;
_as = oldAssembler;
}
@@ -643,12 +651,24 @@ void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target)
void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR::Temp *target)
{
- generateFunctionCall(target, __qmljs_get_property, Assembler::ContextRegister, base, identifier(name));
+ if (useFastLookups) {
+ VM::String *s = identifier(name);
+ uint index = addLookup(s);
+ generateFunctionCall(target, __qmljs_get_property_lookup, Assembler::ContextRegister, base, Assembler::TrustedImm32(index));
+ } else {
+ generateFunctionCall(target, __qmljs_get_property, Assembler::ContextRegister, base, identifier(name));
+ }
}
void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName)
{
- generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, targetBase, identifier(targetName), source);
+ if (useFastLookups) {
+ VM::String *s = identifier(targetName);
+ uint index = addLookup(s);
+ generateFunctionCall(Assembler::Void, __qmljs_set_property_lookup, Assembler::ContextRegister, targetBase, Assembler::TrustedImm32(index), source);
+ } else {
+ generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, targetBase, identifier(targetName), source);
+ }
}
void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target)
@@ -778,10 +798,20 @@ void InstructionSelection::callProperty(IR::Temp *base, const QString &name,
assert(base != 0);
int argc = prepareVariableArguments(args);
- generateFunctionCall(result, __qmljs_call_property,
- Assembler::ContextRegister, base, identifier(name),
- baseAddressForCallArguments(),
- Assembler::TrustedImm32(argc));
+ VM::String *s = identifier(name);
+
+ if (useFastLookups) {
+ uint index = addLookup(s);
+ generateFunctionCall(result, __qmljs_call_property_lookup,
+ Assembler::ContextRegister, base, Assembler::TrustedImm32(index),
+ baseAddressForCallArguments(),
+ Assembler::TrustedImm32(argc));
+ } else {
+ generateFunctionCall(result, __qmljs_call_property,
+ Assembler::ContextRegister, base, s,
+ baseAddressForCallArguments(),
+ Assembler::TrustedImm32(argc));
+ }
}
void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result)
@@ -935,3 +965,13 @@ void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* na
}
+uint InstructionSelection::addLookup(VM::String *name)
+{
+ uint index = (uint)_lookups.size();
+ VM::Lookup l;
+ l.internalClass = 0;
+ l.index = 0;
+ l.name = name;
+ _lookups.append(l);
+ return index;
+}
diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h
index aaa0866be9..f20ffd90bd 100644
--- a/src/v4/qv4isel_masm_p.h
+++ b/src/v4/qv4isel_masm_p.h
@@ -778,9 +778,12 @@ private:
#define callRuntimeMethod(result, function, ...) \
callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__)
+ uint addLookup(VM::String *name);
+
IR::BasicBlock *_block;
IR::Function* _function;
VM::Function* _vmFunction;
+ QVector<VM::Lookup> _lookups;
Assembler* _as;
};
diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp
index 05e7739e00..6a050b3b47 100644
--- a/src/v4/qv4isel_p.cpp
+++ b/src/v4/qv4isel_p.cpp
@@ -18,6 +18,7 @@ using namespace QQmlJS::IR;
EvalInstructionSelection::EvalInstructionSelection(VM::ExecutionEngine *engine, Module *module)
: _engine(engine)
+ , useFastLookups(true)
{
assert(engine);
assert(module);
diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h
index d097f91527..5ec50b3b63 100644
--- a/src/v4/qv4isel_p.h
+++ b/src/v4/qv4isel_p.h
@@ -51,6 +51,8 @@ public:
VM::Function *vmFunction(IR::Function *f);
+ void setUseFastLookups(bool b) { useFastLookups = b; }
+
protected:
VM::Function *createFunctionMapping(VM::ExecutionEngine *engine, IR::Function *irFunction);
VM::ExecutionEngine *engine() const { return _engine; }
@@ -59,6 +61,8 @@ protected:
private:
VM::ExecutionEngine *_engine;
QHash<IR::Function *, VM::Function *> _irToVM;
+protected:
+ bool useFastLookups;
};
class Q_V4_EXPORT EvalISelFactory
diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp
index 25a453f6f0..a6259f2dda 100644
--- a/src/v4/qv4object.cpp
+++ b/src/v4/qv4object.cpp
@@ -89,6 +89,7 @@ void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &va
Value Object::getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const
{
+ assert(p->type != PropertyDescriptor::Generic);
if (p->isData())
return p->value;
if (!p->get)
@@ -112,6 +113,30 @@ Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p
return getValue(ctx, p);
}
+void Object::putValue(ExecutionContext *ctx, PropertyDescriptor *pd, Value value)
+{
+ if (pd->isAccessor()) {
+ if (pd->set) {
+ Value args[1];
+ args[0] = value;
+ pd->set->call(ctx, Value::fromObject(this), args, 1);
+ return;
+ }
+ goto reject;
+ }
+
+ if (!pd->isWritable())
+ goto reject;
+
+ pd->value = value;
+ return;
+
+ reject:
+ if (ctx->strictMode)
+ __qmljs_throw_type_error(ctx);
+
+}
+
void Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx)
{
bool hasProperty = false;
diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h
index 964051f8db..6991a39374 100644
--- a/src/v4/qv4object.h
+++ b/src/v4/qv4object.h
@@ -68,6 +68,7 @@ namespace VM {
struct Value;
struct Function;
+struct Lookup;
struct Object;
struct ObjectIterator;
struct BooleanObject;
@@ -147,6 +148,8 @@ struct Q_V4_EXPORT Object: Managed {
Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const;
Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const;
+ void putValue(ExecutionContext *ctx, PropertyDescriptor *pd, Value value);
+
void inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx);
void inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx);
diff --git a/tests/fact.2.js b/tests/fact.2.js
index c0f087e4c9..d8f750b5a1 100644
--- a/tests/fact.2.js
+++ b/tests/fact.2.js
@@ -3,6 +3,6 @@ function fact(n) {
return n > 1 ? n * fact(n - 1) : 1
}
-for (var i = 0; i < 10000; i = i + 1)
+for (var i = 0; i < 1000000; i = i + 1)
fact(12)
diff --git a/tests/property_lookup.js b/tests/property_lookup.js
new file mode 100644
index 0000000000..ee45b65710
--- /dev/null
+++ b/tests/property_lookup.js
@@ -0,0 +1,9 @@
+function foo() {
+ var obj = { x: 10 }
+
+ for (var i = 0; i < 1000000; ++i) {
+ var y = obj.x;
+ obj.x = y;
+ }
+}
+foo();
diff --git a/tools/v4/main.cpp b/tools/v4/main.cpp
index 2516386eef..01d4f8fdcc 100644
--- a/tools/v4/main.cpp
+++ b/tools/v4/main.cpp
@@ -400,6 +400,7 @@ int main(int argc, char *argv[])
vm.globalCode = f;
ctx->strictMode = f->isStrict;
+ ctx->lookups = f->lookups;
if (debugger)
debugger->aboutToCall(0, ctx);
QQmlJS::VM::Value result = f->code(ctx, f->codeData);