Add naive support for function calls.
authorRoberto Raggi <roberto.raggi@nokia.com>
Mon, 7 May 2012 14:05:05 +0000 (16:05 +0200)
committerRoberto Raggi <roberto.raggi@nokia.com>
Mon, 7 May 2012 15:38:17 +0000 (17:38 +0200)
main.cpp
qmljs_objects.cpp
qmljs_objects.h
qmljs_runtime.cpp
qmljs_runtime.h
qv4codegen.cpp
qv4isel.cpp
qv4isel_p.h

index e95a3aa..4be5e07 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -21,6 +21,24 @@ static inline bool protect(const void *addr, size_t size)
     return mprotect(reinterpret_cast<void*>(roundAddr), size + (iaddr - roundAddr), mode) == 0;
 }
 
+namespace builtins {
+using namespace QQmlJS::VM;
+struct Print: FunctionObject
+{
+    virtual void call(Context *ctx)
+    {
+        for (size_t i = 0; i < ctx->argumentCount; ++i) {
+            Value v;
+            __qmljs_to_string(ctx, &v, &ctx->arguments[i]);
+            if (i)
+                std::cout << ' ';
+            std::cout << qPrintable(v.stringValue->text());
+        }
+        std::cout << std::endl;
+    }
+};
+} // builtins
+
 int main(int argc, char *argv[])
 {
     using namespace QQmlJS;
@@ -62,15 +80,24 @@ int main(int argc, char *argv[])
                 QHash<QString, IR::Function *> codeByName;
                 foreach (IR::Function *function, module.functions) {
                     isel(function);
-                    if (function->name && ! function->name->isEmpty())
+                    if (function->name && ! function->name->isEmpty()) {
                         codeByName.insert(*function->name, function);
+                    }
                 }
 
                 if (! protect(code, codeSize))
                     Q_UNREACHABLE();
 
                 VM::Context *ctx = new VM::Context;
-                ctx->activation = VM::Value::object(ctx, new VM::ArgumentsObject);
+                ctx->activation = VM::Value::object(ctx, new VM::ArgumentsObject(ctx));
+                ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("print")),
+                                                 VM::Value::object(ctx, new builtins::Print()));
+                foreach (IR::Function *function, module.functions) {
+                    if (function->name && ! function->name->isEmpty()) {
+                        ctx->activation.objectValue->put(VM::String::get(ctx, *function->name),
+                                                         VM::Value::object(ctx, new VM::ScriptFunction(function)));
+                    }
+                }
                 codeByName.value(QLatin1String("%entry"))->code(ctx);
             }
         }
index 57332ab..37e3537 100644 (file)
@@ -1,5 +1,7 @@
 
 #include "qmljs_objects.h"
+#include "qv4ir_p.h"
+#include <QtCore/QDebug>
 #include <cassert>
 
 using namespace QQmlJS::VM;
@@ -103,21 +105,32 @@ bool FunctionObject::hasInstance(const Value &value) const
     return false;
 }
 
-Value FunctionObject::call(const Value &thisObject, const Value args[], unsigned argc)
+void FunctionObject::call(Context *ctx)
 {
-    Q_UNUSED(thisObject);
-    Q_UNUSED(args);
-    Q_UNUSED(argc);
+    Q_UNUSED(ctx);
+}
+
+void FunctionObject::construct(Context *ctx)
+{
+    Q_UNUSED(ctx);
+    Q_UNIMPLEMENTED();
+}
 
-    Value v;
-    __qmljs_init_undefined(0, &v);
-    return v;
+void ScriptFunction::call(VM::Context *ctx)
+{
+    // bind the actual arguments. ### slow
+    for (int i = 0; i < function->formals.size(); ++i) {
+        const QString *f = function->formals.at(i);
+        ctx->activation.objectValue->put(String::get(ctx, *f), ctx->arguments[i]);
+    }
+    function->code(ctx);
 }
 
-Value FunctionObject::construct(const Value args[], unsigned argc)
+Property *ArgumentsObject::getOwnProperty(String *name)
 {
-    Value thisObject;
-    __qmljs_init_object(0, &thisObject, new Object);
-    call(thisObject, args, argc);
-    return thisObject;
+    if (Property *prop = Object::getOwnProperty(name))
+        return prop;
+    else if (context && context->scope)
+        return context->scope->getOwnProperty(name);
+    return 0;
 }
index cabdf5c..17c85b0 100644 (file)
@@ -8,6 +8,11 @@
 #include <cassert>
 
 namespace QQmlJS {
+
+namespace IR {
+struct Function;
+}
+
 namespace VM {
 
 struct Value;
@@ -234,8 +239,15 @@ struct FunctionObject: Object {
     virtual FunctionObject *asFunctionObject() { return this; }
 
     virtual bool hasInstance(const Value &value) const;
-    virtual Value call(const Value &thisObject, const Value args[], unsigned argc);
-    virtual Value construct(const Value args[], unsigned argc);
+    virtual void call(Context *ctx);
+    virtual void construct(Context *ctx);
+};
+
+struct ScriptFunction: FunctionObject {
+    IR::Function *function;
+
+    ScriptFunction(IR::Function *function): function(function) {}
+    virtual void call(Context *ctx);
 };
 
 struct ErrorObject: Object {
@@ -244,22 +256,29 @@ struct ErrorObject: Object {
 };
 
 struct ArgumentsObject: Object {
+    Context *context;
+    ArgumentsObject(Context *context): context(context) {}
+    virtual Property *getOwnProperty(String *name);
 };
 
 struct Context {
+    Context *parent;
     Value activation;
     Value thisObject;
     Object *scope;
     Value *arguments;
-    unsigned argumentCount;
+    size_t argumentCount;
+    Value result;
 
     Context()
-        : scope(0)
+        : parent(0)
+        , scope(0)
         , arguments(0)
         , argumentCount(0)
     {
         activation.type = NULL_TYPE;
         thisObject.type = NULL_TYPE;
+        result.type = UNDEFINED_TYPE;
     }
 };
 
index f95060e..4870c0b 100644 (file)
@@ -1,6 +1,7 @@
 
 #include "qmljs_runtime.h"
 #include "qmljs_objects.h"
+#include <QtCore/QDebug>
 #include <cstdio>
 #include <cassert>
 
@@ -326,29 +327,42 @@ bool __qmljs_equal(Context *ctx, const Value *x, const Value *y)
     return false;
 }
 
-void __qmljs_call(Context *ctx, Value *result, const Value *function,
-                 const Value *thisObject, const Value *arguments, int argc)
+Context *__qmljs_new_context(Context *current, Value *thisObject, size_t argc)
 {
-  if (function->type != OBJECT_TYPE) {
-    __qmljs_throw_type_error(ctx, result);
-  } else if (FunctionObject *f = function->objectValue->asFunctionObject()) {
-    *result = f->call(*thisObject, arguments, argc);
-  } else {
-    __qmljs_throw_type_error(ctx, result);
-  }
+    Context *ctx = new Context;
+    ctx->parent = current;
+    ctx->scope = current->activation.objectValue;
+    __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx));
+    if (thisObject)
+        ctx->thisObject = *thisObject;
+    else
+        __qmljs_init_null(ctx, &ctx->thisObject);
+    ctx->arguments = new Value[argc];
+    ctx->argumentCount = argc;
+    return ctx;
 }
 
-void __qmjs_construct(Context *ctx, Value *result, const Value *function, const Value *arguments, int argc)
-{  
-  if (function->type != OBJECT_TYPE) {
-    __qmljs_throw_type_error(ctx, result);
-  } else if (FunctionObject *f = function->objectValue->asFunctionObject()) {
-    *result = f->construct(arguments, argc);
-  } else {
-    __qmljs_throw_type_error(ctx, result);
-  }
+void __qmljs_dispose_context(Context *ctx)
+{
+    delete[] ctx->arguments;
+    delete ctx;
 }
 
+void __qmljs_call_activation_property(Context *context, Value *result, String *name)
+{
+    Value func;
+    context->parent->activation.objectValue->get(name, &func);
+    if (func.type == OBJECT_TYPE) {
+        if (FunctionObject *f = func.objectValue->asFunctionObject()) {
+            f->call(context);
+            __qmljs_copy(result, &context->result);
+        } else {
+            Q_ASSERT(!"not a function");
+        }
+    } else {
+        Q_ASSERT(!"not a callable object");
+    }
+}
 
 } // extern "C"
 
index d295c3f..438206b 100644 (file)
@@ -38,6 +38,11 @@ struct Context;
 
 extern "C" {
 
+// context
+Context *__qmljs_new_context(Context *current, Value *thisObject, size_t argc);
+void __qmljs_dispose_context(Context *ctx);
+void __qmljs_call_activation_property(Context *, Value *result, String *name);
+
 // constructors
 void __qmljs_init_undefined(Context *ctx, Value *result);
 void __qmljs_init_null(Context *ctx, Value *result);
@@ -143,9 +148,6 @@ void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Value *rig
 void __qmljs_se(Context *ctx, Value *result, const Value *left, const Value *right);
 void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Value *right);
 
-void __qmljs_call(Context *ctx, Value *result, const Value *function, const Value *thisObject, const Value *arguments, int argc);
-void __qmjs_construct(Context *ctx, Value *result, const Value *function, const Value *arguments, int argc);
-
 } // extern "C"
 
 struct Value {
@@ -550,7 +552,7 @@ inline void __qmljs_ushr(Context *ctx, Value *result, const Value *left, const V
 
 inline void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Value *right)
 {
-    __qmljs_compare(ctx, result, right, left, false);
+    __qmljs_compare(ctx, result, left, right, false);
 
     if (result->type == UNDEFINED_TYPE)
         __qmljs_init_boolean(ctx, result, false);
@@ -566,7 +568,7 @@ inline void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Val
 
 inline void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Value *right)
 {
-    __qmljs_compare(ctx, result, left, right, false);
+    __qmljs_compare(ctx, result, right, left, false);
 
     bool r = ! (result->type == UNDEFINED_TYPE ||
                 (result->type == BOOLEAN_TYPE && result->booleanValue == true));
index 6f9181b..24c6c81 100644 (file)
@@ -932,7 +932,9 @@ bool Codegen::visit(CallExpression *ast)
         (*args_it)->init(actual);
         args_it = &(*args_it)->next;
     }
-    _expr.code = _block->CALL(*base, args);
+    const unsigned t = _block->newTemp();
+    _block->MOVE(_block->TEMP(t), _block->CALL(*base, args));
+    _expr.code = _block->TEMP(t);
     return false;
 }
 
index a5a63da..f0fc00f 100644 (file)
@@ -92,6 +92,7 @@ InstructionSelection::~InstructionSelection()
 
 void InstructionSelection::operator()(IR::Function *function)
 {
+    _code = _codePtr;
     _code = (uchar *) ((size_t(_code) + 15) & ~15);
     function->code = (void (*)(VM::Context *)) _code;
     _codePtr = _code;
@@ -167,54 +168,43 @@ void InstructionSelection::loadTempAddress(int reg, IR::Temp *t)
     amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * (t->index - 1));
 }
 
-void __qmljs_print(Context *ctx, const Value *args, size_t argc)
+void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result)
 {
-    for (int i = 0; i < argc; ++i) {
-        Value v;
-        __qmljs_to_string(ctx, &v, &args[i]);
-        if (i)
-            std::cout << ' ';
-        std::cout << qPrintable(v.stringValue->text());
+    int argc = 0;
+    for (IR::ExprList *it = call->args; it; it = it->next)
+        ++argc;
+
+    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8);
+    amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI);
+    amd64_mov_reg_imm(_codePtr, AMD64_RDX, argc);
+    amd64_call_code(_codePtr, __qmljs_new_context);
+
+    amd64_mov_reg_reg(_codePtr, AMD64_R15, AMD64_RAX, 8);
+
+    argc = 0;
+    for (IR::ExprList *it = call->args; it; it = it->next) {
+        IR::Temp *t = it->expr->asTemp();
+        Q_ASSERT(t != 0);
+        amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R15, offsetof(Context, arguments), 8);
+        amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RAX, argc * sizeof(Value));
+        loadTempAddress(AMD64_RSI, t);
+        amd64_call_code(_codePtr, __qmljs_copy);
+        ++argc;
     }
-    std::cout << std::endl;
+
+    String *id = identifier(*call->base->asName()->id);
+    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8);
+    loadTempAddress(AMD64_RSI, result);
+    amd64_mov_reg_imm(_codePtr, AMD64_RDX, id);
+    amd64_call_code(_codePtr, __qmljs_call_activation_property);
+    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8);
+    amd64_call_code(_codePtr, __qmljs_dispose_context);
 }
 
-void InstructionSelection::visitExp(IR::Exp *s)
+void InstructionSelection::visitExp(IR::Exp *)
 {
-    if (IR::Call *c = s->expr->asCall()) {
-        IR::Name *n = c->base->asName();
-        if (n && *n->id == QLatin1String("print")) {
-            int argc = 0;
-            for (IR::ExprList *it = c->args; it; it = it->next)
-                ++argc;
-            amd64_mov_reg_imm(_codePtr, AMD64_RDI, argc * sizeof(Value));
-            amd64_mov_reg_imm(_codePtr, AMD64_RAX, malloc);
-            amd64_call_reg(_codePtr, AMD64_RAX);
-            amd64_mov_reg_reg(_codePtr, AMD64_R15, AMD64_RAX, 8);
-
-            argc = 0;
-            for (IR::ExprList *it = c->args; it; it = it->next) {
-                IR::Temp *t = it->expr->asTemp();
-                Q_ASSERT(t != 0);
-                amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_R15, argc * sizeof(Value));
-                loadTempAddress(AMD64_RSI, t);
-                amd64_call_code(_codePtr, __qmljs_copy);
-                ++argc;
-            }
-
-            amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8);
-            amd64_mov_reg_reg(_codePtr, AMD64_RSI, AMD64_R15, 8);
-            amd64_mov_reg_imm(_codePtr, AMD64_RDX, argc);
-            amd64_call_code(_codePtr, __qmljs_print);
-
-            amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8);
-            amd64_mov_reg_imm(_codePtr, AMD64_RAX, free);
-            amd64_call_reg(_codePtr, AMD64_RAX);
-            return;
-        }
-    }
-    Q_UNIMPLEMENTED();
-    assert(!"TODO");
+//    Q_UNIMPLEMENTED();
+//    assert(!"TODO");
 }
 
 void InstructionSelection::visitEnter(IR::Enter *)
@@ -305,6 +295,11 @@ void InstructionSelection::visitMove(IR::Move *s)
                     assert(!"TODO");
                 }
                 return;
+            } else if (IR::Temp *t2 = s->source->asTemp()) {
+                loadTempAddress(AMD64_RDI, t);
+                loadTempAddress(AMD64_RSI, t2);
+                amd64_call_code(_codePtr, __qmljs_copy);
+                return;
             } else if (IR::String *str = s->source->asString()) {
                 amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8);
                 loadTempAddress(AMD64_RSI, t);
@@ -378,6 +373,11 @@ void InstructionSelection::visitMove(IR::Move *s)
                     amd64_call_code(_codePtr, op);
                     return;
                 }
+            } else if (IR::Call *c = s->source->asCall()) {
+                if (c->base->asName()) {
+                    callActivationProperty(c, t);
+                    return;
+                }
             }
         }
     } else {
@@ -417,6 +417,12 @@ void InstructionSelection::visitCJump(IR::CJump *s)
 
 void InstructionSelection::visitRet(IR::Ret *s)
 {
+    if (IR::Temp *t = s->expr->asTemp()) {
+        amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_R14, offsetof(Context, result));
+        loadTempAddress(AMD64_RSI, t);
+        amd64_call_code(_codePtr, __qmljs_copy);
+        return;
+    }
     Q_UNIMPLEMENTED();
     Q_UNUSED(s);
 }
index 0672bc5..753da04 100644 (file)
@@ -21,6 +21,7 @@ protected:
     VM::String *identifier(const QString &s);
     int tempOffset(IR::Temp *t);
     void loadTempAddress(int reg, IR::Temp *t);
+    void callActivationProperty(IR::Call *call, IR::Temp *result);
 
     virtual void visitExp(IR::Exp *);
     virtual void visitEnter(IR::Enter *);