globalCode->code(ctx);
+ if (ctx->hasUncaughtException)
+ qWarning() << "Uncaught exception"; // << qPrintable(ctx->result.toString(ctx)->toQString());
+
delete[] ctx->locals;
delete[] ctx->vars;
}
vars = f->varList;
varCount = f->varCount;
locals = varCount ? new Value[varCount] : 0;
+ hasUncaughtException = false;
+ calledAsConstructor = false;
std::fill(locals, locals + varCount, Value::undefinedValue());
}
size_t formalCount;
String **vars;
size_t varCount;
- bool calledAsConstructor;
+ int calledAsConstructor;
+ int hasUncaughtException;
Value *lookup(String *name)
{
vars = 0;
varCount = 0;
calledAsConstructor = false;
+ hasUncaughtException = false;
}
void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc);
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");
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");
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");
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");
}
}
+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"
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);
, _function(0)
, _block(0)
, _exitBlock(0)
+ , _throwBlock(0)
, _returnAddress(0)
, _env(0)
{
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;
}
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;
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) {
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();
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;
}
IR::Function *_function;
IR::BasicBlock *_block;
IR::BasicBlock *_exitBlock;
+ IR::BasicBlock *_throwBlock;
unsigned _returnAddress;
Environment *_env;
QHash<AST::Node *, Environment *> _envMap;
{
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)
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>();
Stmt *BasicBlock::EXP(Expr *expr)
{
+ if (isTerminated())
+ return 0;
+
Exp *s = function->New<Exp>();
s->init(expr);
statements.append(s);
Stmt *BasicBlock::ENTER(Expr *expr)
{
+ if (isTerminated())
+ return 0;
+
Enter *s = function->New<Enter>();
s->init(expr);
statements.append(s);
Stmt *BasicBlock::LEAVE()
{
+ if (isTerminated())
+ return 0;
+
Leave *s = function->New<Leave>();
s->init();
statements.append(s);
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);
};
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; }
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);
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);
}
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)
--- /dev/null
+
+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")
--- /dev/null
+
+var o = {
+ number: 123,
+ fun: function() {},
+ string: "ciao",
+ array: [],
+}
+
+print(typeof o)
+print(typeof o.number)
+print(typeof o.fun)
+print(typeof o.array)