Fix constructors and some work on the String prototype.
authorRoberto Raggi <roberto.raggi@nokia.com>
Thu, 10 May 2012 10:14:20 +0000 (12:14 +0200)
committerRoberto Raggi <roberto.raggi@nokia.com>
Thu, 10 May 2012 10:14:20 +0000 (12:14 +0200)
main.cpp
qmljs_objects.cpp
qmljs_objects.h
qmljs_runtime.cpp
qv4codegen.cpp
qv4isel.cpp
qv4isel_p.h
tests/string.1.js

index ef5d6eb..8077479 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -73,6 +73,30 @@ struct StringCtor: FunctionObject
     }
 };
 
+struct StringPrototype: Object
+{
+    StringPrototype(Context *ctx, FunctionObject *ctor)
+    {
+        setProperty(ctx, QLatin1String("constructor"), Value::object(ctx, ctor));
+        setProperty(ctx, QLatin1String("toString"), toString);
+    }
+
+    void setProperty(Context *ctx, const QString &name, const Value &value)
+    {
+        put(String::get(ctx, name), value);
+    }
+
+    void setProperty(Context *ctx, const QString &name, void (*code)(Context *))
+    {
+        setProperty(ctx, name, Value::object(ctx, new NativeFunction(code)));
+    }
+
+    static void toString(Context *ctx)
+    {
+        __qmljs_to_string(ctx, &ctx->result, &ctx->thisObject);
+    }
+};
+
 } // builtins
 
 
@@ -116,20 +140,25 @@ void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &co
 
         VM::Context *ctx = new VM::Context;
         ctx->init();
-        ctx->activation = VM::Value::object(ctx, new VM::ArgumentsObject(ctx));
 
-        ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("print")),
+        VM::String *prototype = VM::String::get(ctx, QLatin1String("prototype"));
+
+        VM::Object *globalObject = new VM::ArgumentsObject(ctx);
+        __qmljs_init_object(ctx, &ctx->activation, globalObject);
+
+        globalObject->put(VM::String::get(ctx, QLatin1String("print")),
                                          VM::Value::object(ctx, new builtins::Print()));
 
-        ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("Object")),
+        globalObject->put(VM::String::get(ctx, QLatin1String("Object")),
                                          VM::Value::object(ctx, new builtins::ObjectCtor()));
 
-        ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("String")),
-                                         VM::Value::object(ctx, new builtins::StringCtor()));
+        VM::FunctionObject *stringCtor = new builtins::StringCtor();
+        stringCtor->put(prototype, VM::Value::object(ctx, new builtins::StringPrototype(ctx, stringCtor)));
+        globalObject->put(VM::String::get(ctx, QLatin1String("String")), VM::Value::object(ctx, stringCtor));
 
         foreach (IR::Function *function, module.functions) {
             if (function->name && ! function->name->isEmpty()) {
-                ctx->activation.objectValue->put(VM::String::get(ctx, *function->name),
+                globalObject->put(VM::String::get(ctx, *function->name),
                                                  VM::Value::object(ctx, new VM::ScriptFunction(ctx, function)));
             }
         }
index 7f4b855..7a37351 100644 (file)
@@ -116,11 +116,6 @@ void FunctionObject::construct(Context *ctx)
 {
     __qmljs_init_object(ctx, &ctx->thisObject, new Object());
     call(ctx);
-    Value proto;
-    if (get(String::get(ctx, QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol
-        if (proto.type == OBJECT_TYPE)
-            ctx->thisObject.objectValue->prototype = proto.objectValue;
-    }
 }
 
 ScriptFunction::ScriptFunction(Context *context, IR::Function *function)
@@ -146,6 +141,11 @@ void ScriptFunction::call(VM::Context *ctx)
     function->code(ctx);
 }
 
+void ScriptFunction::construct(VM::Context *ctx)
+{
+    function->code(ctx);
+}
+
 Value *ArgumentsObject::getProperty(String *name, PropertyAttributes *attributes)
 {
     if (context) {
index dbb1ab8..323367a 100644 (file)
@@ -245,6 +245,14 @@ struct FunctionObject: Object {
     virtual void construct(Context *ctx);
 };
 
+struct NativeFunction: FunctionObject {
+    void (*code)(Context *);
+
+    NativeFunction(void (*code)(Context *)): code(code) {}
+    virtual void call(Context *ctx) { code(ctx); }
+    virtual void construct(Context *ctx) { code(ctx); }
+};
+
 struct ScriptFunction: FunctionObject {
     Context *context;
     IR::Function *function;
@@ -253,6 +261,7 @@ struct ScriptFunction: FunctionObject {
     virtual ~ScriptFunction();
 
     virtual void call(Context *ctx);
+    virtual void construct(Context *ctx);
 };
 
 struct ErrorObject: Object {
@@ -276,6 +285,7 @@ struct Context {
     Value result;
     String **formals;
     size_t formalCount;
+    bool calledAsConstructor;
 
     inline Value argument(size_t index = 0)
     {
@@ -303,6 +313,7 @@ struct Context {
         result.type = UNDEFINED_TYPE;
         formals = 0;
         formalCount = 0;
+        calledAsConstructor = false;
     }
 };
 
index 3486a65..b0a3c10 100644 (file)
@@ -445,7 +445,15 @@ void __qmljs_construct_property(Context *context, Value *result, Value *base, St
             context->thisObject = thisObject;
             context->formals = f->formalParameterList;
             context->formalCount = f->formalParameterCount;
+            context->calledAsConstructor = true;
             f->construct(context);
+
+            Value proto;
+            if (f->get(String::get(context, QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol
+                if (proto.type == OBJECT_TYPE)
+                    context->thisObject.objectValue->prototype = proto.objectValue;
+            }
+
             if (result)
                 __qmljs_copy(result, &context->thisObject);
         } else {
index 369d7e7..2bc9dbd 100644 (file)
@@ -300,6 +300,13 @@ void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op)
             _block->MOVE(target, _block->TEMP(t), op);
             return;
         }
+    } else if (! target->asTemp())  {
+        if (! (source->asConst() || source->asTemp() || source->asName() || source->asSubscript())) {
+            const unsigned t = _block->newTemp();
+            _block->MOVE(_block->TEMP(t), source);
+            _block->MOVE(target, _block->TEMP(t), op);
+            return;
+        }
     }
     _block->MOVE(target, source, op);
 }
index 35a7706..a689bec 100644 (file)
@@ -330,6 +330,49 @@ void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *
     amd64_call_code(_codePtr, __qmljs_dispose_context);
 }
 
+void InstructionSelection::constructProperty(IR::New *ctor, IR::Temp *result)
+{
+    IR::Member *member = ctor->base->asMember();
+    assert(member != 0);
+    assert(member->base->asTemp());
+
+    int argc = 0;
+    for (IR::ExprList *it = ctor->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 = ctor->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;
+    }
+
+    // __qmljs_construct_property(Context *context, Value *result, Value *base, String *name)
+
+    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8);
+    if (result)
+        loadTempAddress(AMD64_RSI, result);
+    else
+        amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI);
+    loadTempAddress(AMD64_RDX, member->base->asTemp());
+    amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*member->name));
+    amd64_call_code(_codePtr, __qmljs_construct_property);
+
+    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8);
+    amd64_call_code(_codePtr, __qmljs_dispose_context);
+}
+
 void InstructionSelection::visitExp(IR::Exp *s)
 {
     if (IR::Call *c = s->expr->asCall()) {
@@ -475,8 +518,13 @@ void InstructionSelection::visitMove(IR::Move *s)
                 amd64_call_code(_codePtr, __qmljs_init_closure);
                 return;
             } else if (IR::New *ctor = s->source->asNew()) {
-                constructActivationProperty(ctor, t);
-                return;
+                if (ctor->base->asName()) {
+                    constructActivationProperty(ctor, t);
+                    return;
+                } else if (ctor->base->asMember()) {
+                    constructProperty(ctor, t);
+                    return;
+                }
             } else if (IR::Member *m = s->source->asMember()) {
                 //__qmljs_get_property(ctx, result, object, name);
                 if (IR::Temp *base = m->base->asTemp()) {
@@ -600,6 +648,9 @@ void InstructionSelection::visitMove(IR::Move *s)
                 if (c->base->asName()) {
                     callActivationProperty(c, t);
                     return;
+                } else if (c->base->asMember()) {
+                    callProperty(c, t);
+                    return;
                 } else if (c->base->asTemp()) {
                     callValue(c, t);
                     return;
index 77bf4bb..a52b291 100644 (file)
@@ -24,6 +24,7 @@ protected:
     void callActivationProperty(IR::Call *call, IR::Temp *result);
     void callProperty(IR::Call *call, IR::Temp *result);
     void constructActivationProperty(IR::New *call, IR::Temp *result);
+    void constructProperty(IR::New *ctor, IR::Temp *result);
     void callValue(IR::Call *call, IR::Temp *result);
 
     virtual void visitExp(IR::Exp *);
index 1495552..5ae5535 100644 (file)
@@ -3,5 +3,11 @@ var s = String(123) + 1
 print(s)
 
 var s2 = new String(123)
-print(s2)
+print(s2, s2.toString, s2.toString())
+
+var s3 = String.prototype.constructor(321)
+print(s3)
+
+var s4 = new String.prototype.constructor(321)
+print(s4)