garbage collect InternalClass

Internal classes are now allocated and collected through
the GC. As they are important to the deletion of other
objects (because of the vtable pointer living inside the
internal class), they need to get destroyed after regular
objects have been sweeped. Achieve this by using a separate
block allocator for internal class objects.

Our lookups do often contain pointers to internal classes,
so those need to be marked as well, so we don't accidentally
collect them.

Change-Id: I4762b054361c70c31f79f920f669ea0e8551601f
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Lars Knoll 2018-01-05 15:30:23 +01:00
parent 3932536b59
commit 6002b48c3c
28 changed files with 432 additions and 256 deletions

View File

@ -269,7 +269,7 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr)
Refs collectedRefs;
QV4::ScopedValue v(scope);
QV4::InternalClass *ic = ctxt->internalClass();
QV4::Heap::InternalClass *ic = ctxt->internalClass();
for (uint i = 0; i < ic->size; ++i) {
QString name = ic->nameMap[i]->string;
names.append(name);

View File

@ -491,7 +491,7 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a
collector.collect(&output, QString(), QStringLiteral("this"), thisObject);
QV4::Scoped<QV4::CallContext> callContext(scope, frame->callContext());
if (callContext) {
QV4::InternalClass *ic = callContext->internalClass();
QV4::Heap::InternalClass *ic = callContext->internalClass();
QV4::ScopedValue v(scope);
for (uint i = 0; i < ic->size; ++i) {
QString name = ic->nameMap[i]->string;

View File

@ -96,8 +96,9 @@ static QString cacheFilePath(const QUrl &url)
#endif
CompilationUnit::CompilationUnit(const Unit *unitData)
: data(unitData)
{}
{
data = unitData;
}
#ifndef V4_BOOTSTRAP
CompilationUnit::~CompilationUnit()
@ -157,15 +158,15 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
}
if (data->jsClassTableSize) {
runtimeClasses = (QV4::InternalClass**)malloc(data->jsClassTableSize * sizeof(QV4::InternalClass*));
runtimeClasses = (QV4::Heap::InternalClass **)malloc(data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
// memset the regexps to 0 in case a GC run happens while we're within the loop below
memset(runtimeClasses, 0, data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
for (uint i = 0; i < data->jsClassTableSize; ++i) {
int memberCount = 0;
const CompiledData::JSClassMember *member = data->jsClassAt(i, &memberCount);
QV4::InternalClass *klass = engine->internalClasses(QV4::ExecutionEngine::Class_Object);
runtimeClasses[i] = engine->internalClasses(QV4::ExecutionEngine::Class_Object);
for (int j = 0; j < memberCount; ++j, ++member)
klass = klass->addMember(engine->identifierTable->identifier(runtimeStrings[member->nameOffset]), member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data);
runtimeClasses[i] = klass;
runtimeClasses[i] = runtimeClasses[i]->addMember(engine->identifierTable->identifier(runtimeStrings[member->nameOffset]), member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data);
}
}
@ -244,13 +245,28 @@ void CompilationUnit::unlink()
void CompilationUnit::markObjects(QV4::MarkStack *markStack)
{
for (uint i = 0; i < data->stringTableSize; ++i)
if (runtimeStrings[i])
runtimeStrings[i]->mark(markStack);
if (runtimeStrings) {
for (uint i = 0; i < data->stringTableSize; ++i)
if (runtimeStrings[i])
runtimeStrings[i]->mark(markStack);
}
if (runtimeRegularExpressions) {
for (uint i = 0; i < data->regexpTableSize; ++i)
runtimeRegularExpressions[i].mark(markStack);
}
if (runtimeClasses) {
for (uint i = 0; i < data->jsClassTableSize; ++i)
if (runtimeClasses[i])
runtimeClasses[i]->mark(markStack);
}
for (QV4::Function *f : qAsConst(runtimeFunctions))
if (f && f->internalClass)
f->internalClass->mark(markStack);
if (runtimeLookups) {
for (uint i = 0; i < data->lookupTableSize; ++i)
runtimeLookups[i].markObjects(markStack);
}
}
IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjectIndex)

View File

@ -881,6 +881,8 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnitBase
QV4::Heap::String **runtimeStrings = nullptr; // Array
const Value* constants = nullptr;
QV4::Value *runtimeRegularExpressions = nullptr;
const Unit *data = nullptr;
QV4::Heap::InternalClass **runtimeClasses = nullptr;
};
Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value);
@ -915,8 +917,6 @@ public:
return refCount.load();
}
const Unit *data = nullptr;
// Called only when building QML, when we build the header for JS first and append QML data
QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument);
@ -943,7 +943,6 @@ public:
}
QV4::Lookup *runtimeLookups = nullptr;
QV4::InternalClass **runtimeClasses = nullptr;
QVector<QV4::Function *> runtimeFunctions;
mutable QQmlNullableValue<QUrl> m_url;
mutable QQmlNullableValue<QUrl> m_finalUrl;

View File

@ -199,7 +199,10 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
identifierTable = new IdentifierTable(this);
classes[Class_Empty] = new InternalClass(this);
memset(classes, 0, sizeof(classes));
classes[Class_Empty] = memoryManager->allocIC<InternalClass>();
classes[Class_Empty]->init(this);
classes[Class_String] = classes[Class_Empty]->changeVTable(QV4::String::staticVTable());
classes[Class_MemberData] = classes[Class_Empty]->changeVTable(QV4::MemberData::staticVTable());
classes[Class_SimpleArrayData] = classes[Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable());
@ -246,20 +249,23 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsStrings[String_buffer] = newIdentifier(QStringLiteral("buffer"));
jsStrings[String_lastIndex] = newIdentifier(QStringLiteral("lastIndex"));
InternalClass *ic = classes[Class_Empty]->changeVTable(QV4::Object::staticVTable());
jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic);
Scope scope(this);
Scoped<InternalClass> ic(scope);
ic = classes[Class_Empty]->changeVTable(QV4::Object::staticVTable());
jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic->d());
classes[Class_Object] = ic->changePrototype(objectPrototype()->d());
classes[EngineBase::Class_QmlContextWrapper] = classes[Class_Object]->changeVTable(QV4::QQmlContextWrapper::staticVTable());
ic = newInternalClass(ArrayPrototype::staticVTable(), objectPrototype());
Q_ASSERT(ic->prototype);
Q_ASSERT(ic->d()->prototype);
ic = ic->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable);
Q_ASSERT(ic->prototype);
jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic);
Q_ASSERT(ic->d()->prototype);
jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic->d());
classes[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d());
jsObjects[PropertyListProto] = memoryManager->allocate<PropertyListPrototype>();
InternalClass *argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype());
Scoped<InternalClass> argsClass(scope);
argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype());
argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable);
classes[Class_ArgumentsObject] = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable);
argsClass = newInternalClass(StrictArgumentsObject::staticVTable(), objectPrototype());
@ -273,7 +279,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype());
ic = ic->addMember(id_length(), Attr_ReadOnly);
jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic);
jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic->d());
classes[Class_StringObject] = ic->changePrototype(stringPrototype()->d());
Q_ASSERT(classes[Class_StringObject]->find(id_length()) == Heap::StringObject::LengthPropertyIndex);
@ -285,11 +291,11 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
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>(ic);
jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(ic->d());
ic = newInternalClass(FunctionObject::staticVTable(), functionPrototype());
ic = ic->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index);
Q_ASSERT(index == Heap::FunctionObject::Index_Prototype);
classes[Class_FunctionObject] = ic;
classes[Class_FunctionObject] = ic->d();
ic = ic->addMember(id_name(), Attr_ReadOnly, &index);
Q_ASSERT(index == Heap::ScriptFunction::Index_Name);
ic = ic->changeVTable(ScriptFunction::staticVTable());
@ -298,7 +304,6 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
classes[Class_ObjectProto] = classes[Class_Object]->addMember(id_constructor(), Attr_NotEnumerable, &index);
Q_ASSERT(index == Heap::FunctionObject::Index_ProtoConstructor);
Scope scope(this);
ScopedString str(scope);
classes[Class_RegExp] = classes[Class_Empty]->changeVTable(QV4::RegExp::staticVTable());
ic = newInternalClass(QV4::RegExpObject::staticVTable(), objectPrototype());
@ -312,7 +317,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
Q_ASSERT(index == RegExpObject::Index_IgnoreCase);
ic = ic->addMember((str = newIdentifier(QStringLiteral("multiline"))), Attr_ReadOnly, &index);
Q_ASSERT(index == RegExpObject::Index_Multiline);
jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(ic);
jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(ic->d());
classes[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d());
ic = classes[Class_ArrayObject]->addMember(id_index(), Attr_Data, &index);
@ -326,7 +331,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorObject::Index_FileName);
ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber"))), Attr_Data|Attr_NotEnumerable, &index);
classes[Class_ErrorObject] = ic;
classes[Class_ErrorObject] = ic->d();
Q_ASSERT(index == ErrorObject::Index_LineNumber);
classes[Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorObject::Index_Message);
@ -342,19 +347,20 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
getStackFunction()->defineReadonlyProperty(id_length(), Primitive::fromInt32(0));
jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(classes[Class_ErrorProto]);
jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(classes[Class_ErrorProto]->changePrototype(errorPrototype()->d()));
jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(classes[Class_ErrorProto]->changePrototype(errorPrototype()->d()));
jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(classes[Class_ErrorProto]->changePrototype(errorPrototype()->d()));
jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(classes[Class_ErrorProto]->changePrototype(errorPrototype()->d()));
jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(classes[Class_ErrorProto]->changePrototype(errorPrototype()->d()));
jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(classes[Class_ErrorProto]->changePrototype(errorPrototype()->d()));
ic = classes[Class_ErrorProto]->changePrototype(errorPrototype()->d());
jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(ic->d());
jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(ic->d());
jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(ic->d());
jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(ic->d());
jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(ic->d());
jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(ic->d());
jsObjects[VariantProto] = memoryManager->allocate<VariantPrototype>();
Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d());
#if QT_CONFIG(qml_sequence_object)
ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this));
jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic));
jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic->d()));
#endif
ExecutionContext *global = rootContext();
@ -496,7 +502,6 @@ ExecutionEngine::~ExecutionEngine()
while (!compilationUnits.isEmpty())
(*compilationUnits.begin())->unlink();
internalClasses(Class_Empty)->destroyAll();
delete bumperPointerAllocator;
delete regExpCache;
delete regExpAllocator;
@ -532,14 +537,18 @@ void ExecutionEngine::initRootContext()
jsObjects[IntegerNull] = Encode((int)0);
}
InternalClass *ExecutionEngine::newClass(InternalClass *other)
Heap::InternalClass *ExecutionEngine::newClass(Heap::InternalClass *other)
{
return new InternalClass(other);
Heap::InternalClass *ic = memoryManager->allocIC<InternalClass>();
ic->init(other);
return ic;
}
InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype)
Heap::InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype)
{
return internalClasses(Class_Empty)->changeVTable(vtable)->changePrototype(prototype ? prototype->d() : nullptr);
Scope scope(this);
Scoped<InternalClass> ic(scope, internalClasses(Class_Empty)->changeVTable(vtable));
return ic->changePrototype(prototype ? prototype->d() : nullptr);
}
Heap::Object *ExecutionEngine::newObject()
@ -547,7 +556,7 @@ Heap::Object *ExecutionEngine::newObject()
return memoryManager->allocate<Object>();
}
Heap::Object *ExecutionEngine::newObject(InternalClass *internalClass)
Heap::Object *ExecutionEngine::newObject(Heap::InternalClass *internalClass)
{
return memoryManager->allocObject<Object>(internalClass);
}
@ -624,7 +633,7 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(const QStringList &list)
return memoryManager->allocate<ArrayObject>(list);
}
Heap::ArrayObject *ExecutionEngine::newArrayObject(InternalClass *internalClass)
Heap::ArrayObject *ExecutionEngine::newArrayObject(Heap::InternalClass *internalClass)
{
return memoryManager->allocObject<ArrayObject>(internalClass);
}
@ -916,7 +925,9 @@ void ExecutionEngine::markObjects(MarkStack *markStack)
setter->mark(markStack);
}
InternalClass::markObjects(internalClasses(Class_Empty), markStack);
for (int i = 0; i < NClasses; ++i)
if (classes[i])
classes[i]->mark(markStack);
markStack->drain();
for (auto compilationUnit: compilationUnits) {

View File

@ -400,10 +400,10 @@ public:
int newInternalClassId() { return ++internalClassIdCount; }
InternalClass *newInternalClass(const VTable *vtable, Object *prototype);
Heap::InternalClass *newInternalClass(const VTable *vtable, Object *prototype);
Heap::Object *newObject();
Heap::Object *newObject(InternalClass *internalClass);
Heap::Object *newObject(Heap::InternalClass *internalClass);
Heap::String *newString(const QString &s = QString());
Heap::String *newIdentifier(const QString &text);
@ -415,7 +415,7 @@ public:
Heap::ArrayObject *newArrayObject(int count = 0);
Heap::ArrayObject *newArrayObject(const Value *values, int length);
Heap::ArrayObject *newArrayObject(const QStringList &list);
Heap::ArrayObject *newArrayObject(InternalClass *ic);
Heap::ArrayObject *newArrayObject(Heap::InternalClass *ic);
Heap::ArrayBuffer *newArrayBuffer(const QByteArray &array);
Heap::ArrayBuffer *newArrayBuffer(size_t length);
@ -455,7 +455,7 @@ public:
void initRootContext();
InternalClass *newClass(InternalClass *other);
Heap::InternalClass *newClass(Heap::InternalClass *other);
StackTrace exceptionStackTrace;

View File

@ -115,8 +115,8 @@ struct Q_QML_EXPORT EngineBase {
Class_QmlContextWrapper,
NClasses
};
InternalClass *classes[NClasses];
InternalClass *internalClasses(InternalClassType icType) { return classes[icType]; }
Heap::InternalClass *classes[NClasses];
Heap::InternalClass *internalClasses(InternalClassType icType) { return classes[icType]; }
};
#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
#pragma pack(pop)

View File

@ -329,27 +329,25 @@ inline SyntaxErrorObject *ErrorObject::asSyntaxError()
template <typename T>
Heap::Object *ErrorObject::create(ExecutionEngine *e, const Value &message) {
EngineBase::InternalClassType klass = message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage;
InternalClass *ic = e->internalClasses(klass);
ic = ic->changePrototype(T::defaultPrototype(e)->d());
return e->memoryManager->allocObject<T>(ic, message);
Scope scope(e);
Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d()));
return e->memoryManager->allocObject<T>(ic->d(), 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());
EngineBase::InternalClassType klass = v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage;
InternalClass *ic = e->internalClasses(klass);
ic = ic->changePrototype(T::defaultPrototype(e)->d());
return e->memoryManager->allocObject<T>(ic, v);
Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d()));
return e->memoryManager->allocObject<T>(ic->d(), 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());
EngineBase::InternalClassType klass = v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage;
InternalClass *ic = e->internalClasses(klass);
ic = ic->changePrototype(T::defaultPrototype(e)->d());
return e->memoryManager->allocObject<T>(ic, v, filename, line, column);
Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d()));
return e->memoryManager->allocObject<T>(ic->d(), v, filename, line, column);
}

View File

@ -61,18 +61,18 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit,
, codeRef(nullptr)
, hasQmlDependencies(function->hasQmlDependencies())
{
Q_UNUSED(engine);
internalClass = engine->internalClasses(EngineBase::Class_CallContext);
Scope scope(engine);
Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext));
// first locals
const quint32_le *localsIndices = compiledFunction->localsTable();
for (quint32 i = 0; i < compiledFunction->nLocals; ++i)
internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
ic = ic->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
const quint32_le *formalsIndices = compiledFunction->formalsTable();
for (quint32 i = 0; i < compiledFunction->nFormals; ++i)
internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable);
ic = ic->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable);
internalClass = ic->d();
nFormals = compiledFunction->nFormals;
}

View File

@ -81,7 +81,7 @@ struct Q_QML_EXPORT Function {
JSC::MacroAssemblerCodeRef *codeRef;
// first nArguments names in internalClass are the actual arguments
InternalClass *internalClass;
Heap::InternalClass *internalClass;
uint nFormals;
int interpreterCallCount = 0;
bool hasQmlDependencies;

View File

@ -371,8 +371,7 @@ ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const
const ScriptFunction *f = static_cast<const ScriptFunction *>(fo);
Scope scope(v4);
InternalClass *ic = f->classForConstructor();
ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(ic));
ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(f->classForConstructor()));
ReturnedValue result = Moth::VME::exec(fo, thisObject, argv, argc);
@ -415,19 +414,19 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function
}
}
InternalClass *ScriptFunction::classForConstructor() const
Heap::InternalClass *ScriptFunction::classForConstructor() const
{
const Object *o = d()->protoProperty();
InternalClass *ic = d()->cachedClassForConstructor;
if (ic && ic->prototype == o->d())
return ic;
if (d()->cachedClassForConstructor && d()->cachedClassForConstructor->prototype == o->d())
return d()->cachedClassForConstructor;
ic = engine()->internalClasses(EngineBase::Class_Object);
Scope scope(engine());
Scoped<InternalClass> ic(scope, engine()->internalClasses(EngineBase::Class_Object));
if (o)
ic = ic->changePrototype(o->d());
d()->cachedClassForConstructor = ic;
d()->cachedClassForConstructor.set(scope.engine, ic->d());
return ic;
return ic->d();
}
DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction);

View File

@ -109,14 +109,16 @@ struct IndexedBuiltinFunction : FunctionObject {
uint index;
};
struct ScriptFunction : FunctionObject {
#define ScriptFunctionMembers(class, Member) \
Member(class, Pointer, InternalClass *, cachedClassForConstructor)
DECLARE_HEAP_OBJECT(ScriptFunction, FunctionObject) {
DECLARE_MARKOBJECTS(ScriptFunction)
enum {
Index_Name = FunctionObject::Index_Prototype + 1,
Index_Length
};
void init(QV4::ExecutionContext *scope, Function *function);
QV4::InternalClass *cachedClassForConstructor;
};
#define BoundFunctionMembers(class, Member) \
@ -225,7 +227,7 @@ struct ScriptFunction : FunctionObject {
static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
InternalClass *classForConstructor() const;
Heap::InternalClass *classForConstructor() const;
};

View File

@ -168,6 +168,7 @@ namespace Heap {
struct CallContext;
struct QmlContext;
struct ScriptFunction;
struct InternalClass;
struct BooleanObject;
struct NumberObject;

View File

@ -47,7 +47,7 @@
QT_BEGIN_NAMESPACE
using namespace QV4;
namespace QV4 {
static const uchar prime_deltas[] = {
0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3,
@ -126,40 +126,74 @@ void PropertyHash::detach(bool grow, int classSize)
d = dd;
}
namespace Heap {
InternalClass::InternalClass(ExecutionEngine *engine)
: engine(engine)
, vtable(nullptr)
, prototype(nullptr)
, parent(nullptr)
, size(0)
, extensible(true)
, isSealed(false)
, isFrozen(false)
void InternalClass::init(ExecutionEngine *engine)
{
Base::init();
new (&propertyTable) PropertyHash();
new (&nameMap) SharedInternalClassData<Identifier *>();
new (&propertyData) SharedInternalClassData<PropertyAttributes>();
new (&transitions) std::vector<Transition>();
this->engine = engine;
vtable = QV4::InternalClass::staticVTable();
// prototype = nullptr;
// parent = nullptr;
// size = 0;
extensible = true;
isFrozen = false;
isSealed = false;
isUsedAsProto = false;
id = engine->newInternalClassId();
// Also internal classes need an internal class pointer. Simply make it point to itself
internalClass.set(engine, this);
}
InternalClass::InternalClass(QV4::InternalClass *other)
: engine(other->engine)
, vtable(other->vtable)
, prototype(other->prototype)
, parent(other)
, propertyTable(other->propertyTable)
, nameMap(other->nameMap)
, propertyData(other->propertyData)
, size(other->size)
, extensible(other->extensible)
, isSealed(other->isSealed)
, isFrozen(other->isFrozen)
, isUsedAsProto(other->isUsedAsProto)
void InternalClass::init(Heap::InternalClass *other)
{
Q_ASSERT(!isFrozen);
Base::init();
Q_ASSERT(!other->isFrozen);
new (&propertyTable) PropertyHash(other->propertyTable);
new (&nameMap) SharedInternalClassData<Identifier *>(other->nameMap);
new (&propertyData) SharedInternalClassData<PropertyAttributes>(other->propertyData);
new (&transitions) std::vector<Transition>();
engine = other->engine;
vtable = other->vtable;
prototype = other->prototype;
parent = other;
size = other->size;
extensible = other->extensible;
isSealed = other->isSealed;
isFrozen = other->isFrozen;
isUsedAsProto = other->isUsedAsProto;
id = engine->newInternalClassId();
internalClass.set(engine, other->internalClass);
}
static void insertHoleIntoPropertyData(Object *object, int idx)
void InternalClass::destroy()
{
#ifndef QT_NO_DEBUG
for (const auto &t : transitions) {
Q_ASSERT(!t.lookup || !t.lookup->isMarked());
}
#endif
if (parent && parent->engine && parent->isMarked())
parent->removeChildEntry(this);
propertyTable.~PropertyHash();
nameMap.~SharedInternalClassData<Identifier *>();
propertyData.~SharedInternalClassData<PropertyAttributes>();
transitions.~vector<Transition>();
engine = nullptr;
Base::destroy();
}
static void insertHoleIntoPropertyData(QV4::Object *object, int idx)
{
Heap::Object *o = object->d();
ExecutionEngine *v4 = o->internalClass->engine;
@ -168,7 +202,7 @@ static void insertHoleIntoPropertyData(Object *object, int idx)
o->setProperty(v4, i, *o->propertyData(i - 1));
}
static void removeFromPropertyData(Object *object, int idx, bool accessor = false)
static void removeFromPropertyData(QV4::Object *object, int idx, bool accessor = false)
{
Heap::Object *o = object->d();
ExecutionEngine *v4 = o->internalClass->engine;
@ -180,20 +214,22 @@ static void removeFromPropertyData(Object *object, int idx, bool accessor = fals
o->setProperty(v4, size + 1, Primitive::undefinedValue());
}
void InternalClass::changeMember(Object *object, String *string, PropertyAttributes data, uint *index)
void InternalClass::changeMember(QV4::Object *object, QV4::String *string, PropertyAttributes data, uint *index)
{
uint idx;
InternalClass *oldClass = object->internalClass();
InternalClass *newClass = oldClass->changeMember(string->identifier(), data, &idx);
Heap::InternalClass *oldClass = object->internalClass();
Heap::InternalClass *newClass = oldClass->changeMember(string->identifier(), data, &idx);
if (index)
*index = idx;
uint oldSize = oldClass->size;
object->setInternalClass(newClass);
if (newClass->size > oldClass->size) {
Q_ASSERT(newClass->size == oldClass->size + 1);
// don't use oldClass anymore, it could be GC'ed
if (newClass->size > oldSize) {
Q_ASSERT(newClass->size == oldSize + 1);
insertHoleIntoPropertyData(object, idx);
} else if (newClass->size < oldClass->size) {
Q_ASSERT(newClass->size == oldClass->size - 1);
} else if (newClass->size < oldSize) {
Q_ASSERT(newClass->size == oldSize - 1);
removeFromPropertyData(object, idx + 1);
}
}
@ -218,7 +254,7 @@ static void addDummyEntry(InternalClass *newClass, PropertyHash::Entry e)
++newClass->size;
}
InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttributes data, uint *index)
Heap::InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttributes data, uint *index)
{
data.resolve();
uint idx = find(identifier);
@ -228,7 +264,7 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri
*index = idx;
if (data == propertyData.at(idx))
return this;
return static_cast<Heap::InternalClass *>(this);
Transition temp = { { identifier }, nullptr, (int)data.flags() };
Transition &t = lookupOrInsertTransition(temp);
@ -236,7 +272,7 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri
return t.lookup;
// create a new class and add it to the tree
InternalClass *newClass = engine->newClass(this);
Heap::InternalClass *newClass = engine->newClass(this);
if (data.isAccessor() != propertyData.at(idx).isAccessor()) {
// this changes the layout of the class, so we need to rebuild the data
newClass->propertyTable = PropertyHash();
@ -271,8 +307,10 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri
return newClass;
}
InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
{
Scope scope(engine);
ScopedValue protectThis(scope, this);
if (proto)
proto->setUsedAsProto();
Q_ASSERT(prototype != proto);
@ -286,7 +324,7 @@ InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
return t.lookup;
// create a new class and add it to the tree
InternalClass *newClass = engine->newClass(this);
Heap::InternalClass *newClass = engine->newClass(this);
newClass->prototype = proto;
t.lookup = newClass;
@ -294,7 +332,7 @@ InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
return newClass;
}
InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
{
Q_ASSERT(vtable != vt);
@ -306,7 +344,7 @@ InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
return t.lookup;
// create a new class and add it to the tree
InternalClass *newClass = engine->newClass(this);
Heap::InternalClass *newClass = engine->newClass(this);
newClass->vtable = vt;
t.lookup = newClass;
@ -315,7 +353,7 @@ InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
return newClass;
}
InternalClass *InternalClass::nonExtensible()
Heap::InternalClass *InternalClass::nonExtensible()
{
if (!extensible)
return this;
@ -325,7 +363,7 @@ InternalClass *InternalClass::nonExtensible()
if (t.lookup)
return t.lookup;
InternalClass *newClass = engine->newClass(this);
Heap::InternalClass *newClass = engine->newClass(this);
newClass->extensible = false;
t.lookup = newClass;
@ -333,7 +371,7 @@ InternalClass *InternalClass::nonExtensible()
return newClass;
}
void InternalClass::addMember(Object *object, String *string, PropertyAttributes data, uint *index)
void InternalClass::addMember(QV4::Object *object, QV4::String *string, PropertyAttributes data, uint *index)
{
data.resolve();
object->internalClass()->engine->identifierTable->identifier(string);
@ -343,20 +381,20 @@ void InternalClass::addMember(Object *object, String *string, PropertyAttributes
}
uint idx;
InternalClass *newClass = object->internalClass()->addMemberImpl(string->identifier(), data, &idx);
Heap::InternalClass *newClass = object->internalClass()->addMemberImpl(string->identifier(), data, &idx);
if (index)
*index = idx;
object->setInternalClass(newClass);
}
InternalClass *InternalClass::addMember(String *string, PropertyAttributes data, uint *index)
Heap::InternalClass *InternalClass::addMember(QV4::String *string, PropertyAttributes data, uint *index)
{
engine->identifierTable->identifier(string);
return addMember(string->identifier(), data, index);
}
InternalClass *InternalClass::addMember(Identifier *identifier, PropertyAttributes data, uint *index)
Heap::InternalClass *InternalClass::addMember(Identifier *identifier, PropertyAttributes data, uint *index)
{
data.resolve();
@ -366,7 +404,7 @@ InternalClass *InternalClass::addMember(Identifier *identifier, PropertyAttribut
return addMemberImpl(identifier, data, index);
}
InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index)
Heap::InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index)
{
Transition temp = { { identifier }, nullptr, (int)data.flags() };
Transition &t = lookupOrInsertTransition(temp);
@ -378,7 +416,7 @@ InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttr
return t.lookup;
// create a new class and add it to the tree
InternalClass *newClass = engine->newClass(this);
Heap::InternalClass *newClass = engine->newClass(this);
PropertyHash::Entry e = { identifier, newClass->size };
newClass->propertyTable.addEntry(e, newClass->size);
@ -393,9 +431,22 @@ InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttr
return newClass;
}
void InternalClass::removeMember(Object *object, Identifier *id)
void InternalClass::removeChildEntry(InternalClass *child)
{
InternalClass *oldClass = object->internalClass();
Q_ASSERT(engine);
for (auto &t : transitions) {
if (t.lookup == child) {
t.lookup = nullptr;
return;
}
}
Q_UNREACHABLE();
}
void InternalClass::removeMember(QV4::Object *object, Identifier *id)
{
Heap::InternalClass *oldClass = object->internalClass();
Q_ASSERT(oldClass->propertyTable.lookup(id) < oldClass->size);
Transition temp = { { id }, nullptr, Transition::RemoveMember };
@ -403,7 +454,7 @@ void InternalClass::removeMember(Object *object, Identifier *id)
if (!t.lookup) {
// create a new class and add it to the tree
InternalClass *newClass = oldClass->engine->newClass(oldClass);
Heap::InternalClass *newClass = oldClass->engine->newClass(oldClass);
// simply make the entry inaccessible
int idx = newClass->propertyTable.removeIdentifier(id, oldClass->size);
newClass->nameMap.set(idx, nullptr);
@ -417,7 +468,7 @@ void InternalClass::removeMember(Object *object, Identifier *id)
Q_ASSERT(object->internalClass()->size == oldClass->size);
}
uint InternalClass::find(const String *string)
uint InternalClass::find(const QV4::String *string)
{
engine->identifierTable->identifier(string);
const Identifier *id = string->d()->identifier;
@ -429,7 +480,7 @@ uint InternalClass::find(const String *string)
return UINT_MAX;
}
InternalClass *InternalClass::sealed()
Heap::InternalClass *InternalClass::sealed()
{
if (isSealed)
return this;
@ -458,7 +509,7 @@ InternalClass *InternalClass::sealed()
return t.lookup;
}
InternalClass *s = engine->newClass(this);
Heap::InternalClass *s = engine->newClass(this);
for (uint i = 0; i < size; ++i) {
PropertyAttributes attrs = propertyData.at(i);
@ -474,7 +525,7 @@ InternalClass *InternalClass::sealed()
return s;
}
InternalClass *InternalClass::frozen()
Heap::InternalClass *InternalClass::frozen()
{
if (isFrozen)
return this;
@ -504,7 +555,7 @@ InternalClass *InternalClass::frozen()
return t.lookup;
}
InternalClass *f = engine->newClass(this);
Heap::InternalClass *f = engine->newClass(this);
for (uint i = 0; i < size; ++i) {
PropertyAttributes attrs = propertyData.at(i);
@ -523,9 +574,10 @@ InternalClass *InternalClass::frozen()
return f;
}
InternalClass *InternalClass::propertiesFrozen() const
Heap::InternalClass *InternalClass::propertiesFrozen() const
{
InternalClass *frozen = engine->internalClasses(EngineBase::Class_Empty)->changeVTable(vtable);
Scope scope(engine);
Scoped<QV4::InternalClass> frozen(scope, engine->internalClasses(EngineBase::Class_Empty)->changeVTable(vtable));
frozen = frozen->changePrototype(prototype);
for (uint i = 0; i < size; ++i) {
PropertyAttributes attrs = propertyData.at(i);
@ -535,10 +587,10 @@ InternalClass *InternalClass::propertiesFrozen() const
attrs.setConfigurable(false);
frozen = frozen->addMember(nameMap.at(i), attrs);
}
return frozen;
return frozen->d();
}
InternalClass *InternalClass::asProtoClass()
Heap::InternalClass *InternalClass::asProtoClass()
{
if (isUsedAsProto)
return this;
@ -548,7 +600,7 @@ InternalClass *InternalClass::asProtoClass()
if (t.lookup)
return t.lookup;
InternalClass *newClass = engine->newClass(this);
Heap::InternalClass *newClass = engine->newClass(this);
newClass->isUsedAsProto = true;
t.lookup = newClass;
@ -556,34 +608,13 @@ InternalClass *InternalClass::asProtoClass()
return newClass;
}
void InternalClass::destroyAll()
{
std::vector<InternalClass *> destroyStack;
destroyStack.reserve(64);
destroyStack.push_back(this);
while (!destroyStack.empty()) {
InternalClass *next = destroyStack.back();
destroyStack.pop_back();
Q_ASSERT(next->engine);
next->engine = nullptr;
for (size_t i = 0; i < next->transitions.size(); ++i) {
Q_ASSERT(next->transitions.at(i).lookup);
destroyStack.push_back(next->transitions.at(i).lookup);
}
delete next;
}
}
static void updateProtoUsage(Heap::Object *o, InternalClass *ic)
static void updateProtoUsage(Heap::Object *o, Heap::InternalClass *ic)
{
if (ic->prototype == o)
ic->id = ic->engine->newInternalClassId();
for (auto &t : ic->transitions) {
Q_ASSERT(t.lookup);
updateProtoUsage(o, t.lookup);
if (t.lookup)
updateProtoUsage(o, t.lookup);
}
}
@ -591,30 +622,23 @@ static void updateProtoUsage(Heap::Object *o, InternalClass *ic)
void InternalClass::updateProtoUsage(Heap::Object *o)
{
Q_ASSERT(isUsedAsProto);
InternalClass *ic = engine->internalClasses(EngineBase::Class_Empty);
Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_Empty);
Q_ASSERT(!ic->prototype);
::updateProtoUsage(o, ic);
Heap::updateProtoUsage(o, ic);
}
void InternalClass::updateInternalClassIdRecursive()
{
id = engine->newInternalClassId();
for (auto &t : transitions) {
Q_ASSERT(t.lookup);
t.lookup->updateInternalClassIdRecursive();
}
}
void InternalClass::markObjects(InternalClass *ic, MarkStack *stack)
void InternalClass::markObjects(Heap::Base *b, MarkStack *stack)
{
Heap::InternalClass *ic = static_cast<Heap::InternalClass *>(b);
if (ic->prototype)
ic->prototype->mark(stack);
if (ic->parent)
ic->parent->mark(stack);
}
}
for (auto &t : ic->transitions) {
Q_ASSERT(t.lookup);
markObjects(t.lookup, stack);
}
}
QT_END_NAMESPACE

View File

@ -54,6 +54,7 @@
#include <QHash>
#include <private/qv4identifier_p.h>
#include <private/qv4heap_p.h>
QT_BEGIN_NAMESPACE
@ -240,7 +241,7 @@ struct InternalClassTransition
const VTable *vtable;
Heap::Object *prototype;
};
InternalClass *lookup;
Heap::InternalClass *lookup;
int flags;
enum {
// range 0-0xff is reserved for attribute changes
@ -260,12 +261,14 @@ struct InternalClassTransition
{ return id < other.id || (id == other.id && flags < other.flags); }
};
struct InternalClass {
int id = 0; // unique across the engine, gets changed also when proto chain changes
namespace Heap {
struct InternalClass : Base {
int id; // unique across the engine, gets changed also when proto chain changes
ExecutionEngine *engine;
const VTable *vtable;
Heap::Object *prototype;
InternalClass *parent = nullptr;
InternalClass *parent;
PropertyHash propertyTable; // id to valueIndex
SharedInternalClassData<Identifier *> nameMap;
@ -279,27 +282,21 @@ struct InternalClass {
bool extensible;
bool isSealed;
bool isFrozen;
bool isUsedAsProto = false;
bool isUsedAsProto;
void init(ExecutionEngine *engine);
void init(InternalClass *other);
void destroy();
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);
Q_REQUIRED_RESULT InternalClass *addMember(String *string, PropertyAttributes data, uint *index = nullptr);
static void addMember(QV4::Object *object, QV4::String *string, PropertyAttributes data, uint *index);
Q_REQUIRED_RESULT InternalClass *addMember(QV4::String *string, PropertyAttributes data, uint *index = nullptr);
Q_REQUIRED_RESULT InternalClass *addMember(Identifier *identifier, PropertyAttributes data, uint *index = nullptr);
Q_REQUIRED_RESULT InternalClass *changeMember(Identifier *identifier, PropertyAttributes data, uint *index = nullptr);
static void changeMember(Object *object, String *string, PropertyAttributes data, uint *index = nullptr);
static void removeMember(Object *object, Identifier *id);
uint find(const String *string);
static void changeMember(QV4::Object *object, QV4::String *string, PropertyAttributes data, uint *index = nullptr);
static void removeMember(QV4::Object *object, Identifier *id);
uint find(const QV4::String *string);
uint find(const Identifier *id)
{
uint index = propertyTable.lookup(id);
@ -315,23 +312,38 @@ struct InternalClass {
Q_REQUIRED_RESULT InternalClass *asProtoClass();
void destroyAll();
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);
}
void updateProtoUsage(Heap::Object *o);
static void markObjects(InternalClass *ic, MarkStack *stack);
static void markObjects(Heap::Base *ic, MarkStack *stack);
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);
void updateInternalClassIdRecursive();
void removeChildEntry(InternalClass *child);
friend struct ExecutionEngine;
InternalClass(ExecutionEngine *engine);
InternalClass(InternalClass *other);
};
inline
void Base::markObjects(Base *b, MarkStack *stack)
{
b->internalClass->mark(stack);
}
}
}
QT_END_NAMESPACE

View File

@ -347,7 +347,6 @@ ReturnedValue Lookup::getterProtoAccessor(Lookup *l, ExecutionEngine *engine, co
return static_cast<const FunctionObject *>(getter)->call(&object, nullptr, 0);
}
l->getter = getterTwoClasses;
return getterTwoClasses(l, engine, object);
}
@ -442,7 +441,7 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value
Scope scope(engine);
ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
InternalClass *c = object->internalClass();
Heap::InternalClass *c = object->internalClass();
uint idx = c->find(name);
if (idx != UINT_MAX) {
if (object->isArrayObject() && idx == Heap::ArrayObject::LengthPropertyIndex) {
@ -599,4 +598,53 @@ bool Lookup::arrayLengthSetter(Lookup *, ExecutionEngine *engine, Value &object,
return true;
}
void Lookup::markObjects(MarkStack *stack)
{
if (getter == getterGeneric ||
getter == getterTwoClasses ||
getter == getterFallback) {
;
} else if (getter == getter0MemberData ||
getter == getter0Inline ||
getter == getterAccessor) {
objectLookup.ic->mark(stack);
} else if (getter == getterProto) {
;
} else if (getter == getter0Inlinegetter0Inline ||
getter == getter0Inlinegetter0MemberData ||
getter == getter0MemberDatagetter0MemberData) {
objectLookupTwoClasses.ic->mark(stack);
objectLookupTwoClasses.ic2->mark(stack);
} else if (getter == getterProtoTwoClasses ||
getter == getterProtoAccessor ||
getter == getterProtoAccessorTwoClasses) {
} else if (getter == primitiveGetterProto ||
getter == primitiveGetterAccessor) {
primitiveLookup.proto->mark(stack);
} else if (getter == stringLengthGetter) {
;
} else if (globalGetter == globalGetterGeneric ||
globalGetter == globalGetterProto ||
globalGetter == globalGetterProtoAccessor) {
;
} else if (setter == setterGeneric ||
setter == setterTwoClasses ||
setter == setterFallback) {
;
} else if (setter == setter0 ||
setter == setter0Inline) {
objectLookup.ic->mark(stack);
} else if (setter == setter0setter0) {
objectLookupTwoClasses.ic->mark(stack);
objectLookupTwoClasses.ic2->mark(stack);
} else if (setter == setterInsert) {
insertionLookup.newClass->mark(stack);
} else if (setter == arrayLengthSetter) {
;
}
}
QT_END_NAMESPACE

View File

@ -73,7 +73,7 @@ struct Lookup {
};
union {
struct {
InternalClass *ic;
Heap::InternalClass *ic;
int offset;
} objectLookup;
struct {
@ -81,8 +81,8 @@ struct Lookup {
int icIdentifier;
} protoLookup;
struct {
InternalClass *ic;
InternalClass *ic2;
Heap::InternalClass *ic;
Heap::InternalClass *ic2;
int offset;
int offset2;
} objectLookupTwoClasses;
@ -100,7 +100,7 @@ struct Lookup {
Heap::Object *proto;
} primitiveLookup;
struct {
InternalClass *newClass;
Heap::InternalClass *newClass;
int icIdentifier;
int offset;
} insertionLookup;
@ -144,6 +144,8 @@ struct Lookup {
static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool arrayLengthSetter(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
void markObjects(MarkStack *stack);
};
Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value);

View File

@ -63,6 +63,8 @@ const VTable Managed::static_vtbl =
isEqualTo
};
DEFINE_MANAGED_VTABLE(InternalClass);
QString Managed::className() const
{
@ -114,6 +116,9 @@ QString Managed::className() const
case Type_ForeachIteratorObject:
s = "__ForeachIterator";
break;
case Type_InternalClass:
s = "__InternalClass";
break;
case Type_RegExp:
s = "__RegExp";
break;

View File

@ -92,14 +92,14 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {}
QV4::Heap::DataClass *dptr = d_unchecked(); \
dptr->_checkIsInitialized(); \
return dptr; \
} \
Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value);
}
#define V4_MANAGED(DataClass, superClass) \
private: \
DataClass() Q_DECL_EQ_DELETE; \
Q_DISABLE_COPY(DataClass) \
V4_MANAGED_ITSELF(DataClass, superClass)
V4_MANAGED_ITSELF(DataClass, superClass) \
Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value);
#define Q_MANAGED_TYPE(type) \
public: \
@ -154,7 +154,7 @@ const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname,
QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF
#define V4_INTERNALCLASS(c) \
static QV4::InternalClass *defaultInternalClass(QV4::EngineBase *e) \
static Heap::InternalClass *defaultInternalClass(QV4::EngineBase *e) \
{ return e->internalClasses(QV4::EngineBase::Class_##c); }
struct Q_QML_PRIVATE_EXPORT Managed : Value
@ -193,6 +193,7 @@ public:
Type_MathObject,
Type_ExecutionContext,
Type_InternalClass,
Type_ForeachIteratorObject,
Type_RegExp,
@ -200,7 +201,7 @@ public:
};
Q_MANAGED_TYPE(Invalid)
InternalClass *internalClass() const { return d()->internalClass; }
Heap::InternalClass *internalClass() const { return d()->internalClass; }
const VTable *vtable() const { return d()->internalClass->vtable; }
inline ExecutionEngine *engine() const { return internalClass()->engine; }
@ -255,6 +256,32 @@ inline const Object *Value::as() const {
return objectValue();
}
struct InternalClass : Managed
{
V4_MANAGED_ITSELF(InternalClass, Managed)
Q_MANAGED_TYPE(InternalClass)
V4_INTERNALCLASS(Empty)
V4_NEEDS_DESTROY
Q_REQUIRED_RESULT Heap::InternalClass *changeVTable(const VTable *vt) {
return d()->changeVTable(vt);
}
Q_REQUIRED_RESULT Heap::InternalClass *changePrototype(Heap::Object *proto) {
return d()->changePrototype(proto);
}
Q_REQUIRED_RESULT Heap::InternalClass *addMember(QV4::String *string, PropertyAttributes data, uint *index = 0) {
return d()->addMember(string, data, index);
}
Q_REQUIRED_RESULT Heap::InternalClass *addMember(Identifier *identifier, PropertyAttributes data, uint *index = 0) {
return d()->addMember(identifier, data, index);
}
void operator =(Heap::InternalClass *ic) {
Value::operator=(ic);
}
};
}

View File

@ -57,9 +57,9 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(Object);
void Object::setInternalClass(InternalClass *ic)
void Object::setInternalClass(Heap::InternalClass *ic)
{
d()->internalClass = ic;
d()->internalClass.set(engine(), ic);
if (ic->isUsedAsProto)
ic->updateProtoUsage(d());
Q_ASSERT(ic && ic->vtable);
@ -89,7 +89,7 @@ void Object::setProperty(uint index, const Property *p)
void Heap::Object::setUsedAsProto()
{
internalClass = internalClass->asProtoClass();
internalClass.set(internalClass->engine, internalClass->asProtoClass());
}
bool Object::setPrototype(Object *proto)
@ -121,7 +121,7 @@ ReturnedValue Object::getValue(const Value &thisObject, const Value &v, Property
bool Object::putValue(uint memberIndex, const Value &value)
{
QV4::InternalClass *ic = internalClass();
Heap::InternalClass *ic = internalClass();
if (ic->engine->hasException)
return false;
@ -228,6 +228,7 @@ void Object::defineReadonlyConfigurableProperty(String *name, const Value &value
void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack)
{
Base::markObjects(b, stack);
Object *o = static_cast<Object *>(b);
if (o->memberData)
o->memberData->mark(stack);
@ -248,7 +249,7 @@ void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack)
void Object::insertMember(String *s, const Property *p, PropertyAttributes attributes)
{
uint idx;
InternalClass::addMember(this, s, attributes, &idx);
Heap::InternalClass::addMember(this, s, attributes, &idx);
if (attributes.isAccessor()) {
setProperty(idx + GetterOffset, p->value);
@ -776,7 +777,7 @@ bool Object::internalDeleteProperty(String *name)
uint memberIdx = internalClass()->find(name->identifier());
if (memberIdx != UINT_MAX) {
if (internalClass()->propertyData[memberIdx].isConfigurable()) {
InternalClass::removeMember(this, name->identifier());
Heap::InternalClass::removeMember(this, name->identifier());
return true;
}
return false;
@ -832,7 +833,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const
}
if (attrs.hasWritable() && !attrs.isWritable()) {
cattrs.setWritable(false);
InternalClass::changeMember(this, engine->id_length(), cattrs);
Heap::InternalClass::changeMember(this, engine->id_length(), cattrs);
}
if (!succeeded)
return false;
@ -980,7 +981,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *
current->merge(cattrs, p, attrs);
if (member) {
InternalClass::changeMember(this, member, cattrs);
Heap::InternalClass::changeMember(this, member, cattrs);
setProperty(index, current);
} else {
setArrayAttributes(index, cattrs);

View File

@ -222,7 +222,7 @@ struct Q_QML_EXPORT Object: Managed {
SetterOffset = 1
};
void setInternalClass(InternalClass *ic);
void setInternalClass(Heap::InternalClass *ic);
const Value *propertyData(uint index) const { return d()->propertyData(index); }

View File

@ -1217,8 +1217,8 @@ ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *value
ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4::Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)
{
Scope scope(engine);
QV4::InternalClass *klass = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeClasses[classId];
ScopedObject o(scope, engine->newObject(klass));
Scoped<InternalClass> klass(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeClasses[classId]);
ScopedObject o(scope, engine->newObject(klass->d()));
{
bool needSparseArray = arrayGetterSetterCountAndFlags >> 30;
@ -1226,7 +1226,7 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4::
o->initSparseArray();
}
for (uint i = 0; i < klass->size; ++i)
for (uint i = 0; i < klass->d()->size; ++i)
o->setProperty(i, *args++);
if (arrayValueCount > 0) {
@ -1259,13 +1259,13 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4::
QV4::ReturnedValue Runtime::method_createMappedArgumentsObject(ExecutionEngine *engine)
{
Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext);
QV4::InternalClass *ic = engine->internalClasses(EngineBase::Class_ArgumentsObject);
Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_ArgumentsObject);
return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue();
}
QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine *engine)
{
QV4::InternalClass *ic = engine->internalClasses(EngineBase::Class_StrictArgumentsObject);
Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_StrictArgumentsObject);
return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue();
}

View File

@ -54,6 +54,7 @@ using namespace QV4;
void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack)
{
Base::markObjects(that, markStack);
String *s = static_cast<String *>(that);
if (s->subtype < StringType_Complex)
return;

View File

@ -351,9 +351,9 @@ void Heap::TypedArray::init(Type t)
Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type t)
{
QV4::InternalClass *ic = e->internalClasses(EngineBase::Class_Empty)->changeVTable(staticVTable());
ic = ic->changePrototype(e->typedArrayPrototype[t].d());
return e->memoryManager->allocObject<TypedArray>(ic, t);
Scope scope(e);
Scoped<InternalClass> ic(scope, e->newInternalClass(staticVTable(), e->typedArrayPrototype + t));
return e->memoryManager->allocObject<TypedArray>(ic->d(), t);
}
ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProperty)

View File

@ -113,9 +113,9 @@ Q_STATIC_ASSERT(std::is_trivial< V4PointerCheck >::value);
struct Q_QML_EXPORT Base {
void *operator new(size_t) = delete;
static void markObjects(Base *, MarkStack *) {}
static void markObjects(Base *, MarkStack *);
InternalClass *internalClass;
Pointer<InternalClass *, 0> internalClass;
inline ReturnedValue asReturnedValue() const;
inline void mark(QV4::MarkStack *markStack);
@ -195,7 +195,6 @@ Q_STATIC_ASSERT(std::is_standard_layout<Base>::value);
Q_STATIC_ASSERT(offsetof(Base, internalClass) == 0);
Q_STATIC_ASSERT(sizeof(Base) == QT_POINTER_SIZE);
inline
void Base::mark(QV4::MarkStack *markStack)
{

View File

@ -643,8 +643,9 @@ void BlockAllocator::sweep()
void BlockAllocator::freeAll()
{
for (auto c : chunks) {
for (auto c : chunks)
c->freeAll(engine);
for (auto c : chunks) {
Q_V4_PROFILE_DEALLOC(engine, Chunk::DataSize, Profiling::HeapPage);
chunkAllocator->free(c);
}
@ -758,6 +759,7 @@ MemoryManager::MemoryManager(ExecutionEngine *engine)
: engine(engine)
, chunkAllocator(new ChunkAllocator)
, blockAllocator(chunkAllocator, engine)
, icAllocator(chunkAllocator, engine)
, hugeItemAllocator(chunkAllocator, engine)
, m_persistentValues(new PersistentValueStorage(engine))
, m_weakValues(new PersistentValueStorage(engine))
@ -884,7 +886,7 @@ Heap::Object *MemoryManager::allocObjectWithMemberData(const QV4::VTable *vtable
Chunk::clearBit(c->extendsBitmap, index);
}
o->memberData.set(engine, m);
m->internalClass = engine->internalClasses(EngineBase::Class_MemberData);
m->internalClass.set(engine, engine->internalClasses(EngineBase::Class_MemberData));
Q_ASSERT(o->memberData->internalClass);
m->values.alloc = static_cast<uint>((memberSize - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value));
m->values.size = o->memberData->values.alloc;
@ -1017,8 +1019,11 @@ void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPt
}
}
blockAllocator.sweep();
hugeItemAllocator.sweep(classCountPtr);
if (!lastSweep) {
blockAllocator.sweep(/*classCountPtr*/);
hugeItemAllocator.sweep(classCountPtr);
icAllocator.sweep(/*classCountPtr*/);
}
}
bool MemoryManager::shouldRunGC() const
@ -1167,6 +1172,7 @@ void MemoryManager::runGC()
// reset all black bits
blockAllocator.resetBlackBits();
hugeItemAllocator.resetBlackBits();
icAllocator.resetBlackBits();
}
size_t MemoryManager::getUsedMem() const
@ -1193,6 +1199,7 @@ MemoryManager::~MemoryManager()
sweep(/*lastSweep*/true);
blockAllocator.freeAll();
hugeItemAllocator.freeAll();
icAllocator.freeAll();
delete m_weakValues;
#ifdef V4_USE_VALGRIND

View File

@ -160,37 +160,52 @@ public:
{ return (size + Chunk::SlotSize - 1) & ~(Chunk::SlotSize - 1); }
template<typename ManagedType>
inline typename ManagedType::Data *allocManaged(std::size_t size, InternalClass *ic)
inline typename ManagedType::Data *allocManaged(std::size_t size, Heap::InternalClass *ic)
{
Q_STATIC_ASSERT(std::is_trivial< typename ManagedType::Data >::value);
size = align(size);
Heap::Base *o = allocData(size);
o->internalClass = ic;
Q_ASSERT(o->internalClass && o->internalClass->vtable);
typename ManagedType::Data *d = static_cast<typename ManagedType::Data *>(allocData(size));
d->internalClass.set(engine, ic);
Q_ASSERT(d->internalClass && d->internalClass->vtable);
Q_ASSERT(ic->vtable == ManagedType::staticVTable());
return static_cast<typename ManagedType::Data *>(o);
return d;
}
template<typename ManagedType>
inline typename ManagedType::Data *allocManaged(std::size_t size, InternalClass *ic)
{
return allocManaged<ManagedType>(size, ic->d());
}
template<typename ManagedType>
inline typename ManagedType::Data *allocManaged(std::size_t size)
{
return allocManaged<ManagedType>(size, ManagedType::defaultInternalClass(engine));
Scope scope(engine);
Scoped<InternalClass> ic(scope, ManagedType::defaultInternalClass(engine));
return allocManaged<ManagedType>(size, ic);
}
template <typename ObjectType>
typename ObjectType::Data *allocateObject(Heap::InternalClass *ic)
{
Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size);
o->internalClass.set(engine, ic);
Q_ASSERT(o->internalClass.get() && o->vtable());
Q_ASSERT(o->vtable() == ObjectType::staticVTable());
return static_cast<typename ObjectType::Data *>(o);
}
template <typename ObjectType>
typename ObjectType::Data *allocateObject(InternalClass *ic)
{
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);
return allocateObject<ObjectType>(ic->d());
}
template <typename ObjectType>
typename ObjectType::Data *allocateObject()
{
InternalClass *ic = ObjectType::defaultInternalClass(engine);
Scope scope(engine);
Scoped<InternalClass> ic(scope, ObjectType::defaultInternalClass(engine));
ic = ic->changeVTable(ObjectType::staticVTable());
ic = ic->changePrototype(ObjectType::defaultPrototype(engine)->d());
return allocateObject<ObjectType>(ic);
@ -200,18 +215,18 @@ public:
typename ManagedType::Data *allocWithStringData(std::size_t unmanagedSize, Arg1 arg1)
{
typename ManagedType::Data *o = reinterpret_cast<typename ManagedType::Data *>(allocString(unmanagedSize));
o->internalClass = ManagedType::defaultInternalClass(engine);
o->internalClass.set(engine, ManagedType::defaultInternalClass(engine));
Q_ASSERT(o->internalClass && o->internalClass->vtable);
o->init(arg1);
return o;
}
template <typename ObjectType>
typename ObjectType::Data *allocObject(InternalClass *ic)
template <typename ObjectType, typename... Args>
typename ObjectType::Data *allocObject(Heap::InternalClass *ic, Args... args)
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
t->d_unchecked()->init();
t->d_unchecked()->init(args...);
return t->d();
}
@ -253,6 +268,14 @@ public:
// called when a JS object grows itself. Specifically: Heap::String::append
void changeUnmanagedHeapSizeUsage(qptrdiff delta) { unmanagedHeapSize += delta; }
template<typename ManagedType>
typename ManagedType::Data *allocIC()
{
size_t size = align(sizeof(typename ManagedType::Data));
Heap::Base *b = *icAllocator.allocate(size, true);
return static_cast<typename ManagedType::Data *>(b);
}
protected:
/// expects size to be aligned
Heap::Base *allocString(std::size_t unmanagedSize);
@ -270,6 +293,7 @@ public:
QV4::ExecutionEngine *engine;
ChunkAllocator *chunkAllocator;
BlockAllocator blockAllocator;
BlockAllocator icAllocator;
HugeItemAllocator hugeItemAllocator;
PersistentValueStorage *m_persistentValues;
PersistentValueStorage *m_weakValues;

View File

@ -231,7 +231,7 @@ static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object)
if (!instanceOfObject)
return;
QV4::InternalClass *frozen = object->internalClass()->propertiesFrozen();
QV4::Heap::InternalClass *frozen = object->internalClass()->propertiesFrozen();
if (object->internalClass() == frozen)
return;
object->setInternalClass(frozen);