thisObject = eng->globalObject;
function = 0;
+ lookups = 0;
+
arguments = 0;
argumentCount = 0;
locals = 0;
thisObject = p->thisObject;
function = 0;
+ lookups = parent->lookups;
+
arguments = 0;
argumentCount = 0;
locals = 0;
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());
struct ExecutionEngine;
struct ExecutionContext;
struct DeclarativeEnvironment;
+struct Lookup;
struct Q_V4_EXPORT DiagnosticMessage
{
Value thisObject;
FunctionObject *function;
+ Lookup *lookups;
Value *arguments;
unsigned int argumentCount;
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;
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;
// 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);
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);
, varList(0)
, formalParameterCount(0)
, varCount(0)
+ , function(0)
{
prototype = scope->engine->functionPrototype;
ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function)
: FunctionObject(scope)
- , function(function)
{
+ this->function = function;
assert(function);
assert(function->code);
struct SyntaxErrorPrototype;
struct TypeErrorPrototype;
struct URIErrorPrototype;
+struct InternalClass;
+
+struct Lookup {
+ InternalClass *internalClass;
+ uint index;
+ String *name;
+};
struct Function {
String *name;
QVector<Value> generatedValues;
QVector<String *> identifiers;
+ Lookup *lookups;
+
bool hasNestedFunctions : 1;
bool hasDirectEval : 1;
bool usesArgumentsObject : 1;
: name(name)
, code(0)
, codeData(0)
+ , lookups(0)
, hasNestedFunctions(0)
, hasDirectEval(false)
, usesArgumentsObject(false)
String * const *varList;
unsigned int formalParameterCount;
unsigned int varCount;
+ VM::Function *function;
FunctionObject(ExecutionContext *scope);
};
struct ScriptFunction: FunctionObject {
- VM::Function *function;
-
ScriptFunction(ExecutionContext *scope, VM::Function *function);
virtual ~ScriptFunction();
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);
}
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);
}
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);
_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;
}
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)
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)
}
+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;
+}
#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;
};
EvalInstructionSelection::EvalInstructionSelection(VM::ExecutionEngine *engine, Module *module)
: _engine(engine)
+ , useFastLookups(true)
{
assert(engine);
assert(module);
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; }
private:
VM::ExecutionEngine *_engine;
QHash<IR::Function *, VM::Function *> _irToVM;
+protected:
+ bool useFastLookups;
};
class Q_V4_EXPORT EvalISelFactory
Value Object::getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const
{
+ assert(p->type != PropertyDescriptor::Generic);
if (p->isData())
return p->value;
if (!p->get)
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;
struct Value;
struct Function;
+struct Lookup;
struct Object;
struct ObjectIterator;
struct BooleanObject;
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);
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)
--- /dev/null
+function foo() {
+ var obj = { x: 10 }
+
+ for (var i = 0; i < 1000000; ++i) {
+ var y = obj.x;
+ obj.x = y;
+ }
+}
+foo();
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);