diff options
Diffstat (limited to 'src/qml/jsruntime')
46 files changed, 1035 insertions, 536 deletions
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 955cf585e4..9938f60aea 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -46,7 +46,9 @@ SOURCES += \ HEADERS += \ $$PWD/qv4global_p.h \ + $$PWD/qv4alloca_p.h \ $$PWD/qv4engine_p.h \ + $$PWD/qv4enginebase_p.h \ $$PWD/qv4context_p.h \ $$PWD/qv4math_p.h \ $$PWD/qv4persistent_p.h \ diff --git a/src/qml/jsruntime/qv4alloca_p.h b/src/qml/jsruntime/qv4alloca_p.h index 2f486988c1..c21878fa42 100644 --- a/src/qml/jsruntime/qv4alloca_p.h +++ b/src/qml/jsruntime/qv4alloca_p.h @@ -51,15 +51,58 @@ // We mean it. // -#include <qglobal.h> +#include <QtCore/private/qglobal_p.h> -#if defined(Q_OS_WIN) +#if QT_CONFIG(alloca_h) +# include <alloca.h> +#elif QT_CONFIG(alloca_malloc_h) # include <malloc.h> -# ifndef __GNUC__ +// This does not matter unless compiling in strict standard mode. +# ifdef Q_OS_WIN # define alloca _alloca # endif -#elif !defined(Q_OS_BSD4) || defined(Q_OS_DARWIN) -# include <alloca.h> +#else +# include <stdlib.h> +#endif + +// Define Q_ALLOCA_VAR macro to be used instead of #ifdeffing +// the occurrences of alloca() in case it's not supported. +// Q_ALLOCA_DECLARE and Q_ALLOCA_ASSIGN macros separate +// memory allocation from the declaration and RAII. +#define Q_ALLOCA_VAR(type, name, size) \ + Q_ALLOCA_DECLARE(type, name); \ + Q_ALLOCA_ASSIGN(type, name, size) + +#if QT_CONFIG(alloca) + +#define Q_ALLOCA_DECLARE(type, name) \ + type *name = 0 + +#define Q_ALLOCA_ASSIGN(type, name, size) \ + name = static_cast<type*>(alloca(size)) + +#else +QT_BEGIN_NAMESPACE +class Qt_AllocaWrapper +{ +public: + Qt_AllocaWrapper() { m_data = 0; } + ~Qt_AllocaWrapper() { free(m_data); } + void *data() { return m_data; } + void allocate(int size) { m_data = malloc(size); } +private: + void *m_data; +}; +QT_END_NAMESPACE + +#define Q_ALLOCA_DECLARE(type, name) \ + Qt_AllocaWrapper _qt_alloca_##name; \ + type *name = nullptr + +#define Q_ALLOCA_ASSIGN(type, name, size) \ + _qt_alloca_##name.allocate(size); \ + name = static_cast<type*>(_qt_alloca_##name.data()) + #endif #endif diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 318db4f904..0905c2828a 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -48,7 +48,7 @@ DEFINE_OBJECT_VTABLE(ArgumentsObject); void Heap::ArgumentsObject::init(QV4::CallContext *context) { - ExecutionEngine *v4 = context->d()->engine; + ExecutionEngine *v4 = internalClass->engine; Object::init(); fullyCreated = false; @@ -59,8 +59,8 @@ void Heap::ArgumentsObject::init(QV4::CallContext *context) Scoped<QV4::ArgumentsObject> args(scope, this); if (context->d()->strictMode) { - Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(context->d()->engine->id_callee())); - Q_ASSERT(CallerPropertyIndex == args->internalClass()->find(context->d()->engine->id_caller())); + Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(v4->id_callee())); + Q_ASSERT(CallerPropertyIndex == args->internalClass()->find(v4->id_caller())); args->setProperty(CalleePropertyIndex + QV4::Object::GetterOffset, *v4->thrower()); args->setProperty(CalleePropertyIndex + QV4::Object::SetterOffset, *v4->thrower()); args->setProperty(CallerPropertyIndex + QV4::Object::GetterOffset, *v4->thrower()); @@ -70,10 +70,10 @@ void Heap::ArgumentsObject::init(QV4::CallContext *context) args->arrayPut(0, context->args(), context->argc()); args->d()->fullyCreated = true; } else { - Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(context->d()->engine->id_callee())); + Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(v4->id_callee())); args->setProperty(CalleePropertyIndex, context->d()->function); } - Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(context->d()->engine->id_length())); + Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length())); args->setProperty(LengthPropertyIndex, Primitive::fromInt32(context->d()->callData->argc)); } @@ -82,18 +82,19 @@ void ArgumentsObject::fullyCreate() if (fullyCreated()) return; + Scope scope(engine()); + uint argCount = context()->callData->argc; uint numAccessors = qMin(context()->formalParameterCount(), argCount); ArrayData::realloc(this, Heap::ArrayData::Sparse, argCount, true); - context()->engine->requireArgumentsAccessors(numAccessors); + scope.engine->requireArgumentsAccessors(numAccessors); - Scope scope(engine()); Scoped<MemberData> md(scope, d()->mappedArguments); if (numAccessors) { - d()->mappedArguments.set(scope.engine, md->allocate(engine(), numAccessors)); + d()->mappedArguments.set(scope.engine, md->allocate(scope.engine, numAccessors)); for (uint i = 0; i < numAccessors; ++i) { d()->mappedArguments->values.set(scope.engine, i, context()->callData->args[i]); - arraySet(i, context()->engine->argumentsAccessors + i, Attr_Accessor); + arraySet(i, scope.engine->argumentsAccessors + i, Attr_Accessor); } } arrayPut(numAccessors, context()->callData->args + numAccessors, argCount - numAccessors); @@ -114,7 +115,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con bool isMapped = false; if (arrayData() && index < numAccessors && arrayData()->attributes(index).isAccessor() && - arrayData()->get(index) == context()->engine->argumentsAccessors[index].getter()->asReturnedValue()) + arrayData()->get(index) == scope.engine->argumentsAccessors[index].getter()->asReturnedValue()) isMapped = true; if (isMapped) { diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index 4a619858b4..df9884d84a 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -51,6 +51,8 @@ QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON const QV4::VTable QV4::ArrayData::static_vtbl = { 0, 0, + 0, + 0, QV4::ArrayData::IsExecutionContext, QV4::ArrayData::IsString, QV4::ArrayData::IsObject, diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h index c2c81e886b..1d895c90c5 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -234,6 +234,7 @@ struct Q_QML_EXPORT ArrayData : public Managed struct Q_QML_EXPORT SimpleArrayData : public ArrayData { V4_ARRAYDATA(SimpleArrayData) + V4_INTERNALCLASS(SimpleArrayData) uint mappedIndex(uint index) const { return d()->mappedIndex(index); } Value data(uint index) const { return d()->data(index); } @@ -257,6 +258,7 @@ struct Q_QML_EXPORT SimpleArrayData : public ArrayData struct Q_QML_EXPORT SparseArrayData : public ArrayData { V4_ARRAYDATA(SparseArrayData) + V4_INTERNALCLASS(SparseArrayData) V4_NEEDS_DESTROY ReturnedValue &freeList() { return d()->freeList; } diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index 601066110f..c8e9ebb2dd 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -85,7 +85,7 @@ void BooleanPrototype::method_toString(const BuiltinFunction *, Scope &scope, Ca result = thisObject->value(); } - scope.result = scope.engine->newString(QLatin1String(result ? "true" : "false")); + scope.result = result ? scope.engine->id_true() : scope.engine->id_false(); } void BooleanPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h index 9c8b1d67f1..ca2cf7d17a 100644 --- a/src/qml/jsruntime/qv4booleanobject_p.h +++ b/src/qml/jsruntime/qv4booleanobject_p.h @@ -76,6 +76,7 @@ struct BooleanCtor: FunctionObject struct BooleanPrototype: BooleanObject { + V4_PROTOTYPE(objectPrototype) void init(ExecutionEngine *engine, Object *ctor); static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 02d3af619e..ba72a9e43b 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -65,13 +65,14 @@ Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData uint localsAndFormals = function->compiledFunction->nLocals + sizeof(CallData)/sizeof(Value) - 1 + qMax(static_cast<uint>(callData->argc), function->nFormals); size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * (localsAndFormals); - Heap::CallContext *c = d()->engine->memoryManager->allocManaged<CallContext>(requiredMemory); - c->init(d()->engine, Heap::ExecutionContext::Type_CallContext); + ExecutionEngine *v4 = engine(); + Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory); + c->init(Heap::ExecutionContext::Type_CallContext); c->v4Function = function; c->strictMode = function->isStrict(); - c->outer.set(d()->engine, this->d()); + c->outer.set(v4, this->d()); c->compilationUnit = function->compilationUnit; c->lookups = function->compilationUnit->runtimeLookups; @@ -99,14 +100,14 @@ Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData Heap::WithContext *ExecutionContext::newWithContext(Heap::Object *with) { - return d()->engine->memoryManager->alloc<WithContext>(d(), with); + return engine()->memoryManager->alloc<WithContext>(d(), with); } Heap::CatchContext *ExecutionContext::newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue) { Scope scope(this); ScopedValue e(scope, exceptionValue); - return d()->engine->memoryManager->alloc<CatchContext>(d(), exceptionVarName, e); + return engine()->memoryManager->alloc<CatchContext>(d(), exceptionVarName, e); } void ExecutionContext::createMutableBinding(String *name, bool deletable) @@ -156,35 +157,35 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) void Heap::GlobalContext::init(ExecutionEngine *eng) { - Heap::ExecutionContext::init(eng, Heap::ExecutionContext::Type_GlobalContext); + Heap::ExecutionContext::init(Heap::ExecutionContext::Type_GlobalContext); global.set(eng, eng->globalObject->d()); } void Heap::CatchContext::init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue) { - Heap::ExecutionContext::init(outerContext->engine, Heap::ExecutionContext::Type_CatchContext); - outer.set(engine, outerContext); + Heap::ExecutionContext::init(Heap::ExecutionContext::Type_CatchContext); + outer.set(internalClass->engine, outerContext); strictMode = outer->strictMode; callData = outer->callData; lookups = outer->lookups; constantTable = outer->constantTable; compilationUnit = outer->compilationUnit; - this->exceptionVarName.set(engine, exceptionVarName); - this->exceptionValue.set(engine, exceptionValue); + this->exceptionVarName.set(internalClass->engine, exceptionVarName); + this->exceptionValue.set(internalClass->engine, exceptionValue); } void Heap::WithContext::init(ExecutionContext *outerContext, Object *with) { - Heap::ExecutionContext::init(outerContext->engine, Heap::ExecutionContext::Type_WithContext); - outer.set(engine, outerContext); + Heap::ExecutionContext::init(Heap::ExecutionContext::Type_WithContext); + outer.set(internalClass->engine, outerContext); callData = outer->callData; lookups = outer->lookups; constantTable = outer->constantTable; compilationUnit = outer->compilationUnit; - withObject.set(engine, with); + withObject.set(internalClass->engine, with); } Identifier * const *SimpleCallContext::formals() const @@ -211,6 +212,9 @@ unsigned int SimpleCallContext::variableCount() const bool ExecutionContext::deleteProperty(String *name) { + name->makeIdentifier(); + Identifier *id = name->identifier(); + Scope scope(this); ScopedContext ctx(scope, this); for (; ctx; ctx = ctx->d()->outer) { @@ -235,7 +239,7 @@ bool ExecutionContext::deleteProperty(String *name) } case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); - uint index = c->v4Function->internalClass->find(name); + uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) // ### throw in strict mode? return false; @@ -282,7 +286,7 @@ void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Functio ExecutionContextSaver ctxSaver(scope); - SimpleCallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext(scope.engine); + SimpleCallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext(); ctx->strictMode = function->isStrict(); ctx->callData = callData; @@ -306,6 +310,9 @@ void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Functio void ExecutionContext::setProperty(String *name, const Value &value) { + name->makeIdentifier(); + Identifier *id = name->identifier(); + Scope scope(this); ScopedContext ctx(scope, this); ScopedObject activation(scope); @@ -337,7 +344,7 @@ void ExecutionContext::setProperty(String *name, const Value &value) case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); if (c->v4Function) { - uint index = c->v4Function->internalClass->find(name); + uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) { if (index < c->v4Function->nFormals) { c->callData->args[c->v4Function->nFormals - index - 1] = value; @@ -360,7 +367,7 @@ void ExecutionContext::setProperty(String *name, const Value &value) } if (activation) { - uint member = activation->internalClass()->find(name); + uint member = activation->internalClass()->find(id); if (member < UINT_MAX) { activation->putValue(member, value); return; @@ -368,21 +375,21 @@ void ExecutionContext::setProperty(String *name, const Value &value) } } - if (d()->strictMode || name->equals(d()->engine->id_this())) { + if (d()->strictMode || name->equals(engine()->id_this())) { ScopedValue n(scope, name->asReturnedValue()); engine()->throwReferenceError(n); return; } - d()->engine->globalObject->put(name, value); + engine()->globalObject->put(name, value); } ReturnedValue ExecutionContext::getProperty(String *name) { Scope scope(this); ScopedValue v(scope); - name->makeIdentifier(scope.engine); + name->makeIdentifier(); - if (name->equals(d()->engine->id_this())) + if (name->equals(engine()->id_this())) return thisObject().asReturnedValue(); ScopedContext ctx(scope, this); @@ -413,7 +420,10 @@ ReturnedValue ExecutionContext::getProperty(String *name) } case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); - uint index = c->v4Function->internalClass->find(name); + name->makeIdentifier(); + Identifier *id = name->identifier(); + + uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) { if (index < c->v4Function->nFormals) return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); @@ -456,9 +466,9 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) Scope scope(this); ScopedValue v(scope); base->setM(0); - name->makeIdentifier(scope.engine); + name->makeIdentifier(); - if (name->equals(d()->engine->id_this())) + if (name->equals(engine()->id_this())) return thisObject().asReturnedValue(); ScopedContext ctx(scope, this); @@ -490,7 +500,10 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) } case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); - uint index = c->v4Function->internalClass->find(name); + name->makeIdentifier(); + Identifier *id = name->identifier(); + + uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) { if (index < c->v4Function->nFormals) return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); @@ -531,7 +544,7 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) Function *ExecutionContext::getFunction() const { - Scope scope(d()->engine); + Scope scope(engine()); ScopedContext it(scope, this->d()); for (; it; it = it->d()->outer) { if (const SimpleCallContext *callCtx = it->asSimpleCallContext()) diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 625d9ed424..a854c324d0 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -104,7 +104,6 @@ struct QmlContext; #define ExecutionContextMembers(class, Member) \ Member(class, NoMark, CallData *, callData) \ - Member(class, NoMark, ExecutionEngine *, engine) \ Member(class, Pointer, ExecutionContext *, outer) \ Member(class, NoMark, Lookup *, lookups) \ Member(class, NoMark, const QV4::Value *, constantTable) \ @@ -124,11 +123,10 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) { Type_CallContext = 0x6 }; - void init(ExecutionEngine *engine, ContextType t) + void init(ContextType t) { Base::init(); - this->engine = engine; type = t; lineNumber = -1; } @@ -146,8 +144,7 @@ Q_STATIC_ASSERT(sizeof(ExecutionContext) == sizeof(Base) + sizeof(ExecutionConte Q_STATIC_ASSERT(std::is_standard_layout<ExecutionContextData>::value); Q_STATIC_ASSERT(offsetof(ExecutionContextData, callData) == 0); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, engine) == offsetof(ExecutionContextData, callData) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, outer) == offsetof(ExecutionContextData, engine) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(ExecutionContextData, outer) == offsetof(ExecutionContextData, callData) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(ExecutionContextData, lookups) == offsetof(ExecutionContextData, outer) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(ExecutionContextData, constantTable) == offsetof(ExecutionContextData, lookups) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(ExecutionContextData, compilationUnit) == offsetof(ExecutionContextData, constantTable) + QT_POINTER_SIZE); @@ -160,9 +157,9 @@ Q_STATIC_ASSERT(offsetof(ExecutionContextData, lineNumber) == offsetof(Execution DECLARE_HEAP_OBJECT(SimpleCallContext, ExecutionContext) { DECLARE_MARK_TABLE(SimpleCallContext); - void init(ExecutionEngine *engine, ContextType t = Type_SimpleCallContext) + void init(ContextType t = Type_SimpleCallContext) { - ExecutionContext::init(engine, t); + ExecutionContext::init(t); } inline unsigned int formalParameterCount() const; @@ -240,8 +237,7 @@ struct Q_QML_EXPORT ExecutionContext : public Managed V4_MANAGED(ExecutionContext, Managed) Q_MANAGED_TYPE(ExecutionContext) - - ExecutionEngine *engine() const { return d()->engine; } + V4_INTERNALCLASS(ExecutionContext) Heap::CallContext *newCallContext(Function *f, CallData *callData); Heap::WithContext *newWithContext(Heap::Object *with); @@ -281,6 +277,7 @@ struct Q_QML_EXPORT ExecutionContext : public Managed struct Q_QML_EXPORT SimpleCallContext : public ExecutionContext { V4_MANAGED(SimpleCallContext, ExecutionContext) + V4_INTERNALCLASS(SimpleCallContext) // formals are in reverse order Identifier * const *formals() const; diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index b32b2c3f66..b0373884dd 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -114,6 +114,8 @@ struct DateCtor: FunctionObject struct DatePrototype: Object { + V4_PROTOTYPE(objectPrototype) + void init(ExecutionEngine *engine, Object *ctor); static double getThisDate(Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 6465b15d5a..019936767a 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -109,9 +109,9 @@ using namespace QV4; static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1); -static ReturnedValue throwTypeError(CallContext *ctx) +void throwTypeError(const BuiltinFunction *, Scope &scope, CallData *) { - return ctx->engine()->throwTypeError(); + scope.result = scope.engine->throwTypeError(); } @@ -130,10 +130,8 @@ QQmlEngine *ExecutionEngine::qmlEngine() const qint32 ExecutionEngine::maxCallDepth = -1; ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) - : callDepth(0) - , executableAllocator(new QV4::ExecutableAllocator) + : executableAllocator(new QV4::ExecutableAllocator) , regExpAllocator(new QV4::ExecutableAllocator) - , currentContext(0) , bumperPointerAllocator(new WTF::BumpPointerAllocator) , jsStack(new WTF::PageAllocation) , gcStack(new WTF::PageAllocation) @@ -219,7 +217,13 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) classPool = new InternalClassPool; - emptyClass = new (classPool) InternalClass(this); + internalClasses[Class_Empty] = new (classPool) InternalClass(this); + internalClasses[Class_String] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::String::staticVTable()); + internalClasses[Class_MemberData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::MemberData::staticVTable()); + internalClasses[Class_SimpleArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable()); + internalClasses[Class_SparseArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable()); + internalClasses[Class_ExecutionContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable()); + internalClasses[Class_SimpleCallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable()); jsStrings[String_Empty] = newIdentifier(QString()); jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined")); @@ -258,90 +262,113 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) jsStrings[String_buffer] = newIdentifier(QStringLiteral("buffer")); jsStrings[String_lastIndex] = newIdentifier(QStringLiteral("lastIndex")); - jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(emptyClass); + InternalClass *ic = internalClasses[Class_Empty]->changeVTable(QV4::Object::staticVTable()); + jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic); + internalClasses[Class_Object] = ic->changePrototype(objectPrototype()->d()); - arrayClass = emptyClass->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable); - jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(arrayClass, objectPrototype()); + ic = newInternalClass(ArrayPrototype::staticVTable(), objectPrototype()); + Q_ASSERT(ic->prototype); + ic = ic->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable); + Q_ASSERT(ic->prototype); + jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic, objectPrototype()); + internalClasses[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d()); jsObjects[PropertyListProto] = memoryManager->allocObject<PropertyListPrototype>(); - InternalClass *argsClass = emptyClass->addMember(id_length(), Attr_NotEnumerable); - argumentsObjectClass = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable); - strictArgumentsObjectClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - strictArgumentsObjectClass = strictArgumentsObjectClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + InternalClass *argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype()); + argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable); + internalClasses[EngineBase::Class_ArgumentsObject] = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable); + argsClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + internalClasses[EngineBase::Class_StrictArgumentsObject] = argsClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); *static_cast<Value *>(globalObject) = newObject(); Q_ASSERT(globalObject->d()->vtable()); initRootContext(); - stringClass = emptyClass->addMember(id_length(), Attr_ReadOnly); - Q_ASSERT(stringClass->find(id_length()) == Heap::StringObject::LengthPropertyIndex); - jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(stringClass, objectPrototype()); - jsObjects[NumberProto] = memoryManager->allocObject<NumberPrototype>(emptyClass, objectPrototype()); - jsObjects[BooleanProto] = memoryManager->allocObject<BooleanPrototype>(emptyClass, objectPrototype()); - jsObjects[DateProto] = memoryManager->allocObject<DatePrototype>(emptyClass, objectPrototype()); + ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype()); + ic = ic->addMember(id_length(), Attr_ReadOnly); + jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic); + internalClasses[Class_StringObject] = ic->changePrototype(stringPrototype()->d()); + Q_ASSERT(internalClasses[EngineBase::Class_StringObject]->find(id_length()) == Heap::StringObject::LengthPropertyIndex); + + jsObjects[NumberProto] = memoryManager->allocObject<NumberPrototype>(); + jsObjects[BooleanProto] = memoryManager->allocObject<BooleanPrototype>(); + jsObjects[DateProto] = memoryManager->allocObject<DatePrototype>(); uint index; - InternalClass *functionProtoClass = emptyClass->addMember(id_prototype(), Attr_NotEnumerable, &index); + ic = newInternalClass(QV4::FunctionPrototype::staticVTable(), objectPrototype()); + ic = ic->addMember(id_prototype(), Attr_NotEnumerable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_Prototype); - jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(functionProtoClass, objectPrototype()); - functionClass = emptyClass->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index); + jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(ic, objectPrototype()); + ic = newInternalClass(FunctionObject::staticVTable(), functionPrototype()); + ic = ic->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_Prototype); - scriptFunctionClass = functionClass->addMember(id_name(), Attr_ReadOnly, &index); + internalClasses[EngineBase::Class_FunctionObject] = ic; + ic = ic->addMember(id_name(), Attr_ReadOnly, &index); Q_ASSERT(index == Heap::ScriptFunction::Index_Name); - scriptFunctionClass = scriptFunctionClass->addMember(id_length(), Attr_ReadOnly, &index); + ic = ic->changeVTable(ScriptFunction::staticVTable()); + internalClasses[EngineBase::Class_ScriptFunction] = ic->addMember(id_length(), Attr_ReadOnly, &index); + Q_ASSERT(index == Heap::ScriptFunction::Index_Length); + internalClasses[EngineBase::Class_BuiltinFunction] = ic->changeVTable(BuiltinFunction::staticVTable()); Q_ASSERT(index == Heap::ScriptFunction::Index_Length); - protoClass = emptyClass->addMember(id_constructor(), Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ObjectProto] = internalClasses[Class_Object]->addMember(id_constructor(), Attr_NotEnumerable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_ProtoConstructor); Scope scope(this); ScopedString str(scope); - regExpObjectClass = emptyClass->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index); + internalClasses[Class_RegExp] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::RegExp::staticVTable()); + ic = newInternalClass(QV4::RegExpObject::staticVTable(), objectPrototype()); + ic = ic->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index); Q_ASSERT(index == RegExpObject::Index_LastIndex); - regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("source"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("source"))), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Source); - regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("global"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("global"))), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Global); - regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("ignoreCase"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("ignoreCase"))), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_IgnoreCase); - regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("multiline"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("multiline"))), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Multiline); + jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(ic, objectPrototype()); + internalClasses[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d()); - jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(regExpObjectClass, objectPrototype()); - regExpExecArrayClass = arrayClass->addMember(id_index(), Attr_Data, &index); + ic = internalClasses[Class_ArrayObject]->addMember(id_index(), Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayIndex); - regExpExecArrayClass = regExpExecArrayClass->addMember(id_input(), Attr_Data, &index); + internalClasses[EngineBase::Class_RegExpExecArray] = ic->addMember(id_input(), Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayInput); - errorClass = emptyClass->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index); + ic = newInternalClass(ErrorObject::staticVTable(), 0); + ic = ic->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_Stack); - errorClass = errorClass->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_FileName); - errorClass = errorClass->addMember((str = newIdentifier(QStringLiteral("lineNumber"))), Attr_Data|Attr_NotEnumerable, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber"))), Attr_Data|Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ErrorObject] = ic; Q_ASSERT(index == ErrorObject::Index_LineNumber); - errorClassWithMessage = errorClass->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_Message); - errorProtoClass = emptyClass->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index); + ic = newInternalClass(ErrorObject::staticVTable(), objectPrototype()); + ic = ic->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Constructor); - errorProtoClass = errorProtoClass->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Message); - errorProtoClass = errorProtoClass->addMember(id_name(), Attr_Data|Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ErrorProto] = ic->addMember(id_name(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Name); jsObjects[GetStack_Function] = BuiltinFunction::create(rootContext(), str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack); getStackFunction()->defineReadonlyProperty(id_length(), Primitive::fromInt32(0)); - jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(errorProtoClass, objectPrototype()); - jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(errorProtoClass, errorPrototype()); - jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(errorProtoClass, errorPrototype()); - jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(errorProtoClass, errorPrototype()); - jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(errorProtoClass, errorPrototype()); - jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(errorProtoClass, errorPrototype()); - jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(errorProtoClass, errorPrototype()); + jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto], objectPrototype()); + jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); + jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); + jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); + jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); + jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); + jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); - jsObjects[VariantProto] = memoryManager->allocObject<VariantPrototype>(emptyClass, objectPrototype()); + jsObjects[VariantProto] = memoryManager->allocObject<VariantPrototype>(); Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d()); - jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(arrayClass, arrayPrototype())); + ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this)); + jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic, SequencePrototype::defaultPrototype(this))); ExecutionContext *global = rootContext(); jsObjects[Object_Ctor] = memoryManager->allocObject<ObjectCtor>(global); @@ -490,7 +517,7 @@ ExecutionEngine::~ExecutionEngine() for (QV4::CompiledData::CompilationUnit *unit : qAsConst(remainingUnits)) unit->unlink(); - emptyClass->destroy(); + internalClasses[Class_Empty]->destroy(); delete classPool; delete bumperPointerAllocator; delete regExpCache; @@ -548,6 +575,11 @@ ExecutionContext *ExecutionEngine::pushGlobalContext() return currentContext; } +InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype) +{ + return internalClasses[EngineBase::Class_Empty]->changeVTable(vtable)->changePrototype(prototype ? prototype->d() : 0); +} + Heap::Object *ExecutionEngine::newObject() { return memoryManager->allocObject<Object>(); @@ -864,8 +896,8 @@ static inline char *v4StackTrace(const ExecutionContext *context) QString result; QTextStream str(&result); str << "stack=["; - if (context && context->d()->engine) { - const QVector<StackFrame> stackTrace = context->d()->engine->stackTrace(20); + if (context && context->engine()) { + const QVector<StackFrame> stackTrace = context->engine()->stackTrace(20); for (int i = 0; i < stackTrace.size(); ++i) { if (i) str << ','; diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index a2c774c295..16a043dda5 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -55,6 +55,7 @@ #include "qv4managed_p.h" #include "qv4context_p.h" #include <private/qintrusivelist_p.h> +#include "qv4enginebase_p.h" #ifndef V4_BOOTSTRAP # include <private/qv8engine_p.h> @@ -96,16 +97,10 @@ private: friend struct ExecutionContext; friend struct Heap::ExecutionContext; public: - qint32 callDepth; - ExecutableAllocator *executableAllocator; ExecutableAllocator *regExpAllocator; QScopedPointer<EvalISelFactory> iselFactory; - ExecutionContext *currentContext; - - Value *jsStackLimit; - WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine. enum { @@ -113,7 +108,6 @@ public: GCStackLimit = 2*1024*1024 }; WTF::PageAllocation *jsStack; - Value *jsStackBase; WTF::PageAllocation *gcStack; @@ -125,10 +119,6 @@ public: return ptr; } - IdentifierTable *identifierTable; - - Object *globalObject; - Function *globalCode; #ifdef V4_BOOTSTRAP @@ -239,25 +229,6 @@ public: Object *signalHandlerPrototype() const { return reinterpret_cast<Object *>(jsObjects + SignalHandlerProto); } InternalClassPool *classPool; - InternalClass *emptyClass; - - InternalClass *arrayClass; - InternalClass *stringClass; - - InternalClass *functionClass; - InternalClass *scriptFunctionClass; - InternalClass *protoClass; - - InternalClass *regExpExecArrayClass; - InternalClass *regExpObjectClass; - - InternalClass *argumentsObjectClass; - InternalClass *strictArgumentsObjectClass; - - InternalClass *errorClass; - InternalClass *errorClassWithMessage; - InternalClass *errorProtoClass; - EvalFunction *evalFunction() const { return reinterpret_cast<EvalFunction *>(jsObjects + Eval_Function); } FunctionObject *getStackFunction() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetStack_Function); } FunctionObject *thrower() const { return reinterpret_cast<FunctionObject *>(jsObjects + ThrowerObject); } @@ -391,6 +362,8 @@ public: void popContext(); ExecutionContext *parentContext(ExecutionContext *context) const; + InternalClass *newInternalClass(const VTable *vtable, Object *prototype); + Heap::Object *newObject(); Heap::Object *newObject(InternalClass *internalClass, Object *prototype); diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h new file mode 100644 index 0000000000..88f9dfd85c --- /dev/null +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ENGINEBASE_P_H +#define QV4ENGINEBASE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qv4global_p.h> +#include <private/qv4runtimeapi_p.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +// Base class for the execution engine + +#if defined(Q_CC_MSVC) || defined(Q_CC_GNU) +#pragma pack(push, 1) +#endif +struct EngineBase { + Heap::ExecutionContext *current = 0; + + Value *jsStackTop = 0; + quint8 hasException = false; + quint8 writeBarrierActive = false; + quint16 unused = 0; +#if QT_POINTER_SIZE == 8 + quint8 padding[4]; +#endif + MemoryManager *memoryManager = 0; + Runtime runtime; + + qint32 callDepth = 0; + Value *jsStackLimit = 0; + Value *jsStackBase = 0; + + ExecutionContext *currentContext = 0; + IdentifierTable *identifierTable = 0; + Object *globalObject = 0; + + enum { + Class_Empty, + Class_String, + Class_MemberData, + Class_SimpleArrayData, + Class_SparseArrayData, + Class_ExecutionContext, + Class_SimpleCallContext, + Class_Object, + Class_ArrayObject, + Class_FunctionObject, + Class_StringObject, + Class_ScriptFunction, + Class_BuiltinFunction, + Class_ObjectProto, + Class_RegExp, + Class_RegExpObject, + Class_RegExpExecArray, + Class_ArgumentsObject, + Class_StrictArgumentsObject, + Class_ErrorObject, + Class_ErrorObjectWithMessage, + Class_ErrorProto, + NClasses + }; + InternalClass *internalClasses[NClasses]; +}; +#if defined(Q_CC_MSVC) || defined(Q_CC_GNU) +#pragma pack(pop) +#endif + +Q_STATIC_ASSERT(std::is_standard_layout<EngineBase>::value); +Q_STATIC_ASSERT(offsetof(EngineBase, current) == 0); +Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, current) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(EngineBase, hasException) == offsetof(EngineBase, jsStackTop) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(EngineBase, memoryManager) == offsetof(EngineBase, hasException) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(EngineBase, runtime) == offsetof(EngineBase, memoryManager) + QT_POINTER_SIZE); + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index 58742a0b84..b3bd28e18b 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -75,7 +75,7 @@ void Heap::ErrorObject::init() Scope scope(internalClass->engine); Scoped<QV4::ErrorObject> e(scope, this); - if (internalClass == scope.engine->errorProtoClass) + if (internalClass == scope.engine->internalClasses[EngineBase::Class_ErrorProto]) return; setProperty(scope.engine, QV4::ErrorObject::Index_Stack, scope.engine->getStackFunction()->d()); @@ -175,8 +175,6 @@ void ErrorObject::method_get_stack(const BuiltinFunction *, Scope &scope, CallDa DEFINE_OBJECT_VTABLE(ErrorObject); -DEFINE_OBJECT_VTABLE(SyntaxErrorObject); - void Heap::SyntaxErrorObject::init(const Value &msg) { Heap::ErrorObject::init(msg, SyntaxError); @@ -322,8 +320,7 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, He obj->setProperty(Index_Constructor, ctor->d()); obj->setProperty(Index_Message, engine->id_empty()->d()); obj->setProperty(Index_Name, engine->newString(QString::fromLatin1(ErrorObject::className(t)))); - if (t == Heap::ErrorObject::Error) - obj->defineDefaultProperty(engine->id_toString(), method_toString, 0); + obj->defineDefaultProperty(engine->id_toString(), method_toString, 0); } void ErrorPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index 5afd9efcba..d556617b48 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -160,7 +160,7 @@ struct ErrorObject: Object { V4_OBJECT2(ErrorObject, Object) Q_MANAGED_TYPE(ErrorObject) - V4_INTERNALCLASS(errorClass) + V4_INTERNALCLASS(ErrorObject) V4_PROTOTYPE(errorPrototype) V4_NEEDS_DESTROY @@ -205,8 +205,10 @@ struct ReferenceErrorObject: ErrorObject { }; struct SyntaxErrorObject: ErrorObject { - V4_OBJECT2(SyntaxErrorObject, ErrorObject) + typedef Heap::SyntaxErrorObject Data; V4_PROTOTYPE(syntaxErrorPrototype) + const Data *d() const { return static_cast<const Data *>(ErrorObject::d()); } + Data *d() { return static_cast<Data *>(ErrorObject::d()); } }; struct TypeErrorObject: ErrorObject { @@ -326,19 +328,25 @@ inline SyntaxErrorObject *ErrorObject::asSyntaxError() template <typename T> Heap::Object *ErrorObject::create(ExecutionEngine *e, const Value &message) { - return e->memoryManager->allocObject<T>(message.isUndefined() ? e->errorClass : e->errorClassWithMessage, T::defaultPrototype(e), message); + InternalClass *ic = e->internalClasses[message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage]; + ic = ic->changePrototype(T::defaultPrototype(e)->d()); + return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), message); } template <typename T> Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message) { Scope scope(e); ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue()); - return e->memoryManager->allocObject<T>(v->isUndefined() ? e->errorClass : e->errorClassWithMessage, T::defaultPrototype(e), v); + InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage]; + ic = ic->changePrototype(T::defaultPrototype(e)->d()); + return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), v); } template <typename T> Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message, const QString &filename, int line, int column) { Scope scope(e); ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue()); - return e->memoryManager->allocObject<T>(v->isUndefined() ? e->errorClass : e->errorClassWithMessage, T::defaultPrototype(e), v, filename, line, column); + InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage]; + ic = ic->changePrototype(T::defaultPrototype(e)->d()); + return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), v, filename, line, column); } diff --git a/src/qml/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h index f13f70e01a..353a6eacff 100644 --- a/src/qml/jsruntime/qv4executableallocator_p.h +++ b/src/qml/jsruntime/qv4executableallocator_p.h @@ -67,7 +67,7 @@ QT_BEGIN_NAMESPACE namespace QV4 { -class Q_AUTOTEST_EXPORT ExecutableAllocator +class Q_QML_AUTOTEST_EXPORT ExecutableAllocator { public: struct ChunkOfPages; diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index ed9e3699f2..4c8117527c 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -59,7 +59,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, { Q_UNUSED(engine); - internalClass = engine->emptyClass; + internalClass = engine->internalClasses[EngineBase::Class_Empty]; const CompiledData::LEUInt32 *formalsIndices = compiledFunction->formalsTable(); // iterate backwards, so we get the right ordering for duplicate names Scope scope(engine); @@ -74,7 +74,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, } // duplicate arguments, need some trick to store them MemoryManager *mm = engine->memoryManager; - arg = mm->alloc<String>(mm, arg->d(), engine->newString(QString(0xfffe))); + arg = mm->alloc<String>(arg->d(), engine->newString(QString(0xfffe))); } } nFormals = compiledFunction->nFormals; @@ -92,7 +92,7 @@ Function::~Function() void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters) { - internalClass = engine->emptyClass; + internalClass = engine->internalClasses[EngineBase::Class_Empty]; // iterate backwards, so we get the right ordering for duplicate names Scope scope(engine); @@ -106,7 +106,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr break; } // duplicate arguments, need some trick to store them - arg = engine->memoryManager->alloc<String>(engine->memoryManager, arg->d(), engine->newString(QString(0xfffe))); + arg = engine->memoryManager->alloc<String>(arg->d(), engine->newString(QString(0xfffe))); } } nFormals = parameters.size(); diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 5c8f03dc72..45fdde98f7 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -124,8 +124,8 @@ void FunctionObject::init(String *n, bool createProto) Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()) == Heap::FunctionObject::Index_Prototype); if (createProto) { - ScopedObject proto(s, scope()->engine->newObject(s.engine->protoClass, s.engine->objectPrototype())); - Q_ASSERT(s.engine->protoClass->find(s.engine->id_constructor()) == Heap::FunctionObject::Index_ProtoConstructor); + ScopedObject proto(s, s.engine->newObject(s.engine->internalClasses[EngineBase::Class_ObjectProto], s.engine->objectPrototype())); + Q_ASSERT(s.engine->internalClasses[EngineBase::Class_ObjectProto]->find(s.engine->id_constructor()) == Heap::FunctionObject::Index_ProtoConstructor); proto->setProperty(Heap::FunctionObject::Index_ProtoConstructor, d()); setProperty(Heap::FunctionObject::Index_Prototype, proto); } else { @@ -138,7 +138,7 @@ void FunctionObject::init(String *n, bool createProto) ReturnedValue FunctionObject::name() const { - return get(scope()->engine->id_name()); + return get(scope()->internalClass->engine->id_name()); } void FunctionObject::construct(const Managed *that, Scope &scope, CallData *) @@ -153,7 +153,7 @@ void FunctionObject::call(const Managed *, Scope &scope, CallData *) Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function) { - return scope->d()->engine->memoryManager->allocObject<ScriptFunction>(scope, function); + return scope->engine()->memoryManager->allocObject<ScriptFunction>(scope, function); } bool FunctionObject::isBinding() const @@ -369,8 +369,8 @@ void ScriptFunction::construct(const Managed *that, Scope &scope, CallData *call Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that)); - InternalClass *ic = v4->emptyClass; - ScopedObject proto(scope, f->protoForConstructor()); + InternalClass *ic = f->classForConstructor(); + ScopedObject proto(scope, ic->prototype); ScopedObject obj(scope, v4->newObject(ic, proto)); callData->thisObject = obj.asReturnedValue(); @@ -433,54 +433,24 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function ScopedProperty pd(s); pd->value = s.engine->thrower(); pd->set = s.engine->thrower(); - f->insertMember(scope->d()->engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - f->insertMember(scope->d()->engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + f->insertMember(s.engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } } -Heap::Object *ScriptFunction::protoForConstructor() const +InternalClass *ScriptFunction::classForConstructor() const { const Object *o = d()->protoProperty(); - if (o) - return o->d(); - return engine()->objectPrototype()->d(); -} - - + InternalClass *ic = d()->cachedClassForConstructor; + if (ic && ic->prototype == o->d()) + return ic; -DEFINE_OBJECT_VTABLE(OldBuiltinFunction); - -void Heap::OldBuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(QV4::CallContext *)) -{ - Heap::FunctionObject::init(scope, name); - this->code = code; -} - -void OldBuiltinFunction::construct(const Managed *f, Scope &scope, CallData *) -{ - scope.result = static_cast<const OldBuiltinFunction *>(f)->internalClass()->engine->throwTypeError(); -} - -void OldBuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData) -{ - const OldBuiltinFunction *f = static_cast<const OldBuiltinFunction *>(that); - ExecutionEngine *v4 = scope.engine; - if (v4->hasException) { - scope.result = Encode::undefined(); - return; - } - CHECK_STACK_LIMITS(v4, scope); - - ExecutionContextSaver ctxSaver(scope); - - SimpleCallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(v4); - ctx->strictMode = f->scope()->strictMode; // ### needed? scope or parent context? - ctx->callData = callData; - v4->pushContext(ctx); - Q_ASSERT(v4->current == ctx); + ic = engine()->internalClasses[EngineBase::Class_Object]; + if (o) + ic = ic->changePrototype(o->d()); + d()->cachedClassForConstructor = ic; - scope.result = f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext)); - v4->memoryManager->freeSimpleCallContext(); + return ic; } DEFINE_OBJECT_VTABLE(BuiltinFunction); @@ -520,7 +490,7 @@ void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *c ExecutionContextSaver ctxSaver(scope); - SimpleCallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(v4); + SimpleCallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(); ctx->strictMode = f->scope()->strictMode; // ### needed? scope or parent context? ctx->callData = callData; v4->pushContext(ctx); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index d8929026ca..6ce5734b6d 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -118,6 +118,8 @@ struct ScriptFunction : FunctionObject { Index_Length }; void init(QV4::ExecutionContext *scope, Function *function); + + QV4::InternalClass *cachedClassForConstructor; }; #define BoundFunctionMembers(class, Member) \ @@ -139,7 +141,7 @@ struct Q_QML_EXPORT FunctionObject: Object { }; V4_OBJECT2(FunctionObject, Object) Q_MANAGED_TYPE(FunctionObject) - V4_INTERNALCLASS(functionClass) + V4_INTERNALCLASS(FunctionObject) V4_PROTOTYPE(functionPrototype) V4_NEEDS_DESTROY @@ -192,20 +194,10 @@ struct FunctionPrototype: FunctionObject static void method_bind(const BuiltinFunction *, Scope &scope, CallData *callData); }; -struct Q_QML_EXPORT OldBuiltinFunction : FunctionObject { - V4_OBJECT2(OldBuiltinFunction, FunctionObject) - - static void construct(const Managed *, Scope &scope, CallData *); - static void call(const Managed *that, Scope &scope, CallData *callData); -}; - struct Q_QML_EXPORT BuiltinFunction : FunctionObject { V4_OBJECT2(BuiltinFunction, FunctionObject) + V4_INTERNALCLASS(BuiltinFunction) - static Heap::OldBuiltinFunction *create(ExecutionContext *scope, String *name, ReturnedValue (*code)(CallContext *)) - { - return scope->engine()->memoryManager->allocObject<OldBuiltinFunction>(scope, name, code); - } static Heap::BuiltinFunction *create(ExecutionContext *scope, String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *)) { return scope->engine()->memoryManager->allocObject<BuiltinFunction>(scope, name, code); @@ -238,12 +230,12 @@ void Heap::IndexedBuiltinFunction::init(QV4::ExecutionContext *scope, uint index struct ScriptFunction : FunctionObject { V4_OBJECT2(ScriptFunction, FunctionObject) - V4_INTERNALCLASS(scriptFunctionClass) + V4_INTERNALCLASS(ScriptFunction) static void construct(const Managed *, Scope &scope, CallData *callData); static void call(const Managed *that, Scope &scope, CallData *callData); - Heap::Object *protoForConstructor() const; + InternalClass *classForConstructor() const; }; diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index f0630660d4..0916e8e110 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -332,8 +332,8 @@ DEFINE_OBJECT_VTABLE(EvalFunction); void Heap::EvalFunction::init(QV4::ExecutionContext *scope) { - Heap::FunctionObject::init(scope, scope->d()->engine->id_eval()); Scope s(scope); + Heap::FunctionObject::init(scope, s.engine->id_eval()); ScopedFunctionObject f(s, this); f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1)); } diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 3d9a672f2f..603da1df7b 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -43,6 +43,7 @@ #include <qv4identifier_p.h> #include "qv4object_p.h" #include "qv4identifiertable_p.h" +#include "qv4value_p.h" QT_BEGIN_NAMESPACE @@ -104,6 +105,8 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize) InternalClass::InternalClass(ExecutionEngine *engine) : engine(engine) + , vtable(0) + , prototype(0) , m_sealed(0) , m_frozen(0) , size(0) @@ -115,6 +118,8 @@ InternalClass::InternalClass(ExecutionEngine *engine) InternalClass::InternalClass(const QV4::InternalClass &other) : QQmlJS::Managed() , engine(other.engine) + , vtable(other.vtable) + , prototype(other.prototype) , propertyTable(other.propertyTable) , nameMap(other.nameMap) , propertyData(other.propertyData) @@ -126,6 +131,24 @@ InternalClass::InternalClass(const QV4::InternalClass &other) Q_ASSERT(extensible); } +static void insertHoleIntoPropertyData(Object *object, int idx) +{ + Heap::Object *o = object->d(); + ExecutionEngine *v4 = o->internalClass->engine; + int size = o->internalClass->size; + for (int i = size - 1; i > idx; --i) + o->setProperty(v4, i, *o->propertyData(i - 1)); +} + +static void removeFromPropertyData(Object *object, int idx, bool accessor = false) +{ + Heap::Object *o = object->d(); + ExecutionEngine *v4 = o->internalClass->engine; + int size = o->internalClass->size; + for (int i = idx; i < size; ++i) + o->setProperty(v4, i, *o->propertyData(i + (accessor ? 2 : 1))); +} + void InternalClass::changeMember(Object *object, String *string, PropertyAttributes data, uint *index) { uint idx; @@ -137,10 +160,10 @@ void InternalClass::changeMember(Object *object, String *string, PropertyAttribu object->setInternalClass(newClass); if (newClass->size > oldClass->size) { Q_ASSERT(newClass->size == oldClass->size + 1); - object->d()->memberData->values.insertData(newClass->engine, idx + 1, Primitive::emptyValue()); + insertHoleIntoPropertyData(object, idx); } else if (newClass->size < oldClass->size) { Q_ASSERT(newClass->size == oldClass->size - 1); - object->d()->memberData->values.removeData(newClass->engine, idx + 1); + removeFromPropertyData(object, idx + 1); } } @@ -167,13 +190,14 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri if (data == propertyData.at(idx)) return this; - Transition temp = { identifier, 0, (int)data.flags() }; + Transition temp = { { identifier }, nullptr, (int)data.flags() }; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) return t.lookup; // create a new class and add it to the tree - InternalClass *newClass = engine->emptyClass; + InternalClass *newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); + newClass = newClass->changePrototype(prototype); for (uint i = 0; i < size; ++i) { if (i == idx) { newClass = newClass->addMember(nameMap.at(i), data); @@ -187,12 +211,72 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri return newClass; } +InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) +{ + Q_ASSERT(prototype != proto); + + Transition temp = { { nullptr }, 0, Transition::PrototypeChange }; + temp.prototype = proto; + + Transition &t = lookupOrInsertTransition(temp); + if (t.lookup) + return t.lookup; + + // create a new class and add it to the tree + InternalClass *newClass; + if (!size && !prototype) { + newClass = engine->newClass(*this); + newClass->prototype = proto; + } else { + newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); + newClass = newClass->changePrototype(proto); + for (uint i = 0; i < size; ++i) { + if (!propertyData.at(i).isEmpty()) + newClass = newClass->addMember(nameMap.at(i), propertyData.at(i)); + } + } + + t.lookup = newClass; + return newClass; +} + +InternalClass *InternalClass::changeVTableImpl(const VTable *vt) +{ + Q_ASSERT(vtable != vt); + + Transition temp = { { nullptr }, nullptr, Transition::VTableChange }; + temp.vtable = vt; + + Transition &t = lookupOrInsertTransition(temp); + if (t.lookup) + return t.lookup; + + // create a new class and add it to the tree + InternalClass *newClass; + if (this == engine->internalClasses[EngineBase::Class_Empty]) { + newClass = engine->newClass(*this); + newClass->vtable = vt; + } else { + newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vt); + newClass = newClass->changePrototype(prototype); + for (uint i = 0; i < size; ++i) { + if (!propertyData.at(i).isEmpty()) + newClass = newClass->addMember(nameMap.at(i), propertyData.at(i)); + } + } + + t.lookup = newClass; + Q_ASSERT(t.lookup); + Q_ASSERT(newClass->vtable); + return newClass; +} + InternalClass *InternalClass::nonExtensible() { if (!extensible) return this; - Transition temp = { Q_NULLPTR, Q_NULLPTR, Transition::NotExtensible}; + Transition temp = { { nullptr }, nullptr, Transition::NotExtensible}; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) return t.lookup; @@ -240,7 +324,7 @@ InternalClass *InternalClass::addMember(Identifier *identifier, PropertyAttribut InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index) { - Transition temp = { identifier, 0, (int)data.flags() }; + Transition temp = { { identifier }, nullptr, (int)data.flags() }; Transition &t = lookupOrInsertTransition(temp); if (index) @@ -276,7 +360,7 @@ void InternalClass::removeMember(Object *object, Identifier *id) uint propIdx = oldClass->propertyTable.lookup(id); Q_ASSERT(propIdx < oldClass->size); - Transition temp = { id, 0, -1 }; + Transition temp = { { id }, nullptr, -1 }; Transition &t = object->internalClass()->lookupOrInsertTransition(temp); bool accessor = oldClass->propertyData.at(propIdx).isAccessor(); @@ -285,7 +369,8 @@ void InternalClass::removeMember(Object *object, Identifier *id) object->setInternalClass(t.lookup); } else { // create a new class and add it to the tree - InternalClass *newClass = oldClass->engine->emptyClass; + InternalClass *newClass = oldClass->engine->internalClasses[EngineBase::Class_Empty]->changeVTable(oldClass->vtable); + newClass = newClass->changePrototype(oldClass->prototype); for (uint i = 0; i < oldClass->size; ++i) { if (i == propIdx) continue; @@ -298,14 +383,17 @@ void InternalClass::removeMember(Object *object, Identifier *id) Q_ASSERT(object->internalClass()->size == oldClass->size - (accessor ? 2 : 1)); // remove the entry in the property data - object->d()->memberData->values.removeData(oldClass->engine, propIdx, accessor ? 2 : 1); + removeFromPropertyData(object, propIdx, accessor); t.lookup = object->internalClass(); Q_ASSERT(t.lookup); } -uint InternalClass::find(const Identifier *id) +uint QV4::InternalClass::find(const String *string) { + engine->identifierTable->identifier(string); + const Identifier *id = string->d()->identifier; + uint index = propertyTable.lookup(id); if (index < size) return index; @@ -318,7 +406,8 @@ InternalClass *InternalClass::sealed() if (m_sealed) return m_sealed; - m_sealed = engine->emptyClass; + m_sealed = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); + m_sealed = m_sealed->changePrototype(prototype); for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (attrs.isEmpty()) @@ -347,7 +436,8 @@ InternalClass *InternalClass::frozen() InternalClass *InternalClass::propertiesFrozen() const { - InternalClass *frozen = engine->emptyClass; + InternalClass *frozen = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); + frozen = frozen->changePrototype(prototype); for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (attrs.isEmpty()) @@ -390,7 +480,23 @@ void InternalClass::destroy() void InternalClassPool::markObjects(MarkStack *markStack) { - Q_UNUSED(markStack); + InternalClass *ic = markStack->engine->internalClasses[EngineBase::Class_Empty]; + Q_ASSERT(!ic->prototype); + + // only need to go two levels into the IC hierarchy, as prototype changes + // can only happen there + for (auto &t : ic->transitions) { + Q_ASSERT(t.lookup); + if (t.flags == InternalClassTransition::VTableChange) { + InternalClass *ic2 = t.lookup; + for (auto &t2 : ic2->transitions) { + if (t2.flags == InternalClassTransition::PrototypeChange) + t2.lookup->prototype->mark(markStack); + } + } else if (t.flags == InternalClassTransition::PrototypeChange) { + t.lookup->prototype->mark(markStack); + } + } } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index a29ce5b5ff..df17074e72 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -54,18 +54,17 @@ #include <QHash> #include <private/qqmljsmemorypool_p.h> -#include <private/qv4engine_p.h> -#include <private/qv4identifiertable_p.h> +#include <private/qv4identifier_p.h> QT_BEGIN_NAMESPACE namespace QV4 { struct String; -struct ExecutionEngine; struct Object; struct Identifier; struct VTable; +struct MarkStack; struct PropertyHashData; struct PropertyHash @@ -222,12 +221,18 @@ private: struct InternalClassTransition { - Identifier *id; + union { + Identifier *id; + const VTable *vtable; + Heap::Object *prototype; + }; InternalClass *lookup; int flags; enum { // range 0-0xff is reserved for attribute changes - NotExtensible = 0x100 + NotExtensible = 0x100, + VTableChange = 0x200, + PrototypeChange = 0x201 }; bool operator==(const InternalClassTransition &other) const @@ -239,6 +244,8 @@ struct InternalClassTransition struct InternalClass : public QQmlJS::Managed { ExecutionEngine *engine; + const VTable *vtable; + Heap::Object *prototype; PropertyHash propertyTable; // id to valueIndex SharedInternalClassData<Identifier *> nameMap; @@ -254,41 +261,49 @@ struct InternalClass : public QQmlJS::Managed { uint size; bool extensible; - InternalClass *nonExtensible(); + Q_REQUIRED_RESULT InternalClass *nonExtensible(); + Q_REQUIRED_RESULT InternalClass *changeVTable(const VTable *vt) { + if (vtable == vt) + return this; + return changeVTableImpl(vt); + } + Q_REQUIRED_RESULT InternalClass *changePrototype(Heap::Object *proto) { + if (prototype == proto) + return this; + return changePrototypeImpl(proto); + } + static void addMember(Object *object, String *string, PropertyAttributes data, uint *index); - InternalClass *addMember(String *string, PropertyAttributes data, uint *index = 0); - InternalClass *addMember(Identifier *identifier, PropertyAttributes data, uint *index = 0); - InternalClass *changeMember(Identifier *identifier, PropertyAttributes data, uint *index = 0); + Q_REQUIRED_RESULT InternalClass *addMember(String *string, PropertyAttributes data, uint *index = 0); + Q_REQUIRED_RESULT InternalClass *addMember(Identifier *identifier, PropertyAttributes data, uint *index = 0); + Q_REQUIRED_RESULT InternalClass *changeMember(Identifier *identifier, PropertyAttributes data, uint *index = 0); static void changeMember(Object *object, String *string, PropertyAttributes data, uint *index = 0); static void removeMember(Object *object, Identifier *id); uint find(const String *string); - uint find(const Identifier *id); + uint find(const Identifier *id) + { + uint index = propertyTable.lookup(id); + if (index < size) + return index; + + return UINT_MAX; + } - InternalClass *sealed(); - InternalClass *frozen(); - InternalClass *propertiesFrozen() const; + Q_REQUIRED_RESULT InternalClass *sealed(); + Q_REQUIRED_RESULT InternalClass *frozen(); + Q_REQUIRED_RESULT InternalClass *propertiesFrozen() const; void destroy(); private: + Q_QML_EXPORT InternalClass *changeVTableImpl(const VTable *vt); + Q_QML_EXPORT InternalClass *changePrototypeImpl(Heap::Object *proto); InternalClass *addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index); friend struct ExecutionEngine; InternalClass(ExecutionEngine *engine); InternalClass(const InternalClass &other); }; -inline uint InternalClass::find(const String *string) -{ - engine->identifierTable->identifier(string); - const Identifier *id = string->d()->identifier; - - uint index = propertyTable.lookup(id); - if (index < size) - return index; - - return UINT_MAX; -} - struct InternalClassPool : public QQmlJS::MemoryPool { void markObjects(MarkStack *markStack); diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 11d7767e05..d943ae1340 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -63,7 +63,7 @@ ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttribu return !attrs->isAccessor() ? v->asReturnedValue() : Object::getValue(thisObject, *v, *attrs); } - obj = obj->prototype; + obj = obj->prototype(); ++i; } level = Size; @@ -76,7 +76,7 @@ ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttribu return !attrs->isAccessor() ? v->asReturnedValue() : Object::getValue(thisObject, *v, *attrs); } - obj = obj->prototype; + obj = obj->prototype(); } return Primitive::emptyValue().asReturnedValue(); } @@ -98,7 +98,7 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs return !attrs->isAccessor() ? v->asReturnedValue() : thisObject->getValue(*v, *attrs); } - obj = obj->prototype; + obj = obj->prototype(); ++i; } level = Size; @@ -111,7 +111,7 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs return !attrs->isAccessor() ? v->asReturnedValue() : thisObject->getValue(*v, *attrs); } - obj = obj->prototype; + obj = obj->prototype(); } return Primitive::emptyValue().asReturnedValue(); } @@ -284,11 +284,17 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va ReturnedValue v = l->lookup(object, proto, &attrs); if (v != Primitive::emptyValue().asReturnedValue()) { l->type = object.type(); - l->proto = proto; + l->proto = proto->d(); if (attrs.isData()) { - if (l->level == 0) - l->getter = Lookup::primitiveGetter0; - else if (l->level == 1) + if (l->level == 0) { + uint nInline = l->proto->vtable()->nInlineProperties; + if (l->index < nInline) + l->getter = Lookup::primitiveGetter0Inline; + else { + l->index -= nInline; + l->getter = Lookup::primitiveGetter0MemberData; + } + } else if (l->level == 1) l->getter = Lookup::primitiveGetter1; return v; } else { @@ -307,15 +313,18 @@ ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const { Lookup l1 = *l; - if (l1.getter == Lookup::getter0 || l1.getter == Lookup::getter1) { + if (l1.getter == Lookup::getter0MemberData || l1.getter == Lookup::getter0Inline || l1.getter == Lookup::getter1) { if (const Object *o = object.as<Object>()) { ReturnedValue v = o->getLookup(l); Lookup l2 = *l; - if (l->index != UINT_MAX && (l2.getter == Lookup::getter0 || l2.getter == Lookup::getter1)) { - // if we have a getter0, make sure it comes first - if (l2.getter == Lookup::getter0) - qSwap(l1, l2); + if (l2.index != UINT_MAX) { + if (l1.getter != Lookup::getter0Inline) { + if (l2.getter == Lookup::getter0Inline || + (l1.getter != Lookup::getter0MemberData && l2.getter == Lookup::getter0MemberData)) + // sort the better getter first + qSwap(l1, l2); + } l->classList[0] = l1.classList[0]; l->classList[1] = l1.classList[1]; @@ -324,8 +333,22 @@ ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const l->index = l1.index; l->index2 = l2.index; - if (l1.getter == Lookup::getter0) { - l->getter = (l2.getter == Lookup::getter0) ? Lookup::getter0getter0 : Lookup::getter0getter1; + if (l1.getter == Lookup::getter0Inline) { + if (l2.getter == Lookup::getter0Inline) + l->getter = Lookup::getter0Inlinegetter0Inline; + else if (l2.getter == Lookup::getter0MemberData) + l->getter = Lookup::getter0Inlinegetter0MemberData; + else if (l2.getter == Lookup::getter1) + l->getter = Lookup::getter0Inlinegetter1; + else + Q_UNREACHABLE(); + } else if (l1.getter == Lookup::getter0MemberData) { + if (l2.getter == Lookup::getter0MemberData) + l->getter = Lookup::getter0MemberDatagetter0MemberData; + else if (l2.getter == Lookup::getter1) + l->getter = Lookup::getter0MemberDatagetter1; + else + Q_UNREACHABLE(); } else { Q_ASSERT(l1.getter == Lookup::getter1 && l2.getter == Lookup::getter1); l->getter = Lookup::getter1getter1; @@ -349,14 +372,26 @@ ReturnedValue Lookup::getterFallback(Lookup *l, ExecutionEngine *engine, const V return o->get(name); } -ReturnedValue Lookup::getter0(Lookup *l, ExecutionEngine *engine, const Value &object) +ReturnedValue Lookup::getter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object) { // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) - return o->propertyData(l->index)->asReturnedValue(); + return o->memberData->values.data()[l->index].asReturnedValue(); + } + return getterTwoClasses(l, engine, object); +} + +ReturnedValue Lookup::getter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object) +{ + // we can safely cast to a QV4::Object here. If object is actually a string, + // the internal class won't match + Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); + if (o) { + if (l->classList[0] == o->internalClass) + return o->inlinePropertyData(l->index)->asReturnedValue(); } return getterTwoClasses(l, engine, object); } @@ -367,8 +402,8 @@ ReturnedValue Lookup::getter1(Lookup *l, ExecutionEngine *engine, const Value &o // the internal class won't match Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { - if (l->classList[0] == o->internalClass && l->classList[1] == o->prototype->internalClass) - return o->prototype->propertyData(l->index)->asReturnedValue(); + if (l->classList[0] == o->internalClass && l->classList[1] == l->proto->internalClass) + return l->proto->propertyData(l->index)->asReturnedValue(); } return getterTwoClasses(l, engine, object); } @@ -380,9 +415,9 @@ ReturnedValue Lookup::getter2(Lookup *l, ExecutionEngine *engine, const Value &o Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) { - Heap::Object *p = o->prototype; - if (l->classList[1] == p->internalClass) { - p = p->prototype; + Q_ASSERT(l->proto == o->prototype()); + if (l->classList[1] == l->proto->internalClass) { + Heap::Object *p = l->proto->prototype(); if (l->classList[2] == p->internalClass) return p->propertyData(l->index)->asReturnedValue(); } @@ -392,31 +427,76 @@ ReturnedValue Lookup::getter2(Lookup *l, ExecutionEngine *engine, const Value &o return getterFallback(l, engine, object); } -ReturnedValue Lookup::getter0getter0(Lookup *l, ExecutionEngine *engine, const Value &object) +ReturnedValue Lookup::getter0Inlinegetter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object) { // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) - return o->propertyData(l->index)->asReturnedValue(); + return o->inlinePropertyData(l->index)->asReturnedValue(); if (l->classList[2] == o->internalClass) - return o->propertyData(l->index2)->asReturnedValue(); + return o->inlinePropertyData(l->index2)->asReturnedValue(); + } + l->getter = getterFallback; + return getterFallback(l, engine, object); +} + +ReturnedValue Lookup::getter0Inlinegetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object) +{ + // we can safely cast to a QV4::Object here. If object is actually a string, + // the internal class won't match + Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); + if (o) { + if (l->classList[0] == o->internalClass) + return o->inlinePropertyData(l->index)->asReturnedValue(); + if (l->classList[2] == o->internalClass) + return o->memberData->values.data()[l->index2].asReturnedValue(); + } + l->getter = getterFallback; + return getterFallback(l, engine, object); +} + +ReturnedValue Lookup::getter0MemberDatagetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object) +{ + // we can safely cast to a QV4::Object here. If object is actually a string, + // the internal class won't match + Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); + if (o) { + if (l->classList[0] == o->internalClass) + return o->memberData->values.data()[l->index].asReturnedValue(); + if (l->classList[2] == o->internalClass) + return o->memberData->values.data()[l->index2].asReturnedValue(); + } + l->getter = getterFallback; + return getterFallback(l, engine, object); +} + +ReturnedValue Lookup::getter0Inlinegetter1(Lookup *l, ExecutionEngine *engine, const Value &object) +{ + // we can safely cast to a QV4::Object here. If object is actually a string, + // the internal class won't match + Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); + if (o) { + if (l->classList[0] == o->internalClass) + return o->inlinePropertyData(l->index)->asReturnedValue(); + if (l->classList[2] == o->internalClass && l->classList[3] == o->prototype()->internalClass) + return o->prototype()->propertyData(l->index2)->asReturnedValue(); } l->getter = getterFallback; return getterFallback(l, engine, object); } -ReturnedValue Lookup::getter0getter1(Lookup *l, ExecutionEngine *engine, const Value &object) +ReturnedValue Lookup::getter0MemberDatagetter1(Lookup *l, ExecutionEngine *engine, const Value &object) { // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) - return o->propertyData(l->index)->asReturnedValue(); - if (l->classList[2] == o->internalClass && l->classList[3] == o->prototype->internalClass) - return o->prototype->propertyData(l->index2)->asReturnedValue(); + return o->memberData->values.data()[l->index].asReturnedValue(); + if (l->classList[2] == o->internalClass && l->classList[3] == o->prototype()->internalClass) + return o->prototype()->propertyData(l->index2)->asReturnedValue(); } l->getter = getterFallback; return getterFallback(l, engine, object); @@ -429,11 +509,11 @@ ReturnedValue Lookup::getter1getter1(Lookup *l, ExecutionEngine *engine, const V Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass && - l->classList[1] == o->prototype->internalClass) - return o->prototype->propertyData(l->index)->asReturnedValue(); + l->classList[1] == o->prototype()->internalClass) + return o->prototype()->propertyData(l->index)->asReturnedValue(); if (l->classList[2] == o->internalClass && - l->classList[3] == o->prototype->internalClass) - return o->prototype->propertyData(l->index2)->asReturnedValue(); + l->classList[3] == o->prototype()->internalClass) + return o->prototype()->propertyData(l->index2)->asReturnedValue(); return getterFallback(l, engine, object); } l->getter = getterFallback; @@ -470,9 +550,9 @@ ReturnedValue Lookup::getterAccessor1(Lookup *l, ExecutionEngine *engine, const Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass && - l->classList[1] == o->prototype->internalClass) { + l->classList[1] == l->proto->internalClass) { Scope scope(o->internalClass->engine); - ScopedFunctionObject getter(scope, o->prototype->propertyData(l->index + Object::GetterOffset)); + ScopedFunctionObject getter(scope, o->prototype()->propertyData(l->index + Object::GetterOffset)); if (!getter) return Encode::undefined(); @@ -493,9 +573,9 @@ ReturnedValue Lookup::getterAccessor2(Lookup *l, ExecutionEngine *engine, const Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) { - o = o->prototype; - if (l->classList[1] == o->internalClass) { - o = o->prototype; + Q_ASSERT(o->prototype() == l->proto); + if (l->classList[1] == l->proto->internalClass) { + o = l->proto->prototype(); if (l->classList[2] == o->internalClass) { Scope scope(o->internalClass->engine); ScopedFunctionObject getter(scope, o->propertyData(l->index + Object::GetterOffset)); @@ -514,12 +594,23 @@ ReturnedValue Lookup::getterAccessor2(Lookup *l, ExecutionEngine *engine, const return getterFallback(l, engine, object); } -ReturnedValue Lookup::primitiveGetter0(Lookup *l, ExecutionEngine *engine, const Value &object) +ReturnedValue Lookup::primitiveGetter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object) { if (object.type() == l->type) { - Object *o = l->proto; - if (l->classList[0] == o->internalClass()) - return o->propertyData(l->index)->asReturnedValue(); + Heap::Object *o = l->proto; + if (l->classList[0] == o->internalClass) + return o->inlinePropertyData(l->index)->asReturnedValue(); + } + l->getter = getterGeneric; + return getterGeneric(l, engine, object); +} + +ReturnedValue Lookup::primitiveGetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object) +{ + if (object.type() == l->type) { + Heap::Object *o = l->proto; + if (l->classList[0] == o->internalClass) + return o->memberData->values.data()[l->index].asReturnedValue(); } l->getter = getterGeneric; return getterGeneric(l, engine, object); @@ -528,8 +619,8 @@ ReturnedValue Lookup::primitiveGetter0(Lookup *l, ExecutionEngine *engine, const ReturnedValue Lookup::primitiveGetter1(Lookup *l, ExecutionEngine *engine, const Value &object) { if (object.type() == l->type) { - Object *o = l->proto; - if (l->classList[0] == o->internalClass() && + Heap::Object *o = l->proto; + if (l->classList[0] == o->internalClass && l->classList[1] == o->prototype()->internalClass) return o->prototype()->propertyData(l->index)->asReturnedValue(); } @@ -540,9 +631,9 @@ ReturnedValue Lookup::primitiveGetter1(Lookup *l, ExecutionEngine *engine, const ReturnedValue Lookup::primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engine, const Value &object) { if (object.type() == l->type) { - Object *o = l->proto; - if (l->classList[0] == o->internalClass()) { - Scope scope(o->engine()); + Heap::Object *o = l->proto; + if (l->classList[0] == o->internalClass) { + Scope scope(o->internalClass->engine); ScopedFunctionObject getter(scope, o->propertyData(l->index + Object::GetterOffset)); if (!getter) return Encode::undefined(); @@ -560,10 +651,10 @@ ReturnedValue Lookup::primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engin ReturnedValue Lookup::primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engine, const Value &object) { if (object.type() == l->type) { - Object *o = l->proto; - if (l->classList[0] == o->internalClass() && + Heap::Object *o = l->proto; + if (l->classList[0] == o->internalClass && l->classList[1] == o->prototype()->internalClass) { - Scope scope(o->engine()); + Scope scope(o->internalClass->engine); ScopedFunctionObject getter(scope, o->prototype()->propertyData(l->index + Object::GetterOffset)); if (!getter) return Encode::undefined(); @@ -604,9 +695,15 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) ReturnedValue v = l->lookup(o, &attrs); if (v != Primitive::emptyValue().asReturnedValue()) { if (attrs.isData()) { - if (l->level == 0) - l->globalGetter = globalGetter0; - else if (l->level == 1) + if (l->level == 0) { + uint nInline = o->d()->vtable()->nInlineProperties; + if (l->index < nInline) + l->globalGetter = globalGetter0Inline; + else { + l->index -= nInline; + l->globalGetter = globalGetter0MemberData; + } + } else if (l->level == 1) l->globalGetter = globalGetter1; else if (l->level == 2) l->globalGetter = globalGetter2; @@ -626,11 +723,21 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) return engine->throwReferenceError(n); } -ReturnedValue Lookup::globalGetter0(Lookup *l, ExecutionEngine *engine) +ReturnedValue Lookup::globalGetter0Inline(Lookup *l, ExecutionEngine *engine) { Object *o = engine->globalObject; if (l->classList[0] == o->internalClass()) - return o->propertyData(l->index)->asReturnedValue(); + return o->d()->inlinePropertyData(l->index)->asReturnedValue(); + + l->globalGetter = globalGetterGeneric; + return globalGetterGeneric(l, engine); +} + +ReturnedValue Lookup::globalGetter0MemberData(Lookup *l, ExecutionEngine *engine) +{ + Object *o = engine->globalObject; + if (l->classList[0] == o->internalClass()) + return o->d()->memberData->values.data()[l->index].asReturnedValue(); l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -651,11 +758,11 @@ ReturnedValue Lookup::globalGetter2(Lookup *l, ExecutionEngine *engine) { Heap::Object *o = engine->globalObject->d(); if (l->classList[0] == o->internalClass) { - o = o->prototype; + o = o->prototype(); if (l->classList[1] == o->internalClass) { - o = o->prototype; + o = o->prototype(); if (l->classList[2] == o->internalClass) { - return o->prototype->propertyData(l->index)->asReturnedValue(); + return o->prototype()->propertyData(l->index)->asReturnedValue(); } } } @@ -704,9 +811,9 @@ ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine) { Heap::Object *o = engine->globalObject->d(); if (l->classList[0] == o->internalClass) { - o = o->prototype; + o = o->prototype(); if (l->classList[1] == o->internalClass) { - o = o->prototype; + o = o->prototype(); if (l->classList[2] == o->internalClass) { Scope scope(o->internalClass->engine); ScopedFunctionObject getter(scope, o->propertyData(l->index + Object::GetterOffset)); @@ -746,7 +853,7 @@ void Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, if (Object *o = object.as<Object>()) { o->setLookup(l, value); - if (l->setter == Lookup::setter0) { + if (l->setter == Lookup::setter0 || l->setter == Lookup::setter0Inline) { l->setter = setter0setter0; l->classList[1] = l1.classList[0]; l->index2 = l1.index; @@ -770,7 +877,7 @@ void Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, c void Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = object.as<Object>(); + Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { o->setProperty(engine, l->index, value); return; @@ -779,15 +886,25 @@ void Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Va setterTwoClasses(l, engine, object, value); } +void Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +{ + Object *o = static_cast<Object *>(object.managed()); + if (o && o->internalClass() == l->classList[0]) { + o->d()->setInlineProperty(engine, l->index, value); + return; + } + + setterTwoClasses(l, engine, object, value); +} + void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = object.as<Object>(); + Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { - if (!o->prototype()) { - o->setInternalClass(l->classList[3]); - o->setProperty(l->index, value); - return; - } + Q_ASSERT(!o->prototype()); + o->setInternalClass(l->classList[3]); + o->setProperty(l->index, value); + return; } l->setter = setterFallback; @@ -796,10 +913,12 @@ void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, co void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = object.as<Object>(); + Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { Heap::Object *p = o->prototype(); - if (p && p->internalClass == l->classList[1]) { + Q_ASSERT(p); + if (p->internalClass == l->classList[1]) { + Q_ASSERT(!p->prototype()); o->setInternalClass(l->classList[3]); o->setProperty(l->index, value); return; @@ -812,12 +931,15 @@ void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, co void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = object.as<Object>(); + Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { Heap::Object *p = o->prototype(); - if (p && p->internalClass == l->classList[1]) { - p = p->prototype; - if (p && p->internalClass == l->classList[2]) { + Q_ASSERT(p); + if (p->internalClass == l->classList[1]) { + p = p->prototype(); + Q_ASSERT(p); + if (p->internalClass == l->classList[2]) { + Q_ASSERT(!p->prototype()); o->setInternalClass(l->classList[3]); o->setProperty(l->index, value); return; @@ -831,7 +953,7 @@ void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, co void Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = object.as<Object>(); + Object *o = static_cast<Object *>(object.managed()); if (o) { if (o->internalClass() == l->classList[0]) { o->setProperty(l->index, value); diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index daf3c71e27..ce5189a780 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -78,13 +78,14 @@ struct Lookup { struct { void *dummy0; void *dummy1; - Object *proto; - unsigned type; + void *dummy2; + Heap::Object *proto; }; }; union { int level; uint index2; + unsigned type; }; uint index; uint nameIndex; @@ -101,17 +102,22 @@ struct Lookup { static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterFallback(Lookup *l, ExecutionEngine *engine, const Value &object); - static ReturnedValue getter0(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getter1(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getter2(Lookup *l, ExecutionEngine *engine, const Value &object); - static ReturnedValue getter0getter0(Lookup *l, ExecutionEngine *engine, const Value &object); - static ReturnedValue getter0getter1(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getter0Inlinegetter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getter0Inlinegetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getter0MemberDatagetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getter0Inlinegetter1(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getter0MemberDatagetter1(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getter1getter1(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterAccessor0(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterAccessor1(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterAccessor2(Lookup *l, ExecutionEngine *engine, const Value &object); - static ReturnedValue primitiveGetter0(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue primitiveGetter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue primitiveGetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue primitiveGetter1(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engine, const Value &object); @@ -119,7 +125,8 @@ struct Lookup { static ReturnedValue arrayLengthGetter(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue globalGetterGeneric(Lookup *l, ExecutionEngine *engine); - static ReturnedValue globalGetter0(Lookup *l, ExecutionEngine *engine); + static ReturnedValue globalGetter0Inline(Lookup *l, ExecutionEngine *engine); + static ReturnedValue globalGetter0MemberData(Lookup *l, ExecutionEngine *engine); static ReturnedValue globalGetter1(Lookup *l, ExecutionEngine *engine); static ReturnedValue globalGetter2(Lookup *l, ExecutionEngine *engine); static ReturnedValue globalGetterAccessor0(Lookup *l, ExecutionEngine *engine); @@ -130,6 +137,7 @@ struct Lookup { static void setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static void setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static void setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static void setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static void setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static void setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index 1b43fd86e8..e00eaa0d9b 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -48,6 +48,8 @@ const VTable Managed::static_vtbl = { 0, 0, + 0, + 0, Managed::IsExecutionContext, Managed::IsString, Managed::IsObject, diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index f97771831c..40dfc244ea 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -52,6 +52,7 @@ #include "qv4global_p.h" #include "qv4value_p.h" +#include "qv4enginebase_p.h" #include <private/qv4heap_p.h> #include <private/qv4writebarrier_p.h> @@ -132,6 +133,9 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} { \ parentVTable, \ markTable, \ + (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ + (sizeof(classname::Data) + (std::is_same<classname, Object>::value ? 2*sizeof(QV4::Value) : 0) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \ + - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ classname::IsExecutionContext, \ classname::IsString, \ classname::IsObject, \ @@ -151,6 +155,10 @@ QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \ const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, 0) \ QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF +#define V4_INTERNALCLASS(c) \ + static QV4::InternalClass *defaultInternalClass(QV4::EngineBase *e) \ + { return e->internalClasses[QV4::EngineBase::Class_##c]; } + struct Q_QML_PRIVATE_EXPORT Managed : Value { V4_MANAGED_ITSELF(Base, Managed) @@ -193,6 +201,9 @@ public: }; Q_MANAGED_TYPE(Invalid) + InternalClass *internalClass() const { return d()->internalClass; } + inline ExecutionEngine *engine() const { return internalClass()->engine; } + bool isListType() const { return d()->vtable()->type == Type_QmlSequence; } bool isArrayObject() const { return d()->vtable()->type == Type_ArrayObject; } diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h index fbe66757e0..9d333011fc 100644 --- a/src/qml/jsruntime/qv4memberdata_p.h +++ b/src/qml/jsruntime/qv4memberdata_p.h @@ -72,17 +72,18 @@ V4_ASSERT_IS_TRIVIAL(MemberData) struct MemberData : Managed { V4_MANAGED(MemberData, Managed) + V4_INTERNALCLASS(MemberData) struct Index { - Heap::MemberData *memberData; - uint index; + Heap::Base *base; + Value *slot; - void set(ExecutionEngine *e, Value newVal) { - memberData->values.set(e, index, newVal); + void set(EngineBase *e, Value newVal) { + WriteBarrier::write(e, base, slot, newVal); } - const Value *operator->() const { return &memberData->values[index]; } - const Value &operator*() const { return memberData->values[index]; } - bool isNull() const { return !memberData; } + const Value *operator->() const { return slot; } + const Value &operator*() const { return *slot; } + bool isNull() const { return !slot; } }; const Value &operator[] (uint idx) const { return d()->values[idx]; } diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index e18267c50c..0bc2cd8c65 100644 --- a/src/qml/jsruntime/qv4numberobject_p.h +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -85,6 +85,7 @@ struct NumberCtor: FunctionObject struct NumberPrototype: NumberObject { + V4_PROTOTYPE(objectPrototype) void init(ExecutionEngine *engine, Object *ctor); static void method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index d400c2ae64..94d5a74fe5 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -61,9 +61,14 @@ DEFINE_OBJECT_VTABLE(Object); void Object::setInternalClass(InternalClass *ic) { d()->internalClass = ic; + Q_ASSERT(ic && ic->vtable); + uint nInline = d()->vtable()->nInlineProperties; + if (ic->size <= nInline) + return; bool hasMD = d()->memberData != nullptr; - if ((!hasMD && ic->size) || (hasMD && d()->memberData->values.size < ic->size)) - d()->memberData.set(engine(), MemberData::allocate(ic->engine, ic->size, d()->memberData)); + uint requiredSize = ic->size - nInline; + if (!(hasMD && requiredSize) || (hasMD && d()->memberData->values.size < requiredSize)) + d()->memberData.set(ic->engine, MemberData::allocate(ic->engine, requiredSize, d()->memberData)); } void Object::getProperty(uint index, Property *p, PropertyAttributes *attrs) const @@ -83,13 +88,14 @@ void Object::setProperty(uint index, const Property *p) bool Object::setPrototype(Object *proto) { - Heap::Object *pp = proto ? proto->d() : 0; + Heap::Object *p = proto ? proto->d() : 0; + Heap::Object *pp = p; while (pp) { if (pp == d()) return false; - pp = pp->prototype; + pp = pp->prototype(); } - d()->prototype.set(engine(), proto ? proto->d() : 0); + setInternalClass(internalClass()->changePrototype(p)); return true; } @@ -150,17 +156,6 @@ void Object::defineDefaultProperty(const QString &name, const Value &value) defineDefaultProperty(s, value); } -void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(CallContext *), int argumentCount) -{ - ExecutionEngine *e = engine(); - Scope scope(e); - ScopedString s(scope, e->newIdentifier(name)); - ExecutionContext *global = e->rootContext(); - ScopedFunctionObject function(scope, BuiltinFunction::create(global, s, code)); - function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); - defineDefaultProperty(s, function); -} - void Object::defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount) { ExecutionEngine *e = engine(); @@ -172,16 +167,6 @@ void Object::defineDefaultProperty(const QString &name, void (*code)(const Built defineDefaultProperty(s, function); } -void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(CallContext *), int argumentCount) -{ - ExecutionEngine *e = engine(); - Scope scope(e); - ExecutionContext *global = e->rootContext(); - ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code)); - function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); - defineDefaultProperty(name, function); -} - void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount) { ExecutionEngine *e = engine(); @@ -192,25 +177,6 @@ void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunct defineDefaultProperty(name, function); } -void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)) -{ - ExecutionEngine *e = engine(); - Scope scope(e); - ScopedString s(scope, e->newIdentifier(name)); - defineAccessorProperty(s, getter, setter); -} - -void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)) -{ - ExecutionEngine *v4 = engine(); - QV4::Scope scope(v4); - ScopedProperty p(scope); - ExecutionContext *global = v4->rootContext(); - p->setGetter(ScopedFunctionObject(scope, (getter ? BuiltinFunction::create(global, name, getter) : 0))); - p->setSetter(ScopedFunctionObject(scope, (setter ? BuiltinFunction::create(global, name, setter) : 0))); - insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); -} - void Object::defineAccessorProperty(const QString &name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), void (*setter)(const BuiltinFunction *, Scope &, CallData *)) { @@ -258,6 +224,18 @@ void Object::defineReadonlyConfigurableProperty(String *name, const Value &value insertMember(name, value, Attr_ReadOnly_ButConfigurable); } +void Object::markObjects(Heap::Base *b, MarkStack *stack) +{ + Heap::Object *o = static_cast<Heap::Object *>(b); + uint nInline = o->vtable()->nInlineProperties; + Value *v = reinterpret_cast<Value *>(o) + o->vtable()->inlinePropertyOffset; + const Value *end = v + nInline; + while (v < end) { + v->mark(stack); + ++v; + } +} + void Object::insertMember(String *s, const Property *p, PropertyAttributes attributes) { uint idx; @@ -278,7 +256,10 @@ void Object::getOwnProperty(String *name, PropertyAttributes *attrs, Property *p if (idx != UINT_MAX) return getOwnProperty(idx, attrs, p); - uint member = internalClass()->find(name); + name->makeIdentifier(); + Identifier *id = name->identifier(); + + uint member = internalClass()->find(id); if (member < UINT_MAX) { *attrs = internalClass()->propertyData[member]; if (p) { @@ -317,15 +298,18 @@ MemberData::Index Object::getValueOrSetter(String *name, PropertyAttributes *att { Q_ASSERT(name->asArrayIndex() == UINT_MAX); + name->makeIdentifier(); + Identifier *id = name->identifier(); + Heap::Object *o = d(); while (o) { - uint idx = o->internalClass->find(name); + uint idx = o->internalClass->find(id); if (idx < UINT_MAX) { *attrs = o->internalClass->propertyData[idx]; - return MemberData::Index{ o->memberData, attrs->isAccessor() ? idx + SetterOffset : idx }; + return o->writablePropertyData(attrs->isAccessor() ? idx + SetterOffset : idx ); } - o = o->prototype; + o = o->prototype(); } *attrs = Attr_Invalid; return { 0, 0 }; @@ -350,7 +334,7 @@ ArrayData::Index Object::getValueOrSetter(uint index, PropertyAttributes *attrs) return { reinterpret_cast<Heap::ArrayData *>(0x1), 0 }; } } - o = o->prototype; + o = o->prototype(); } *attrs = Attr_Invalid; return { 0, 0 }; @@ -394,7 +378,10 @@ bool Object::hasOwnProperty(String *name) const if (idx != UINT_MAX) return hasOwnProperty(idx); - if (internalClass()->find(name) < UINT_MAX) + name->makeIdentifier(); + Identifier *id = name->identifier(); + + if (internalClass()->find(id) < UINT_MAX) return true; if (!query(name).isEmpty()) return true; @@ -451,8 +438,11 @@ PropertyAttributes Object::query(const Managed *m, String *name) if (idx != UINT_MAX) return queryIndexed(m, idx); + name->makeIdentifier(); + Identifier *id = name->identifier(); + const Object *o = static_cast<const Object *>(m); - idx = o->internalClass()->find(name); + idx = o->internalClass()->find(id); if (idx < UINT_MAX) return o->internalClass()->propertyData[idx]; @@ -488,9 +478,18 @@ ReturnedValue Object::getLookup(const Managed *m, Lookup *l) PropertyAttributes attrs; ReturnedValue v = l->lookup(o, &attrs); if (v != Primitive::emptyValue().asReturnedValue()) { + l->proto = l->classList[0]->prototype; if (attrs.isData()) { - if (l->level == 0) - l->getter = Lookup::getter0; + Q_ASSERT(l->classList[0] == o->internalClass()); + if (l->level == 0) { + uint nInline = o->d()->vtable()->nInlineProperties; + if (l->index < nInline) + l->getter = Lookup::getter0Inline; + else { + l->index -= nInline; + l->getter = Lookup::getter0MemberData; + } + } else if (l->level == 1) l->getter = Lookup::getter1; else if (l->level == 2) @@ -525,7 +524,7 @@ void Object::setLookup(Managed *m, Lookup *l, const Value &value) if (idx != UINT_MAX && o->internalClass()->propertyData[idx].isData() && o->internalClass()->propertyData[idx].isWritable()) { l->classList[0] = o->internalClass(); l->index = idx; - l->setter = Lookup::setter0; + l->setter = idx < o->d()->vtable()->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0; o->setProperty(idx, value); return; } @@ -642,12 +641,13 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const if (idx != UINT_MAX) return getIndexed(idx, hasProperty); - Scope scope(engine()); - name->makeIdentifier(scope.engine); + name->makeIdentifier(); + Identifier *id = name->identifier(); + Scope scope(engine()); ScopedObject o(scope, this); while (o) { - uint idx = o->internalClass()->find(name); + uint idx = o->internalClass()->find(id); if (idx < UINT_MAX) { if (hasProperty) *hasProperty = true; @@ -709,14 +709,15 @@ bool Object::internalPut(String *name, const Value &value) if (idx != UINT_MAX) return putIndexed(idx, value); - name->makeIdentifier(engine); + name->makeIdentifier(); + Identifier *id = name->identifier(); MemberData::Index memberIndex{0, 0}; - uint member = internalClass()->find(name); + uint member = internalClass()->find(id); PropertyAttributes attrs; if (member < UINT_MAX) { attrs = internalClass()->propertyData[member]; - memberIndex = { d()->memberData, (attrs.isAccessor() ? member + SetterOffset : member) }; + memberIndex = d()->writablePropertyData(attrs.isAccessor() ? member + SetterOffset : member); } // clause 1 @@ -869,9 +870,9 @@ bool Object::internalDeleteProperty(String *name) if (idx != UINT_MAX) return deleteIndexedProperty(idx); - name->makeIdentifier(engine()); + name->makeIdentifier(); - uint memberIdx = internalClass()->find(name); + uint memberIdx = internalClass()->find(name->identifier()); if (memberIdx != UINT_MAX) { if (internalClass()->propertyData[memberIdx].isConfigurable()) { InternalClass::removeMember(this, name->identifier()); @@ -908,7 +909,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const return __defineOwnProperty__(engine, idx, p, attrs); Scope scope(engine); - name->makeIdentifier(scope.engine); + name->makeIdentifier(); uint memberIndex; @@ -942,7 +943,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const } // Clause 1 - memberIndex = internalClass()->find(name); + memberIndex = internalClass()->find(name->identifier()); if (memberIndex == UINT_MAX) { // clause 3 @@ -1188,7 +1189,7 @@ ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var) // 15.3.5.3, 4 while (v) { // 15.3.5.3, 4, a - v = v->prototype; + v = v->prototype(); // 15.3.5.3, 4, b if (!v) diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index df9d68525d..066a93cc61 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -68,8 +68,6 @@ struct BuiltinFunction; namespace Heap { #define ObjectMembers(class, Member) \ - Member(class, NoMark, InternalClass *, internalClass) \ - Member(class, Pointer, Object *, prototype) \ Member(class, Pointer, MemberData *, memberData) \ Member(class, Pointer, ArrayData *, arrayData) @@ -78,12 +76,59 @@ DECLARE_HEAP_OBJECT(Object, Base) { void init() { Base::init(); } void destroy() { Base::destroy(); } - const Value *propertyData(uint index) const { return memberData->values.data() + index; } - void setProperty(ExecutionEngine *e, uint index, Value v) const { memberData->values.set(e, index, v); } - void setProperty(ExecutionEngine *e, uint index, Heap::Base *b) const { memberData->values.set(e, index, b); } + const Value *inlinePropertyData(uint index) const { + Q_ASSERT(index < vtable()->nInlineProperties); + return reinterpret_cast<const Value *>(this) + vtable()->inlinePropertyOffset + index; + } + void setInlineProperty(ExecutionEngine *e, uint index, Value v) { + Q_ASSERT(index < vtable()->nInlineProperties); + Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index; + WriteBarrier::write(e, this, prop, v); + } + void setInlineProperty(ExecutionEngine *e, uint index, Heap::Base *b) { + Q_ASSERT(index < vtable()->nInlineProperties); + Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index; + WriteBarrier::write(e, this, prop, b); + } + + QV4::MemberData::Index writablePropertyData(uint index) { + uint nInline = vtable()->nInlineProperties; + if (index < nInline) + return { this, reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index}; + index -= nInline; + return { memberData, memberData->values.values + index }; + } + + const Value *propertyData(uint index) const { + uint nInline = vtable()->nInlineProperties; + if (index < nInline) + return reinterpret_cast<const Value *>(this) + vtable()->inlinePropertyOffset + index; + index -= nInline; + return memberData->values.data() + index; + } + void setProperty(ExecutionEngine *e, uint index, Value v) { + uint nInline = vtable()->nInlineProperties; + if (index < nInline) { + setInlineProperty(e, index, v); + return; + } + index -= nInline; + memberData->values.set(e, index, v); + } + void setProperty(ExecutionEngine *e, uint index, Heap::Base *b) { + uint nInline = vtable()->nInlineProperties; + if (index < nInline) { + setInlineProperty(e, index, b); + return; + } + index -= nInline; + memberData->values.set(e, index, b); + } + + Heap::Object *prototype() const { return internalClass->prototype; } }; -Q_STATIC_ASSERT(Object::markTable == ((2 << 4) | (2 << 6) | (2 << 8))); +Q_STATIC_ASSERT(Object::markTable == ((2 << 2) | (2 << 4))); } @@ -122,9 +167,6 @@ Q_STATIC_ASSERT(Object::markTable == ((2 << 4) | (2 << 6) | (2 << 8))); V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass); \ static Q_CONSTEXPR quint64 markTable = QV4::Heap::DataClass::markTable; -#define V4_INTERNALCLASS(c) \ - static QV4::InternalClass *defaultInternalClass(QV4::ExecutionEngine *e) \ - { return e->c; } #define V4_PROTOTYPE(p) \ static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \ { return e->p(); } @@ -183,7 +225,7 @@ QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF struct Q_QML_EXPORT Object: Managed { V4_OBJECT2(Object, Object) Q_MANAGED_TYPE(Object) - V4_INTERNALCLASS(emptyClass) + V4_INTERNALCLASS(Object) V4_PROTOTYPE(objectPrototype) enum { @@ -192,7 +234,6 @@ struct Q_QML_EXPORT Object: Managed { SetterOffset = 1 }; - InternalClass *internalClass() const { return d()->internalClass; } void setInternalClass(InternalClass *ic); const Value *propertyData(uint index) const { return d()->propertyData(index); } @@ -208,7 +249,7 @@ struct Q_QML_EXPORT Object: Managed { void setProperty(ExecutionEngine *engine, uint index, Heap::Base *b) const { d()->setProperty(engine, index, b); } const ObjectVTable *vtable() const { return reinterpret_cast<const ObjectVTable *>(d()->vtable()); } - Heap::Object *prototype() const { return d()->prototype; } + Heap::Object *prototype() const { return d()->prototype(); } bool setPrototype(Object *proto); void getOwnProperty(String *name, PropertyAttributes *attrs, Property *p = 0); @@ -246,12 +287,8 @@ struct Q_QML_EXPORT Object: Managed { insertMember(name, value, Attr_Data|Attr_NotEnumerable); } void defineDefaultProperty(const QString &name, const Value &value); - void defineDefaultProperty(const QString &name, ReturnedValue (*code)(CallContext *), int argumentCount = 0); void defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0); - void defineDefaultProperty(String *name, ReturnedValue (*code)(CallContext *), int argumentCount = 0); void defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0); - void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)); - void defineAccessorProperty(String *name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)); void defineAccessorProperty(const QString &name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), void (*setter)(const BuiltinFunction *, Scope &, CallData *)); void defineAccessorProperty(String *name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), @@ -272,8 +309,6 @@ struct Q_QML_EXPORT Object: Managed { } void insertMember(String *s, const Property *p, PropertyAttributes attributes); - inline ExecutionEngine *engine() const { return internalClass()->engine; } - bool isExtensible() const { return d()->internalClass->extensible; } // Array handling @@ -424,6 +459,7 @@ protected: static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static uint getLength(const Managed *m); static ReturnedValue instanceOf(const Object *typeObject, const Value &var); + static void markObjects(Heap::Base *, MarkStack *); private: ReturnedValue internalGet(String *name, bool *hasProperty) const; @@ -498,7 +534,7 @@ struct NumberObject: Object { struct ArrayObject: Object { V4_OBJECT2(ArrayObject, Object) Q_MANAGED_TYPE(ArrayObject) - V4_INTERNALCLASS(arrayClass) + V4_INTERNALCLASS(ArrayObject) V4_PROTOTYPE(arrayPrototype) void init(ExecutionEngine *engine); diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index c08dd7baa3..61d785066f 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -297,15 +297,15 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value) void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml) { - Heap::ExecutionContext::init(outerContext->engine(), Heap::ExecutionContext::Type_QmlContext); - outer.set(engine, outerContext->d()); + Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext); + outer.set(internalClass->engine, outerContext->d()); strictMode = false; callData = outer->callData; lookups = outer->lookups; constantTable = outer->constantTable; compilationUnit = outer->compilationUnit; - this->qml.set(engine, qml->d()); + this->qml.set(internalClass->engine, qml->d()); } Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, const QUrl &source, Value *sendFunction) @@ -327,7 +327,8 @@ Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, cons qml->QV4::Object::put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("WorkerScript"))), api); qml->setReadOnly(true); - Heap::QmlContext *c = parent->d()->engine->memoryManager->alloc<QmlContext>(parent, qml); + Heap::QmlContext *c = scope.engine->memoryManager->alloc<QmlContext>(parent, qml); + Q_ASSERT(c->vtable() == staticVTable()); return c; } @@ -336,7 +337,8 @@ Heap::QmlContext *QmlContext::create(ExecutionContext *parent, QQmlContextData * Scope scope(parent); Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QQmlContextWrapper>(context, scopeObject)); - Heap::QmlContext *c = parent->d()->engine->memoryManager->alloc<QmlContext>(parent, qml); + Heap::QmlContext *c = scope.engine->memoryManager->alloc<QmlContext>(parent, qml); + Q_ASSERT(c->vtable() == staticVTable()); return c; } diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 83730d632a..91650f16e2 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1060,7 +1060,6 @@ void QObjectWrapper::destroyObject(bool lastCall) } } - h->internalClass = 0; h->~Data(); } @@ -1813,7 +1812,7 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine) ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index) { Scope valueScope(scope); - Scoped<QObjectMethod> method(valueScope, scope->d()->engine->memoryManager->allocObject<QObjectMethod>(scope)); + Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope)); method->d()->setObject(object); if (QQmlData *ddata = QQmlData::get(object)) @@ -1864,7 +1863,7 @@ QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) co result = QLatin1String("null"); } - return ctx->d()->engine->newString(result)->asReturnedValue(); + return ctx->engine()->newString(result)->asReturnedValue(); } QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h index 348af0fb14..7ab12fe245 100644 --- a/src/qml/jsruntime/qv4regexp_p.h +++ b/src/qml/jsruntime/qv4regexp_p.h @@ -100,6 +100,7 @@ struct RegExp : public Managed V4_MANAGED(RegExp, Managed) Q_MANAGED_TYPE(RegExp) V4_NEEDS_DESTROY + V4_INTERNALCLASS(RegExp) QString pattern() const { return *d()->pattern; } JSC::Yarr::BytecodePattern *byteCode() { return d()->byteCode; } diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 85e37ebe82..735951e085 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -356,7 +356,7 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat } // fill in result data - ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->regExpExecArrayClass, scope.engine->arrayPrototype())); + ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses[EngineBase::Class_RegExpExecArray], scope.engine->arrayPrototype())); int len = r->value()->captureCount(); array->arrayReserve(len); ScopedValue v(scope); diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index 0fcfe93135..6f54a4ab92 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -103,7 +103,7 @@ DECLARE_HEAP_OBJECT(RegExpCtor, FunctionObject) { struct RegExpObject: Object { V4_OBJECT2(RegExpObject, Object) Q_MANAGED_TYPE(RegExpObject) - V4_INTERNALCLASS(regExpObjectClass) + V4_INTERNALCLASS(RegExpObject) V4_PROTOTYPE(regExpPrototype) // needs to be compatible with the flags in qv4jsir_p.h diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 610db8ec00..df2af9de40 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -554,7 +554,7 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu if (!sright->d()->length()) return sleft->asReturnedValue(); MemoryManager *mm = engine->memoryManager; - return (mm->alloc<String>(mm, sleft->d(), sright->d()))->asReturnedValue(); + return (mm->alloc<String>(sleft->d(), sright->d()))->asReturnedValue(); } double x = RuntimeHelpers::toNumber(pleft); double y = RuntimeHelpers::toNumber(pright); @@ -586,7 +586,7 @@ QV4::ReturnedValue Runtime::method_addString(ExecutionEngine *engine, const Valu if (!sright->d()->length()) return pleft->asReturnedValue(); MemoryManager *mm = engine->memoryManager; - return (mm->alloc<String>(mm, sleft->d(), sright->d()))->asReturnedValue(); + return (mm->alloc<String>(sleft->d(), sright->d()))->asReturnedValue(); } void Runtime::method_setProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) @@ -599,41 +599,53 @@ void Runtime::method_setProperty(ExecutionEngine *engine, const Value &object, i o->put(name, value); } -ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &object, const Value &index) +static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engine, const Value &object, uint idx) { + Q_ASSERT(idx < UINT_MAX); Scope scope(engine); - uint idx = index.asArrayIndex(); ScopedObject o(scope, object); if (!o) { - if (idx < UINT_MAX) { - if (const String *str = object.as<String>()) { - if (idx >= (uint)str->toQString().length()) { - return Encode::undefined(); - } - const QString s = str->toQString().mid(idx, 1); - return scope.engine->newString(s)->asReturnedValue(); + if (const String *str = object.as<String>()) { + if (idx >= (uint)str->toQString().length()) { + return Encode::undefined(); } + const QString s = str->toQString().mid(idx, 1); + return scope.engine->newString(s)->asReturnedValue(); } if (object.isNullOrUndefined()) { - QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQStringNoThrow()).arg(object.toQStringNoThrow()); + QString message = QStringLiteral("Cannot read property '%1' of %2").arg(idx).arg(object.toQStringNoThrow()); return engine->throwTypeError(message); } o = RuntimeHelpers::convertToObject(scope.engine, object); - if (!o) // type error - return Encode::undefined(); + Q_ASSERT(!!o); // can't fail as null/undefined is covered above + } + + if (o->arrayData() && !o->arrayData()->attrs) { + ScopedValue v(scope, o->arrayData()->get(idx)); + if (!v->isEmpty()) + return v->asReturnedValue(); } - if (idx < UINT_MAX) { - if (o->arrayData() && !o->arrayData()->attrs) { - ScopedValue v(scope, o->arrayData()->get(idx)); - if (!v->isEmpty()) - return v->asReturnedValue(); + return o->getIndexed(idx); +} + +static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, const Value &object, const Value &index) +{ + Q_ASSERT(index.asArrayIndex() == UINT_MAX); + Scope scope(engine); + + ScopedObject o(scope, object); + if (!o) { + if (object.isNullOrUndefined()) { + QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQStringNoThrow()).arg(object.toQStringNoThrow()); + return engine->throwTypeError(message); } - return o->getIndexed(idx); + o = RuntimeHelpers::convertToObject(scope.engine, object); + Q_ASSERT(!!o); // can't fail as null/undefined is covered above } ScopedString name(scope, index.toString(engine)); @@ -642,18 +654,39 @@ ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &o return o->get(name); } -void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) +ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &object, const Value &index) +{ + uint idx; + if (index.asArrayIndex(idx)) { + if (Heap::Base *b = object.heapObject()) { + if (b->vtable()->isObject) { + Heap::Object *o = static_cast<Heap::Object *>(b); + if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { + Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); + if (idx < s->values.size) + if (!s->data(idx).isEmpty()) + return s->data(idx).asReturnedValue(); + } + } + } + return getElementIntFallback(engine, object, idx); + } + + return getElementFallback(engine, object, index); +} + +static Q_NEVER_INLINE void setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { Scope scope(engine); ScopedObject o(scope, object.toObject(engine)); - if (scope.engine->hasException) + if (engine->hasException) return; - uint idx = index.asArrayIndex(); - if (idx < UINT_MAX) { - if (o->arrayType() == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = static_cast<Heap::SimpleArrayData *>(o->arrayData()); - if (s && idx < s->values.size && !s->data(idx).isEmpty()) { + uint idx; + if (index.asArrayIndex(idx)) { + if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) { + Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>(); + if (idx < s->values.size) { s->setData(engine, idx, value); return; } @@ -666,6 +699,27 @@ void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, co o->put(name, value); } +void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) +{ + uint idx; + if (index.asArrayIndex(idx)) { + if (Heap::Base *b = object.heapObject()) { + if (b->vtable()->isObject) { + Heap::Object *o = static_cast<Heap::Object *>(b); + if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { + Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); + if (idx < s->values.size) { + s->setData(engine, idx, value); + return; + } + } + } + } + } + + return setElementFallback(engine, object, index, value); +} + ReturnedValue Runtime::method_foreachIterator(ExecutionEngine *engine, const Value &in) { Scope scope(engine); @@ -1354,7 +1408,7 @@ QV4::ReturnedValue Runtime::method_setupArgumentsObject(ExecutionEngine *engine) { Q_ASSERT(engine->current->type == Heap::ExecutionContext::Type_CallContext); QV4::CallContext *c = static_cast<QV4::CallContext *>(engine->currentContext); - QV4::InternalClass *ic = c->d()->strictMode ? engine->strictArgumentsObjectClass : engine->argumentsObjectClass; + QV4::InternalClass *ic = engine->internalClasses[c->d()->strictMode ? EngineBase::Class_StrictArgumentsObject : EngineBase::Class_ArgumentsObject]; return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->objectPrototype(), c)->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index bc882bbd95..04a0c74133 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -102,7 +102,7 @@ struct ScopedValue; struct Scope { inline Scope(ExecutionContext *ctx) - : engine(ctx->d()->engine) + : engine(ctx->engine()) , mark(engine->jsStackTop) , result(*engine->jsAlloca(1)) { diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index dcab34d092..2b8d1ea716 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -65,6 +65,7 @@ namespace QV4 { struct SequencePrototype : public QV4::Object { + V4_PROTOTYPE(arrayPrototype) void init(); static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index 71f85c2d71..c0183a46a7 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -75,10 +75,9 @@ bool String::isEqualTo(Managed *t, Managed *o) } -void Heap::String::init(MemoryManager *mm, const QString &t) +void Heap::String::init(const QString &t) { Base::init(); - this->mm = mm; subtype = String::StringType_Unknown; @@ -90,10 +89,9 @@ void Heap::String::init(MemoryManager *mm, const QString &t) len = text->size; } -void Heap::String::init(MemoryManager *mm, String *l, String *r) +void Heap::String::init(String *l, String *r) { Base::init(); - this->mm = mm; subtype = String::StringType_Unknown; @@ -116,7 +114,7 @@ void Heap::String::init(MemoryManager *mm, String *l, String *r) void Heap::String::destroy() { if (!largestSubLength) { - mm->changeUnmanagedHeapSizeUsage(qptrdiff(-text->size) * (int)sizeof(QChar)); + internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(-text->size) * (int)sizeof(QChar)); if (!text->ref.deref()) QStringData::deallocate(text); } @@ -141,12 +139,12 @@ uint String::toUInt(bool *ok) const return UINT_MAX; } -void String::makeIdentifierImpl(ExecutionEngine *e) const +void String::makeIdentifierImpl() const { if (d()->largestSubLength) d()->simplifyString(); Q_ASSERT(!d()->largestSubLength); - e->identifierTable->identifier(this); + engine()->identifierTable->identifier(this); } void Heap::String::simplifyString() const @@ -161,7 +159,7 @@ void Heap::String::simplifyString() const text->ref.ref(); identifier = 0; largestSubLength = 0; - mm->changeUnmanagedHeapSizeUsage(qptrdiff(text->size) * (qptrdiff)sizeof(QChar)); + internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(text->size) * (qptrdiff)sizeof(QChar)); } void Heap::String::append(const String *data, QChar *ch) diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 71e55cbcd4..df0394d4cb 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -53,6 +53,7 @@ #include <QtCore/qstring.h> #include "qv4managed_p.h" #include <QtCore/private/qnumeric_p.h> +#include "qv4enginebase_p.h" QT_BEGIN_NAMESPACE @@ -71,8 +72,8 @@ struct Q_QML_PRIVATE_EXPORT String : Base { }; #ifndef V4_BOOTSTRAP - void init(MemoryManager *mm, const QString &text); - void init(MemoryManager *mm, String *l, String *n); + void init(const QString &text); + void init(String *l, String *n); void destroy(); void simplifyString() const; int length() const { @@ -125,7 +126,6 @@ struct Q_QML_PRIVATE_EXPORT String : Base { mutable uint stringHash; mutable uint largestSubLength; uint len; - MemoryManager *mm; private: static void append(const String *data, QChar *ch); #endif @@ -138,6 +138,7 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { #ifndef V4_BOOTSTRAP V4_MANAGED(String, Managed) Q_MANAGED_TYPE(String) + V4_INTERNALCLASS(String) V4_NEEDS_DESTROY enum { IsString = true @@ -174,13 +175,17 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { } uint toUInt(bool *ok) const; - void makeIdentifier(ExecutionEngine *e) const { + Q_DECL_DEPRECATED void makeIdentifier(ExecutionEngine *) { + makeIdentifier(); + } + + void makeIdentifier() const { if (d()->identifier) return; - makeIdentifierImpl(e); + makeIdentifierImpl(); } - void makeIdentifierImpl(ExecutionEngine *e) const; + void makeIdentifierImpl() const; static uint createHashValue(const QChar *ch, int length, uint *subtype) { diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index 5ccee3335e..ce046f4844 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -86,7 +86,7 @@ struct StringCtor : FunctionObject { struct StringObject: Object { V4_OBJECT2(StringObject, Object) Q_MANAGED_TYPE(StringObject) - V4_INTERNALCLASS(stringClass) + V4_INTERNALCLASS(StringObject) V4_PROTOTYPE(stringPrototype) Heap::String *getIndex(uint index) const { @@ -112,6 +112,7 @@ struct StringCtor: FunctionObject struct StringPrototype: StringObject { + V4_PROTOTYPE(objectPrototype) void init(ExecutionEngine *engine, Object *ctor); static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index a34a8922e1..fe27d7c2d2 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -372,7 +372,9 @@ void Heap::TypedArray::init(Type t) Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type t) { - return e->memoryManager->allocObject<TypedArray>(e->emptyClass, e->typedArrayPrototype + t, t); + QV4::InternalClass *ic = e->internalClasses[EngineBase::Class_Empty]->changeVTable(staticVTable()); + ic = ic->changePrototype(e->typedArrayPrototype[t].d()); + return e->memoryManager->allocObject<TypedArray>(ic, e->typedArrayPrototype + t, t); } ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProperty) diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index 96786c8231..a472dfa607 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -149,6 +149,7 @@ struct TypedArrayCtor: FunctionObject struct TypedArrayPrototype : Object { V4_OBJECT2(TypedArrayPrototype, Object) + V4_PROTOTYPE(objectPrototype) void init(ExecutionEngine *engine, TypedArrayCtor *ctor); diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h index e281602bb5..a7c6fa320c 100644 --- a/src/qml/jsruntime/qv4variantobject_p.h +++ b/src/qml/jsruntime/qv4variantobject_p.h @@ -105,6 +105,7 @@ struct VariantObject : Object struct VariantPrototype : VariantObject { public: + V4_PROTOTYPE(objectPrototype) void init(); static void method_preserve(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index e16df8dc60..7293630924 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -172,7 +172,7 @@ static QV4::Function *qt_v4ExtractFunction(QV4::ExecutionContext *context) if (QV4::Function *function = context->getFunction()) return function; else - return context->d()->engine->globalCode; + return context->engine()->globalCode; } static void qt_v4TriggerBreakpoint(const Breakpoint &bp, QV4::Function *function) @@ -304,7 +304,7 @@ using namespace QV4::Moth; # define MOTH_END_INSTR(I) } \ genericInstr = reinterpret_cast<const Instr *>(code); \ - goto *genericInstr->common.code; \ + goto *jumpTable[genericInstr->common.instructionType]; \ #else @@ -362,11 +362,7 @@ Param traceParam(const Param ¶m) if (engine->hasException) \ goto catchException -QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code -#ifdef MOTH_THREADED_INTERPRETER - , void ***storeJumpTable -#endif - ) +QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) { #ifdef DO_TRACE_INSTR qDebug("Starting VME with context=%p and code=%p", context, code); @@ -375,15 +371,11 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code qt_v4ResolvePendingBreakpointsHook(); #ifdef MOTH_THREADED_INTERPRETER - if (storeJumpTable) { #define MOTH_INSTR_ADDR(I, FMT) &&op_##I, - static void *jumpTable[] = { - FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR) - }; + static void *jumpTable[] = { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR) + }; #undef MOTH_INSTR_ADDR - *storeJumpTable = jumpTable; - return QV4::Primitive::undefinedValue().asReturnedValue(); - } #endif QV4::Value *stack = 0; @@ -392,7 +384,6 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code const uchar *exceptionHandler = 0; QV4::Scope scope(engine); - QV4::ExecutionContext *context = engine->currentContext; engine->current->lineNumber = -1; #ifdef DO_TRACE_INSTR @@ -402,7 +393,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code // setup lookup scopes int scopeDepth = 0; { - QV4::Heap::ExecutionContext *scope = context->d(); + QV4::Heap::ExecutionContext *scope = engine->current; while (scope) { ++scopeDepth; scope = scope->outer; @@ -415,10 +406,10 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code }; Q_ALLOCA_VAR(Scopes, scopes, sizeof(Scopes)*(2 + 2*scopeDepth)); { - scopes[0] = { const_cast<QV4::Value *>(static_cast<CompiledData::CompilationUnit*>(context->d()->compilationUnit)->constants), 0 }; + scopes[0] = { const_cast<QV4::Value *>(static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->constants), 0 }; // stack gets setup in push instruction scopes[1] = { 0, 0 }; - QV4::Heap::ExecutionContext *scope = context->d(); + QV4::Heap::ExecutionContext *scope = engine->current; int i = 0; while (scope) { if (scope->type == QV4::Heap::ExecutionContext::Type_SimpleCallContext) { @@ -442,7 +433,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code for (;;) { const Instr *genericInstr = reinterpret_cast<const Instr *>(code); #ifdef MOTH_THREADED_INTERPRETER - goto *genericInstr->common.code; + goto *jumpTable[genericInstr->common.instructionType]; #else switch (genericInstr->common.instructionType) { #endif @@ -461,12 +452,12 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(LoadRuntimeString) // TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); - VALUE(instr.result) = context->d()->compilationUnit->runtimeStrings[instr.stringId]; + VALUE(instr.result) = engine->current->compilationUnit->runtimeStrings[instr.stringId]; MOTH_END_INSTR(LoadRuntimeString) MOTH_BEGIN_INSTR(LoadRegExp) // TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); - VALUE(instr.result) = static_cast<CompiledData::CompilationUnit*>(context->d()->compilationUnit)->runtimeRegularExpressions[instr.regExpId]; + VALUE(instr.result) = static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->runtimeRegularExpressions[instr.regExpId]; MOTH_END_INSTR(LoadRegExp) MOTH_BEGIN_INSTR(LoadClosure) @@ -479,7 +470,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(GetGlobalLookup) - QV4::Lookup *l = context->d()->lookups + instr.index; + QV4::Lookup *l = engine->current->lookups + instr.index; STOREVALUE(instr.result, l->globalGetter(l, engine)); MOTH_END_INSTR(GetGlobalLookup) @@ -494,7 +485,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(LoadElementLookup) - QV4::Lookup *l = context->d()->lookups + instr.lookup; + QV4::Lookup *l = engine->current->lookups + instr.lookup; STOREVALUE(instr.result, l->indexedGetter(l, engine, VALUE(instr.base), VALUE(instr.index))); MOTH_END_INSTR(LoadElementLookup) @@ -504,7 +495,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(StoreElementLookup) - QV4::Lookup *l = context->d()->lookups + instr.lookup; + QV4::Lookup *l = engine->current->lookups + instr.lookup; l->indexedSetter(l, engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreElementLookup) @@ -514,7 +505,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) - QV4::Lookup *l = context->d()->lookups + instr.index; + QV4::Lookup *l = engine->current->lookups + instr.index; STOREVALUE(instr.result, l->getter(l, engine, VALUE(instr.base))); MOTH_END_INSTR(GetLookup) @@ -524,7 +515,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(StoreProperty) MOTH_BEGIN_INSTR(SetLookup) - QV4::Lookup *l = context->d()->lookups + instr.index; + QV4::Lookup *l = engine->current->lookups + instr.index; l->setter(l, engine, VALUE(instr.base), VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(SetLookup) @@ -595,7 +586,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) - TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); + TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData()); Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = quint32(Value::ValueTypeInternal::Integer); @@ -614,7 +605,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallScopeObjectProperty) - TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); + TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData()); Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = quint32(Value::ValueTypeInternal::Integer); @@ -624,7 +615,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(CallScopeObjectProperty) MOTH_BEGIN_INSTR(CallContextObjectProperty) - TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); + TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData()); Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = quint32(Value::ValueTypeInternal::Integer); @@ -675,18 +666,15 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CallBuiltinPushCatchScope) Runtime::method_pushCatchScope(static_cast<QV4::NoThrowEngine*>(engine), instr.name); - context = engine->currentContext; MOTH_END_INSTR(CallBuiltinPushCatchScope) MOTH_BEGIN_INSTR(CallBuiltinPushScope) Runtime::method_pushWithScope(VALUE(instr.arg), static_cast<QV4::NoThrowEngine*>(engine)); - context = engine->currentContext; CHECK_EXCEPTION; MOTH_END_INSTR(CallBuiltinPushScope) MOTH_BEGIN_INSTR(CallBuiltinPopScope) Runtime::method_popScope(static_cast<QV4::NoThrowEngine*>(engine)); - context = engine->currentContext; MOTH_END_INSTR(CallBuiltinPopScope) MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) @@ -926,22 +914,22 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code #ifndef QT_NO_QML_DEBUGGER MOTH_BEGIN_INSTR(Debug) engine->current->lineNumber = instr.lineNumber; - QV4::Debugging::Debugger *debugger = context->engine()->debugger(); + QV4::Debugging::Debugger *debugger = engine->debugger(); if (debugger && debugger->pauseAtNextOpportunity()) debugger->maybeBreakAtInstruction(); if (qt_v4IsDebugging) - qt_v4CheckForBreak(context); + qt_v4CheckForBreak(engine->currentContext); MOTH_END_INSTR(Debug) MOTH_BEGIN_INSTR(Line) engine->current->lineNumber = instr.lineNumber; if (qt_v4IsDebugging) - qt_v4CheckForBreak(context); + qt_v4CheckForBreak(engine->currentContext); MOTH_END_INSTR(Line) #endif // QT_NO_QML_DEBUGGER MOTH_BEGIN_INSTR(LoadThis) - VALUE(instr.result) = context->thisObject(); + VALUE(instr.result) = engine->currentContext->thisObject(); MOTH_END_INSTR(LoadThis) MOTH_BEGIN_INSTR(LoadQmlContext) @@ -967,25 +955,13 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code Q_ASSERT(false); catchException: - Q_ASSERT(context->engine()->hasException); + Q_ASSERT(engine->hasException); if (!exceptionHandler) return QV4::Encode::undefined(); code = exceptionHandler; } } -#ifdef MOTH_THREADED_INTERPRETER -void **VME::instructionJumpTable() -{ - static void **jumpTable = 0; - if (!jumpTable) { - const uchar *code = 0; - VME().run(0, code, &jumpTable); - } - return jumpTable; -} -#endif - QV4::ReturnedValue VME::exec(ExecutionEngine *engine, const uchar *code) { VME vme; diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h index f8893509d9..8d46207f2b 100644 --- a/src/qml/jsruntime/qv4vme_moth_p.h +++ b/src/qml/jsruntime/qv4vme_moth_p.h @@ -67,16 +67,8 @@ class VME public: static QV4::ReturnedValue exec(QV4::ExecutionEngine *, const uchar *); -#ifdef MOTH_THREADED_INTERPRETER - static void **instructionJumpTable(); -#endif - private: - QV4::ReturnedValue run(QV4::ExecutionEngine *, const uchar *code -#ifdef MOTH_THREADED_INTERPRETER - , void ***storeJumpTable = 0 -#endif - ); + QV4::ReturnedValue run(QV4::ExecutionEngine *, const uchar *code); }; } // namespace Moth |