Fix nested functions
authorRoberto Raggi <roberto.raggi@nokia.com>
Sun, 13 May 2012 11:50:55 +0000 (13:50 +0200)
committerRoberto Raggi <roberto.raggi@nokia.com>
Mon, 14 May 2012 08:43:36 +0000 (10:43 +0200)
main.cpp
qmljs_objects.cpp
qmljs_objects.h
qmljs_runtime.cpp
qmljs_runtime.h
qv4codegen.cpp
qv4codegen_p.h
qv4ir_p.h
qv4isel.cpp
tests/fun.3.js [new file with mode: 0644]

index 8077479..1bf71b4 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -28,6 +28,8 @@ using namespace QQmlJS::VM;
 
 struct Print: FunctionObject
 {
+    Print(Context *scope): FunctionObject(scope) {}
+
     virtual void call(Context *ctx)
     {
         for (size_t i = 0; i < ctx->argumentCount; ++i) {
@@ -43,9 +45,11 @@ struct Print: FunctionObject
 
 struct ObjectCtor: FunctionObject
 {
+    ObjectCtor(Context *scope): FunctionObject(scope) {}
+
     virtual void construct(Context *ctx)
     {
-        __qmljs_init_object(ctx, &ctx->result, new Object());
+        __qmljs_init_object(ctx, &ctx->thisObject, new Object());
     }
 
     virtual void call(Context *)
@@ -56,6 +60,8 @@ struct ObjectCtor: FunctionObject
 
 struct StringCtor: FunctionObject
 {
+    StringCtor(Context *scope): FunctionObject(scope) {}
+
     virtual void construct(Context *ctx)
     {
         Value arg = ctx->argument(0);
@@ -88,7 +94,7 @@ struct StringPrototype: Object
 
     void setProperty(Context *ctx, const QString &name, void (*code)(Context *))
     {
-        setProperty(ctx, name, Value::object(ctx, new NativeFunction(code)));
+        setProperty(ctx, name, Value::object(ctx, new NativeFunction(ctx, code)));
     }
 
     static void toString(Context *ctx)
@@ -147,12 +153,12 @@ void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &co
         __qmljs_init_object(ctx, &ctx->activation, globalObject);
 
         globalObject->put(VM::String::get(ctx, QLatin1String("print")),
-                                         VM::Value::object(ctx, new builtins::Print()));
+                                         VM::Value::object(ctx, new builtins::Print(ctx)));
 
         globalObject->put(VM::String::get(ctx, QLatin1String("Object")),
-                                         VM::Value::object(ctx, new builtins::ObjectCtor()));
+                                         VM::Value::object(ctx, new builtins::ObjectCtor(ctx)));
 
-        VM::FunctionObject *stringCtor = new builtins::StringCtor();
+        VM::FunctionObject *stringCtor = new builtins::StringCtor(ctx);
         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));
 
index 7a37351..a1a97e5 100644 (file)
@@ -118,8 +118,8 @@ void FunctionObject::construct(Context *ctx)
     call(ctx);
 }
 
-ScriptFunction::ScriptFunction(Context *context, IR::Function *function)
-    : context(context)
+ScriptFunction::ScriptFunction(Context *scope, IR::Function *function)
+    : FunctionObject(scope)
     , function(function)
 {
     formalParameterCount = function->formals.size();
@@ -143,6 +143,7 @@ void ScriptFunction::call(VM::Context *ctx)
 
 void ScriptFunction::construct(VM::Context *ctx)
 {
+    __qmljs_init_object(ctx, &ctx->thisObject, new Object());
     function->code(ctx);
 }
 
@@ -160,7 +161,5 @@ Value *ArgumentsObject::getProperty(String *name, PropertyAttributes *attributes
     }
     if (Value *prop = Object::getProperty(name, attributes))
         return prop;
-    else if (context && context->scope)
-        return context->scope->getProperty(name, attributes);
     return 0;
 }
index 323367a..5631b7d 100644 (file)
@@ -233,11 +233,11 @@ struct ArrayObject: Object {
 };
 
 struct FunctionObject: Object {
-    Object *scope;
+    Context *scope;
     String **formalParameterList;
     size_t formalParameterCount;
 
-    FunctionObject(Object *scope = 0): scope(scope), formalParameterList(0), formalParameterCount(0) {}
+    FunctionObject(Context *scope): scope(scope), formalParameterList(0), formalParameterCount(0) {}
     virtual FunctionObject *asFunctionObject() { return this; }
 
     virtual bool hasInstance(const Value &value) const;
@@ -248,16 +248,15 @@ struct FunctionObject: Object {
 struct NativeFunction: FunctionObject {
     void (*code)(Context *);
 
-    NativeFunction(void (*code)(Context *)): code(code) {}
+    NativeFunction(Context *scope, void (*code)(Context *)): FunctionObject(scope), code(code) {}
     virtual void call(Context *ctx) { code(ctx); }
     virtual void construct(Context *ctx) { code(ctx); }
 };
 
 struct ScriptFunction: FunctionObject {
-    Context *context;
     IR::Function *function;
 
-    ScriptFunction(Context *context, IR::Function *function);
+    ScriptFunction(Context *scope, IR::Function *function);
     virtual ~ScriptFunction();
 
     virtual void call(Context *ctx);
@@ -279,7 +278,6 @@ struct Context {
     Context *parent;
     Value activation;
     Value thisObject;
-    Object *scope;
     Value *arguments;
     size_t argumentCount;
     Value result;
@@ -287,6 +285,14 @@ struct Context {
     size_t formalCount;
     bool calledAsConstructor;
 
+    Value *lookup(String *name) {
+        if (activation.is(OBJECT_TYPE)) {
+            if (Value *prop = activation.objectValue->getProperty(name))
+                return prop;
+        }
+        return parent ? parent->lookup(name) : 0;
+    }
+
     inline Value argument(size_t index = 0)
     {
         Value arg;
@@ -305,7 +311,6 @@ struct Context {
     void init()
     {
         parent = 0;
-        scope = 0;
         arguments = 0;
         argumentCount = 0;
         activation.type = NULL_TYPE;
index b0a3c10..0f75105 100644 (file)
@@ -1,6 +1,7 @@
 
 #include "qmljs_runtime.h"
 #include "qmljs_objects.h"
+#include "qv4ir_p.h"
 #include <QtCore/QDebug>
 #include <cstdio>
 #include <cassert>
@@ -200,38 +201,52 @@ void __qmljs_set_property_string(Context *ctx, Value *object, String *name, Stri
 void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR::Function *function)
 {
     Value value;
-    __qmljs_init_object(ctx, &value, new VM::ScriptFunction(ctx, function));
+    __qmljs_init_closure(ctx, &value, function);
     object->objectValue->put(name, value, /*flag*/ 0);
 }
 
 void __qmljs_set_activation_property(Context *ctx, String *name, Value *value)
 {
-    __qmljs_set_property(ctx, &ctx->activation, name, value);
+    if (Value *prop = ctx->lookup(name))
+        __qmljs_copy(prop, value);
+    else
+        ctx->activation.objectValue->put(name, *value);
 }
 
 void __qmljs_copy_activation_property(Context *ctx, String *name, String *other)
 {
-    __qmljs_copy_property(ctx, &ctx->activation, name, &ctx->activation, other);
+    if (Value *source = ctx->lookup(other))
+        __qmljs_set_activation_property(ctx, name, source);
+    else
+        assert(!"reference error");
 }
 
-void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool value)
+void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool b)
 {
-    __qmljs_set_property_boolean(ctx, &ctx->activation, name, value);
+    Value value;
+    __qmljs_init_boolean(ctx, &value, b);
+    __qmljs_set_activation_property(ctx, name, &value);
 }
 
-void __qmljs_set_activation_property_number(Context *ctx, String *name, double value)
+void __qmljs_set_activation_property_number(Context *ctx, String *name, double number)
 {
-    __qmljs_set_property_number(ctx, &ctx->activation, name, value);
+    Value value;
+    __qmljs_init_number(ctx, &value, number);
+    __qmljs_set_activation_property(ctx, name, &value);
 }
 
-void __qmljs_set_activation_property_string(Context *ctx, String *name, String *value)
+void __qmljs_set_activation_property_string(Context *ctx, String *name, String *string)
 {
-    __qmljs_set_property_string(ctx, &ctx->activation, name, value);
+    Value value;
+    __qmljs_init_string(ctx, &value, string);
+    __qmljs_set_activation_property(ctx, name, &value);
 }
 
-void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Function *value)
+void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Function *clos)
 {
-    __qmljs_set_property_closure(ctx, &ctx->activation, name, value);
+    Value value;
+    __qmljs_init_closure(ctx, &value, clos);
+    __qmljs_set_activation_property(ctx, name, &value);
 }
 
 void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name)
@@ -248,7 +263,10 @@ void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *na
 
 void __qmljs_get_activation_property(Context *ctx, Value *result, String *name)
 {
-    __qmljs_get_property(ctx, result, &ctx->activation, name);
+    if (Value *prop = ctx->lookup(name))
+        *result = *prop;
+    else
+        assert(!"reference error");
 }
 
 void __qmljs_get_activation(Context *ctx, Value *result)
@@ -349,34 +367,16 @@ bool __qmljs_equal(Context *ctx, const Value *x, const Value *y)
     return false;
 }
 
-Context *__qmljs_new_context(Context *current, Value *thisObject, size_t argc)
+void __qmljs_call_activation_property(Context *context, Value *result, String *name, Value *args, int argc)
 {
-    Context *ctx = new Context;
-    ctx->init();
-    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 __qmljs_dispose_context(Context *ctx)
-{
-    delete[] ctx->arguments;
-    delete ctx;
-}
+    Value *func = context->lookup(name);
+    if (! func)
+        assert(!"reference error");
 
-void __qmljs_call_activation_property(Context *context, Value *result, String *name)
-{
-    __qmljs_call_property(context, result, &context->parent->activation, name);
+    __qmljs_call_value(context, result, func, args, argc);
 }
 
-void __qmljs_call_property(Context *context, Value *result, Value *base, String *name)
+void __qmljs_call_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc)
 {
     Value baseObject;
     Value thisObject;
@@ -394,12 +394,18 @@ void __qmljs_call_property(Context *context, Value *result, Value *base, String
     baseObject.objectValue->get(name, &func);
     if (func.type == OBJECT_TYPE) {
         if (FunctionObject *f = func.objectValue->asFunctionObject()) {
-            context->thisObject = thisObject;
-            context->formals = f->formalParameterList;
-            context->formalCount = f->formalParameterCount;
-            f->call(context);
+            Context *ctx = new Context;
+            ctx->init();
+            __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx));
+            ctx->parent = f->scope;
+            ctx->thisObject = thisObject;
+            ctx->formals = f->formalParameterList;
+            ctx->formalCount = f->formalParameterCount;
+            ctx->arguments = args;
+            ctx->argumentCount = argc;
+            f->call(ctx);
             if (result)
-                __qmljs_copy(result, &context->result);
+                __qmljs_copy(result, &ctx->result);
         } else {
             assert(!"not a function");
         }
@@ -408,16 +414,27 @@ void __qmljs_call_property(Context *context, Value *result, Value *base, String
     }
 }
 
-void __qmljs_call_value(Context *context, Value *result, Value *func)
+void __qmljs_call_value(Context *context, Value *result, const Value *func, Value *args, int argc)
 {
     if (func->type == OBJECT_TYPE) {
         if (FunctionObject *f = func->objectValue->asFunctionObject()) {
-            __qmljs_init_null(context, &context->thisObject);
-            context->formals = f->formalParameterList;
-            context->formalCount = f->formalParameterCount;
-            f->call(context);
+            Context *ctx = new Context;
+            ctx->init();
+            ctx->parent = f->scope;
+            __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx));
+            __qmljs_init_null(ctx, &ctx->thisObject);
+            ctx->formals = f->formalParameterList;
+            ctx->formalCount = f->formalParameterCount;
+            if (argc) {
+                ctx->arguments = new Value[argc];
+                std::copy(args, args + argc, ctx->arguments);
+            } else {
+                ctx->arguments = 0;
+            }
+            ctx->argumentCount = argc;
+            f->call(ctx);
             if (result)
-                __qmljs_copy(result, &context->result);
+                __qmljs_copy(result, &ctx->result);
         } else {
             assert(!"not a function");
         }
@@ -426,12 +443,49 @@ void __qmljs_call_value(Context *context, Value *result, Value *func)
     }
 }
 
-void __qmljs_construct_activation_property(Context *context, Value *result, String *name)
+void __qmljs_construct_activation_property(Context *context, Value *result, String *name, Value *args, int argc)
 {
-    __qmljs_construct_property(context, result, &context->activation, name);
+    Value *func = context->lookup(name);
+    if (! func)
+        assert(!"reference error");
+
+    __qmljs_construct_value(context, result, func, args, argc);
+}
+
+void __qmljs_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc)
+{
+    if (func->type == OBJECT_TYPE) {
+        if (FunctionObject *f = func->objectValue->asFunctionObject()) {
+            Context *ctx = new Context;
+            ctx->init();
+            ctx->parent = f->scope;
+            __qmljs_init_null(ctx, &ctx->thisObject);
+            __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx));
+            ctx->formals = f->formalParameterList;
+            ctx->formalCount = f->formalParameterCount;
+            ctx->arguments = args;
+            ctx->argumentCount = argc;
+            ctx->calledAsConstructor = true;
+            f->construct(ctx);
+            assert(ctx->thisObject.is(OBJECT_TYPE));
+            ctx->result = ctx->thisObject;
+            Value proto;
+            if (f->get(String::get(ctx, QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol
+                if (proto.type == OBJECT_TYPE)
+                    ctx->thisObject.objectValue->prototype = proto.objectValue;
+            }
+
+            if (result)
+                __qmljs_copy(result, &ctx->thisObject);
+        } else {
+            assert(!"not a function");
+        }
+    } else {
+        assert(!"not a callable object");
+    }
 }
 
-void __qmljs_construct_property(Context *context, Value *result, Value *base, String *name)
+void __qmljs_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc)
 {
     Value func;
     Value thisObject = *base;
@@ -442,20 +496,28 @@ void __qmljs_construct_property(Context *context, Value *result, Value *base, St
     thisObject.objectValue->get(name, &func);
     if (func.type == OBJECT_TYPE) {
         if (FunctionObject *f = func.objectValue->asFunctionObject()) {
-            context->thisObject = thisObject;
-            context->formals = f->formalParameterList;
-            context->formalCount = f->formalParameterCount;
-            context->calledAsConstructor = true;
-            f->construct(context);
+            Context *ctx = new Context;
+            ctx->init();
+            ctx->parent = f->scope;
+            ctx->thisObject = thisObject;
+            __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx));
+            ctx->formals = f->formalParameterList;
+            ctx->formalCount = f->formalParameterCount;
+            ctx->calledAsConstructor = true;
+            ctx->arguments = args;
+            ctx->argumentCount = argc;
+            ctx->calledAsConstructor = true;
+            f->construct(ctx);
+            assert(ctx->thisObject.is(OBJECT_TYPE));
 
             Value proto;
-            if (f->get(String::get(context, QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol
+            if (f->get(String::get(ctx, QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol
                 if (proto.type == OBJECT_TYPE)
-                    context->thisObject.objectValue->prototype = proto.objectValue;
+                    ctx->thisObject.objectValue->prototype = proto.objectValue;
             }
 
             if (result)
-                __qmljs_copy(result, &context->thisObject);
+                __qmljs_copy(result, &ctx->thisObject);
         } else {
             assert(!"not a function");
         }
index c75e502..143319e 100644 (file)
@@ -44,13 +44,13 @@ 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);
-void __qmljs_construct_activation_property(Context *, Value *result, String *name);
-void __qmljs_call_property(Context *context, Value *result, Value *base, String *name);
-void __qmljs_construct_property(Context *context, Value *result, Value *base, String *name);
-void __qmljs_call_value(Context *context, Value *result, Value *func);
+void __qmljs_call_activation_property(Context *, Value *result, String *name, Value *args, int argc);
+void __qmljs_call_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc);
+void __qmljs_call_value(Context *context, Value *result, const Value *func, Value *args, int argc);
+
+void __qmljs_construct_activation_property(Context *, Value *result, String *name, Value *args, int argc);
+void __qmljs_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc);
+void __qmljs_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc);
 
 // constructors
 void __qmljs_init_undefined(Context *ctx, Value *result);
index 2bc9dbd..221ab5e 100644 (file)
@@ -159,15 +159,19 @@ struct ScanFunctionBody: Visitor
 
     // search for locals
     QList<QStringRef> locals;
-    bool directEval;
+    int maxNumberOfArguments;
+    bool hasDirectEval;
+    bool hasNestedFunctions;
 
     ScanFunctionBody()
-        : directEval(false)
+        : maxNumberOfArguments(0)
+        , hasDirectEval(false)
+        , hasNestedFunctions(false)
     {
     }
 
     void operator()(Node *node) {
-        directEval = false;
+        hasDirectEval = false;
         locals.clear();
         if (node)
             node->accept(this);
@@ -176,13 +180,26 @@ struct ScanFunctionBody: Visitor
 protected:
     virtual bool visit(CallExpression *ast)
     {
-        if (! directEval) {
+        if (! hasDirectEval) {
             if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) {
                 if (id->name == QLatin1String("eval")) {
-                    directEval = true;
+                    hasDirectEval = true;
                 }
             }
         }
+        int argc = 0;
+        for (AST::ArgumentList *it = ast->arguments; it; it = it->next)
+            ++argc;
+        maxNumberOfArguments = qMax(maxNumberOfArguments, argc);
+        return true;
+    }
+
+    virtual bool visit(NewMemberExpression *ast)
+    {
+        int argc = 0;
+        for (AST::ArgumentList *it = ast->arguments; it; it = it->next)
+            ++argc;
+        maxNumberOfArguments = qMax(maxNumberOfArguments, argc);
         return true;
     }
 
@@ -195,6 +212,7 @@ protected:
 
     virtual bool visit(FunctionExpression *ast)
     {
+        hasNestedFunctions = true;
         if (! locals.contains(ast->name))
             locals.append(ast->name);
         return false;
@@ -202,6 +220,7 @@ protected:
 
     virtual bool visit(FunctionDeclaration *ast)
     {
+        hasNestedFunctions = true;
         if (! locals.contains(ast->name))
             locals.append(ast->name);
         return false;
@@ -223,7 +242,9 @@ void Codegen::operator()(AST::Program *node, IR::Module *module)
     _module = module;
 
     IR::Function *globalCode = _module->newFunction(QLatin1String("%entry"));
-    globalCode->directEval = true; // ### remove
+    globalCode->hasDirectEval = true; // ### remove
+    globalCode->hasNestedFunctions = true; // ### remove
+    globalCode->redArea = 10; // ### remove
     _function = globalCode;
     _block = _function->newBasicBlock();
     _exitBlock = _function->newBasicBlock();
@@ -291,6 +312,15 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right)
     return _block->BINOP(op, left, right);
 }
 
+IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args)
+{
+    if (base->asMember() || base->asName() || base->asTemp())
+        return _block->CALL(base, args);
+    const unsigned t = _block->newTemp();
+    _block->MOVE(_block->TEMP(t), base);
+    return _block->CALL(_block->TEMP(t), args);
+}
+
 void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op)
 {
     if (target->asMember()) {
@@ -519,7 +549,7 @@ void Codegen::variableDeclaration(VariableDeclaration *ast)
         if (! expr.code)
             expr.code = _block->CONST(IR::UndefinedType, 0);
 
-        if (! _function->directEval) {
+        if (! _function->needsActivation()) {
             const int index = tempForLocalVariable(ast->name);
             if (index != -1) {
                 move(_block->TEMP(index), *expr);
@@ -895,7 +925,7 @@ bool Codegen::visit(CallExpression *ast)
         (*args_it)->init(actual);
         args_it = &(*args_it)->next;
     }
-    _expr.code = _block->CALL(*base, args);
+    _expr.code = call(*base, args);
     return false;
 }
 
@@ -951,7 +981,7 @@ bool Codegen::visit(FunctionExpression *ast)
 
 bool Codegen::visit(IdentifierExpression *ast)
 {
-    if (! _function->directEval) {
+    if (! _function->needsActivation()) {
         int index = tempForLocalVariable(ast->name);
         if (index != -1) {
             _expr.code = _block->TEMP(index);
@@ -1121,7 +1151,7 @@ bool Codegen::visit(TypeOfExpression *ast)
     Result expr = expression(ast->expression);
     IR::ExprList *args = _function->New<IR::ExprList>();
     args->init(argument(*expr));
-    _expr.code = _block->CALL(_block->NAME(QLatin1String("typeof"), ast->typeofToken.startLine, ast->typeofToken.startColumn), args);
+    _expr.code = call(_block->NAME(QLatin1String("typeof"), ast->typeofToken.startLine, ast->typeofToken.startColumn), args);
     return false;
 }
 
@@ -1308,8 +1338,10 @@ void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/)
     IR::Function *function = _module->newFunction(ast->name.toString());
     IR::BasicBlock *entryBlock = function->newBasicBlock();
     IR::BasicBlock *exitBlock = function->newBasicBlock();
+    function->hasDirectEval = functionInfo.hasDirectEval;
+    function->redArea = functionInfo.maxNumberOfArguments;
 
-    if (! functionInfo.directEval) {
+    if (! functionInfo.hasDirectEval) {
         for (int i = 0; i < functionInfo.locals.size(); ++i) {
             unsigned t = entryBlock->newTemp();
             Q_ASSERT(t == unsigned(i));
index e58ceb5..63c12c3 100644 (file)
@@ -71,6 +71,7 @@ protected:
     IR::Expr *subscript(IR::Expr *base, IR::Expr *index);
     IR::Expr *argument(IR::Expr *expr);
     IR::Expr *binop(IR::AluOp op, IR::Expr *left, IR::Expr *right);
+    IR::Expr *call(IR::Expr *base, IR::ExprList *args);
     void move(IR::Expr *target, IR::Expr *source, IR::AluOp op = IR::OpInvalid);
 
     void linearize(IR::Function *function);
index a77a58f..57fe54b 100644 (file)
--- a/qv4ir_p.h
+++ b/qv4ir_p.h
@@ -587,11 +587,13 @@ struct Function {
     const QString *name;
     QVector<BasicBlock *> basicBlocks;
     int tempCount;
+    int redArea;
     QSet<QString> strings;
     QList<const QString *> formals;
     QList<const QString *> locals;
     void (*code)(VM::Context *);
-    bool directEval;
+    bool hasDirectEval: 1;
+    bool hasNestedFunctions: 1;
 
     template <typename _Tp> _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); }
 
@@ -599,8 +601,10 @@ struct Function {
         : module(module)
         , pool(&module->pool)
         , tempCount(0)
+        , redArea(0)
         , code(0)
-        , directEval(false)
+        , hasDirectEval(false)
+        , hasNestedFunctions(false)
     { this->name = newString(name); }
 
     ~Function();
@@ -614,6 +618,8 @@ struct Function {
     inline BasicBlock *i(BasicBlock *block) { basicBlocks.append(block); return block; }
 
     void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR);
+
+    inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval; }
 };
 
 struct BasicBlock {
index a689bec..c53741b 100644 (file)
@@ -101,7 +101,7 @@ void InstructionSelection::operator()(IR::Function *function)
     function->code = (void (*)(VM::Context *)) _code;
     _codePtr = _code;
 
-    int locals = function->tempCount * sizeof(Value);
+    int locals = (function->tempCount + function->redArea) * sizeof(Value);
     locals = (locals + 15) & ~15;
 
     amd64_push_reg(_codePtr, AMD64_RBP);
@@ -179,37 +179,30 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu
     assert(baseName != 0);
 
     int argc = 0;
-    for (IR::ExprList *it = call->args; it; it = it->next)
+    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);
+    int i = 0;
+    for (IR::ExprList *it = call->args; it; it = it->next, ++i) {
+        IR::Temp *arg = it->expr->asTemp();
+        assert(arg != 0);
+        amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i);
+        loadTempAddress(AMD64_RSI, arg);
         amd64_call_code(_codePtr, __qmljs_copy);
-        ++argc;
     }
 
-    String *id = identifier(*baseName->id);
-    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8);
+    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context
+
     if (result)
         loadTempAddress(AMD64_RSI, result);
     else
         amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI);
-    amd64_mov_reg_imm(_codePtr, AMD64_RDX, id);
+
+    amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id));
+    amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0);
+    amd64_mov_reg_imm(_codePtr, AMD64_R8, argc);
     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);
 }
 
 
@@ -219,36 +212,30 @@ void InstructionSelection::callValue(IR::Call *call, IR::Temp *result)
     assert(baseTemp != 0);
 
     int argc = 0;
-    for (IR::ExprList *it = call->args; it; it = it->next)
+    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);
+    int i = 0;
+    for (IR::ExprList *it = call->args; it; it = it->next, ++i) {
+        IR::Temp *arg = it->expr->asTemp();
+        assert(arg != 0);
+        amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i);
+        loadTempAddress(AMD64_RSI, arg);
         amd64_call_code(_codePtr, __qmljs_copy);
-        ++argc;
     }
 
-    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8);
+    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context
+
     if (result)
         loadTempAddress(AMD64_RSI, result);
     else
         amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI);
+
     loadTempAddress(AMD64_RDX, baseTemp);
+    amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0);
+    amd64_mov_reg_imm(_codePtr, AMD64_R8, argc);
     amd64_call_code(_codePtr, __qmljs_call_value);
-    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8);
-    amd64_call_code(_codePtr, __qmljs_dispose_context);
 }
 
 void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result)
@@ -258,119 +245,99 @@ void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result)
     assert(member->base->asTemp());
 
     int argc = 0;
-    for (IR::ExprList *it = call->args; it; it = it->next)
+    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);
+    int i = 0;
+    for (IR::ExprList *it = call->args; it; it = it->next, ++i) {
+        IR::Temp *arg = it->expr->asTemp();
+        assert(arg != 0);
+        amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i);
+        loadTempAddress(AMD64_RSI, arg);
         amd64_call_code(_codePtr, __qmljs_copy);
-        ++argc;
     }
 
-    // __qmljs_call_property(Context *context, Value *result, Value *base, String *name)
+    //__qmljs_call_property(ctx, result, base, name, args, argc);
+    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context
 
-    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_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0);
+    amd64_mov_reg_imm(_codePtr, AMD64_R9, argc);
     amd64_call_code(_codePtr, __qmljs_call_property);
-
-    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8);
-    amd64_call_code(_codePtr, __qmljs_dispose_context);
 }
 
 void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result)
 {
+    IR::Name *baseName = call->base->asName();
+    assert(baseName != 0);
+
     int argc = 0;
-    for (IR::ExprList *it = call->args; it; it = it->next)
+    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);
+    int i = 0;
+    for (IR::ExprList *it = call->args; it; it = it->next, ++i) {
+        IR::Temp *arg = it->expr->asTemp();
+        assert(arg != 0);
+        amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i);
+        loadTempAddress(AMD64_RSI, arg);
         amd64_call_code(_codePtr, __qmljs_copy);
-        ++argc;
     }
 
-    String *id = identifier(*call->base->asName()->id);
-    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8);
+    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context
+
     if (result)
         loadTempAddress(AMD64_RSI, result);
     else
         amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI);
-    amd64_mov_reg_imm(_codePtr, AMD64_RDX, id);
+
+    amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id));
+    amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0);
+    amd64_mov_reg_imm(_codePtr, AMD64_R8, argc);
     amd64_call_code(_codePtr, __qmljs_construct_activation_property);
-    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8);
-    amd64_call_code(_codePtr, __qmljs_dispose_context);
 }
 
-void InstructionSelection::constructProperty(IR::New *ctor, IR::Temp *result)
+void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result)
 {
-    IR::Member *member = ctor->base->asMember();
+    IR::Member *member = call->base->asMember();
     assert(member != 0);
     assert(member->base->asTemp());
 
     int argc = 0;
-    for (IR::ExprList *it = ctor->args; it; it = it->next)
+    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 = 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);
+    int i = 0;
+    for (IR::ExprList *it = call->args; it; it = it->next, ++i) {
+        IR::Temp *arg = it->expr->asTemp();
+        assert(arg != 0);
+        amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i);
+        loadTempAddress(AMD64_RSI, arg);
         amd64_call_code(_codePtr, __qmljs_copy);
-        ++argc;
     }
 
-    // __qmljs_construct_property(Context *context, Value *result, Value *base, String *name)
+    //__qmljs_call_property(ctx, result, base, name, args, argc);
+    amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context
 
-    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_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0);
+    amd64_mov_reg_imm(_codePtr, AMD64_R9, argc);
     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)
diff --git a/tests/fun.3.js b/tests/fun.3.js
new file mode 100644 (file)
index 0000000..5add270
--- /dev/null
@@ -0,0 +1,17 @@
+
+function fix(f) {
+    var k = function (x) {
+        return f(function (z) { return x(x)(z) })
+    }
+    return k(k)
+}
+
+var F = function (f) {
+    return function (n) {
+        return n == 0 ? 1 : n * f(n - 1)
+    }
+}
+
+var fact = fix(F)
+
+print("the factorial of 12 is", fact(12))