diff options
Diffstat (limited to 'src/qml')
60 files changed, 1058 insertions, 686 deletions
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index d59a72cde2..ac4192bc12 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -177,7 +177,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) for (uint i = 0; i < data->jsClassTableSize; ++i) { int memberCount = 0; const CompiledData::JSClassMember *member = data->jsClassAt(i, &memberCount); - QV4::InternalClass *klass = engine->emptyClass; + QV4::InternalClass *klass = engine->internalClasses[QV4::ExecutionEngine::Class_Object]; for (int j = 0; j < memberCount; ++j, ++member) klass = klass->addMember(runtimeStrings[member->nameOffset]->identifier, member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data); diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index fbd6ac8f99..dabda7bae8 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -170,11 +170,7 @@ QT_BEGIN_NAMESPACE #define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1) -#ifdef MOTH_THREADED_INTERPRETER -# define MOTH_INSTR_HEADER union { quint32 instructionType; void *code; }; -#else -# define MOTH_INSTR_HEADER quint32 instructionType; -#endif +#define MOTH_INSTR_HEADER quint32 instructionType; #define MOTH_INSTR_ENUM(I, FMT) I, #define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QV4::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK) @@ -194,8 +190,8 @@ struct Param { // Arg(outer): 4 // Local(outer): 5 // ... - unsigned scope; - unsigned index; + unsigned scope : 12; + unsigned index : 20; bool isConstant() const { return !scope; } bool isArgument() const { return scope >= 2 && !(scope &1); } diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index aefb084971..fb805dce02 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -638,7 +638,7 @@ void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) { - if (useFastLookups) { + if (0 && useFastLookups) { Instruction::LoadElementLookup load; load.lookup = registerIndexedGetterLookup(); load.base = getParam(base); @@ -657,7 +657,7 @@ void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr void InstructionSelection::setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) { - if (useFastLookups) { + if (0 && useFastLookups) { Instruction::StoreElementLookup store; store.lookup = registerIndexedSetterLookup(); store.base = getParam(targetBase); @@ -1436,29 +1436,6 @@ CompilationUnit::~CompilationUnit() void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) { -#ifdef MOTH_THREADED_INTERPRETER - // link byte code against addresses of instructions - for (int i = 0; i < codeRefs.count(); ++i) { - QByteArray &codeRef = codeRefs[i]; - char *code = codeRef.data(); - int index = 0; - while (index < codeRef.size()) { - Instr *genericInstr = reinterpret_cast<Instr *>(code + index); - - switch (genericInstr->common.instructionType) { -#define LINK_INSTRUCTION(InstructionType, Member) \ - case Instr::InstructionType: \ - genericInstr->common.code = VME::instructionJumpTable()[static_cast<int>(genericInstr->common.instructionType)]; \ - index += InstrMeta<(int)Instr::InstructionType>::Size; \ - break; - - FOR_EACH_MOTH_INSTR(LINK_INSTRUCTION) - - } - } - } -#endif - runtimeFunctions.resize(data->functionTableSize); runtimeFunctions.fill(0); for (int i = 0 ;i < runtimeFunctions.size(); ++i) { @@ -1516,17 +1493,6 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit QByteArray padding; -#if defined(MOTH_THREADED_INTERPRETER) && !defined(V4_BOOTSTRAP) - // Map from instruction label back to instruction type. Only needed when persisting - // already linked compilation units; - QHash<void*, int> reverseInstructionMapping; - if (engine) { - void **instructions = VME::instructionJumpTable(); - for (int i = 0; i < Instr::LastInstruction; ++i) - reverseInstructionMapping.insert(instructions[i], i); - } -#endif - for (int i = 0; i < codeRefs.size(); ++i) { const CompiledData::Function *compiledFunction = unit->functionAt(i); @@ -1545,27 +1511,6 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit QByteArray code = codeRefs.at(i); -#if defined(MOTH_THREADED_INTERPRETER) && !defined(V4_BOOTSTRAP) - if (!reverseInstructionMapping.isEmpty()) { - char *codePtr = code.data(); // detaches - int index = 0; - while (index < code.size()) { - Instr *genericInstr = reinterpret_cast<Instr *>(codePtr + index); - - genericInstr->common.instructionType = reverseInstructionMapping.value(genericInstr->common.code); - - switch (genericInstr->common.instructionType) { - #define REVERSE_INSTRUCTION(InstructionType, Member) \ - case Instr::InstructionType: \ - index += InstrMeta<(int)Instr::InstructionType>::Size; \ - break; - - FOR_EACH_MOTH_INSTR(REVERSE_INSTRUCTION) - } - } - } -#endif - written = device->write(code.constData(), compiledFunction->codeSize); if (written != qint64(compiledFunction->codeSize)) { *errorString = device->errorString(); diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index 731c6ad38f..fc136b09ff 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -5417,7 +5417,11 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool pee showMeTheCode(function, "After loop detection"); // cfg2dot(function, loopDetection.allLoops()); - if (peelLoops) { + // ### disable loop peeling for now. It doesn't give any measurable performance + // improvements at this time, but significantly increases the size of the + // JIT generated code + Q_UNUSED(peelLoops); + if (0 && peelLoops) { QVector<LoopDetection::LoopInfo *> innerLoops = loopDetection.innermostLoops(); LoopPeeling(df).run(innerLoops); diff --git a/src/qml/configure.json b/src/qml/configure.json index 2c4887365f..257bedecbc 100644 --- a/src/qml/configure.json +++ b/src/qml/configure.json @@ -15,17 +15,20 @@ "features": { "qml-interpreter": { "label": "QML interpreter", - "purpose": "Support for the QML interpreter", + "purpose": "Provides the QML interpreter.", + "section": "QML", "output": [ "privateFeature" ] }, "qml-network": { "label": "QML network support", - "purpose": "Provides network transparency for QML", + "purpose": "Provides network transparency.", + "section": "QML", "output": [ "publicFeature" ] }, "qml-profiler": { "label": "Command line QML Profiler", - "purpose": "The QML Profiler retrieves QML tracing data from an application.", + "purpose": "Supports retrieving QML tracing data from an application.", + "section": "QML", "condition": [ "features.commandlineparser", "features.localserver", diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index ac72d2e8f5..50d40f6f98 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -618,7 +618,7 @@ void InstructionSelection<JITAssembler>::setQObjectProperty(IR::Expr *source, IR template <typename JITAssembler> void InstructionSelection<JITAssembler>::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) { - if (useFastLookups) { + if (0 && useFastLookups) { uint lookup = registerIndexedGetterLookup(); generateLookupCall(target, lookup, offsetof(QV4::Lookup, indexedGetter), PointerToValue(base), @@ -633,7 +633,7 @@ void InstructionSelection<JITAssembler>::getElement(IR::Expr *base, IR::Expr *in template <typename JITAssembler> void InstructionSelection<JITAssembler>::setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) { - if (useFastLookups) { + if (0 && useFastLookups) { uint lookup = registerIndexedSetterLookup(); generateLookupCall(JITAssembler::Void, lookup, offsetof(QV4::Lookup, indexedSetter), PointerToValue(targetBase), PointerToValue(targetIndex), diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index bab2e633a7..3a3cf46ddb 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -1019,7 +1019,7 @@ QJSValue QJSValue::property(const QString& name) const if (idx < UINT_MAX) return property(idx); - s->makeIdentifier(engine); + s->makeIdentifier(); QV4::ScopedValue result(scope, o->get(s)); if (engine->hasException) result = engine->catchException(); @@ -1090,7 +1090,7 @@ void QJSValue::setProperty(const QString& name, const QJSValue& value) return; } - s->makeIdentifier(scope.engine); + s->makeIdentifier(); QV4::ScopedValue v(scope, QJSValuePrivate::convertedToValue(engine, value)); o->put(s, v); if (engine->hasException) diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 76ac8d4a91..9938f60aea 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -48,6 +48,7 @@ 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 c21878fa42..1e9f83a90e 100644 --- a/src/qml/jsruntime/qv4alloca_p.h +++ b/src/qml/jsruntime/qv4alloca_p.h @@ -58,7 +58,7 @@ #elif QT_CONFIG(alloca_malloc_h) # include <malloc.h> // This does not matter unless compiling in strict standard mode. -# ifdef Q_OS_WIN +# ifdef Q_CC_MSVC # define alloca _alloca # endif #else diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 9354bcb1a3..6ab838c387 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -53,13 +53,13 @@ void Heap::ArgumentsObject::init(QV4::CallContext *context) this->context = context->d(); Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable()); - ExecutionEngine *v4 = context->d()->engine; + ExecutionEngine *v4 = context->engine(); Scope scope(v4); 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->propertyData(CalleePropertyIndex + QV4::Object::GetterOffset) = v4->thrower(); *args->propertyData(CalleePropertyIndex + QV4::Object::SetterOffset) = v4->thrower(); *args->propertyData(CallerPropertyIndex + QV4::Object::GetterOffset) = v4->thrower(); @@ -69,10 +69,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->propertyData(CalleePropertyIndex) = context->d()->function->asReturnedValue(); } - Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(context->d()->engine->id_length())); + Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length())); *args->propertyData(LengthPropertyIndex) = Primitive::fromInt32(context->d()->callData->argc); } @@ -81,18 +81,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 = md->allocate(engine(), numAccessors); + d()->mappedArguments = md->allocate(scope.engine, numAccessors); for (uint i = 0; i < numAccessors; ++i) { d()->mappedArguments->data[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 uint numAccessors = qMin((int)context()->formalParameterCount(), context()->callData->argc); if (pd && index < (uint)numAccessors) isMapped = arrayData()->attributes(index).isAccessor() && - pd->getter() == context()->engine->argumentsAccessors[index].getter(); + pd->getter() == scope.engine->argumentsAccessors[index].getter(); if (isMapped) { Q_ASSERT(arrayData()); diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index c29cedaa9b..6d1fb62e0a 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -50,6 +50,8 @@ QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON const QV4::VTable QV4::ArrayData::static_vtbl = { 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 24b948f01e..daf8c36814 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -236,6 +236,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); } @@ -262,6 +263,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_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 c4a0539750..3ff864d7b9 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -66,9 +66,9 @@ DEFINE_MANAGED_VTABLE(GlobalContext); Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData *callData) { - Heap::CallContext *c = d()->engine->memoryManager->allocManaged<CallContext>( + Heap::CallContext *c = engine()->memoryManager->allocManaged<CallContext>( requiredMemoryForExecutionContect(function, callData->argc)); - c->init(d()->engine, Heap::ExecutionContext::Type_CallContext); + c->init(Heap::ExecutionContext::Type_CallContext); c->v4Function = function; @@ -95,27 +95,16 @@ Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData return c; } -Heap::CallContext *Heap::CallContext::createSimpleContext(ExecutionEngine *v4) -{ - Heap::CallContext *ctxt = v4->memoryManager->allocSimpleCallContext(v4); - return ctxt; -} - -void Heap::CallContext::freeSimpleCallContext() -{ - engine->memoryManager->freeSimpleCallContext(); -} - 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) @@ -165,14 +154,14 @@ 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 = eng->globalObject->d(); } void Heap::CatchContext::init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue) { - Heap::ExecutionContext::init(outerContext->engine, Heap::ExecutionContext::Type_CatchContext); + Heap::ExecutionContext::init(Heap::ExecutionContext::Type_CatchContext); outer = outerContext; strictMode = outer->strictMode; callData = outer->callData; @@ -209,6 +198,9 @@ unsigned int CallContext::variableCount() const bool ExecutionContext::deleteProperty(String *name) { + name->makeIdentifier(); + Identifier *id = name->identifier(); + Scope scope(this); bool hasWith = false; ScopedContext ctx(scope, this); @@ -237,7 +229,7 @@ bool ExecutionContext::deleteProperty(String *name) case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); if (c->v4Function && (c->v4Function->needsActivation() || hasWith)) { - uint index = c->v4Function->internalClass->find(name); + uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) // ### throw in strict mode? return false; @@ -336,7 +328,7 @@ void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Functio ExecutionContextSaver ctxSaver(scope); - CallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext(scope.engine); + CallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext(); ctx->strictMode = function->isStrict(); ctx->callData = callData; @@ -361,6 +353,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); @@ -392,7 +387,7 @@ void ExecutionContext::setProperty(String *name, const Value &value) case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(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; @@ -414,7 +409,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; @@ -422,21 +417,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(); bool hasWith = false; @@ -473,7 +468,10 @@ ReturnedValue ExecutionContext::getProperty(String *name) case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); if (c->v4Function && (c->v4Function->needsActivation() || hasWith || hasCatchScope)) { - 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(); @@ -511,9 +509,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(); bool hasWith = false; @@ -551,7 +549,10 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); if (c->v4Function && (c->v4Function->needsActivation() || hasWith || hasCatchScope)) { - 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(); @@ -588,7 +589,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 CallContext *callCtx = it->asCallContext()) diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index c769dcd142..0b63922a4b 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -105,7 +105,6 @@ struct QmlContext; // can use the Members macro struct ExecutionContextData { CallData *callData; - ExecutionEngine *engine; ExecutionContext *outer; Lookup *lookups; const QV4::Value *constantTable; @@ -120,8 +119,7 @@ struct ExecutionContextData { 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); @@ -141,11 +139,10 @@ struct ExecutionContext : Base, public ExecutionContextData { Type_CallContext = 0x6 }; - void init(ExecutionEngine *engine, ContextType t) + void init(ContextType t) { Base::init(); - this->engine = engine; type = t; lineNumber = -1; } @@ -172,12 +169,10 @@ struct CallContextSizeStruct : public ExecutionContext, public CallContextData { struct CallContext : ExecutionContext, public CallContextData { static Q_CONSTEXPR size_t baseOffset = sizeof(CallContextSizeStruct) - sizeof(CallContextData); - static CallContext *createSimpleContext(ExecutionEngine *v4); - void freeSimpleCallContext(); - 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; @@ -204,7 +199,7 @@ V4_ASSERT_IS_TRIVIAL(CatchContext) struct WithContext : ExecutionContext { void init(ExecutionContext *outerContext, Object *with) { - Heap::ExecutionContext::init(outerContext->engine, Heap::ExecutionContext::Type_WithContext); + Heap::ExecutionContext::init(Heap::ExecutionContext::Type_WithContext); outer = outerContext; callData = outer->callData; lookups = outer->lookups; @@ -228,8 +223,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); @@ -271,6 +265,7 @@ struct Q_QML_EXPORT ExecutionContext : public Managed struct Q_QML_EXPORT CallContext : public ExecutionContext { V4_MANAGED(CallContext, ExecutionContext) + V4_INTERNALCLASS(CallContext) // 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 a56d17f9b1..ecd57bcd8d 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -114,6 +114,8 @@ struct DateCtor: FunctionObject struct DatePrototype: DateObject { + 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 0b61c6e7e6..0cb1b1ee13 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) , globalCode(0) @@ -214,7 +212,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_CallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable()); jsStrings[String_Empty] = newIdentifier(QString()); jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined")); @@ -253,90 +257,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); @@ -466,7 +493,7 @@ ExecutionEngine::~ExecutionEngine() for (QV4::CompiledData::CompilationUnit *unit : qAsConst(remainingUnits)) unit->unlink(); - emptyClass->destroy(); + internalClasses[Class_Empty]->destroy(); delete classPool; delete bumperPointerAllocator; delete regExpCache; @@ -522,6 +549,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>(); @@ -836,8 +868,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 5182f24235..a569b8ddf9 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,21 +97,14 @@ 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 { JSStackLimit = 4*1024*1024 }; WTF::PageAllocation *jsStack; - Value *jsStackBase; void pushForGC(Heap::Base *m) { *jsStackTop = m; @@ -129,10 +123,6 @@ public: return ptr; } - IdentifierTable *identifierTable; - - Object *globalObject; - Function *globalCode; #ifdef V4_BOOTSTRAP @@ -243,25 +233,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); } @@ -395,6 +366,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..06c346b3c0 --- /dev/null +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** 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; + quint32 hasException = false; +#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_CallContext, + 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 f290bc5136..6811438915 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; *propertyData(QV4::ErrorObject::Index_Stack) = scope.engine->getStackFunction(); @@ -183,8 +183,6 @@ void ErrorObject::markObjects(Heap::Base *that, ExecutionEngine *e) DEFINE_OBJECT_VTABLE(ErrorObject); -DEFINE_OBJECT_VTABLE(SyntaxErrorObject); - void Heap::SyntaxErrorObject::init(const Value &msg) { Heap::ErrorObject::init(msg, SyntaxError); @@ -330,8 +328,7 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, He *obj->propertyData(Index_Constructor) = ctor; *obj->propertyData(Index_Message) = engine->id_empty(); *obj->propertyData(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 9ba9f05234..12531c9309 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -157,7 +157,7 @@ struct ErrorObject: Object { V4_OBJECT2(ErrorObject, Object) Q_MANAGED_TYPE(ErrorObject) - V4_INTERNALCLASS(errorClass) + V4_INTERNALCLASS(ErrorObject) V4_PROTOTYPE(errorPrototype) V4_NEEDS_DESTROY @@ -203,8 +203,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 { @@ -324,19 +326,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 358c2d079c..31b57b97e9 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; @@ -95,7 +95,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); @@ -109,7 +109,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 b2d89220ea..9eb9d2ad36 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -122,8 +122,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->propertyData(Heap::FunctionObject::Index_ProtoConstructor) = this->asReturnedValue(); *propertyData(Heap::FunctionObject::Index_Prototype) = proto.asReturnedValue(); } else { @@ -136,7 +136,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 *) @@ -160,7 +160,7 @@ void FunctionObject::markObjects(Heap::Base *that, ExecutionEngine *e) 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 @@ -375,8 +375,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(); @@ -439,55 +439,26 @@ 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; -} + ic = engine()->internalClasses[EngineBase::Class_Object]; + if (o) + ic = ic->changePrototype(o->d()); + d()->cachedClassForConstructor = ic; -void OldBuiltinFunction::construct(const Managed *f, Scope &scope, CallData *) -{ - scope.result = static_cast<const OldBuiltinFunction *>(f)->internalClass()->engine->throwTypeError(); + return ic; } -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); - - CallContext::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); - - scope.result = f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext)); - v4->memoryManager->freeSimpleCallContext(); -} DEFINE_OBJECT_VTABLE(BuiltinFunction); @@ -526,7 +497,7 @@ void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *c ExecutionContextSaver ctxSaver(scope); - CallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(v4); + CallContext::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 f4ac37219c..d691b869fe 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -117,6 +117,8 @@ struct ScriptFunction : FunctionObject { Index_Length }; void init(QV4::ExecutionContext *scope, Function *function); + + QV4::InternalClass *cachedClassForConstructor; }; struct BoundFunction : FunctionObject { @@ -134,7 +136,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 @@ -190,20 +192,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); @@ -236,12 +228,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 bac71b4537..0bcd510541 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) @@ -128,22 +133,48 @@ InternalClass::InternalClass(const QV4::InternalClass &other) static void insertHoleIntoPropertyData(Object *object, int idx) { + int inlineSize = object->d()->vtable()->nInlineProperties; int icSize = object->internalClass()->size; - int from = idx; + int from = qMax(idx, inlineSize); int to = from + 1; - if (from < icSize) + if (from < icSize) { memmove(object->propertyData(to), object->propertyData(from), (icSize - from - 1) * sizeof(Value)); + } + if (from == idx) + return; + if (inlineSize < icSize) + *object->propertyData(inlineSize) = *object->propertyData(inlineSize - 1); + from = idx; + to = from + 1; + if (from < inlineSize - 1) { + memmove(object->propertyData(to), object->propertyData(from), + (inlineSize - from - 1) * sizeof(Value)); + } } static void removeFromPropertyData(Object *object, int idx, bool accessor = false) { + int inlineSize = object->d()->vtable()->nInlineProperties; int delta = (accessor ? 2 : 1); int oldSize = object->internalClass()->size + delta; int to = idx; int from = to + delta; - if (from < oldSize) + if (from < inlineSize) { + memmove(object->propertyData(to), object->d()->propertyData(from), (inlineSize - from)*sizeof(Value)); + to = inlineSize - delta; + from = inlineSize; + } + if (to < inlineSize && from < oldSize) { + Q_ASSERT(from >= inlineSize); + memcpy(object->propertyData(to), object->d()->propertyData(from), (inlineSize - to)*sizeof(Value)); + to = inlineSize; + from = inlineSize + delta; + } + if (from < oldSize) { + Q_ASSERT(to >= inlineSize && from > to); memmove(object->propertyData(to), object->d()->propertyData(from), (oldSize - to)*sizeof(Value)); + } } void InternalClass::changeMember(Object *object, String *string, PropertyAttributes data, uint *index) @@ -187,13 +218,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); @@ -207,12 +239,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; @@ -260,7 +352,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) @@ -296,7 +388,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(); @@ -305,7 +397,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; @@ -324,8 +417,11 @@ void InternalClass::removeMember(Object *object, Identifier *id) 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; @@ -338,7 +434,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()) @@ -367,7 +464,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()) @@ -410,7 +508,23 @@ void InternalClass::destroy() void InternalClassPool::markObjects(ExecutionEngine *engine) { - Q_UNUSED(engine); + InternalClass *ic = 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(engine); + } + } else if (t.flags == InternalClassTransition::PrototypeChange) { + t.lookup->prototype->mark(engine); + } + } } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 1d8ef4b0fb..9e8ab9e73e 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -54,15 +54,13 @@ #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; @@ -222,12 +220,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 +243,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 +260,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(ExecutionEngine *engine); diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 52ed449664..faaa5539ab 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->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->inlinePropertyData(l->index)->asReturnedValue(); + if (l->classList[2] == o->internalClass) + 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->propertyData(l->index)->asReturnedValue(); + return o->inlinePropertyData(l->index)->asReturnedValue(); if (l->classList[2] == o->internalClass) - return o->propertyData(l->index2)->asReturnedValue(); + return o->memberData->data[l->index2].asReturnedValue(); } l->getter = getterFallback; return getterFallback(l, engine, object); } -ReturnedValue Lookup::getter0getter1(Lookup *l, ExecutionEngine *engine, const Value &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->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->data[l->index].asReturnedValue(); + if (l->classList[2] == o->internalClass) + return o->memberData->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::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->memberData->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->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->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->propertyData(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()->inlinePropertyData(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->propertyData(l->index) = value; - return; - } + Q_ASSERT(!o->prototype()); + o->setInternalClass(l->classList[3]); + *o->propertyData(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->propertyData(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->propertyData(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->propertyData(l->index) = value; diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 2ffb43cce9..151231991f 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -79,13 +79,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; @@ -102,17 +103,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); @@ -120,7 +126,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); @@ -131,6 +138,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 3a84a83b9c..200380eda0 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -47,6 +47,8 @@ using namespace QV4; const VTable Managed::static_vtbl = { 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 5c764e7ff0..6859334797 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> QT_BEGIN_NAMESPACE @@ -129,6 +130,9 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} #define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \ { \ parentVTable, \ + (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, \ @@ -148,6 +152,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) @@ -190,6 +198,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 5c89dfe8ec..e239458849 100644 --- a/src/qml/jsruntime/qv4memberdata_p.h +++ b/src/qml/jsruntime/qv4memberdata_p.h @@ -73,6 +73,7 @@ V4_ASSERT_IS_TRIVIAL(MemberData) struct MemberData : Managed { V4_MANAGED(MemberData, Managed) + V4_INTERNALCLASS(MemberData) Value &operator[] (uint idx) { return d()->data[idx]; } const Value *data() const { return d()->data; } diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index 364b866a16..85d306020c 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 12157af728..98f5c7464f 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->size < ic->size)) - d()->memberData = MemberData::allocate(ic->engine, ic->size, d()->memberData); + uint requiredSize = ic->size - nInline; + if (!hasMD || (hasMD && d()->memberData->size < requiredSize)) + d()->memberData = 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 = proto ? proto->d() : 0; + setInternalClass(internalClass()->changePrototype(p)); return true; } @@ -156,17 +162,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->defineReadonlyProperty(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(); @@ -178,16 +173,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->defineReadonlyProperty(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(); @@ -198,25 +183,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 *)) { @@ -259,8 +225,13 @@ void Object::markObjects(Heap::Base *that, ExecutionEngine *e) o->memberData->mark(e); if (o->arrayData) o->arrayData->mark(e); - if (o->prototype) - o->prototype->mark(e); + uint nInline = o->vtable()->nInlineProperties; + Value *v = reinterpret_cast<Value *>(o) + o->vtable()->inlinePropertyOffset; + const Value *end = v + nInline; + while (v < end) { + v->mark(e); + ++v; + } } void Object::insertMember(String *s, const Property *p, PropertyAttributes attributes) @@ -283,7 +254,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) { @@ -325,15 +299,18 @@ Value *Object::getValueOrSetter(String *name, PropertyAttributes *attrs) { 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 o->propertyData(attrs->isAccessor() ? idx + SetterOffset : idx); } - o = o->prototype; + o = o->prototype(); } *attrs = Attr_Invalid; return 0; @@ -356,7 +333,7 @@ Value *Object::getValueOrSetter(uint index, PropertyAttributes *attrs) return reinterpret_cast<Value *>(0x1); } } - o = o->prototype; + o = o->prototype(); } *attrs = Attr_Invalid; return 0; @@ -400,7 +377,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; @@ -457,8 +437,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]; @@ -494,9 +477,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) @@ -531,7 +523,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->propertyData(idx) = value; return; } @@ -648,12 +640,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; @@ -715,9 +708,10 @@ void Object::internalPut(String *name, const Value &value) if (idx != UINT_MAX) return putIndexed(idx, value); - name->makeIdentifier(engine()); + name->makeIdentifier(); + Identifier *id = name->identifier(); - uint member = internalClass()->find(name); + uint member = internalClass()->find(id); Value *v = 0; PropertyAttributes attrs; if (member < UINT_MAX) { @@ -868,9 +862,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()); @@ -907,7 +901,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; @@ -941,7 +935,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 @@ -1185,7 +1179,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 6a543ae1a8..9592ff5d1b 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -71,11 +71,31 @@ struct Object : Base { void init() { Base::init(); } void destroy() { Base::destroy(); } - const Value *propertyData(uint index) const { return memberData->data + index; } - Value *propertyData(uint index) { return memberData->data + index; } + const Value *inlinePropertyData(uint index) const { + Q_ASSERT(index < vtable()->nInlineProperties); + return reinterpret_cast<const Value *>(this) + vtable()->inlinePropertyOffset + index; + } + Value *inlinePropertyData(uint index) { + Q_ASSERT(index < vtable()->nInlineProperties); + return reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + 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->data + index; + } + Value *propertyData(uint index) { + uint nInline = vtable()->nInlineProperties; + if (index < nInline) + return reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index; + index -= nInline; + return memberData->data + index; + } - InternalClass *internalClass; - Pointer<Object> prototype; + Heap::Object *prototype() const { return internalClass->prototype; } Pointer<MemberData> memberData; Pointer<ArrayData> arrayData; }; @@ -116,9 +136,6 @@ struct Object : Base { } \ V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass); -#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(); } @@ -177,7 +194,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 { @@ -186,7 +203,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); } @@ -199,7 +215,7 @@ struct Q_QML_EXPORT Object: Managed { void setProperty(uint index, const Property *p); 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); @@ -239,12 +255,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 *), @@ -261,8 +273,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 @@ -451,7 +461,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 889f4ea288..91d65a70c9 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -300,7 +300,7 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QmlContextWrapper *qml) { - Heap::ExecutionContext::init(outerContext->engine(), Heap::ExecutionContext::Type_QmlContext); + Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext); outer = outerContext->d(); strictMode = false; callData = outer->callData; @@ -330,7 +330,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; } @@ -339,7 +340,8 @@ Heap::QmlContext *QmlContext::create(ExecutionContext *parent, QQmlContextData * Scope scope(parent); Scoped<QmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QmlContextWrapper>(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 67b1356e65..d7978cc212 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -997,7 +997,6 @@ void QObjectWrapper::destroyObject(bool lastCall) } } - h->internalClass = 0; h->~Data(); } @@ -1689,7 +1688,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)) @@ -1740,7 +1739,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 d3e63375a5..04cdb468bf 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 0894d0c25b..1f758e36f6 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -379,7 +379,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 c0c7dfa78a..54731cef14 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -96,7 +96,7 @@ struct 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 42bf24d7f3..37a2bfdf90 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->len) + 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->len && !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->len) { s->data(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->len) { + s->data(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 6f96b9f760..0879f149fa 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 cde2131aab..515d61c8e4 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 5b0fd292d6..f5311ae5d4 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,13 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { } uint toUInt(bool *ok) const; - void makeIdentifier(ExecutionEngine *e) const { + 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 0ee7a6ece9..2e64364f9f 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -82,7 +82,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 { @@ -109,6 +109,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 cecd1e6958..80655aded6 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); } void TypedArray::markObjects(Heap::Base *that, ExecutionEngine *e) diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index eefed2db4b..a6a74e4b9b 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -148,6 +148,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 ef51b6632d..dba14b13fb 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 a74016ab0c..9d65f67f0f 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 @@ -356,11 +356,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); @@ -369,15 +365,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; @@ -386,7 +378,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 @@ -396,7 +387,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; @@ -405,10 +396,10 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code Q_ALLOCA_VAR(QV4::Value*, scopes, sizeof(QV4::Value *)*(2 + 2*scopeDepth)); { - scopes[0] = const_cast<QV4::Value *>(static_cast<CompiledData::CompilationUnit*>(context->d()->compilationUnit)->constants); + scopes[0] = const_cast<QV4::Value *>(static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->constants); // stack gets setup in push instruction scopes[1] = 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) { @@ -428,7 +419,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 @@ -447,12 +438,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) @@ -465,7 +456,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) @@ -480,7 +471,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, VALUE(instr.base), VALUE(instr.index))); MOTH_END_INSTR(LoadElementLookup) @@ -490,7 +481,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, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreElementLookup) @@ -500,7 +491,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) @@ -510,7 +501,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) @@ -581,7 +572,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); @@ -600,7 +591,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); @@ -610,7 +601,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); @@ -661,18 +652,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) @@ -912,22 +900,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) @@ -953,25 +941,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 diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h index 173c0a3e20..a4e96b4c84 100644 --- a/src/qml/memory/qv4heap_p.h +++ b/src/qml/memory/qv4heap_p.h @@ -53,6 +53,7 @@ #include <QtCore/QString> #include <private/qv4global_p.h> #include <private/qv4mmdefs_p.h> +#include <private/qv4internalclass_p.h> #include <QSharedPointer> // To check if Heap::Base::init is called (meaning, all subclasses did their init and called their @@ -69,9 +70,13 @@ QT_BEGIN_NAMESPACE namespace QV4 { +struct InternalClass; + struct VTable { const VTable * const parent; + uint inlinePropertyOffset : 16; + uint nInlineProperties : 16; uint isExecutionContext : 1; uint isString : 1; uint isObject : 1; @@ -91,13 +96,12 @@ namespace Heap { struct Q_QML_EXPORT Base { void *operator new(size_t) = delete; - const VTable *vt; + InternalClass *internalClass; inline ReturnedValue asReturnedValue() const; inline void mark(QV4::ExecutionEngine *engine); - void setVtable(const VTable *v) { vt = v; } - const VTable *vtable() const { return vt; } + const VTable *vtable() const { return internalClass->vtable; } inline bool isMarked() const { const HeapItem *h = reinterpret_cast<const HeapItem *>(this); Chunk *c = h->chunk(); @@ -164,7 +168,7 @@ V4_ASSERT_IS_TRIVIAL(Base) // for a size/offset translation when cross-compiling between 32- and // 64-bit. Q_STATIC_ASSERT(std::is_standard_layout<Base>::value); -Q_STATIC_ASSERT(offsetof(Base, vt) == 0); +Q_STATIC_ASSERT(offsetof(Base, internalClass) == 0); Q_STATIC_ASSERT(sizeof(Base) == QT_POINTER_SIZE); template <typename T> diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 7aa8f91503..56f1254421 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -275,8 +275,9 @@ QString binary(quintptr) { return QString(); } #define SDUMP if (1) ; else qDebug #endif -void Chunk::sweep() +bool Chunk::sweep() { + bool hasUsedSlots = false; SDUMP() << "sweeping chunk" << this; HeapItem *o = realBase(); bool lastSlotFree = false; @@ -316,6 +317,7 @@ void Chunk::sweep() } } objectBitmap[i] = blackBitmap[i]; + hasUsedSlots |= (blackBitmap[i] != 0); blackBitmap[i] = 0; extendsBitmap[i] = e; lastSlotFree = !((objectBitmap[i]|extendsBitmap[i]) >> (sizeof(quintptr)*8 - 1)); @@ -325,6 +327,7 @@ void Chunk::sweep() o += Chunk::Bits; } // DEBUG << "swept chunk" << this << "freed" << slotsFreed << "slots."; + return hasUsedSlots; } void Chunk::freeAll() @@ -579,12 +582,21 @@ void BlockAllocator::sweep() // qDebug() << "BlockAlloc: sweep"; usedSlotsAfterLastSweep = 0; - for (auto c : chunks) { - c->sweep(); - c->sortIntoBins(freeBins, NumBins); -// qDebug() << "used slots in chunk" << c << ":" << c->nUsedSlots(); - usedSlotsAfterLastSweep += c->nUsedSlots(); - } + + auto isFree = [this] (Chunk *c) { + bool isUsed = c->sweep(); + + if (isUsed) { + c->sortIntoBins(freeBins, NumBins); + usedSlotsAfterLastSweep += c->nUsedSlots(); + } else { + chunkAllocator->free(c); + } + return !isUsed; + }; + + auto newEnd = std::remove_if(chunks.begin(), chunks.end(), isFree); + chunks.erase(newEnd, chunks.end()); } void BlockAllocator::freeAll() @@ -757,12 +769,15 @@ Heap::Base *MemoryManager::allocData(std::size_t size) return *m; } -Heap::Object *MemoryManager::allocObjectWithMemberData(std::size_t size, uint nMembers) +Heap::Object *MemoryManager::allocObjectWithMemberData(const QV4::VTable *vtable, uint nMembers) { + uint size = (vtable->nInlineProperties + vtable->inlinePropertyOffset)*sizeof(Value); + Q_ASSERT(!(size % sizeof(HeapItem))); Heap::Object *o = static_cast<Heap::Object *>(allocData(size)); // ### Could optimize this and allocate both in one go through the block allocator - if (nMembers) { + if (nMembers > vtable->nInlineProperties) { + nMembers -= vtable->nInlineProperties; std::size_t memberSize = align(sizeof(Heap::MemberData) + (nMembers - 1)*sizeof(Value)); // qDebug() << "allocating member data for" << o << nMembers << memberSize; Heap::Base *m; @@ -772,7 +787,8 @@ Heap::Object *MemoryManager::allocObjectWithMemberData(std::size_t size, uint nM m = *blockAllocator.allocate(memberSize, true); memset(m, 0, memberSize); o->memberData = static_cast<Heap::MemberData *>(m); - o->memberData->setVtable(MemberData::staticVTable()); + o->memberData->internalClass = engine->internalClasses[EngineBase::Class_MemberData]; + Q_ASSERT(o->memberData->internalClass); o->memberData->size = static_cast<uint>((memberSize - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value)); o->memberData->init(); // qDebug() << " got" << o->memberData << o->memberData->size; @@ -947,7 +963,8 @@ void MemoryManager::runGC() #ifndef QT_NO_DEBUG qDebug() << " Triggered by alloc request of" << lastAllocRequestedSlots << "slots."; #endif - qDebug() << "Allocated" << totalMem << "bytes in" << blockAllocator.chunks.size() << "chunks"; + size_t oldChunks = blockAllocator.chunks.size(); + qDebug() << "Allocated" << totalMem << "bytes in" << oldChunks << "chunks"; qDebug() << "Fragmented memory before GC" << (totalMem - usedBefore); dumpBins(&blockAllocator); @@ -972,6 +989,7 @@ void MemoryManager::runGC() qDebug() << "Used memory before GC:" << usedBefore; qDebug() << "Used memory after GC:" << usedAfter; qDebug() << "Freed up bytes:" << (usedBefore - usedAfter); + qDebug() << "Freed up chunks:" << (oldChunks - blockAllocator.chunks.size()); size_t lost = blockAllocator.allocatedMem() - memInBins - usedAfter; if (lost) qDebug() << "!!!!!!!!!!!!!!!!!!!!! LOST MEM:" << lost << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"; diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index 00daf8a622..77c5885dfe 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -206,12 +206,13 @@ public: Q_DECL_CONSTEXPR static inline std::size_t align(std::size_t size) { return (size + Chunk::SlotSize - 1) & ~(Chunk::SlotSize - 1); } - QV4::Heap::CallContext *allocSimpleCallContext(QV4::ExecutionEngine *v4) + QV4::Heap::CallContext *allocSimpleCallContext() { Heap::CallContext *ctxt = stackAllocator.allocate(); memset(ctxt, 0, sizeof(Heap::CallContext)); - ctxt->setVtable(QV4::CallContext::staticVTable()); - ctxt->init(v4); + ctxt->internalClass = CallContext::defaultInternalClass(engine); + Q_ASSERT(ctxt->internalClass && ctxt->internalClass->vtable); + ctxt->init(); return ctxt; } @@ -224,16 +225,20 @@ public: V4_ASSERT_IS_TRIVIAL(typename ManagedType::Data) size = align(size); Heap::Base *o = allocData(size); - o->setVtable(ManagedType::staticVTable()); + InternalClass *ic = ManagedType::defaultInternalClass(engine); + ic = ic->changeVTable(ManagedType::staticVTable()); + o->internalClass = ic; + Q_ASSERT(o->internalClass && o->internalClass->vtable); return static_cast<typename ManagedType::Data *>(o); } template <typename ObjectType> typename ObjectType::Data *allocateObject(InternalClass *ic) { - Heap::Object *o = allocObjectWithMemberData(align(sizeof(typename ObjectType::Data)), ic->size); - o->setVtable(ObjectType::staticVTable()); + Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size); o->internalClass = ic; + Q_ASSERT(o->internalClass && o->internalClass->vtable); + Q_ASSERT(ic->vtable == ObjectType::staticVTable()); return static_cast<typename ObjectType::Data *>(o); } @@ -241,11 +246,12 @@ public: typename ObjectType::Data *allocateObject() { InternalClass *ic = ObjectType::defaultInternalClass(engine); - Heap::Object *o = allocObjectWithMemberData(align(sizeof(typename ObjectType::Data)), ic->size); - o->setVtable(ObjectType::staticVTable()); - Object *prototype = ObjectType::defaultPrototype(engine); + ic = ic->changeVTable(ObjectType::staticVTable()); + ic = ic->changePrototype(ObjectType::defaultPrototype(engine)->d()); + Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size); o->internalClass = ic; - o->prototype = prototype->d(); + Q_ASSERT(o->internalClass && o->internalClass->vtable); + Q_ASSERT(o->internalClass->prototype == ObjectType::defaultPrototype(engine)->d()); return static_cast<typename ObjectType::Data *>(o); } @@ -253,8 +259,9 @@ public: typename ManagedType::Data *allocWithStringData(std::size_t unmanagedSize, Arg1 arg1) { typename ManagedType::Data *o = reinterpret_cast<typename ManagedType::Data *>(allocString(unmanagedSize)); - o->setVtable(ManagedType::staticVTable()); - o->init(this, arg1); + o->internalClass = ManagedType::defaultInternalClass(engine); + Q_ASSERT(o->internalClass && o->internalClass->vtable); + o->init(arg1); return o; } @@ -272,7 +279,8 @@ public: { Scope scope(engine); Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - t->d_unchecked()->prototype = prototype->d(); + Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : 0)); + Q_UNUSED(prototype); t->d_unchecked()->init(); return t->d(); } @@ -282,7 +290,8 @@ public: { Scope scope(engine); Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - t->d_unchecked()->prototype = prototype->d(); + Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : 0)); + Q_UNUSED(prototype); t->d_unchecked()->init(arg1); return t->d(); } @@ -292,7 +301,8 @@ public: { Scope scope(engine); Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - t->d_unchecked()->prototype = prototype->d(); + Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : 0)); + Q_UNUSED(prototype); t->d_unchecked()->init(arg1, arg2); return t->d(); } @@ -302,7 +312,8 @@ public: { Scope scope(engine); Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - t->d_unchecked()->prototype = prototype->d(); + Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : 0)); + Q_UNUSED(prototype); t->d_unchecked()->init(arg1, arg2, arg3); return t->d(); } @@ -312,7 +323,8 @@ public: { Scope scope(engine); Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - t->d_unchecked()->prototype = prototype->d(); + Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : 0)); + Q_UNUSED(prototype); t->d_unchecked()->init(arg1, arg2, arg3, arg4); return t->d(); } @@ -433,7 +445,7 @@ protected: /// expects size to be aligned Heap::Base *allocString(std::size_t unmanagedSize); Heap::Base *allocData(std::size_t size); - Heap::Object *allocObjectWithMemberData(std::size_t size, uint nMembers); + Heap::Object *allocObjectWithMemberData(const QV4::VTable *vtable, uint nMembers); #ifdef DETAILED_MM_STATS void willAllocate(std::size_t size); diff --git a/src/qml/memory/qv4mmdefs_p.h b/src/qml/memory/qv4mmdefs_p.h index db0ffe11a2..ef93971ab8 100644 --- a/src/qml/memory/qv4mmdefs_p.h +++ b/src/qml/memory/qv4mmdefs_p.h @@ -59,6 +59,10 @@ QT_BEGIN_NAMESPACE namespace QV4 { +struct MarkStack; + +typedef void(*ClassDestroyStatsCallback)(const char *); + /* * Chunks are the basic structure containing GC managed objects. * @@ -175,7 +179,7 @@ struct Chunk { return usedSlots; } - void sweep(); + bool sweep(); void freeAll(); void sortIntoBins(HeapItem **bins, uint nBins); @@ -256,33 +260,6 @@ Q_STATIC_ASSERT(sizeof(HeapItem) == Chunk::SlotSize); Q_STATIC_ASSERT(QT_POINTER_SIZE*8 == Chunk::Bits); Q_STATIC_ASSERT((1 << Chunk::BitShift) == Chunk::Bits); -// 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; - quint32 hasException = false; -#if QT_POINTER_SIZE == 8 - quint8 padding[4]; -#endif - MemoryManager *memoryManager = 0; - Runtime runtime; -}; -#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 diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index d0d9f080da..f3a39313c1 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -895,7 +895,7 @@ ReturnedValue NamedNodeMap::get(const Managed *m, String *name, bool *hasPropert const NamedNodeMap *r = static_cast<const NamedNodeMap *>(m); QV4::ExecutionEngine *v4 = r->engine(); - name->makeIdentifier(v4); + name->makeIdentifier(); if (name->equals(v4->id_length())) return Primitive::fromInt32(r->d()->list().count()).asReturnedValue(); @@ -940,7 +940,7 @@ ReturnedValue NodeList::get(const Managed *m, String *name, bool *hasProperty) const NodeList *r = static_cast<const NodeList *>(m); QV4::ExecutionEngine *v4 = r->engine(); - name->makeIdentifier(v4); + name->makeIdentifier(); if (name->equals(v4->id_length())) return Primitive::fromInt32(r->d()->d->children.count()).asReturnedValue(); diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h index 0313d8e33b..4c0ba338d8 100644 --- a/src/qml/qtqmlglobal_p.h +++ b/src/qml/qtqmlglobal_p.h @@ -59,4 +59,10 @@ #define Q_QML_PRIVATE_EXPORT Q_QML_EXPORT +#if !defined(QT_QMLDEVTOOLS_LIB) && !defined(QT_BUILD_QMLDEVTOOLS_LIB) +# define Q_QML_AUTOTEST_EXPORT Q_AUTOTEST_EXPORT +#else +# define Q_QML_AUTOTEST_EXPORT +#endif + #endif // QTQMLGLOBAL_P_H diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index cbf0f69093..7607c19374 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -287,6 +287,7 @@ void QQmlConnections::connectSignals() int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex(); QQmlBoundSignal *signal = new QQmlBoundSignal(target, signalIndex, this, qmlEngine(this)); + signal->setEnabled(d->enabled); QQmlBoundSignalExpression *expression = ctxtdata ? new QQmlBoundSignalExpression(target, signalIndex, diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index b9d312d41f..d4aa2e19ed 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -61,14 +61,13 @@ public: V4_DEFINE_EXTENSION(QQmlAdaptorModelEngineData, engineData) -static QV4::ReturnedValue get_index(QV4::CallContext *ctx) +static void get_index(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, ctx->thisObject().as<QQmlDelegateModelItemObject>()); + QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); if (!o) - return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object")); + RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"))); - return QV4::Encode(o->d()->item->index); + RETURN_RESULT(QV4::Encode(o->d()->item->index)); } template <typename T, typename M> static void setModelDataType(QMetaObjectBuilder *builder, M *metaType) @@ -195,19 +194,18 @@ public: dataType->watchedRoles += newRoles; } - static QV4::ReturnedValue get_hasModelChildren(QV4::CallContext *ctx) + static void get_hasModelChildren(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, ctx->thisObject().as<QQmlDelegateModelItemObject>()); + QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); if (!o) - return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object")); + RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"))); const QQmlAdaptorModel *const model = static_cast<QQmlDMCachedModelData *>(o->d()->item)->type->model; if (o->d()->item->index >= 0 && *model) { const QAbstractItemModel * const aim = model->aim(); - return QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex))); + RETURN_RESULT(QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex)))); } else { - return QV4::Encode(false); + RETURN_RESULT(QV4::Encode(false)); } } @@ -583,27 +581,25 @@ public: } } - static QV4::ReturnedValue get_modelData(QV4::CallContext *ctx) + static void get_modelData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, ctx->thisObject().as<QQmlDelegateModelItemObject>()); + QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); if (!o) - return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object")); + RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"))); - return scope.engine->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData); + RETURN_RESULT(scope.engine->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData)); } - static QV4::ReturnedValue set_modelData(QV4::CallContext *ctx) + static void set_modelData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, ctx->thisObject().as<QQmlDelegateModelItemObject>()); + QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>()); if (!o) - return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object")); - if (!ctx->argc()) - return ctx->engine()->throwTypeError(); + RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"))); + if (!callData->argc) + RETURN_RESULT(scope.engine->throwTypeError()); - static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData(scope.engine->toVariant(ctx->args()[0], QVariant::Invalid)); - return QV4::Encode::undefined(); + static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData(scope.engine->toVariant(callData->args[0], QVariant::Invalid)); + RETURN_RESULT(QV4::Encode::undefined()); } QV4::ReturnedValue get() |