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:
parent
8caa74dce2
commit
0e0a0bbb00
1
main.cpp
1
main.cpp
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -133,6 +133,8 @@ struct ExecutionContext
|
|||
return arguments[index];
|
||||
return Value::undefinedValue();
|
||||
}
|
||||
|
||||
void mark();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
41
qv4managed.h
41
qv4managed.h
|
|
@ -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
100
qv4mm.cpp
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
6
qv4mm.h
6
qv4mm.h
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
26
qv4string.h
26
qv4string.h
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue