Throw exceptions
authorRoberto Raggi <roberto.raggi@nokia.com>
Wed, 23 May 2012 16:48:48 +0000 (18:48 +0200)
committerRoberto Raggi <roberto.raggi@nokia.com>
Wed, 23 May 2012 16:50:24 +0000 (18:50 +0200)
12 files changed:
main.cpp
qmljs_objects.cpp
qmljs_objects.h
qmljs_runtime.cpp
qmljs_runtime.h
qv4codegen.cpp
qv4codegen_p.h
qv4ir.cpp
qv4ir_p.h
qv4isel.cpp
tests/exceptions.1.js [new file with mode: 0644]
tests/typeof.1.js [new file with mode: 0644]

index 354485c..de7cb87 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -104,6 +104,9 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS
 
     globalCode->code(ctx);
 
+    if (ctx->hasUncaughtException)
+        qWarning() << "Uncaught exception"; // << qPrintable(ctx->result.toString(ctx)->toQString());
+
     delete[] ctx->locals;
     delete[] ctx->vars;
 }
index cd5a0e9..4b4d858 100644 (file)
@@ -463,6 +463,8 @@ void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionO
     vars = f->varList;
     varCount = f->varCount;
     locals = varCount ? new Value[varCount] : 0;
+    hasUncaughtException = false;
+    calledAsConstructor = false;
     std::fill(locals, locals + varCount, Value::undefinedValue());
 }
 
index 562da2f..1d728a0 100644 (file)
@@ -322,7 +322,8 @@ struct Context {
     size_t formalCount;
     String **vars;
     size_t varCount;
-    bool calledAsConstructor;
+    int calledAsConstructor;
+    int hasUncaughtException;
 
     Value *lookup(String *name)
     {
@@ -366,6 +367,7 @@ struct Context {
         vars = 0;
         varCount = 0;
         calledAsConstructor = false;
+        hasUncaughtException = false;
     }
 
     void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc);
index 91b23ed..f00eb29 100644 (file)
@@ -692,6 +692,10 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S
             Context *ctx = f->needsActivation ? context->engine->newContext() : &k;
             ctx->initCallContext(context->engine, &thisObject, f, args, argc);
             f->call(ctx);
+            if (ctx->hasUncaughtException) {
+                context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception
+                context->result = ctx->result;
+            }
             ctx->leaveCallContext(f, result);
         } else {
             assert(!"not a function");
@@ -709,6 +713,10 @@ void __qmljs_call_value(Context *context, Value *result, const Value *thisObject
             Context *ctx = f->needsActivation ? context->engine->newContext() : &k;
             ctx->initCallContext(context->engine, thisObject, f, args, argc);
             f->call(ctx);
+            if (ctx->hasUncaughtException) {
+                context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception
+                context->result = ctx->result;
+            }
             ctx->leaveCallContext(f, result);
         } else {
             assert(!"not a function");
@@ -736,6 +744,10 @@ void __qmljs_construct_value(Context *context, Value *result, const Value *func,
             Context *ctx = f->needsActivation ? context->engine->newContext() : &k;
             ctx->initConstructorContext(context->engine, 0, f, args, argc);
             f->construct(ctx);
+            if (ctx->hasUncaughtException) {
+                context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception
+                context->result = ctx->result;
+            }
             ctx->leaveConstructorContext(f, result);
         } else {
             assert(!"not a function");
@@ -761,6 +773,10 @@ void __qmljs_construct_property(Context *context, Value *result, const Value *ba
             ctx->initConstructorContext(context->engine, 0, f, args, argc);
             ctx->calledAsConstructor = true;
             f->construct(ctx);
+            if (ctx->hasUncaughtException) {
+                context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception
+                context->result = ctx->result;
+            }
             ctx->leaveConstructorContext(f, result);
         } else {
             assert(!"not a function");
@@ -770,6 +786,21 @@ void __qmljs_construct_property(Context *context, Value *result, const Value *ba
     }
 }
 
+void __qmljs_builtin_typeof(Context *context, Value *result, Value *args, int argc)
+{
+    Q_UNUSED(argc);
+    __qmljs_typeof(context, result, &args[0]);
+}
+
+void __qmljs_builtin_throw(Context *context, Value *result, Value *args, int argc)
+{
+    Q_UNUSED(result);
+    Q_UNUSED(argc);
+    context->result = args[0];
+    context->hasUncaughtException = true;
+}
+
+
 } // extern "C"
 
 
index cd571fd..afe3f07 100644 (file)
@@ -61,6 +61,9 @@ void __qmljs_construct_activation_property(Context *, Value *result, String *nam
 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);
 
+void __qmljs_builtin_typeof(Context *context, Value *result, Value *args, int argc);
+void __qmljs_builtin_throw(Context *context, Value *result, Value *args, int argc);
+
 // constructors
 void __qmljs_init_undefined(Value *result);
 void __qmljs_init_null(Value *result);
index 86dfe84..6d475b3 100644 (file)
@@ -272,6 +272,7 @@ Codegen::Codegen()
     , _function(0)
     , _block(0)
     , _exitBlock(0)
+    , _throwBlock(0)
     , _returnAddress(0)
     , _env(0)
 {
@@ -1237,7 +1238,7 @@ bool Codegen::visit(TypeOfExpression *ast)
     Result expr = expression(ast->expression);
     IR::ExprList *args = _function->New<IR::ExprList>();
     args->init(argument(*expr));
-    _expr.code = call(_block->NAME(QLatin1String("typeof"), ast->typeofToken.startLine, ast->typeofToken.startColumn), args);
+    _expr.code = call(_block->NAME(IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args);
     return false;
 }
 
@@ -1439,6 +1440,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
     IR::Function *function = _module->newFunction(name);
     IR::BasicBlock *entryBlock = function->newBasicBlock();
     IR::BasicBlock *exitBlock = function->newBasicBlock();
+    IR::BasicBlock *throwBlock = function->newBasicBlock();
     function->hasDirectEval = _env->hasDirectEval;
     function->hasNestedFunctions = _env->hasNestedFunctions;
     function->maxNumberOfArguments = _env->maxNumberOfArguments;
@@ -1452,10 +1454,14 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
 
     entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(IR::UndefinedType, 0));
     exitBlock->RET(exitBlock->TEMP(returnAddress), IR::InvalidType);
+    IR::ExprList *throwArgs = function->New<IR::ExprList>();
+    throwArgs->expr = throwBlock->TEMP(returnAddress);
+    throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs));
 
     qSwap(_function, function);
     qSwap(_block, entryBlock);
     qSwap(_exitBlock, exitBlock);
+    qSwap(_throwBlock, throwBlock);
     qSwap(_returnAddress, returnAddress);
 
     for (FormalParameterList *it = formals; it; it = it->next) {
@@ -1471,9 +1477,13 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
     if (! _block->isTerminated())
         _block->JUMP(_exitBlock);
 
+    if (! _throwBlock->isTerminated())
+        _throwBlock->JUMP(_exitBlock);
+
     qSwap(_function, function);
     qSwap(_block, entryBlock);
     qSwap(_exitBlock, exitBlock);
+    qSwap(_throwBlock, throwBlock);
     qSwap(_returnAddress, returnAddress);
 
     leaveEnvironment();
@@ -1708,10 +1718,11 @@ bool Codegen::visit(SwitchStatement *)
     return false;
 }
 
-bool Codegen::visit(ThrowStatement *)
+bool Codegen::visit(ThrowStatement *ast)
 {
-    //Q_ASSERT(!"not implemented");
-    _expr.code = _block->CONST(IR::UndefinedType, 0);
+    Result expr = expression(ast->expression);
+    _block->MOVE(_block->TEMP(_returnAddress), *expr);
+    _block->JUMP(_throwBlock);
     return false;
 }
 
index b93dac1..9deec76 100644 (file)
@@ -283,6 +283,7 @@ private:
     IR::Function *_function;
     IR::BasicBlock *_block;
     IR::BasicBlock *_exitBlock;
+    IR::BasicBlock *_throwBlock;
     unsigned _returnAddress;
     Environment *_env;
     QHash<AST::Node *, Environment *> _envMap;
index 6bcf6b5..e42f055 100644 (file)
--- a/qv4ir.cpp
+++ b/qv4ir.cpp
@@ -230,13 +230,26 @@ void Name::init(Type type, const QString *id, quint32 line, quint32 column)
 {
     this->type = type;
     this->id = id;
+    this->builtin = builtin_invalid;
+    this->line = line;
+    this->column = column;
+}
+
+void Name::init(Type type, Builtin builtin, quint32 line, quint32 column)
+{
+    this->type = type;
+    this->id = 0;
+    this->builtin = builtin;
     this->line = line;
     this->column = column;
 }
 
 void Name::dump(QTextStream &out)
 {
-    out << *id;
+    if (id)
+        out << *id;
+    else
+        out << "__qmljs_builtin_%" << (int) builtin;
 }
 
 void Temp::dump(QTextStream &out)
@@ -525,6 +538,13 @@ Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column)
     return e;
 }
 
+Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column)
+{
+    Name *e = function->New<Name>();
+    e->init(InvalidType, builtin, line, column);
+    return e;
+}
+
 Closure *BasicBlock::CLOSURE(Function *function)
 {
     Closure *clos = function->New<Closure>();
@@ -622,6 +642,9 @@ Expr *BasicBlock::MEMBER(Expr *base, const QString *name)
 
 Stmt *BasicBlock::EXP(Expr *expr)
 { 
+    if (isTerminated())
+        return 0;
+
     Exp *s = function->New<Exp>();
     s->init(expr);
     statements.append(s);
@@ -630,6 +653,9 @@ Stmt *BasicBlock::EXP(Expr *expr)
 
 Stmt *BasicBlock::ENTER(Expr *expr)
 {
+    if (isTerminated())
+        return 0;
+
     Enter *s = function->New<Enter>();
     s->init(expr);
     statements.append(s);
@@ -638,6 +664,9 @@ Stmt *BasicBlock::ENTER(Expr *expr)
 
 Stmt *BasicBlock::LEAVE()
 {
+    if (isTerminated())
+        return 0;
+
     Leave *s = function->New<Leave>();
     s->init();
     statements.append(s);
@@ -646,6 +675,9 @@ Stmt *BasicBlock::LEAVE()
 
 Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op)
 { 
+    if (isTerminated())
+        return 0;
+
     Move *s = function->New<Move>();
     s->init(target, source, op);
     statements.append(s);
index e65b52a..d35f7e4 100644 (file)
--- a/qv4ir_p.h
+++ b/qv4ir_p.h
@@ -255,11 +255,19 @@ struct String: Expr {
 };
 
 struct Name: Expr {
+    enum Builtin {
+        builtin_invalid,
+        builtin_typeof,
+        builtin_throw
+    };
+
     const QString *id;
+    Builtin builtin;
     quint32 line;
     quint32 column;
 
     void init(Type type, const QString *id, quint32 line, quint32 column);
+    void init(Type type, Builtin builtin, quint32 line, quint32 column);
 
     virtual void accept(ExprVisitor *v) { v->visitName(this); }
     virtual Name *asName() { return this; }
@@ -661,6 +669,7 @@ struct BasicBlock {
     Expr *STRING(const QString *value);
 
     Name *NAME(const QString &id, quint32 line, quint32 column);
+    Name *NAME(Name::Builtin builtin, quint32 line, quint32 column);
 
     Closure *CLOSURE(Function *function);
 
index dbfaad0..0ef8268 100644 (file)
@@ -203,10 +203,33 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu
     else
         amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI);
 
-    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);
+    if (baseName->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);
+    } else {
+        switch (baseName->builtin) {
+        case IR::Name::builtin_invalid:
+            Q_UNREACHABLE();
+            break;
+        case IR::Name::builtin_typeof:
+            amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0);
+            amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc);
+            amd64_call_code(_codePtr, __qmljs_builtin_typeof);
+            break;
+        case IR::Name::builtin_throw:
+            amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0);
+            amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc);
+            amd64_call_code(_codePtr, __qmljs_builtin_throw);
+            break;
+        }
+    }
+
+    amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R14, offsetof(Context, hasUncaughtException), 4);
+    amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 1, 1);
+    _patches[_function->basicBlocks.last()].append(_codePtr); // ### TODO: jump to the exception handler
+    amd64_branch32(_codePtr, X86_CC_E, /* exception handler */ 0, 1);
 }
 
 
@@ -241,6 +264,11 @@ void InstructionSelection::callValue(IR::Call *call, IR::Temp *result)
     amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0);
     amd64_mov_reg_imm(_codePtr, AMD64_R9, argc);
     amd64_call_code(_codePtr, __qmljs_call_value);
+
+    amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R14, offsetof(Context, hasUncaughtException), 4);
+    amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 1, 1);
+    _patches[_function->basicBlocks.last()].append(_codePtr); // ### TODO: jump to the exception handler
+    amd64_branch32(_codePtr, X86_CC_E, /* exception handler */ 0, 1);
 }
 
 void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result)
diff --git a/tests/exceptions.1.js b/tests/exceptions.1.js
new file mode 100644 (file)
index 0000000..1f10d98
--- /dev/null
@@ -0,0 +1,16 @@
+
+function foo(a) {
+    x = 1
+    if (a)
+        throw 0;
+    print("unreachable.1")
+}
+
+function bar(a) {
+    print("reachable");
+    foo(a)
+    print("unreachable.2")
+}
+
+bar(1)
+print("unreachable.3")
diff --git a/tests/typeof.1.js b/tests/typeof.1.js
new file mode 100644 (file)
index 0000000..a24cfc6
--- /dev/null
@@ -0,0 +1,12 @@
+
+var o = {
+    number: 123,
+    fun: function() {},
+    string: "ciao",
+    array: [],
+}
+
+print(typeof o)
+print(typeof o.number)
+print(typeof o.fun)
+print(typeof o.array)