Add a MemoryManager, which does GC for the interpreter.
authorErik Verbruggen <erik.verbruggen@digia.com>
Tue, 4 Dec 2012 12:40:18 +0000 (13:40 +0100)
committerLars Knoll <lars.knoll@digia.com>
Sat, 8 Dec 2012 03:47:53 +0000 (04:47 +0100)
Todo:
- stack walking for MASM
- fix all TODOs/FIXMEs and hidden treasures (bugs).

Change-Id: I36f8cdc3a545df7287ce1df17b3570a9c017865e
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
18 files changed:
main.cpp
moth/moth.pri
moth/qv4mm_moth.cpp [new file with mode: 0644]
moth/qv4mm_moth.h [new file with mode: 0644]
moth/qv4vme_moth.cpp
qmljs_engine.cpp
qmljs_engine.h
qmljs_environment.cpp
qmljs_objects.cpp
qmljs_objects.h
qv4array_p.h
qv4codegen.cpp
qv4ecmaobjects.cpp
qv4ir.cpp
qv4ir_p.h
qv4mm.cpp [new file with mode: 0644]
qv4mm.h [new file with mode: 0644]
v4.pro

index 6f6d512..decb799 100644 (file)
--- 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;
     }
 }
index 4381c9a..160c1aa 100644 (file)
@@ -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 (file)
index 0000000..44e45fe
--- /dev/null
@@ -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 (file)
index 0000000..a2b1ebd
--- /dev/null
@@ -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
index d38d7d7..86b396f 100644 (file)
@@ -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)
 
index 6617810..ea6b32a 100644 (file)
@@ -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
index 2837adb..520acab 100644 (file)
@@ -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();
index 0127b04..0d6b200 100644 (file)
@@ -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;
index 7fdb86a..d74a501 100644 (file)
@@ -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>
 
 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)
index fc8deed..9daa841 100644 (file)
@@ -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 {
index 20022af..22c29f9 100644 (file)
@@ -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)
index 34c785d..8b3361b 100644 (file)
@@ -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
index 607c1f1..6eca545 100644 (file)
@@ -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;
 }
 
index 7646b37..2a9414b 100644 (file)
--- 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);
index 92276d0..14c73e5 100644 (file)
--- 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 (file)
index 0000000..4be7219
--- /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 (file)
index 0000000..ece3765
--- /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 24ab7c0..ce1409b 100644 (file)
--- 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 {