aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@digia.com>2012-12-04 13:40:18 +0100
committerLars Knoll <lars.knoll@digia.com>2012-12-08 04:47:53 +0100
commit5f22fbd7fc4ca6a7f4629cbd34e0fc2e3c1b1cee (patch)
tree31a606540d68674315fbc2c71c76592be2ae3dc8
parent3b3f3bebcd24073455de9f4abf2f0c7712a1c1ee (diff)
Add a MemoryManager, which does GC for the interpreter.
Todo: - stack walking for MASM - fix all TODOs/FIXMEs and hidden treasures (bugs). Change-Id: I36f8cdc3a545df7287ce1df17b3570a9c017865e Reviewed-by: Lars Knoll <lars.knoll@digia.com>
-rw-r--r--main.cpp23
-rw-r--r--moth/moth.pri2
-rw-r--r--moth/qv4mm_moth.cpp81
-rw-r--r--moth/qv4mm_moth.h59
-rw-r--r--moth/qv4vme_moth.cpp43
-rw-r--r--qmljs_engine.cpp136
-rw-r--r--qmljs_engine.h4
-rw-r--r--qmljs_environment.cpp6
-rw-r--r--qmljs_objects.cpp65
-rw-r--r--qmljs_objects.h36
-rw-r--r--qv4array_p.h13
-rw-r--r--qv4codegen.cpp5
-rw-r--r--qv4ecmaobjects.cpp23
-rw-r--r--qv4ir.cpp16
-rw-r--r--qv4ir_p.h3
-rw-r--r--qv4mm.cpp375
-rw-r--r--qv4mm.h184
-rw-r--r--v4.pro6
18 files changed, 972 insertions, 108 deletions
diff --git a/main.cpp b/main.cpp
index 6f6d5126f1..decb799ce6 100644
--- a/main.cpp
+++ b/main.cpp
@@ -53,6 +53,7 @@
#include "qv4syntaxchecker_p.h"
#include "qv4ecmaobjects_p.h"
#include "qv4isel_p.h"
+#include "qv4mm_moth.h"
#include <QtCore>
#include <private/qqmljsengine_p.h>
@@ -144,12 +145,12 @@ int executeLLVMCode(void *codePtr)
void (*code)(VM::ExecutionContext *) = (void (*)(VM::ExecutionContext *)) codePtr;
QScopedPointer<QQmlJS::EvalISelFactory> iSelFactory(new QQmlJS::Moth::ISelFactory);
- VM::ExecutionEngine vm(iSelFactory.data());
+ VM::ExecutionEngine vm(0, iSelFactory.data());
VM::ExecutionContext *ctx = vm.rootContext;
QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue();
globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")),
- QQmlJS::VM::Value::fromObject(new builtins::Print(ctx)));
+ QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::Print(ctx)));
void * buf = __qmljs_create_exception_handler(ctx);
if (setjmp(*(jmp_buf *)buf)) {
@@ -318,13 +319,17 @@ int main(int argc, char *argv[])
#endif // QMLJS_NO_LLVM
case use_masm:
case use_moth: {
+ QScopedPointer<QQmlJS::VM::MemoryManager> mm;
QScopedPointer<QQmlJS::EvalISelFactory> iSelFactory;
- if (mode == use_moth)
+ if (mode == use_moth) {
+ mm.reset(new QQmlJS::Moth::MemoryManager);
iSelFactory.reset(new QQmlJS::Moth::ISelFactory);
- else
+ } else {
+ mm.reset(new QQmlJS::VM::MemoryManagerWithoutGC);
iSelFactory.reset(new QQmlJS::MASM::ISelFactory);
+ }
- QQmlJS::VM::ExecutionEngine vm(iSelFactory.data());
+ QQmlJS::VM::ExecutionEngine vm(mm.data(), iSelFactory.data());
QScopedPointer<QQmlJS::Debugging::Debugger> debugger;
if (enableDebugging)
@@ -335,12 +340,12 @@ int main(int argc, char *argv[])
QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue();
globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")),
- QQmlJS::VM::Value::fromObject(new builtins::Print(ctx)));
+ QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::Print(ctx)));
bool errorInTestHarness = false;
if (!qgetenv("IN_TEST_HARNESS").isEmpty())
globalObject->__put__(ctx, vm.identifier(QStringLiteral("$ERROR")),
- QQmlJS::VM::Value::fromObject(new builtins::TestHarnessError(ctx, errorInTestHarness)));
+ QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::TestHarnessError(ctx, errorInTestHarness)));
foreach (const QString &fn, args) {
QFile file(fn);
@@ -354,7 +359,7 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
- QQmlJS::IR::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode);
+ QScopedPointer<QQmlJS::IR::Function> f(QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode));
if (!f)
continue;
@@ -376,6 +381,8 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
}
+
+ mm->dumpStats();
} return EXIT_SUCCESS;
}
}
diff --git a/moth/moth.pri b/moth/moth.pri
index 4381c9a53b..160c1aac00 100644
--- a/moth/moth.pri
+++ b/moth/moth.pri
@@ -4,10 +4,12 @@ HEADERS += \
$$PWD/qv4isel_moth_p.h \
$$PWD/qv4instr_moth_p.h \
$$PWD/qv4vme_moth_p.h \
+ $$PWD/qv4mm_moth.h
SOURCES += \
$$PWD/qv4isel_moth.cpp \
$$PWD/qv4instr_moth.cpp \
$$PWD/qv4vme_moth.cpp \
+ $$PWD/qv4mm_moth.cpp
#DEFINES += DO_TRACE_INSTR
diff --git a/moth/qv4mm_moth.cpp b/moth/qv4mm_moth.cpp
new file mode 100644
index 0000000000..44e45fe027
--- /dev/null
+++ b/moth/qv4mm_moth.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmljs_engine.h"
+#include "qv4mm_moth.h"
+
+#include <QList>
+
+using namespace QQmlJS;
+using namespace QQmlJS::Moth;
+
+MemoryManager::MemoryManager()
+{
+ stackFrames.reserve(64);
+}
+
+MemoryManager::~MemoryManager()
+{
+}
+
+VM::Value *MemoryManager::allocStackFrame(std::size_t frameSize)
+{
+ std::size_t size = frameSize * sizeof(VM::Value);
+ MMObject *m = alloc(align(size));
+ stackFrames.append(m);
+ return reinterpret_cast<VM::Value *>(&m->data);
+}
+
+void MemoryManager::deallocStackFrame(VM::Value *stackFrame)
+{
+ MMObject *o = toObject(stackFrame);
+ for (int i = stackFrames.size() - 1; i >= 0; --i) {
+ if (stackFrames[i] == o) {
+ stackFrames.remove(i);
+ dealloc(o);
+ return;
+ }
+ }
+
+ Q_UNREACHABLE();
+}
+
+void MemoryManager::collectRootsOnStack(QVector<VM::Object *> &roots) const
+{
+ for (int i = 0, ei = stackFrames.size(); i < ei; ++i) {
+ MMObject *m = stackFrames[i];
+ VM::Value *frame = reinterpret_cast<VM::Value *>(&m->data);
+ std::size_t frameSize = (m->info.size - align(sizeof(MMInfo))) / sizeof(VM::Value);
+ for (std::size_t j = 0; j < frameSize; ++j) {
+ if (VM::Object *o = frame[j].asObject()) {
+ roots.append(o);
+ }
+ }
+ }
+}
diff --git a/moth/qv4mm_moth.h b/moth/qv4mm_moth.h
new file mode 100644
index 0000000000..a2b1ebd556
--- /dev/null
+++ b/moth/qv4mm_moth.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QV4GC_MOTH_H
+#define QV4GC_MOTH_H
+
+#include "qv4mm.h"
+
+#include <QVector>
+
+namespace QQmlJS {
+namespace Moth {
+
+class MemoryManager: public QQmlJS::VM::MemoryManager
+{
+public:
+ MemoryManager();
+ ~MemoryManager();
+
+ VM::Value *allocStackFrame(std::size_t frameSize);
+ void deallocStackFrame(VM::Value *stackFrame);
+
+protected:
+ virtual void collectRootsOnStack(QVector<VM::Object *> &roots) const;
+
+private:
+ QVector<MMObject *> stackFrames;
+};
+
+} // namespace Moth
+} // namespace QQmlJS
+
+#endif // QV4GC_MOTH_H
diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp
index d38d7d7cb7..86b396f422 100644
--- a/moth/qv4vme_moth.cpp
+++ b/moth/qv4vme_moth.cpp
@@ -2,6 +2,7 @@
#include "qv4instr_moth_p.h"
#include "qmljs_value.h"
#include "debugging.h"
+#include "qv4mm_moth.h"
#include <iostream>
@@ -51,7 +52,7 @@ using namespace QQmlJS::Moth;
#endif
-static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVector<VM::Value> &stack, int index)
+static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, VM::Value* stack, int index)
{
#ifdef DO_TRACE_INSTR
const char *kind;
@@ -87,25 +88,32 @@ static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVecto
int off = index - context->variableCount();
Q_ASSERT(off >= 0);
- Q_ASSERT(off < stack.size());
- return stack.data() + off;
+ return stack + off;
}
}
class FunctionState: public Debugging::FunctionState
{
public:
- FunctionState(QQmlJS::VM::ExecutionContext *context, QVector<VM::Value> *stack, const uchar **code)
+ FunctionState(QQmlJS::VM::ExecutionContext *context, const uchar **code)
: Debugging::FunctionState(context)
- , stack(stack)
+ , stack(0)
+ , stackSize(0)
, code(code)
{}
- virtual VM::Value *temp(unsigned idx) { return stack->data() + idx; }
+ ~FunctionState()
+ { if (stack) static_cast<MemoryManager *>(context()->engine->memoryManager)->deallocStackFrame(stack); }
+
+ virtual VM::Value *temp(unsigned idx) { return stack + idx; }
+
+ void setStack(VM::Value *stack, unsigned stackSize)
+ { this->stack = stack; this->stackSize = stackSize; }
private:
- QVector<VM::Value> *stack;
+ VM::Value *stack;
+ unsigned stackSize;
const uchar **code;
};
@@ -130,8 +138,9 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co
}
#endif
- QVector<VM::Value> stack;
- FunctionState state(context, &stack, &code);
+ VM::Value *stack = 0;
+ unsigned stackSize = 0;
+ FunctionState state(context, &code);
#ifdef MOTH_THREADED_INTERPRETER
const Instr *genericInstr = reinterpret_cast<const Instr *>(code);
@@ -191,7 +200,9 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co
MOTH_BEGIN_INSTR(Push)
TRACE(inline, "stack size: %u", instr.value);
- stack.resize(instr.value);
+ stackSize = instr.value;
+ stack = static_cast<MemoryManager *>(context->engine->memoryManager)->allocStackFrame(stackSize);
+ state.setStack(stack, stackSize);
MOTH_END_INSTR(Push)
MOTH_BEGIN_INSTR(CallValue)
@@ -207,20 +218,20 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co
#endif // DO_TRACE_INSTR
quint32 argStart = instr.args - context->variableCount();
TRACE(Call, "value index = %d, argStart = %d, argc = %d, result temp index = %d", instr.destIndex, argStart, instr.argc, instr.targetTempIndex);
- VM::Value *args = stack.data() + argStart;
+ VM::Value *args = stack + argStart;
TEMP(instr.targetTempIndex) = __qmljs_call_value(context, VM::Value::undefinedValue(), TEMP(instr.destIndex), args, instr.argc);
MOTH_END_INSTR(CallValue)
MOTH_BEGIN_INSTR(CallProperty)
quint32 argStart = instr.args - context->variableCount();
- VM::Value *args = stack.data() + argStart;
+ VM::Value *args = stack + argStart;
VM::Value base = TEMP(instr.baseTemp);
TEMP(instr.targetTempIndex) = __qmljs_call_property(context, base, instr.name, args, instr.argc);
MOTH_END_INSTR(CallProperty)
MOTH_BEGIN_INSTR(CallBuiltin)
quint32 argStart = instr.args - context->variableCount();
- VM::Value *args = stack.data() + argStart;
+ VM::Value *args = stack + argStart;
void *buf;
switch (instr.builtin) {
case Instr::instr_callBuiltin::builtin_typeof:
@@ -300,20 +311,20 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co
MOTH_BEGIN_INSTR(CreateValue)
quint32 argStart = instr.args - context->variableCount();
- VM::Value *args = stack.data() + argStart;
+ VM::Value *args = stack + argStart;
TEMP(instr.targetTempIndex) = __qmljs_construct_value(context, TEMP(instr.func), args, instr.argc);
MOTH_END_INSTR(CreateValue)
MOTH_BEGIN_INSTR(CreateProperty)
quint32 argStart = instr.args - context->variableCount();
- VM::Value *args = stack.data() + argStart;
+ VM::Value *args = stack + argStart;
TEMP(instr.targetTempIndex) = __qmljs_construct_property(context, TEMP(instr.base), instr.name, args, instr.argc);
MOTH_END_INSTR(CreateProperty)
MOTH_BEGIN_INSTR(CreateActivationProperty)
TRACE(inline, "property name = %s, argc = %d", instr.name->toQString().toUtf8().constData(), instr.argc);
quint32 argStart = instr.args - context->variableCount();
- VM::Value *args = stack.data() + argStart;
+ VM::Value *args = stack + argStart;
TEMP(instr.targetTempIndex) = __qmljs_construct_activation_property(context, instr.name, args, instr.argc);
MOTH_END_INSTR(CreateActivationProperty)
diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp
index 661781001d..ea6b32a079 100644
--- a/qmljs_engine.cpp
+++ b/qmljs_engine.cpp
@@ -41,6 +41,7 @@
#include <qmljs_engine.h>
#include <qmljs_objects.h>
#include <qv4ecmaobjects_p.h>
+#include "qv4mm.h"
namespace QQmlJS {
namespace VM {
@@ -49,6 +50,9 @@ struct StringPool
{
QHash<QString, String*> strings;
+ ~StringPool()
+ { qDeleteAll(strings.values()); }
+
String *newString(const QString &s)
{
QHash<QString, String*>::const_iterator it = strings.find(s);
@@ -60,11 +64,18 @@ struct StringPool
}
};
-ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
- : iselFactory(factory)
+ExecutionEngine::ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory *factory)
+ : memoryManager(memoryManager)
+ , iselFactory(factory)
, debugger(0)
+ , globalObject(Value::nullValue())
+ , exception(Value::nullValue())
{
+ MemoryManager::GCBlocker gcBlocker(memoryManager);
+
stringPool = new StringPool;
+ memoryManager->setStringPool(stringPool);
+ memoryManager->setExecutionEngine(this);
rootContext = newContext();
rootContext->init(this);
@@ -75,21 +86,21 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
id_arguments = identifier(QStringLiteral("arguments"));
id___proto__ = identifier(QStringLiteral("__proto__"));
- objectPrototype = new ObjectPrototype();
- stringPrototype = new StringPrototype(rootContext);
- numberPrototype = new NumberPrototype();
- booleanPrototype = new BooleanPrototype();
- arrayPrototype = new ArrayPrototype();
- datePrototype = new DatePrototype();
- functionPrototype = new FunctionPrototype(rootContext);
- regExpPrototype = new RegExpPrototype();
- errorPrototype = new ErrorPrototype();
- evalErrorPrototype = new EvalErrorPrototype(rootContext);
- rangeErrorPrototype = new RangeErrorPrototype(rootContext);
- referenceErrorPrototype = new ReferenceErrorPrototype(rootContext);
- syntaxErrorPrototype = new SyntaxErrorPrototype(rootContext);
- typeErrorPrototype = new TypeErrorPrototype(rootContext);
- uRIErrorPrototype = new URIErrorPrototype(rootContext);
+ objectPrototype = new (memoryManager) ObjectPrototype();
+ stringPrototype = new (memoryManager) StringPrototype(rootContext);
+ numberPrototype = new (memoryManager) NumberPrototype();
+ booleanPrototype = new (memoryManager) BooleanPrototype();
+ arrayPrototype = new (memoryManager) ArrayPrototype();
+ datePrototype = new (memoryManager) DatePrototype();
+ functionPrototype = new (memoryManager) FunctionPrototype(rootContext);
+ regExpPrototype = new (memoryManager) RegExpPrototype();
+ errorPrototype = new (memoryManager) ErrorPrototype();
+ evalErrorPrototype = new (memoryManager) EvalErrorPrototype(rootContext);
+ rangeErrorPrototype = new (memoryManager) RangeErrorPrototype(rootContext);
+ referenceErrorPrototype = new (memoryManager) ReferenceErrorPrototype(rootContext);
+ syntaxErrorPrototype = new (memoryManager) SyntaxErrorPrototype(rootContext);
+ typeErrorPrototype = new (memoryManager) TypeErrorPrototype(rootContext);
+ uRIErrorPrototype = new (memoryManager) URIErrorPrototype(rootContext);
stringPrototype->prototype = objectPrototype;
numberPrototype->prototype = objectPrototype;
@@ -106,21 +117,21 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
typeErrorPrototype->prototype = errorPrototype;
uRIErrorPrototype->prototype = errorPrototype;
- objectCtor = Value::fromObject(new ObjectCtor(rootContext));
- stringCtor = Value::fromObject(new StringCtor(rootContext));
- numberCtor = Value::fromObject(new NumberCtor(rootContext));
- booleanCtor = Value::fromObject(new BooleanCtor(rootContext));
- arrayCtor = Value::fromObject(new ArrayCtor(rootContext));
- functionCtor = Value::fromObject(new FunctionCtor(rootContext));
- dateCtor = Value::fromObject(new DateCtor(rootContext));
- regExpCtor = Value::fromObject(new RegExpCtor(rootContext));
- errorCtor = Value::fromObject(new ErrorCtor(rootContext));
- evalErrorCtor = Value::fromObject(new EvalErrorCtor(rootContext));
- rangeErrorCtor = Value::fromObject(new RangeErrorCtor(rootContext));
- referenceErrorCtor = Value::fromObject(new ReferenceErrorCtor(rootContext));
- syntaxErrorCtor = Value::fromObject(new SyntaxErrorCtor(rootContext));
- typeErrorCtor = Value::fromObject(new TypeErrorCtor(rootContext));
- uRIErrorCtor = Value::fromObject(new URIErrorCtor(rootContext));
+ objectCtor = Value::fromObject(new (memoryManager) ObjectCtor(rootContext));
+ stringCtor = Value::fromObject(new (memoryManager) StringCtor(rootContext));
+ numberCtor = Value::fromObject(new (memoryManager) NumberCtor(rootContext));
+ booleanCtor = Value::fromObject(new (memoryManager) BooleanCtor(rootContext));
+ arrayCtor = Value::fromObject(new (memoryManager) ArrayCtor(rootContext));
+ functionCtor = Value::fromObject(new (memoryManager) FunctionCtor(rootContext));
+ dateCtor = Value::fromObject(new (memoryManager) DateCtor(rootContext));
+ regExpCtor = Value::fromObject(new (memoryManager) RegExpCtor(rootContext));
+ errorCtor = Value::fromObject(new (memoryManager) ErrorCtor(rootContext));
+ evalErrorCtor = Value::fromObject(new (memoryManager) EvalErrorCtor(rootContext));
+ rangeErrorCtor = Value::fromObject(new (memoryManager) RangeErrorCtor(rootContext));
+ referenceErrorCtor = Value::fromObject(new (memoryManager) ReferenceErrorCtor(rootContext));
+ syntaxErrorCtor = Value::fromObject(new (memoryManager) SyntaxErrorCtor(rootContext));
+ typeErrorCtor = Value::fromObject(new (memoryManager) TypeErrorCtor(rootContext));
+ uRIErrorCtor = Value::fromObject(new (memoryManager) URIErrorCtor(rootContext));
stringCtor.objectValue()->prototype = functionPrototype;
numberCtor.objectValue()->prototype = functionPrototype;
@@ -190,19 +201,19 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
pd.value = Value::fromDouble(INFINITY);
glo->__defineOwnProperty__(rootContext, identifier(QStringLiteral("Infinity")), &pd);
- glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new EvalFunction(rootContext)));
+ glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new (memoryManager) EvalFunction(rootContext)));
// TODO: parseInt [15.1.2.2]
// TODO: parseFloat [15.1.2.3]
- glo->__put__(rootContext, identifier(QStringLiteral("isNaN")), Value::fromObject(new IsNaNFunction(rootContext))); // isNaN [15.1.2.4]
- glo->__put__(rootContext, identifier(QStringLiteral("isFinite")), Value::fromObject(new IsFiniteFunction(rootContext))); // isFinite [15.1.2.5]
+ glo->__put__(rootContext, identifier(QStringLiteral("isNaN")), Value::fromObject(new (memoryManager) IsNaNFunction(rootContext))); // isNaN [15.1.2.4]
+ glo->__put__(rootContext, identifier(QStringLiteral("isFinite")), Value::fromObject(new (memoryManager) IsFiniteFunction(rootContext))); // isFinite [15.1.2.5]
}
ExecutionEngine::~ExecutionEngine()
{
delete globalObject.asObject();
delete rootContext;
- delete stringPool; // the String pointers should get GC-ed.
+ delete stringPool;
}
ExecutionContext *ExecutionEngine::newContext()
@@ -220,14 +231,16 @@ String *ExecutionEngine::identifier(const QString &s)
FunctionObject *ExecutionEngine::newNativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *))
{
- NativeFunction *f = new NativeFunction(scope, name, code);
+ NativeFunction *f = new (memoryManager) NativeFunction(scope, name, code);
f->prototype = scope->engine->functionPrototype;
return f;
}
FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, IR::Function *function)
{
- ScriptFunction *f = new ScriptFunction(scope, function);
+ MemoryManager::GCBlocker gcBlocker(memoryManager);
+
+ ScriptFunction *f = new (memoryManager) ScriptFunction(scope, function);
Object *proto = scope->engine->newObject();
proto->__put__(scope, scope->engine->id_constructor, Value::fromObject(f));
f->__put__(scope, scope->engine->id_prototype, Value::fromObject(proto));
@@ -237,14 +250,14 @@ FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, IR::
Object *ExecutionEngine::newObject()
{
- Object *object = new Object();
+ Object *object = new (memoryManager) Object();
object->prototype = objectPrototype;
return object;
}
FunctionObject *ExecutionEngine::newObjectCtor(ExecutionContext *ctx)
{
- return new ObjectCtor(ctx);
+ return new (memoryManager) ObjectCtor(ctx);
}
String *ExecutionEngine::newString(const QString &s)
@@ -254,76 +267,76 @@ String *ExecutionEngine::newString(const QString &s)
Object *ExecutionEngine::newStringObject(const Value &value)
{
- StringObject *object = new StringObject(value);
+ StringObject *object = new (memoryManager) StringObject(value);
object->prototype = stringPrototype;
return object;
}
FunctionObject *ExecutionEngine::newStringCtor(ExecutionContext *ctx)
{
- return new StringCtor(ctx);
+ return new (memoryManager) StringCtor(ctx);
}
Object *ExecutionEngine::newNumberObject(const Value &value)
{
- NumberObject *object = new NumberObject(value);
+ NumberObject *object = new (memoryManager) NumberObject(value);
object->prototype = numberPrototype;
return object;
}
FunctionObject *ExecutionEngine::newNumberCtor(ExecutionContext *ctx)
{
- return new NumberCtor(ctx);
+ return new (memoryManager) NumberCtor(ctx);
}
Object *ExecutionEngine::newBooleanObject(const Value &value)
{
- Object *object = new BooleanObject(value);
+ Object *object = new (memoryManager) BooleanObject(value);
object->prototype = booleanPrototype;
return object;
}
FunctionObject *ExecutionEngine::newBooleanCtor(ExecutionContext *ctx)
{
- return new BooleanCtor(ctx);
+ return new (memoryManager) BooleanCtor(ctx);
}
Object *ExecutionEngine::newFunctionObject(ExecutionContext *ctx)
{
- Object *object = new FunctionObject(ctx);
+ Object *object = new (memoryManager) FunctionObject(ctx);
object->prototype = functionPrototype;
return object;
}
ArrayObject *ExecutionEngine::newArrayObject()
{
- ArrayObject *object = new ArrayObject();
+ ArrayObject *object = new (memoryManager) ArrayObject();
object->prototype = arrayPrototype;
return object;
}
ArrayObject *ExecutionEngine::newArrayObject(const Array &value)
{
- ArrayObject *object = new ArrayObject(value);
+ ArrayObject *object = new (memoryManager) ArrayObject(value);
object->prototype = arrayPrototype;
return object;
}
FunctionObject *ExecutionEngine::newArrayCtor(ExecutionContext *ctx)
{
- return new ArrayCtor(ctx);
+ return new (memoryManager) ArrayCtor(ctx);
}
Object *ExecutionEngine::newDateObject(const Value &value)
{
- Object *object = new DateObject(value);
+ Object *object = new (memoryManager) DateObject(value);
object->prototype = datePrototype;
return object;
}
FunctionObject *ExecutionEngine::newDateCtor(ExecutionContext *ctx)
{
- return new DateCtor(ctx);
+ return new (memoryManager) DateCtor(ctx);
}
Object *ExecutionEngine::newRegExpObject(const QString &pattern, int flags)
@@ -335,61 +348,60 @@ Object *ExecutionEngine::newRegExpObject(const QString &pattern, int flags)
if (flags & IR::RegExp::RegExp_Multiline)
options |= QRegularExpression::MultilineOption;
- Object *object = new RegExpObject(QRegularExpression(pattern, options), global);
+ Object *object = new (memoryManager) RegExpObject(QRegularExpression(pattern, options), global);
object->prototype = regExpPrototype;
return object;
}
FunctionObject *ExecutionEngine::newRegExpCtor(ExecutionContext *ctx)
{
- return new RegExpCtor(ctx);
+ return new (memoryManager) RegExpCtor(ctx);
}
Object *ExecutionEngine::newErrorObject(const Value &value)
{
- ErrorObject *object = new ErrorObject(value);
+ ErrorObject *object = new (memoryManager) ErrorObject(value);
object->prototype = errorPrototype;
return object;
}
Object *ExecutionEngine::newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message)
{
- SyntaxErrorObject *object = new SyntaxErrorObject(ctx, message);
+ SyntaxErrorObject *object = new (memoryManager) SyntaxErrorObject(ctx, message);
object->prototype = syntaxErrorPrototype;
return object;
}
Object *ExecutionEngine::newReferenceErrorObject(ExecutionContext *ctx, const QString &message)
{
- ReferenceErrorObject *object = new ReferenceErrorObject(ctx, message);
+ ReferenceErrorObject *object = new (memoryManager) ReferenceErrorObject(ctx, message);
object->prototype = referenceErrorPrototype;
return object;
}
Object *ExecutionEngine::newTypeErrorObject(ExecutionContext *ctx, const QString &message)
{
- TypeErrorObject *object = new TypeErrorObject(ctx, message);
+ TypeErrorObject *object = new (memoryManager) TypeErrorObject(ctx, message);
object->prototype = typeErrorPrototype;
return object;
}
Object *ExecutionEngine::newMathObject(ExecutionContext *ctx)
{
- MathObject *object = new MathObject(ctx);
+ MathObject *object = new (memoryManager) MathObject(ctx);
object->prototype = objectPrototype;
return object;
}
Object *ExecutionEngine::newActivationObject()
{
- return new Object();
+ return new (memoryManager) Object();
}
Object *ExecutionEngine::newForEachIteratorObject(Object *o)
{
- return new ForEachIteratorObject(o);
+ return new (memoryManager) ForEachIteratorObject(o);
}
-
} // namespace VM
} // namespace QQmlJS
diff --git a/qmljs_engine.h b/qmljs_engine.h
index 2837adb882..520acab675 100644
--- a/qmljs_engine.h
+++ b/qmljs_engine.h
@@ -68,6 +68,7 @@ struct ErrorObject;
struct ArgumentsObject;
struct ExecutionContext;
struct ExecutionEngine;
+class MemoryManager;
struct ObjectPrototype;
struct StringPrototype;
@@ -87,6 +88,7 @@ struct URIErrorPrototype;
struct ExecutionEngine
{
+ MemoryManager *memoryManager;
EvalISelFactory *iselFactory;
ExecutionContext *current;
ExecutionContext *rootContext;
@@ -148,7 +150,7 @@ struct ExecutionEngine
struct StringPool *stringPool;
- ExecutionEngine(EvalISelFactory *iselFactory);
+ ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory *iselFactory);
~ExecutionEngine();
ExecutionContext *newContext();
diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp
index 0127b04852..0d6b2006ea 100644
--- a/qmljs_environment.cpp
+++ b/qmljs_environment.cpp
@@ -44,6 +44,7 @@
#include <qmljs_environment.h>
#include <qmljs_objects.h>
#include <qv4ecmaobjects_p.h>
+#include "qv4mm.h"
namespace QQmlJS {
namespace VM {
@@ -266,7 +267,6 @@ void ExecutionContext::setProperty(String *name, Value value)
Value ExecutionContext::getProperty(String *name)
{
- PropertyDescriptor tmp;
for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
if (ctx->withObject) {
With *w = ctx->withObject;
@@ -286,7 +286,7 @@ Value ExecutionContext::getProperty(String *name)
if (ctx->activation && ctx->activation->__hasProperty__(ctx, name))
return ctx->activation->__get__(ctx, name);
if (name->isEqualTo(ctx->engine->id_arguments)) {
- Value arguments = Value::fromObject(new ArgumentsObject(this));
+ Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this));
createMutableBinding(ctx->engine->id_arguments, false);
setMutableBinding(this, ctx->engine->id_arguments, arguments);
return arguments;
@@ -345,6 +345,8 @@ void ExecutionContext::throwReferenceError(Value value)
void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc)
{
+ MemoryManager::GCBlocker blockGC(parent->engine->memoryManager);
+
engine = parent->engine;
this->parent = parent;
thisObject = that;
diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp
index 7fdb86aa72..d74a501a7e 100644
--- a/qmljs_objects.cpp
+++ b/qmljs_objects.cpp
@@ -43,6 +43,7 @@
#include "qv4ir_p.h"
#include "qv4isel_p.h"
#include "qv4ecmaobjects_p.h"
+#include "qv4mm.h"
#include <private/qqmljsengine_p.h>
#include <private/qqmljslexer_p.h>
@@ -59,6 +60,28 @@
using namespace QQmlJS::VM;
+
+Managed::~Managed()
+{
+}
+
+void *Managed::operator new(size_t size, MemoryManager *mm)
+{
+ assert(mm);
+
+ return mm->allocManaged(size);
+}
+
+void Managed::operator delete(void *ptr)
+{
+ if (!ptr)
+ return;
+
+ Managed *m = reinterpret_cast<Managed *>(ptr);
+ assert(m->mm);
+ m->mm->deallocManaged(m);
+}
+
//
// Object
//
@@ -107,6 +130,20 @@ bool Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ct
return inplaceBinOp(rhs, name, op, ctx);
}
+void Object::getCollectables(QVector<Object *> &objects)
+{
+ if (prototype)
+ objects.append(prototype);
+
+ if (members) {
+ for (PropertyTable::iterator it = members->begin(), eit = members->end(); it < eit; ++it) {
+ if ((*it)->descriptor.isData())
+ if (Object *o = (*it)->descriptor.value.asObject())
+ objects.append(o);
+ }
+ }
+}
+
// Section 8.12.1
PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *, String *name)
{
@@ -349,6 +386,15 @@ String *ForEachIteratorObject::nextPropertyName()
}
}
+void ForEachIteratorObject::getCollectables(QVector<Object *> &objects)
+{
+ Object::getCollectables(objects);
+ if (object)
+ objects.append(object);
+ if (current)
+ objects.append(current);
+}
+
Value ArrayObject::__get__(ExecutionContext *ctx, String *name)
{
if (name->isEqualTo(ctx->engine->id_length))
@@ -368,6 +414,12 @@ bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContex
return Object::inplaceBinOp(rhs, index, op, ctx);
}
+void ArrayObject::getCollectables(QVector<Object *> &objects)
+{
+ Object::getCollectables(objects);
+ value.getCollectables(objects);
+}
+
bool FunctionObject::hasInstance(ExecutionContext *ctx, const Value &value)
{
if (! value.isObject()) {
@@ -490,7 +542,7 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value
bool directCall = true;
const QString code = args[0].stringValue()->toQString();
- QQmlJS::IR::Function *f = parseSource(context, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode);
+ QScopedPointer<QQmlJS::IR::Function> f(parseSource(context, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode));
if (!f)
return Value::undefinedValue();
@@ -534,6 +586,8 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct
{
using namespace QQmlJS;
+ MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager);
+
VM::ExecutionEngine *vm = ctx->engine;
IR::Module module;
IR::Function *globalCode = 0;
@@ -654,6 +708,13 @@ void ErrorObject::setNameProperty(ExecutionContext *ctx)
__put__(ctx, QLatin1String("name"), Value::fromString(ctx, className()));
}
+void ErrorObject::getCollectables(QVector<Object *> &objects)
+{
+ Object::getCollectables(objects);
+ if (Object *o = value.asObject())
+ objects.append(o);
+}
+
SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message)
: ErrorObject(ctx->argument(0))
, msg(message)
@@ -675,7 +736,6 @@ Value ScriptFunction::construct(VM::ExecutionContext *ctx)
return ctx->thisObject;
}
-
Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name)
{
if (name->isEqualTo(ctx->engine->id_length))
@@ -698,7 +758,6 @@ PropertyDescriptor *ArgumentsObject::__getPropertyDescriptor__(ExecutionContext
return Object::__getPropertyDescriptor__(ctx, name, to_fill);
}
-
NativeFunction::NativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *))
: FunctionObject(scope)
, code(code)
diff --git a/qmljs_objects.h b/qmljs_objects.h
index fc8deed7f9..9daa84132b 100644
--- a/qmljs_objects.h
+++ b/qmljs_objects.h
@@ -75,6 +75,7 @@ struct ErrorObject;
struct ArgumentsObject;
struct ExecutionContext;
struct ExecutionEngine;
+class MemoryManager;
struct ObjectPrototype;
struct StringPrototype;
@@ -92,6 +93,29 @@ struct SyntaxErrorPrototype;
struct TypeErrorPrototype;
struct URIErrorPrototype;
+struct Managed
+{
+private:
+ Managed(const Managed &other);
+ void operator = (const Managed &other);
+
+protected:
+ Managed() {}
+
+public:
+ virtual ~Managed();
+
+ void *operator new(size_t size, MemoryManager *mm);
+ void operator delete(void *ptr);
+
+protected:
+ virtual void getCollectables(QVector<Object *> &objects) = 0;
+
+private:
+ friend class MemoryManager;
+ MemoryManager *mm;
+};
+
struct String {
inline bool isEqualTo(const String *other) const {
if (this == other)
@@ -395,7 +419,7 @@ private:
int _allocated: 27;
};
-struct Object {
+struct Object: Managed {
Object *prototype;
String *klass;
PropertyTable *members;
@@ -440,6 +464,9 @@ struct Object {
Value getValue(ExecutionContext *ctx, PropertyDescriptor *p) const;
bool inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx);
virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx);
+
+protected:
+ virtual void getCollectables(QVector<Object *> &objects);
};
struct ForEachIteratorObject: Object {
@@ -450,6 +477,9 @@ struct ForEachIteratorObject: Object {
virtual QString className() { return QStringLiteral("__ForEachIteratorObject"); }
String *nextPropertyName();
+
+protected:
+ virtual void getCollectables(QVector<Object *> &objects);
};
struct BooleanObject: Object {
@@ -489,6 +519,9 @@ struct ArrayObject: Object {
virtual Value __get__(ExecutionContext *ctx, String *name);
virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx);
+
+protected:
+ virtual void getCollectables(QVector<Object *> &objects);
};
struct FunctionObject: Object {
@@ -591,6 +624,7 @@ struct ErrorObject: Object {
protected:
void setNameProperty(ExecutionContext *ctx);
+ virtual void getCollectables(QVector<Object *> &objects);
};
struct EvalErrorObject: ErrorObject {
diff --git a/qv4array_p.h b/qv4array_p.h
index 20022affe7..22c29f91b5 100644
--- a/qv4array_p.h
+++ b/qv4array_p.h
@@ -74,8 +74,11 @@ public:
Array &other);
inline void push(const Value &value);
+ void getCollectables(QVector<Object *> &objects);
+
private:
- std::deque<Value> *to_vector;
+ typedef std::deque<Value> ToVectorType;
+ ToVectorType *to_vector;
};
class ArrayElementLessThan
@@ -194,6 +197,14 @@ inline void Array::push(const Value &value)
to_vector->push_back(value);
}
+inline void Array::getCollectables(QVector<Object *> &objects)
+{
+ for (ToVectorType::const_iterator it = to_vector->begin(), eit = to_vector->end(); it != eit; ++it) {
+ if (Object *o = it->asObject())
+ objects.append(o);
+ }
+}
+
inline void Array::splice(double start, double deleteCount,
const QVector<Value> &items,
Array &other)
diff --git a/qv4codegen.cpp b/qv4codegen.cpp
index 34c785d634..8b3361b3bb 100644
--- a/qv4codegen.cpp
+++ b/qv4codegen.cpp
@@ -151,7 +151,7 @@ void liveness(IR::Function *function)
computeUseDef(s);
}
- dfs(function->basicBlocks.first(), &V, &blocks);
+ dfs(function->basicBlocks.at(0), &V, &blocks);
bool changed;
do {
@@ -1471,6 +1471,9 @@ void Codegen::linearize(IR::Function *function)
exitBlock->index = trace.size();
trace.append(exitBlock);
+ foreach (IR::BasicBlock *b, function->basicBlocks)
+ if (!trace.contains(b))
+ delete b;
function->basicBlocks = trace;
#ifndef QV4_NO_LIVENESS
diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp
index 607c1f1624..6eca545f48 100644
--- a/qv4ecmaobjects.cpp
+++ b/qv4ecmaobjects.cpp
@@ -42,6 +42,7 @@
#include "qv4ecmaobjects_p.h"
#include "qv4array_p.h"
+#include "qv4mm.h"
#include <QtCore/qnumeric.h>
#include <QtCore/qmath.h>
#include <QtCore/QDateTime>
@@ -1860,6 +1861,8 @@ FunctionCtor::FunctionCtor(ExecutionContext *scope)
// 15.3.2
Value FunctionCtor::construct(ExecutionContext *ctx)
{
+ MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager);
+
QString args;
QString body;
if (ctx->argumentCount > 0)
@@ -1899,7 +1902,7 @@ Value FunctionCtor::construct(ExecutionContext *ctx)
isel->run(irf);
delete isel;
- ctx->thisObject = Value::fromObject(new ScriptFunction(ctx->engine->rootContext, irf));
+ ctx->thisObject = Value::fromObject(ctx->engine->newScriptFunction(ctx->engine->rootContext, irf));
return ctx->thisObject;
}
@@ -2593,7 +2596,7 @@ Value RegExpCtor::construct(ExecutionContext *ctx)
if (!f.isUndefined())
ctx->throwTypeError();
- return Value::fromObject(new RegExpObject(re->value, false));
+ return Value::fromObject(new (ctx->engine->memoryManager) RegExpObject(re->value, false));
}
if (r.isUndefined())
@@ -2623,7 +2626,7 @@ Value RegExpCtor::construct(ExecutionContext *ctx)
if (!re.isValid())
ctx->throwTypeError();
- ctx->thisObject = Value::fromObject(new RegExpObject(re, global));
+ ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) RegExpObject(re, global));
return ctx->thisObject;
}
@@ -2715,7 +2718,7 @@ ErrorCtor::ErrorCtor(ExecutionContext *scope)
Value ErrorCtor::construct(ExecutionContext *ctx)
{
- ctx->thisObject = Value::fromObject(new ErrorObject(ctx->argument(0)));
+ ctx->thisObject = Value::fromObject(ctx->engine->newErrorObject(ctx->argument(0)));
return ctx->thisObject;
}
@@ -2732,37 +2735,37 @@ Value ErrorCtor::call(ExecutionContext *ctx)
Value EvalErrorCtor::construct(ExecutionContext *ctx)
{
- ctx->thisObject = Value::fromObject(new EvalErrorObject(ctx));
+ ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) EvalErrorObject(ctx));
return ctx->thisObject;
}
Value RangeErrorCtor::construct(ExecutionContext *ctx)
{
- ctx->thisObject = Value::fromObject(new RangeErrorObject(ctx));
+ ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) RangeErrorObject(ctx));
return ctx->thisObject;
}
Value ReferenceErrorCtor::construct(ExecutionContext *ctx)
{
- ctx->thisObject = Value::fromObject(new ReferenceErrorObject(ctx));
+ ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) ReferenceErrorObject(ctx));
return ctx->thisObject;
}
Value SyntaxErrorCtor::construct(ExecutionContext *ctx)
{
- ctx->thisObject = Value::fromObject(new SyntaxErrorObject(ctx, 0));
+ ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) SyntaxErrorObject(ctx, 0));
return ctx->thisObject;
}
Value TypeErrorCtor::construct(ExecutionContext *ctx)
{
- ctx->thisObject = Value::fromObject(new TypeErrorObject(ctx));
+ ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) TypeErrorObject(ctx));
return ctx->thisObject;
}
Value URIErrorCtor::construct(ExecutionContext *ctx)
{
- ctx->thisObject = Value::fromObject(new URIErrorObject(ctx));
+ ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) URIErrorObject(ctx));
return ctx->thisObject;
}
diff --git a/qv4ir.cpp b/qv4ir.cpp
index 7646b371d0..2a9414bb5d 100644
--- a/qv4ir.cpp
+++ b/qv4ir.cpp
@@ -391,8 +391,20 @@ Function *Module::newFunction(const QString &name)
return f;
}
+Module::~Module()
+{
+ foreach (Function *f, functions)
+ f->releaseModuleManagedData();
+}
+
Function::~Function()
{
+ delete[] codeData;
+}
+
+
+void Function::releaseModuleManagedData()
+{
// destroy the Stmt::Data blocks manually, because memory pool cleanup won't
// call the Stmt destructors.
foreach (IR::BasicBlock *b, basicBlocks)
@@ -400,9 +412,11 @@ Function::~Function()
s->destroyData();
qDeleteAll(basicBlocks);
- delete[] codeData;
+ pool = 0;
+ module = 0;
}
+
const QString *Function::newString(const QString &text)
{
return &*strings.insert(text);
diff --git a/qv4ir_p.h b/qv4ir_p.h
index 92276d0b6d..14c73e5b1a 100644
--- a/qv4ir_p.h
+++ b/qv4ir_p.h
@@ -587,6 +587,8 @@ struct Module {
QVector<Function *> functions;
Function *newFunction(const QString &name);
+
+ ~Module();
};
struct Function {
@@ -626,6 +628,7 @@ struct Function {
{ this->name = newString(name); }
~Function();
+ void releaseModuleManagedData();
enum BasicBlockInsertMode {
InsertBlock,
diff --git a/qv4mm.cpp b/qv4mm.cpp
new file mode 100644
index 0000000000..4be7219d7c
--- /dev/null
+++ b/qv4mm.cpp
@@ -0,0 +1,375 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmljs_engine.h"
+#include "qmljs_objects.h"
+#include "qv4ecmaobjects_p.h"
+#include "qv4mm.h"
+
+#include <QTime>
+#include <QVector>
+#include <QLinkedList>
+
+#include <iostream>
+
+using namespace QQmlJS::VM;
+
+static const std::size_t CHUNK_SIZE = 65536;
+
+struct MemoryManager::Data
+{
+ bool enableGC;
+ bool gcBlocked;
+ bool scribble;
+ bool aggressiveGC;
+ ExecutionEngine *engine;
+ StringPool *stringPool;
+
+ // FIXME: this freeList will get out of hand if there is one allocation+deallocation of, say, 16M.
+ // TODO: turn the freeList into a fixed length array which can hold the most common sizes (e.g. up to 64K), then use a tree for anything afterwards.
+ // TODO: this requires that the interaction with the freeList is factored out first into separate methods.
+ QVector<MMObject *> freeList;
+ QLinkedList<QPair<char *, std::size_t> > heapChunks;
+
+ // statistics:
+#ifdef DETAILED_MM_STATS
+ QVector<unsigned> allocSizeCounters;
+#endif // DETAILED_MM_STATS
+
+ Data(bool enableGC)
+ : enableGC(enableGC)
+ , gcBlocked(false)
+ , engine(0)
+ , stringPool(0)
+ , freeList(0)
+ {
+ scribble = qgetenv("MM_NO_SCRIBBLE").isEmpty();
+ aggressiveGC = !qgetenv("MM_AGGRESSIVE_GC").isEmpty();
+ }
+
+ ~Data()
+ {
+ for (QLinkedList<QPair<char *, std::size_t> >::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i)
+ delete[] i->first;
+ }
+};
+
+MemoryManager::MemoryManager()
+ : m_d(new Data(true))
+{
+}
+
+MemoryManager::MMObject *MemoryManager::alloc(std::size_t size)
+{
+ if (m_d->aggressiveGC)
+ runGC();
+#ifdef DETAILED_MM_STATS
+ willAllocate(size);
+#endif // DETAILED_MM_STATS
+
+ size += sizeof(MMInfo);
+ assert(size >= 16);
+ assert(size % 16 == 0);
+
+ for (std::size_t s = size / 16, es = m_d->freeList.size(); s < es; ++s) {
+ if (MMObject *m = m_d->freeList.at(s)) {
+ m_d->freeList[s] = m->info.next;
+
+ if (s != size / 16) {
+ MMObject *tail = reinterpret_cast<MMObject *>(reinterpret_cast<char *>(m) + size);
+ assert(m->info.size == s * 16);
+ tail->info.inUse = 0;
+ tail->info.markBit = 0;
+ tail->info.size = m->info.size - size;
+ MMObject *&f = m_d->freeList[tail->info.size / 16];
+ tail->info.next = f;
+ f = tail;
+ m->info.size = size;
+ }
+
+ m->info.inUse = 1;
+ m->info.markBit = 0;
+ scribble(m, 0xaa);
+// qDebug("alloc(%lu) -> %p", size, m);
+ return m;
+ }
+ }
+
+ if (!m_d->aggressiveGC)
+ if (runGC() >= size)
+ return alloc(size - sizeof(MMInfo));
+
+ std::size_t allocSize = std::max(size, CHUNK_SIZE);
+ char *ptr = new char[allocSize];
+ m_d->heapChunks.append(qMakePair(ptr, allocSize));
+// qDebug("Allocated new chunk of %lu bytes @ %p", allocSize, ptr);
+
+ if (allocSize > size) {
+ MMObject *m = reinterpret_cast<MMObject *>(ptr + size);
+ m->info.size = allocSize - size;
+ std::size_t off = m->info.size / 16;
+ if (((std::size_t) m_d->freeList.size()) <= off)
+ m_d->freeList.resize(off + 1);
+ MMObject *&f = m_d->freeList[off];
+ m->info.next = f;
+ f = m;
+ }
+
+ MMObject *m = reinterpret_cast<MMObject *>(ptr);
+ m->info.inUse = 1;
+ m->info.markBit = 0;
+ m->info.size = size;
+ scribble(m, 0xaa);
+// qDebug("alloc(%lu) -> %p", size, ptr);
+ return m;
+}
+
+void MemoryManager::dealloc(MMObject *ptr)
+{
+ if (!ptr)
+ return;
+
+ assert(ptr->info.size >= 16);
+ assert(ptr->info.size % 16 == 0);
+
+// qDebug("dealloc %p (%lu)", ptr, ptr->info.size);
+
+ std::size_t off = ptr->info.size / 16;
+ if (((std::size_t) m_d->freeList.size()) <= off)
+ m_d->freeList.resize(off + 1);
+ MMObject *&f = m_d->freeList[off];
+ ptr->info.next = f;
+ ptr->info.inUse = 0;
+ ptr->info.markBit = 0;
+ ptr->info.needsManagedDestructorCall = 0;
+ f = ptr;
+ scribble(ptr, 0x55);
+}
+
+void MemoryManager::scribble(MemoryManager::MMObject *obj, int c) const
+{
+ if (m_d->scribble)
+ ::memset(&obj->data, c, obj->info.size - sizeof(MMInfo));
+}
+
+std::size_t MemoryManager::mark(const QVector<Object *> &objects)
+{
+ std::size_t marks = 0;
+
+ QVector<Object *> kids;
+ kids.reserve(32);
+
+ foreach (Object *o, objects) {
+ if (!o)
+ continue;
+
+ MMObject *obj = toObject(o);
+ assert(obj->info.inUse);
+ if (obj->info.markBit == 0) {
+ obj->info.markBit = 1;
+ ++marks;
+ static_cast<Managed *>(o)->getCollectables(kids);
+ marks += mark(kids);
+ kids.resize(0);
+ }
+ }
+
+ return marks;
+}
+
+std::size_t MemoryManager::sweep(std::size_t &largestFreedBlock)
+{
+ std::size_t freedCount = 0;
+
+ for (QLinkedList<QPair<char *, std::size_t> >::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i)
+ freedCount += sweep(i->first, i->second, largestFreedBlock);
+
+ return freedCount;
+}
+
+std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, std::size_t &largestFreedBlock)
+{
+// qDebug("chunkStart @ %p", chunkStart);
+ std::size_t freedCount = 0;
+
+ for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize; chunk < chunkEnd; ) {
+ MMObject *m = reinterpret_cast<MMObject *>(chunk);
+// qDebug("chunk @ %p, size = %lu, in use: %s, mark bit: %s",
+// chunk, m->info.size, (m->info.inUse ? "yes" : "no"), (m->info.markBit ? "true" : "false"));
+
+ assert((intptr_t) chunk % 16 == 0);
+ assert(m->info.size >= 16);
+ assert(m->info.size % 16 == 0);
+
+ chunk = chunk + m->info.size;
+ if (m->info.inUse) {
+ if (m->info.markBit) {
+ m->info.markBit = 0;
+ } else {
+// qDebug("-- collecting it.");
+ if (m->info.needsManagedDestructorCall)
+ reinterpret_cast<VM::Managed *>(&m->data)->~Managed();
+ dealloc(m);
+ largestFreedBlock = std::max(largestFreedBlock, m->info.size);
+ ++freedCount;
+ }
+ }
+ }
+
+ return freedCount;
+}
+
+bool MemoryManager::isGCBlocked() const
+{
+ return m_d->gcBlocked;
+}
+
+void MemoryManager::setGCBlocked(bool blockGC)
+{
+ m_d->gcBlocked = blockGC;
+}
+
+std::size_t MemoryManager::runGC()
+{
+ if (!m_d->enableGC || m_d->gcBlocked) {
+// qDebug() << "Not running GC.";
+ return 0;
+ }
+
+// QTime t; t.start();
+
+ QVector<Object *> roots;
+ collectRoots(roots);
+// std::cerr << "GC: found " << roots.size()
+// << " roots in " << t.elapsed()
+// << "ms" << std::endl;
+
+// t.restart();
+ /*std::size_t marks =*/ mark(roots);
+// std::cerr << "GC: marked " << marks
+// << " objects in " << t.elapsed()
+// << "ms" << std::endl;
+
+// t.restart();
+ std::size_t freedCount = 0, largestFreedBlock = 0;
+ freedCount = sweep(largestFreedBlock);
+// std::cerr << "GC: sweep freed " << freedCount
+// << " objects in " << t.elapsed()
+// << "ms" << std::endl;
+
+ return largestFreedBlock;
+}
+
+void MemoryManager::setEnableGC(bool enableGC)
+{
+ m_d->enableGC = enableGC;
+}
+
+MemoryManager::~MemoryManager()
+{
+ std::size_t dummy = 0;
+ sweep(dummy);
+}
+
+static inline void add(QVector<Object *> &values, const Value &v)
+{
+ if (Object *o = v.asObject())
+ values.append(o);
+}
+
+void MemoryManager::setExecutionEngine(ExecutionEngine *engine)
+{
+ m_d->engine = engine;
+}
+
+void MemoryManager::setStringPool(StringPool *stringPool)
+{
+ m_d->stringPool = stringPool;
+}
+
+void MemoryManager::dumpStats() const
+{
+ std::cerr << "=================" << std::endl;
+ std::cerr << "Allocation stats:" << std::endl;
+#ifdef DETAILED_MM_STATS
+ std::cerr << "Requests for each chunk size:" << std::endl;
+ for (int i = 0; i < m_d->allocSizeCounters.size(); ++i) {
+ if (unsigned count = m_d->allocSizeCounters[i]) {
+ std::cerr << "\t" << (i << 4) << " bytes chunks: " << count << std::endl;
+ }
+ }
+#endif // DETAILED_MM_STATS
+}
+
+ExecutionEngine *MemoryManager::engine() const
+{
+ return m_d->engine;
+}
+
+#ifdef DETAILED_MM_STATS
+void MemoryManager::willAllocate(std::size_t size)
+{
+ unsigned alignedSize = (size + 15) >> 4;
+ QVector<unsigned> &counters = m_d->allocSizeCounters;
+ if ((unsigned) counters.size() < alignedSize + 1)
+ counters.resize(alignedSize + 1);
+ counters[alignedSize]++;
+}
+#endif // DETAILED_MM_STATS
+
+void MemoryManager::collectRoots(QVector<VM::Object *> &roots) const
+{
+ add(roots, m_d->engine->globalObject);
+ add(roots, m_d->engine->exception);
+
+ 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);
+ for (ExecutionContext::With *it = ctxt->withObject; it; it = it->next)
+ if (it->object)
+ roots.append(it->object);
+ }
+
+ collectRootsOnStack(roots);
+}
+
+MemoryManagerWithoutGC::~MemoryManagerWithoutGC()
+{}
+
+void MemoryManagerWithoutGC::collectRootsOnStack(QVector<VM::Object *> &roots) const
+{
+ Q_UNUSED(roots);
+}
diff --git a/qv4mm.h b/qv4mm.h
new file mode 100644
index 0000000000..ece3765e3b
--- /dev/null
+++ b/qv4mm.h
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QV4GC_H
+#define QV4GC_H
+
+#include "qmljs_objects.h"
+
+#include <QScopedPointer>
+
+#define DETAILED_MM_STATS
+
+namespace QQmlJS {
+namespace VM {
+
+class MemoryManager
+{
+ MemoryManager(const MemoryManager &);
+ MemoryManager &operator=(const MemoryManager&);
+
+ struct Data;
+
+public:
+ class GCBlocker
+ {
+ public:
+ GCBlocker(MemoryManager *mm)
+ : mm(mm)
+ , wasBlocked(mm->isGCBlocked())
+ {
+ mm->setGCBlocked(true);
+ }
+
+ ~GCBlocker()
+ {
+ mm->setGCBlocked(wasBlocked);
+ }
+
+ private:
+ MemoryManager *mm;
+ bool wasBlocked;
+ };
+
+public:
+ MemoryManager();
+ virtual ~MemoryManager() = 0;
+
+ // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries).
+ // Note: all occurances of "16" in alloc/dealloc are also due to the alignment.
+ static inline std::size_t align(std::size_t size)
+ { return (size + 15) & ~0xf; }
+
+ inline Managed *allocManaged(std::size_t size)
+ {
+ size = align(size);
+ MMObject *o = alloc(size);
+ o->info.needsManagedDestructorCall = 1;
+ Managed *ptr = reinterpret_cast<Managed *>(&o->data);
+ ptr->mm = this;
+ return ptr;
+ }
+
+ inline void deallocManaged(Managed *m)
+ {
+ if (!m)
+ return;
+
+ assert(m->mm == this);
+ dealloc(toObject(m));
+ }
+
+ bool isGCBlocked() const;
+ void setGCBlocked(bool blockGC);
+ std::size_t runGC();
+
+ void setEnableGC(bool enableGC);
+ void setExecutionEngine(ExecutionEngine *engine);
+ void setStringPool(StringPool *stringPool);
+
+ void dumpStats() const;
+
+protected:
+#if 1 // 64bit and x86:
+ struct MMObject;
+ struct MMInfo {
+ std::size_t inUse : 1;
+ std::size_t markBit : 1;
+ std::size_t needsManagedDestructorCall : 1;
+ std::size_t size : 61;
+ MMObject *next;
+ };
+ struct MMObject {
+ MMInfo info;
+ std::size_t data;
+ };
+#endif
+#if 0 // for 32bits:
+ // untested!
+ struct MMInfo {
+ std::size_t inUse : 1;
+ std::size_t markBit : 1;
+ std::size_t size : 30;
+ };
+ struct MMObject {
+ MMInfo info;
+ union {
+ struct MMObject *next;
+ char data[1];
+ }
+ };
+#endif
+
+protected:
+ static inline MMObject *toObject(void *ptr) { return reinterpret_cast<MMObject *>(reinterpret_cast<char *>(ptr) - sizeof(MMInfo)); }
+
+ /// expects size to be aligned
+ // TODO: try to inline
+ MMObject *alloc(std::size_t size);
+
+ // TODO: try to inline
+ void dealloc(MMObject *ptr);
+
+ void scribble(MMObject *obj, int c) const;
+
+ virtual void collectRootsOnStack(QVector<VM::Object *> &roots) const = 0;
+
+ ExecutionEngine *engine() const;
+
+#ifdef DETAILED_MM_STATS
+ void willAllocate(std::size_t size);
+#endif // DETAILED_MM_STATS
+
+private:
+ void collectRoots(QVector<VM::Object *> &roots) const;
+ static std::size_t mark(const QVector<Object *> &objects);
+ std::size_t sweep(std::size_t &largestFreedBlock);
+ std::size_t sweep(char *chunkStart, std::size_t chunkSize, std::size_t &largestFreedBlock);
+
+private:
+ QScopedPointer<Data> m_d;
+};
+
+class MemoryManagerWithoutGC: public MemoryManager
+{
+public:
+ MemoryManagerWithoutGC()
+ { setEnableGC(false); }
+
+ virtual ~MemoryManagerWithoutGC();
+
+protected:
+ virtual void collectRootsOnStack(QVector<VM::Object *> &roots) const;
+};
+
+} // namespace VM
+} // namespace QQmlJS
+
+#endif // QV4GC_H
diff --git a/v4.pro b/v4.pro
index 24ab7c0d0e..ce1409b303 100644
--- a/v4.pro
+++ b/v4.pro
@@ -24,7 +24,8 @@ SOURCES += main.cpp \
qv4isel_masm.cpp \
llvm_runtime.cpp \
qv4isel_p.cpp \
- debugging.cpp
+ debugging.cpp \
+ qv4mm.cpp
HEADERS += \
qv4codegen_p.h \
@@ -41,7 +42,8 @@ HEADERS += \
qv4isel_masm_p.h \
qv4isel_p.h \
qv4isel_util_p.h \
- debugging.h
+ debugging.h \
+ qv4mm.h
llvm {