Garbage collection for Strings

Let String inherit from Managed, and manage them through
the same garbage collector as for regular objects.

Change-Id: Iae1e047daeff683519d52beddcaca7824a01bb27
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
This commit is contained in:
Lars Knoll 2013-01-28 16:46:09 +01:00
parent 8caa74dce2
commit 0e0a0bbb00
25 changed files with 281 additions and 191 deletions

View File

@ -399,6 +399,7 @@ int main(int argc, char *argv[])
/*strictMode =*/ false, /*inheritContext =*/ false);
if (!f)
continue;
vm.globalCode = f;
ctx->strictMode = f->isStrict;
if (debugger)

View File

@ -245,6 +245,7 @@ inline ALUFunction aluOpFunction(IR::AluOp op)
InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module)
: EvalInstructionSelection(engine, module)
, _function(0)
, _vmFunction(0)
, _block(0)
, _codeStart(0)
, _codeNext(0)
@ -269,6 +270,7 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function)
uchar *codeEnd = codeStart + codeSize;
qSwap(_function, function);
qSwap(_vmFunction, vmFunction);
qSwap(block, _block);
qSwap(patches, _patches);
qSwap(addrs, _addrs);
@ -295,10 +297,11 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function)
patchJumpAddresses();
vmFunction->code = VME::exec;
vmFunction->codeData = squeezeCode();
_vmFunction->code = VME::exec;
_vmFunction->codeData = squeezeCode();
qSwap(_function, function);
qSwap(_vmFunction, vmFunction);
qSwap(block, _block);
qSwap(patches, _patches);
qSwap(addrs, _addrs);
@ -323,7 +326,7 @@ void InstructionSelection::callProperty(IR::Temp *base, const QString &name, IR:
// call the property on the loaded base
Instruction::CallProperty call;
call.base = getParam(base);
call.name = engine()->newString(name);
call.name = identifier(name);
prepareCallArgs(args, call.argc, call.args);
call.result = getResultParam(result);
addInstruction(call);
@ -345,7 +348,7 @@ void InstructionSelection::constructActivationProperty(IR::Name *func,
IR::Temp *result)
{
Instruction::CreateActivationProperty create;
create.name = engine()->newString(*func->id);
create.name = identifier(*func->id);
prepareCallArgs(args, create.argc, create.args);
create.result = getResultParam(result);
addInstruction(create);
@ -355,7 +358,7 @@ void InstructionSelection::constructProperty(IR::Temp *base, const QString &name
{
Instruction::CreateProperty create;
create.base = getParam(base);
create.name = engine()->newString(name);
create.name = identifier(name);
prepareCallArgs(args, create.argc, create.args);
create.result = getResultParam(result);
addInstruction(create);
@ -390,7 +393,7 @@ void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTem
void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp)
{
Instruction::LoadValue load;
load.value = Instr::Param::createValue(VM::Value::fromString(engine()->newString(str)));
load.value = Instr::Param::createValue(VM::Value::fromString(identifier(str)));
load.result = getResultParam(targetTemp);
addInstruction(load);
}
@ -409,7 +412,7 @@ void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *target
void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp)
{
Instruction::LoadName load;
load.name = engine()->newString(name);
load.name = identifier(name);
load.result = getResultParam(temp);
addInstruction(load);
}
@ -418,7 +421,7 @@ void InstructionSelection::setActivationProperty(IR::Expr *source, const QString
{
Instruction::StoreName store;
store.source = getParam(source);
store.name = engine()->newString(targetName);
store.name = identifier(targetName);
addInstruction(store);
}
@ -436,7 +439,7 @@ void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR::
{
Instruction::LoadProperty load;
load.base = getParam(base);
load.name = engine()->newString(name);
load.name = identifier(name);
load.result = getResultParam(target);
addInstruction(load);
}
@ -445,7 +448,7 @@ void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, c
{
Instruction::StoreProperty store;
store.base = getParam(targetBase);
store.name = engine()->newString(targetName);
store.name = identifier(targetName);
store.source = getParam(source);
addInstruction(store);
}
@ -532,7 +535,7 @@ void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, c
if (op) {
Instruction::InplaceNameOp ieo;
ieo.alu = op;
ieo.name = engine()->newString(targetName);
ieo.name = identifier(targetName);
ieo.source = getParam(sourceExpr);
addInstruction(ieo);
}
@ -585,7 +588,7 @@ void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR:
Instruction::InplaceMemberOp imo;
imo.alu = op;
imo.base = getParam(targetBase);
imo.member = engine()->newString(targetName);
imo.member = identifier(targetName);
imo.source = getParam(source);
addInstruction(imo);
}
@ -676,7 +679,7 @@ void InstructionSelection::visitRet(IR::Ret *s)
void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result)
{
Instruction::CallActivationProperty call;
call.name = engine()->newString(*func->id);
call.name = identifier(*func->id);
prepareCallArgs(args, call.argc, call.args);
call.result = getResultParam(result);
addInstruction(call);
@ -686,7 +689,7 @@ void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString
{
Instruction::CallBuiltinTypeofMember call;
call.base = getParam(base);
call.member = engine()->identifier(name);
call.member = identifier(name);
call.result = getResultParam(result);
addInstruction(call);
}
@ -703,7 +706,7 @@ void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *
void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result)
{
Instruction::CallBuiltinTypeofName call;
call.name = engine()->identifier(name);
call.name = identifier(name);
call.result = getResultParam(result);
addInstruction(call);
}
@ -720,7 +723,7 @@ void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString
{
Instruction::CallBuiltinDeleteMember call;
call.base = getParam(base);
call.member = engine()->newString(name);
call.member = identifier(name);
call.result = getResultParam(result);
addInstruction(call);
}
@ -737,7 +740,7 @@ void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *
void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result)
{
Instruction::CallBuiltinDeleteName call;
call.name = engine()->newString(name);
call.name = identifier(name);
call.result = getResultParam(result);
addInstruction(call);
}
@ -754,7 +757,7 @@ void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const
{
Instruction::CallBuiltinPostDecMember call;
call.base = getParam(base);
call.member = engine()->identifier(name);
call.member = identifier(name);
call.result = getResultParam(result);
addInstruction(call);
}
@ -771,7 +774,7 @@ void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR:
void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result)
{
Instruction::CallBuiltinPostDecName call;
call.name = engine()->identifier(name);
call.name = identifier(name);
call.result = getResultParam(result);
addInstruction(call);
}
@ -788,7 +791,7 @@ void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const
{
Instruction::CallBuiltinPostIncMember call;
call.base = getParam(base);
call.member = engine()->identifier(name);
call.member = identifier(name);
call.result = getResultParam(result);
addInstruction(call);
}
@ -805,7 +808,7 @@ void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR:
void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result)
{
Instruction::CallBuiltinPostIncName call;
call.name = engine()->identifier(name);
call.name = identifier(name);
call.result = getResultParam(result);
addInstruction(call);
}
@ -878,7 +881,7 @@ void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &
{
Instruction::CallBuiltinDeclareVar call;
call.isDeletable = deletable;
call.varName = engine()->newString(name);
call.varName = identifier(name);
addInstruction(call);
}
@ -886,7 +889,7 @@ void InstructionSelection::callBuiltinDefineGetterSetter(IR::Temp *object, const
{
Instruction::CallBuiltinDefineGetterSetter call;
call.object = getParam(object);
call.name = engine()->newString(name);
call.name = identifier(name);
call.getter = getParam(getter);
call.setter = getParam(setter);
addInstruction(call);
@ -896,7 +899,7 @@ void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QSt
{
Instruction::CallBuiltinDefineProperty call;
call.object = getParam(object);
call.name = engine()->newString(name);
call.name = identifier(name);
call.value = getParam(value);
addInstruction(call);
}
@ -963,3 +966,10 @@ uchar *InstructionSelection::squeezeCode() const
::memcpy(squeezed, _codeStart, codeSize);
return squeezed;
}
VM::String *InstructionSelection::identifier(const QString &s)
{
VM::String *str = engine()->identifier(s);
_vmFunction->identifiers.append(str);
return str;
}

View File

@ -131,7 +131,10 @@ private:
void patchJumpAddresses();
uchar *squeezeCode() const;
VM::String *identifier(const QString &s);
IR::Function *_function;
VM::Function *_vmFunction;
IR::BasicBlock *_block;
QHash<IR::BasicBlock *, QVector<ptrdiff_t> > _patches;

View File

@ -39,6 +39,7 @@
**
****************************************************************************/
#include <qmljs_engine.h>
#include <qmljs_value.h>
#include <qv4object.h>
#include <qv4objectproto.h>
#include <qv4arrayobject.h>
@ -59,36 +60,16 @@
namespace QQmlJS {
namespace VM {
class StringPool
{
QHash<QString, String*> strings;
public:
~StringPool()
{
qDeleteAll(strings);
}
String *newString(const QString &s)
{
QHash<QString, String*>::const_iterator it = strings.find(s);
if (it != strings.end())
return it.value();
String *str = new String(s);
strings.insert(s, str);
return str;
}
};
ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
: memoryManager(new QQmlJS::VM::MemoryManager)
, iselFactory(factory)
, debugger(0)
, globalObject(Value::nullValue())
, globalCode(0)
, exception(Value::nullValue())
{
MemoryManager::GCBlocker gcBlocker(memoryManager);
stringPool.reset(new StringPool);
memoryManager->setExecutionEngine(this);
rootContext = newContext();
@ -250,12 +231,12 @@ ExecutionContext *ExecutionEngine::newContext()
String *ExecutionEngine::identifier(const QString &s)
{
return stringPool->newString(s);
return new (memoryManager) String(s);
}
Function *ExecutionEngine::newFunction(const QString &name)
{
VM::Function *f = new VM::Function(name);
VM::Function *f = new VM::Function(identifier(name));
functions.append(f);
return f;
}
@ -297,7 +278,7 @@ FunctionObject *ExecutionEngine::newObjectCtor(ExecutionContext *ctx)
String *ExecutionEngine::newString(const QString &s)
{
return stringPool->newString(s);
return new (memoryManager) String(s);
}
Object *ExecutionEngine::newStringObject(ExecutionContext *ctx, const Value &value)
@ -467,5 +448,40 @@ void ExecutionEngine::requireArgumentsAccessors(int n)
}
}
void ExecutionEngine::markObjects()
{
globalObject.mark();
if (globalCode)
globalCode->mark();
exception.mark();
for (int i = 0; i < argumentsAccessors.size(); ++i) {
const PropertyDescriptor &pd = argumentsAccessors.at(i);
pd.get->mark();
pd.set->mark();
}
for (int i = 0; i < functions.size(); ++i)
functions.at(i)->mark();
id_length->mark();
id_prototype->mark();
id_constructor->mark();
id_arguments->mark();
id_caller->mark();
id_this->mark();
id___proto__->mark();
id_enumerable->mark();
id_configurable->mark();
id_writable->mark();
id_value->mark();
id_get->mark();
id_set->mark();
id_eval->mark();
}
} // namespace VM
} // namespace QQmlJS

View File

@ -106,6 +106,8 @@ struct ExecutionEngine
Value globalObject;
VM::Function *globalCode;
Value objectCtor;
Value stringCtor;
Value numberCtor;
@ -167,7 +169,6 @@ struct ExecutionEngine
QVector<ExceptionHandler> unwindStack;
Value exception;
QScopedPointer<class StringPool> stringPool;
QVector<Function *> functions;
ExecutionEngine(EvalISelFactory *iselFactory);
@ -222,6 +223,8 @@ struct ExecutionEngine
Object *newForEachIteratorObject(ExecutionContext *ctx, Object *o);
void requireArgumentsAccessors(int n);
void markObjects();
};
} // namespace VM

View File

@ -277,6 +277,21 @@ bool ExecutionContext::deleteProperty(String *name)
return true;
}
void ExecutionContext::mark()
{
thisObject.mark();
if (function)
function->mark();
for (unsigned arg = 0, lastArg = formalCount(); arg < lastArg; ++arg)
arguments[arg].mark();
for (unsigned local = 0, lastLocal = variableCount(); local < lastLocal; ++local)
locals[local].mark();
if (activation)
activation->mark();
if (withObject)
withObject->mark();
}
void ExecutionContext::setProperty(String *name, Value value)
{
// qDebug() << "=== SetProperty" << value.toString(this)->toQString();
@ -294,14 +309,14 @@ void ExecutionContext::setProperty(String *name, Value value)
return;
}
}
if (strictMode || name == engine->id_this)
if (strictMode || name->isEqualTo(engine->id_this))
throwReferenceError(Value::fromString(name));
engine->globalObject.objectValue()->__put__(this, name, value);
}
Value ExecutionContext::getProperty(String *name)
{
if (name == engine->id_this)
if (name->isEqualTo(engine->id_this))
return thisObject;
bool hasWith = false;
@ -342,7 +357,7 @@ Value ExecutionContext::getProperty(String *name)
Value ExecutionContext::getPropertyNoThrow(String *name)
{
if (name == engine->id_this)
if (name->isEqualTo(engine->id_this))
return thisObject;
bool hasWith = false;
@ -380,7 +395,7 @@ Value ExecutionContext::getPropertyAndBase(String *name, Object **base)
{
*base = 0;
if (name == engine->id_this)
if (name->isEqualTo(engine->id_this))
return thisObject;
bool hasWith = false;

View File

@ -133,6 +133,8 @@ struct ExecutionContext
return arguments[index];
return Value::undefinedValue();
}
void mark();
};

View File

@ -134,10 +134,10 @@ Function *__qmljs_register_function(ExecutionContext *ctx, String *name,
for (unsigned i = 0; i < formalCount; ++i)
if (formals[i])
f->formals.append(formals[i]->toQString());
f->formals.append(formals[i]);
for (unsigned i = 0; i < localCount; ++i)
if (locals[i])
f->locals.append(locals[i]->toQString());
f->locals.append(locals[i]);
return f;
}
@ -741,7 +741,7 @@ Value __qmljs_call_activation_property(ExecutionContext *context, String *name,
Value thisObject = base ? Value::fromObject(base) : Value::undefinedValue();
if (o == context->engine->evalFunction && name == context->engine->id_eval)
if (o == context->engine->evalFunction && name->isEqualTo(context->engine->id_eval))
return static_cast<EvalFunction *>(o)->evalCall(context, thisObject, args, argc, true);
return o->call(context, thisObject, args, argc);

View File

@ -78,7 +78,6 @@ Value Value::toObject(ExecutionContext *ctx) const
return __qmljs_to_object(*this, ctx);
}
bool Value::sameValue(Value other) const {
if (val == other.val)
return true;

View File

@ -240,6 +240,7 @@ struct Value
return b;
}
Managed *asManaged() const;
Object *asObject() const;
FunctionObject *asFunctionObject() const;
BooleanObject *asBooleanObject() const;
@ -256,6 +257,12 @@ struct Value
// Section 9.12
bool sameValue(Value other) const;
void mark() const {
Managed *m = asManaged();
if (m)
m->mark();
}
};
inline Value Value::undefinedValue()
@ -415,6 +422,13 @@ inline uint Value::asArrayLength(ExecutionContext *ctx, bool *ok) const
}
inline Managed *Value::asManaged() const
{
if (isObject() || isString())
return managed();
return 0;
}
inline Object *Value::asObject() const
{
return isObject() ? objectValue() : 0;

View File

@ -121,9 +121,9 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const
void ArgumentsObject::markObjects()
{
for (int i = 0; i < mappedArguments.size(); ++i) {
Object *o = mappedArguments.at(i).asObject();
if (o)
o->mark();
Managed *m = mappedArguments.at(i).asManaged();
if (m)
m->mark();
}
Object::markObjects();
}

View File

@ -634,8 +634,8 @@ void Array::markObjects() const
for (; i < (uint)values.size(); ++i) {
const PropertyDescriptor &pd = values.at(i);
if (pd.isData()) {
if (Object *o = pd.value.asObject())
o->mark();
if (Managed *m = pd.value.asManaged())
m->mark();
} else if (pd.isAccessor()) {
if (pd.get)
pd.get->mark();

View File

@ -69,6 +69,21 @@ Function::~Function()
delete[] codeData;
}
void Function::mark()
{
if (name)
name->mark();
for (int i = 0; i < formals.size(); ++i)
formals.at(i)->mark();
for (int i = 0; i < locals.size(); ++i)
locals.at(i)->mark();
for (int i = 0; i < generatedValues.size(); ++i)
if (Managed *m = generatedValues.at(i).asManaged())
m->mark();
for (int i = 0; i < identifiers.size(); ++i)
identifiers.at(i)->mark();
}
FunctionObject::FunctionObject(ExecutionContext *scope)
: scope(scope)
, name(0)
@ -146,6 +161,18 @@ Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *a
return result;
}
void FunctionObject::markObjects()
{
if (name)
name->mark();
for (uint i = 0; i < formalParameterCount; ++i)
formalParameterList[i]->mark();
for (uint i = 0; i < varCount; ++i)
varList[i]->mark();
scope->mark();
Object::markObjects();
}
Value FunctionObject::call(ExecutionContext *ctx)
{
Q_UNUSED(ctx);
@ -305,25 +332,26 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function)
MemoryManager::GCBlocker gcBlocker(scope->engine->memoryManager);
if (!function->name.isEmpty())
name = scope->engine->identifier(function->name);
name = function->name;
needsActivation = function->needsActivation();
usesArgumentsObject = function->usesArgumentsObject;
strictMode = function->isStrict;
formalParameterCount = function->formals.size();
// ### no need to copy
if (formalParameterCount) {
formalParameterList = new String*[formalParameterCount];
for (unsigned int i = 0; i < formalParameterCount; ++i) {
formalParameterList[i] = scope->engine->identifier(function->formals.at(i));
formalParameterList[i] = function->formals.at(i);
}
}
defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(formalParameterCount));
varCount = function->locals.size();
// ### no need to copy
if (varCount) {
varList = new String*[varCount];
for (unsigned int i = 0; i < varCount; ++i) {
varList[i] = scope->engine->identifier(function->locals.at(i));
varList[i] = function->locals.at(i);
}
}
@ -358,6 +386,12 @@ Value ScriptFunction::call(VM::ExecutionContext *ctx)
return function->code(ctx, function->codeData);
}
void ScriptFunction::markObjects()
{
function->mark();
FunctionObject::markObjects();
}
BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *))
: FunctionObject(scope)
@ -419,10 +453,10 @@ bool BoundFunction::hasInstance(ExecutionContext *ctx, const Value &value)
void BoundFunction::markObjects()
{
target->mark();
if (Object *o = boundThis.asObject())
o->mark();
if (Managed *m = boundThis.asManaged())
m->mark();
for (int i = 0; i < boundArgs.size(); ++i)
if (Object *o = boundArgs.at(i).asObject())
o->mark();
if (Managed *m = boundArgs.at(i).asManaged())
m->mark();
FunctionObject::markObjects();
}

View File

@ -99,22 +99,23 @@ struct TypeErrorPrototype;
struct URIErrorPrototype;
struct Function {
QString name;
String *name;
VM::Value (*code)(VM::ExecutionContext *, const uchar *);
const uchar *codeData;
JSC::MacroAssemblerCodeRef codeRef;
QList<QString> formals;
QList<QString> locals;
QList<String *> formals;
QList<String *> locals;
QVector<Value> generatedValues;
QVector<String *> identifiers;
bool hasNestedFunctions : 1;
bool hasDirectEval : 1;
bool usesArgumentsObject : 1;
bool isStrict : 1;
Function(const QString &name)
Function(String *name)
: name(name)
, code(0)
, codeData(0)
@ -126,6 +127,8 @@ struct Function {
~Function();
inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; }
void mark();
};
struct FunctionObject: Object {
@ -146,6 +149,8 @@ struct FunctionObject: Object {
virtual struct ScriptFunction *asScriptFunction() { return 0; }
virtual void markObjects();
protected:
virtual Value call(ExecutionContext *ctx);
virtual Value construct(ExecutionContext *ctx);
@ -187,6 +192,8 @@ struct ScriptFunction: FunctionObject {
virtual Value call(ExecutionContext *ctx);
virtual ScriptFunction *asScriptFunction() { return this; }
virtual void markObjects();
};
struct BoundFunction: FunctionObject {

View File

@ -606,7 +606,7 @@ void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTem
void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp)
{
Value v = Value::fromString(engine()->newString(str));
Value v = Value::fromString(identifier(str));
_asm->storeValue(v, targetTemp);
}
@ -793,7 +793,9 @@ void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::Ex
String *InstructionSelection::identifier(const QString &s)
{
return engine()->identifier(s);
String *str = engine()->identifier(s);
_vmFunction->identifiers.append(str);
return str;
}
void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result)

View File

@ -46,10 +46,10 @@ VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngin
foreach (const QString *formal, irFunction->formals)
if (formal)
vmFunction->formals.append(*formal);
vmFunction->formals.append(engine->identifier(*formal));
foreach (const QString *local, irFunction->locals)
if (local)
vmFunction->locals.append(*local);
vmFunction->locals.append(engine->identifier(*local));
foreach (IR::Function *function, irFunction->nestedFunctions)
createFunctionMapping(engine, function);

View File

@ -51,6 +51,7 @@ namespace QQmlJS {
namespace VM {
class MemoryManager;
struct String;
struct Object;
struct ObjectPrototype;
struct ExecutionContext;
@ -75,8 +76,19 @@ private:
void operator = (const Managed &other);
protected:
Managed() : markBit(0), inUse(1), extensible(true),
isNonStrictArgumentsObject(false), isBuiltinFunction(false), type(Type_Object), unused(0) { }
Managed()
: markBit(0)
, inUse(1)
, extensible(1)
, isNonStrictArgumentsObject(0)
, isBuiltinFunction(0)
, needsActivation(0)
, usesArgumentsObject(0)
, strictMode(0)
, type(Type_Invalid)
, unused(0)
, stringHash(0)
{}
virtual ~Managed();
public:
@ -87,11 +99,14 @@ public:
if (markBit)
return;
markBit = 1;
markObjects();
if (type != Type_String)
markObjects();
}
enum Type {
Type_Object = 0,
Type_Invalid,
Type_String,
Type_Object,
Type_ArrayObject,
Type_FunctionObject,
Type_BooleanObject,
@ -104,6 +119,7 @@ public:
Type_ForeachIteratorObject
};
String *asString() { return reinterpret_cast<String *>(this); }
Object *asObject() { return reinterpret_cast<Object *>(this); }
ArrayObject *asArrayObject() { return type == Type_ArrayObject ? reinterpret_cast<ArrayObject *>(this) : 0; }
FunctionObject *asFunctionObject() { return type == Type_FunctionObject ? reinterpret_cast<FunctionObject *>(this) : 0; }
@ -120,7 +136,7 @@ public:
bool isStringObject() const { return type == Type_StringObject; }
protected:
virtual void markObjects() = 0;
virtual void markObjects() {}
union {
Managed *nextFree;
@ -134,25 +150,14 @@ protected:
quintptr usesArgumentsObject : 1; // used by FunctionObject
quintptr strictMode : 1; // used by FunctionObject
quintptr type : 4;
#if CPU(X86_64)
quintptr unused : 51;
#elif CPU(X86)
quintptr unused : 19;
#else
#error "implement me"
#endif
quintptr unused : 20;
mutable quintptr stringHash : 32;
};
};
private:
friend class MemoryManager;
friend struct Object;
friend struct ObjectPrototype;
friend class Array;
friend struct ArrayPrototype;
friend struct FunctionObject;
friend struct ExecutionContext;
friend struct ScriptFunction;
};
}

100
qv4mm.cpp
View File

@ -142,6 +142,7 @@ Managed *MemoryManager::alloc(std::size_t size)
qSort(m_d->heapChunks);
char *chunk = (char *)allocation.memory.base();
char *end = chunk + allocation.memory.size() - size;
memset(chunk, 0, allocation.memory.size());
Managed **last = &m_d->smallItems[pos];
while (chunk <= end) {
Managed *o = reinterpret_cast<Managed *>(chunk);
@ -166,13 +167,17 @@ void MemoryManager::scribble(Managed *obj, int c, int size) const
::memset((void *)(obj + 1), c, size - sizeof(Managed));
}
void MemoryManager::mark(const QVector<Managed *> &objects)
void MemoryManager::mark()
{
foreach (Managed *m, objects) {
if (!m)
continue;
m->mark();
}
m_d->engine->markObjects();
for (ExecutionContext *ctxt = engine()->current; ctxt; ctxt = ctxt->parent)
ctxt->mark();
for (QHash<Managed *, uint>::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it)
it.key()->mark();
collectFromStack();
return;
}
@ -240,14 +245,8 @@ void MemoryManager::runGC()
// QTime t; t.start();
// qDebug() << ">>>>>>>>runGC";
QVector<Managed *> roots;
collectRoots(roots);
// std::cerr << "GC: found " << roots.size()
// << " roots in " << t.elapsed()
// << "ms" << std::endl;
// t.restart();
/*std::size_t marks =*/ mark(roots);
mark();
// std::cerr << "GC: marked " << marks
// << " objects in " << t.elapsed()
// << "ms" << std::endl;
@ -322,64 +321,33 @@ void MemoryManager::willAllocate(std::size_t size)
#endif // DETAILED_MM_STATS
void MemoryManager::collectRoots(QVector<Managed *> &roots) const
{
add(roots, m_d->engine->globalObject);
add(roots, m_d->engine->exception);
for (int i = 0; i < m_d->engine->argumentsAccessors.size(); ++i) {
const PropertyDescriptor &pd = m_d->engine->argumentsAccessors.at(i);
add(roots, Value::fromObject(pd.get));
add(roots, Value::fromObject(pd.set));
}
for (ExecutionContext *ctxt = engine()->current; ctxt; ctxt = ctxt->parent) {
add(roots, ctxt->thisObject);
if (ctxt->function)
roots.append(ctxt->function);
for (unsigned arg = 0, lastArg = ctxt->formalCount(); arg < lastArg; ++arg)
add(roots, ctxt->arguments[arg]);
for (unsigned local = 0, lastLocal = ctxt->variableCount(); local < lastLocal; ++local)
add(roots, ctxt->locals[local]);
if (ctxt->activation)
roots.append(ctxt->activation);
if (ctxt->withObject)
roots.append(ctxt->withObject);
}
for (int i = 0; i < m_d->engine->functions.size(); ++i) {
Function* f = m_d->engine->functions.at(i);
for (int k = 0; k < f->generatedValues.count(); ++k)
add(roots, f->generatedValues.at(k));
}
collectRootsOnStack(roots);
for (QHash<Managed *, uint>::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it)
roots.append(it.key());
}
void MemoryManager::collectRootsOnStack(QVector<VM::Managed *> &roots) const
void MemoryManager::collectFromStack() const
{
if (!m_d->heapChunks.count())
return;
Value valueOnStack = Value::undefinedValue();
quintptr valueOnStack = 0;
void* stackTop = 0;
#if USE(PTHREADS)
#if OS(DARWIN)
void* stackTop = 0;
stackTop = pthread_get_stackaddr_np(pthread_self());
quintptr *top = static_cast<quintptr *>(stackTop);
#else
void* stackBottom = 0;
pthread_attr_t attr;
pthread_getattr_np(pthread_self(), &attr);
size_t stackSize = 0;
pthread_attr_getstack(&attr, &stackTop, &stackSize);
#endif
#endif
pthread_attr_getstack(&attr, &stackBottom, &stackSize);
pthread_attr_destroy(&attr);
Value* top = reinterpret_cast<Value*>(stackTop) - 1;
Value* current = (&valueOnStack) + 1;
quintptr *top = static_cast<quintptr *>(stackBottom) + stackSize/sizeof(quintptr);
#endif
#endif
// qDebug() << "stack:" << hex << stackTop << stackSize << (stackTop + stackSize);
quintptr *current = (&valueOnStack) + 1;
// qDebug() << "collectFromStack" << top << current << &valueOnStack;
char** heapChunkBoundaries = (char**)alloca(m_d->heapChunks.count() * 2 * sizeof(char*));
char** heapChunkBoundariesEnd = heapChunkBoundaries + 2 * m_d->heapChunks.count();
@ -391,18 +359,21 @@ void MemoryManager::collectRootsOnStack(QVector<VM::Managed *> &roots) const
}
for (; current < top; ++current) {
Object* possibleObject = current->asObject();
if (!possibleObject)
continue;
char* genericPtr =
#if CPU(X86_64)
reinterpret_cast<char *>((*current) & ~(quint64(Value::Type_Mask) << Value::Tag_Shift));
#else
reinterpret_cast<char *>(*current);
#endif
char* genericPtr = reinterpret_cast<char*>(possibleObject);
if (genericPtr < *heapChunkBoundaries || genericPtr >= *(heapChunkBoundariesEnd - 1))
continue;
int index = qLowerBound(heapChunkBoundaries, heapChunkBoundariesEnd, genericPtr) - heapChunkBoundaries;
// An odd index means the pointer is _before_ the end of a heap chunk and therefore valid.
if (index & 1) {
int size = m_d->heapChunks.at(index >> 1).chunkSize;
Managed *m = possibleObject;
Managed *m = reinterpret_cast<Managed *>(genericPtr);
// qDebug() << " inside" << size << m;
if (((quintptr)m - (quintptr)heapChunkBoundaries[index-1]) % size)
// wrongly aligned value, skip it
@ -412,7 +383,8 @@ void MemoryManager::collectRootsOnStack(QVector<VM::Managed *> &roots) const
// Skip pointers to already freed objects, they are bogus as well
continue;
roots.append(possibleObject);
m->mark();
// qDebug() << " marking";
}
}
}

View File

@ -104,8 +104,6 @@ protected:
void scribble(Managed *obj, int c, int size) const;
void collectRootsOnStack(QVector<Managed *> &roots) const;
ExecutionEngine *engine() const;
#ifdef DETAILED_MM_STATS
@ -113,8 +111,8 @@ protected:
#endif // DETAILED_MM_STATS
private:
void collectRoots(QVector<VM::Managed *> &roots) const;
static void mark(const QVector<Managed *> &objects);
void collectFromStack() const;
void mark();
std::size_t sweep();
std::size_t sweep(char *chunkStart, std::size_t chunkSize, size_t size);

View File

@ -177,10 +177,11 @@ void Object::markObjects()
for (PropertyTable::iterator it = members->begin(), eit = members->end(); it < eit; ++it) {
if (!(*it))
continue;
(*it)->name->mark();
PropertyDescriptor &pd = (*it)->descriptor;
if (pd.isData()) {
if (Object *o = pd.value.asObject())
o->mark();
if (Managed *m = pd.value.asManaged())
m->mark();
} else if (pd.isAccessor()) {
if (pd.get)
pd.get->mark();

View File

@ -104,7 +104,7 @@ struct Object: Managed {
Array array;
Object()
: prototype(0) {}
: prototype(0) { type = Type_Object; }
Object(const Array &a)
: prototype(0), array(a) {}

View File

@ -41,6 +41,7 @@
#include "qv4string.h"
#include "qmljs_runtime.h"
#include <QtCore/QHash>
namespace QQmlJS {
namespace VM {
@ -71,8 +72,8 @@ static uint toArrayIndex(const QChar *ch, const QChar *end)
uint String::asArrayIndexSlow() const
{
if (_hashValue < LargestHashedArrayIndex)
return _hashValue;
if (stringHash < LargestHashedArrayIndex)
return stringHash;
const QChar *ch = _text.constData();
const QChar *end = ch + _text.length();
@ -83,10 +84,10 @@ uint String::toUInt(bool *ok) const
{
*ok = true;
if (_hashValue == InvalidHashValue)
if (stringHash == InvalidHashValue)
createHashValue();
if (_hashValue < LargestHashedArrayIndex)
return _hashValue;
if (stringHash < LargestHashedArrayIndex)
return stringHash;
double d = __qmljs_string_to_number(this);
uint l = (uint)d;
@ -102,10 +103,10 @@ void String::createHashValue() const
const QChar *end = ch + _text.length();
// array indices get their number as hash value, for large numbers we set to INT_MAX
_hashValue = toArrayIndex(ch, end);
if (_hashValue < UINT_MAX) {
if (_hashValue > INT_MAX)
_hashValue = INT_MAX;
stringHash = toArrayIndex(ch, end);
if (stringHash < UINT_MAX) {
if (stringHash > INT_MAX)
stringHash = INT_MAX;
return;
}
@ -116,7 +117,7 @@ void String::createHashValue() const
}
// set highest bit to mark it as a non number
_hashValue = h | 0xf0000000;
stringHash = h | 0xf0000000;
}
}

View File

@ -42,12 +42,15 @@
#define QV4STRING_H
#include <QtCore/qstring.h>
#include <QtCore/QHash>
#include <qv4managed.h>
namespace QQmlJS {
namespace VM {
struct String {
struct String : public Managed {
String(const QString &text)
: _text(text) { type = Type_String; stringHash = InvalidHashValue; }
inline bool isEqualTo(const String *other) const {
if (this == other)
return true;
@ -61,10 +64,10 @@ struct String {
}
inline unsigned hashValue() const {
if (_hashValue == InvalidHashValue)
if (stringHash == InvalidHashValue)
createHashValue();
return _hashValue;
return stringHash;
}
enum {
InvalidArrayIndex = 0xffffffff,
@ -72,26 +75,21 @@ struct String {
InvalidHashValue = 0xffffffff
};
uint asArrayIndex() const {
if (_hashValue == InvalidHashValue)
if (stringHash == InvalidHashValue)
createHashValue();
if (_hashValue > LargestHashedArrayIndex)
if (stringHash > LargestHashedArrayIndex)
return InvalidArrayIndex;
if (_hashValue < LargestHashedArrayIndex)
return _hashValue;
if (stringHash < LargestHashedArrayIndex)
return stringHash;
return asArrayIndexSlow();
}
uint asArrayIndexSlow() const;
uint toUInt(bool *ok) const;
private:
friend class StringPool;
String(const QString &text)
: _text(text), _hashValue(InvalidHashValue) {}
private:
void createHashValue() const;
QString _text;
mutable unsigned _hashValue;
};
}

View File

@ -100,6 +100,12 @@ PropertyDescriptor *StringObject::getIndex(ExecutionContext *ctx, uint index)
return &tmpProperty;
}
void StringObject::markObjects()
{
value.stringValue()->mark();
Object::markObjects();
}
StringCtor::StringCtor(ExecutionContext *scope)
: FunctionObject(scope)

View File

@ -55,6 +55,9 @@ struct StringObject: Object {
virtual QString className() { return QStringLiteral("String"); }
PropertyDescriptor *getIndex(ExecutionContext *ctx, uint index);
protected:
virtual void markObjects();
};
struct StringCtor: FunctionObject