From dc716f8d372f282027ff7bd950b6f000594f2224 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 7 May 2012 16:05:05 +0200 Subject: [PATCH] Add naive support for function calls. --- main.cpp | 31 +++++++++++++++++-- qmljs_objects.cpp | 37 ++++++++++++++-------- qmljs_objects.h | 27 +++++++++++++--- qmljs_runtime.cpp | 50 +++++++++++++++++++----------- qmljs_runtime.h | 12 +++++--- qv4codegen.cpp | 4 ++- qv4isel.cpp | 92 +++++++++++++++++++++++++++++-------------------------- qv4isel_p.h | 1 + 8 files changed, 169 insertions(+), 85 deletions(-) diff --git a/main.cpp b/main.cpp index e95a3aa..4be5e07 100644 --- a/main.cpp +++ b/main.cpp @@ -21,6 +21,24 @@ static inline bool protect(const void *addr, size_t size) return mprotect(reinterpret_cast(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 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); } } diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 57332ab..37e3537 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -1,5 +1,7 @@ #include "qmljs_objects.h" +#include "qv4ir_p.h" +#include #include 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; } diff --git a/qmljs_objects.h b/qmljs_objects.h index cabdf5c..17c85b0 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -8,6 +8,11 @@ #include 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; } }; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index f95060e..4870c0b 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -1,6 +1,7 @@ #include "qmljs_runtime.h" #include "qmljs_objects.h" +#include #include #include @@ -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" diff --git a/qmljs_runtime.h b/qmljs_runtime.h index d295c3f..438206b 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -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)); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 6f9181b..24c6c81 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -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; } diff --git a/qv4isel.cpp b/qv4isel.cpp index a5a63da..f0fc00f 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -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); } diff --git a/qv4isel_p.h b/qv4isel_p.h index 0672bc5..753da04 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -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 *); -- 2.7.4