__qmljs_throw(*value, context);
}
-void __qmljs_llvm_rethrow(Context *context, Value *result)
-{
- *result = __qmljs_rethrow(context);
-}
-
void __qmljs_llvm_get_this_object(Context *ctx, Value *result)
{
*result = __qmljs_get_thisObject(ctx);
#endif
#include "qmljs_objects.h"
+#include "qmljs_runtime.h"
#include "qv4codegen_p.h"
#include "qv4isel_masm_p.h"
#include "qv4isel_moth_p.h"
return;
}
- ctx->hasUncaughtException = false;
if (! ctx->activation.isObject())
ctx->activation = VM::Value::fromObject(new QQmlJS::VM::Object());
ctx->activation.objectValue()->setProperty(ctx, *local, QQmlJS::VM::Value::undefinedValue());
}
+ void * buf = __qmljs_create_exception_handler(ctx);
+ if (setjmp(*(jmp_buf *)buf)) {
+ if (VM::ErrorObject *e = ctx->result.asErrorObject())
+ std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl;
+ else
+ std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl;
+ return;
+ }
+
if (useMoth) {
Moth::VME vme;
vme(ctx, code);
globalCode->code(ctx, globalCode->codeData);
}
- if (ctx->hasUncaughtException) {
- if (VM::ErrorObject *e = ctx->result.asErrorObject())
- std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl;
- else
- std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl;
- } else if (! ctx->result.isUndefined()) {
+ if (! ctx->result.isUndefined()) {
if (! qgetenv("SHOW_EXIT_VALUE").isNull())
std::cout << "exit value: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl;
}
#include <QtCore/QRegularExpression>
#include <cstdio>
#include <cassert>
+#include <setjmp.h>
namespace QQmlJS {
String *id_arguments;
String *id___proto__;
+ struct ExceptionHandler {
+ Context *context;
+ jmp_buf stackFrame;
+ };
+
+ QVector<ExceptionHandler> unwindStack;
+
ExecutionEngine();
Context *newContext();
vars = 0;
varCount = 0;
calledAsConstructor = false;
- hasUncaughtException = false;
}
Value *Context::lookupPropertyDescriptor(String *name)
void Context::throwError(Value value)
{
result = value;
- hasUncaughtException = true;
+ __qmljs_builtin_throw(value, this);
}
void Context::throwError(const QString &message)
throwError(Value::fromObject(engine->newErrorObject(Value::fromString(this, msg))));
}
-void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc)
+void Context::initCallContext(Context *parent, const Value *object, FunctionObject *f, Value *args, unsigned argc)
{
- engine = e;
- parent = f->scope;
+ engine = parent->engine;
+ this->parent = f->scope;
+ assert(this->parent == f->scope);
result = Value::undefinedValue();
if (f->needsActivation)
vars = f->varList;
varCount = f->varCount;
locals = varCount ? new Value[varCount] : 0;
- hasUncaughtException = false;
calledAsConstructor = false;
if (varCount)
std::fill(locals, locals + varCount, Value::undefinedValue());
}
-void Context::leaveCallContext(FunctionObject *f)
+void Context::leaveCallContext()
{
- if (! f->needsActivation) {
+ if (!activation.isNull()) {
delete[] locals;
locals = 0;
}
}
-void Context::initConstructorContext(ExecutionEngine *e, Value *object, FunctionObject *f, Value *args, unsigned argc)
+void Context::initConstructorContext(Context *parent, Value *object, FunctionObject *f, Value *args, unsigned argc)
{
- initCallContext(e, object, f, args, argc);
+ initCallContext(parent, object, f, args, argc);
calledAsConstructor = true;
}
if (! thisObject.isObject())
thisObject.objectValue()->prototype = engine->objectPrototype;
- leaveCallContext(f);
+ leaveCallContext();
}
extern "C" {
if (FunctionObject *f = func.asFunctionObject()) {
Context k;
Context *ctx = f->needsActivation ? context->engine->newContext() : &k;
- ctx->initCallContext(context->engine, &thisObject, f, args, argc);
+ ctx->initCallContext(context, &thisObject, f, args, argc);
f->call(ctx);
- if (ctx->hasUncaughtException) {
- context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception
- context->result = ctx->result;
- }
- ctx->leaveCallContext(f);
+ ctx->leaveCallContext();
result = ctx->result;
} else {
context->throwTypeError();
Context k;
Context *ctx = f->needsActivation ? context->engine->newContext() : &k;
const Value *that = thisObject.isUndefined() ? 0 : &thisObject;
- ctx->initCallContext(context->engine, that, f, args, argc);
+ ctx->initCallContext(context, that, f, args, argc);
f->call(ctx);
- if (ctx->hasUncaughtException) {
- context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception
- context->result = ctx->result;
- }
- ctx->leaveCallContext(f);
+ ctx->leaveCallContext();
return ctx->result;
}
if (FunctionObject *f = func.asFunctionObject()) {
Context k;
Context *ctx = f->needsActivation ? context->engine->newContext() : &k;
- ctx->initConstructorContext(context->engine, 0, f, args, argc);
+ ctx->initConstructorContext(context, 0, f, args, argc);
f->construct(ctx);
- if (ctx->hasUncaughtException) {
- context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception
- context->result = ctx->result;
- }
ctx->leaveConstructorContext(f);
return ctx->result;
}
if (FunctionObject *f = func.asFunctionObject()) {
Context k;
Context *ctx = f->needsActivation ? context->engine->newContext() : &k;
- ctx->initConstructorContext(context->engine, 0, f, args, argc);
+ ctx->initConstructorContext(context, 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);
return ctx->result;
}
void __qmljs_throw(Value value, Context *context)
{
- context->hasUncaughtException = true;
- context->result = value;
+ assert(!context->engine->unwindStack.isEmpty());
+
+ ExecutionEngine::ExceptionHandler handler = context->engine->unwindStack.last();
+
+ // clean up call contexts
+ while (context != handler.context) {
+ context->leaveCallContext();
+ context = context->parent;
+ }
+
+ handler.context->result = value;
+
+ longjmp(handler.stackFrame, 1);
+}
+
+void *__qmljs_create_exception_handler(Context *context)
+{
+ context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler());
+ ExecutionEngine::ExceptionHandler *handler = &context->engine->unwindStack.last();
+ handler->context = context;
+ return handler->stackFrame;
+}
+
+void __qmljs_delete_exception_handler(Context *context)
+{
+ assert(!context->engine->unwindStack.isEmpty());
+
+ context->engine->unwindStack.pop_back();
}
-Value __qmljs_rethrow(Context *context)
+Value __qmljs_get_exception(Context *context)
{
return context->result;
}
__qmljs_throw(val, context);
}
-Value __qmljs_builtin_rethrow(Context *context)
-{
- return context->result;
-}
-
} // extern "C"
Value __qmljs_builtin_typeof(Value val, Context *context);
void __qmljs_builtin_throw(Value val, Context *context);
-Value __qmljs_builtin_rethrow(Context *context);
+
// constructors
Value __qmljs_init_closure(IR::Function *clos, Context *ctx);
Value __qmljs_typeof(Value value, Context *ctx);
void __qmljs_throw(Value value, Context *context);
-Value __qmljs_rethrow(Context *context);
+// actually returns a jmp_buf *
+void *__qmljs_create_exception_handler(Context *context);
+void __qmljs_delete_exception_handler(Context *context);
+Value __qmljs_get_exception(Context *context);
// binary operators
Value __qmljs_instanceof(Value left, Value right, Context *ctx);
String **vars;
unsigned int varCount;
int calledAsConstructor;
- int hasUncaughtException;
Value *lookupPropertyDescriptor(String *name);
void throwUnimplemented(const QString &message);
#endif
- void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc);
- void leaveCallContext(FunctionObject *f);
+ void initCallContext(Context *parent, const Value *object, FunctionObject *f, Value *args, unsigned argc);
+ void leaveCallContext();
- void initConstructorContext(ExecutionEngine *e, Value *object, FunctionObject *f, Value *args, unsigned argc);
+ void initConstructorContext(Context *parent, Value *object, FunctionObject *f, Value *args, unsigned argc);
void leaveConstructorContext(FunctionObject *f);
};
, _block(0)
, _exitBlock(0)
, _throwBlock(0)
- , _handlersBlock(0)
, _returnAddress(0)
, _mode(GlobalCode)
, _env(0)
bool Codegen::visit(IdentifierExpression *ast)
{
+ int index = _env->findMember(ast->name.toString());
+
if (! _function->hasDirectEval && _env->parent) {
- int index = _env->findMember(ast->name.toString());
if (index != -1) {
_expr.code = _block->TEMP(index);
return false;
}
}
+ if (index >= _env->vars.size()) {
+ // named local variable, e.g. in a catch statement
+ _expr.code = _block->TEMP(index);
+ return false;
+ }
+
_expr.code = _block->NAME(ast->name.toString(),
ast->identifierToken.startLine,
ast->identifierToken.startColumn);
qSwap(_block, entryBlock);
qSwap(_exitBlock, exitBlock);
qSwap(_throwBlock, throwBlock);
- qSwap(_handlersBlock, handlersBlock);
qSwap(_returnAddress, returnAddress);
for (FormalParameterList *it = formals; it; it = it->next) {
_block->JUMP(_exitBlock);
- _throwBlock->JUMP(_function->handlersBlock);
-
- _handlersBlock->MOVE(_handlersBlock->TEMP(_returnAddress),
- _handlersBlock->CALL(_handlersBlock->NAME(IR::Name::builtin_rethrow, 0, 0), 0));
- _handlersBlock->JUMP(_exitBlock);
+ // ### should not be required
+ _throwBlock->JUMP(_exitBlock);
qSwap(_function, function);
qSwap(_block, entryBlock);
qSwap(_exitBlock, exitBlock);
qSwap(_throwBlock, throwBlock);
- qSwap(_handlersBlock, handlersBlock);
qSwap(_returnAddress, returnAddress);
leaveEnvironment();
return false;
}
-bool Codegen::visit(TryStatement *)
+bool Codegen::visit(TryStatement *ast)
{
- assert(!"not implemented");
+ IR::BasicBlock *tryBody = _function->newBasicBlock();
+ IR::BasicBlock *catchBody = ast->catchExpression ? _function->newBasicBlock() : 0;
+ IR::BasicBlock *finallyBody = ast->finallyExpression ? _function->newBasicBlock() : 0;
+ IR::BasicBlock *after = _function->newBasicBlock();
+ assert(catchBody || finallyBody);
+
+ int inCatch = 0;
+ if (catchBody && finallyBody) {
+ inCatch = _block->newTemp();
+ move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, false));
+ }
+
+ int hasException = _block->newTemp();
+ move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_create_exception_handler, 0, 0), 0));
+
+ _block->CJUMP(_block->TEMP(hasException), catchBody ? catchBody : finallyBody, tryBody);
+
+ _block = tryBody;
+ statement(ast->statement);
+ _block->JUMP(finallyBody ? finallyBody : after);
+
+ // regular flow does not go into the catch statement
+ if (catchBody) {
+ _block = catchBody;
+
+ if (finallyBody) {
+ if (inCatch != 0) {
+ // an exception got thrown within catch. Go to finally
+ // and then rethrow
+ IR::BasicBlock *b = _function->newBasicBlock();
+ _block->CJUMP(_block->TEMP(inCatch), finallyBody, b);
+ _block = b;
+ }
+ // if we have finally we need to clear the exception here, so we don't rethrow
+ move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, true));
+ move(_block->TEMP(hasException), _block->CONST(IR::BoolType, false));
+ } else {
+ // otherwise remove the exception handler, so that throw inside catch will
+ // give the correct result
+ _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0));
+ }
+
+ const int exception = _block->newTemp();
+ move(_block->TEMP(exception), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0));
+
+ // the variable ued in the catch statement is local and hides any global
+ // variable with the same name.
+ int hiddenIndex = _env->findMember(ast->catchExpression->name.toString());
+ _env->members.insert(ast->catchExpression->name.toString(), exception);
+
+ statement(ast->catchExpression->statement);
+
+ // reset the variable name to the one from the outer scope
+ _env->members.insert(ast->catchExpression->name.toString(), hiddenIndex);
+ _block->JUMP(finallyBody ? finallyBody : after);
+ }
+
+ if (finallyBody) {
+ _block = finallyBody;
+ _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0));
+ statement(ast->finallyExpression->statement);
+ _block->CJUMP(_block->TEMP(hasException), _throwBlock, after);
+ }
+
+ _block = after;
+
return false;
}
IR::BasicBlock *_block;
IR::BasicBlock *_exitBlock;
IR::BasicBlock *_throwBlock;
- IR::BasicBlock *_handlersBlock;
unsigned _returnAddress;
Mode _mode;
Environment *_env;
this->column = column;
}
+static const char *builtin_to_string(Name::Builtin b)
+{
+ switch (b) {
+ case Name::builtin_invalid:
+ return "builtin_invalid";
+ case Name::builtin_typeof:
+ return "builtin_typeof";
+ case Name::builtin_delete:
+ return "builtin_delete";
+ case Name::builtin_throw:
+ return "builtin_throw";
+ case Name::builtin_create_exception_handler:
+ return "builtin_create_exception_handler";
+ case Name::builtin_delete_exception_handler:
+ return "builtin_delete_exception_handler";
+ case Name::builtin_get_exception:
+ return "builtin_get_exception";
+ }
+ return "builtin_(###FIXME)";
+};
+
+
void Name::dump(QTextStream &out)
{
if (id)
out << *id;
else
- out << "__qmljs_builtin_%" << (int) builtin;
+ out << builtin_to_string(builtin);
}
void Temp::dump(QTextStream &out)
builtin_typeof,
builtin_delete,
builtin_throw,
- builtin_rethrow
+ builtin_create_exception_handler,
+ builtin_delete_exception_handler,
+ builtin_get_exception
};
const QString *id;
_llvmValue = llvm::UndefValue::get(_valueTy);
return;
- case IR::Name::builtin_rethrow:
- CreateCall2(_llvmModule->getFunction("__qmljs_llvm_rethrow"),
- _llvmFunction->arg_begin(), result);
- _llvmValue = CreateLoad(result);
- return;
- }
-
Q_UNREACHABLE();
} else {
llvm::Value *name = getIdentifier(*base->id);
functions[ctl.externalFunction.value()] = ctl.functionName;
}
+ foreach (CatchBlockToLink cbl, _catchHandlers) {
+ Label target = _addrs.value(cbl.catchBlock);
+ linkBuffer.patch(cbl.ptr, linkBuffer.locationOf(target));
+ }
+
#if OS(LINUX)
char* disasmOutput = 0;
size_t disasmLength = 0;
IR::Temp *arg = call->args->expr->asTemp();
assert(arg != 0);
generateFunctionCall(result, __qmljs_builtin_typeof, arg, ContextRegister);
- checkExceptions();
}
break;
case IR::Name::builtin_delete:
IR::Temp *arg = call->args->expr->asTemp();
assert(arg != 0);
generateFunctionCall(Void, __qmljs_builtin_throw, arg, ContextRegister);
- checkExceptions();
}
break;
- case IR::Name::builtin_rethrow:
- // don't use callRuntimeMethod, as we need to return to avoid checking the exceptions
- generateFunctionCall(result, __qmljs_builtin_rethrow, ContextRegister);
- return;
+ case IR::Name::builtin_create_exception_handler:
+ generateFunctionCall(ReturnValueRegister, __qmljs_create_exception_handler, ContextRegister);
+ generateFunctionCall(result, setjmp, ReturnValueRegister);
+ break;
+ case IR::Name::builtin_delete_exception_handler:
+ generateFunctionCall(Void, __qmljs_delete_exception_handler, ContextRegister);
+ break;
+ case IR::Name::builtin_get_exception:
+ generateFunctionCall(result, __qmljs_get_exception, ContextRegister);
+ break;
}
}
}
int argc = prepareVariableArguments(call->args);
IR::Temp* thisObject = 0;
generateFunctionCall(result, __qmljs_call_value, ContextRegister, thisObject, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc));
- checkExceptions();
}
void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result)
int argc = prepareVariableArguments(call->args);
generateFunctionCall(result, __qmljs_call_property, ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc));
- checkExceptions();
}
void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result)
int argc = prepareVariableArguments(call->args);
generateFunctionCall(result, __qmljs_construct_property, ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc));
- checkExceptions();
}
void InstructionSelection::constructValue(IR::New *call, IR::Temp *result)
int argc = prepareVariableArguments(call->args);
generateFunctionCall(result, __qmljs_construct_value, ContextRegister, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc));
- checkExceptions();
-}
-
-void InstructionSelection::checkExceptions()
-{
- Address addr(ContextRegister, offsetof(Context, hasUncaughtException));
- Jump jmp = branch8(NotEqual, addr, TrustedImm32(0));
- _patches[_function->handlersBlock].append(jmp);
}
void InstructionSelection::visitExp(IR::Exp *s)
if (IR::Temp *t = s->source->asTemp()) {
generateFunctionCall(Void, __qmljs_set_activation_property, ContextRegister, propertyName, t);
- checkExceptions();
return;
} else {
Q_UNREACHABLE();
} else {
String *propertyName = identifier(*n->id);
generateFunctionCall(t, __qmljs_get_activation_property, ContextRegister, propertyName);
- checkExceptions();
}
return;
} else if (IR::Const *c = s->source->asConst()) {
//__qmljs_get_property(ctx, result, object, name);
if (IR::Temp *base = m->base->asTemp()) {
generateFunctionCall(t, __qmljs_get_property, ContextRegister, base, identifier(*m->name));
- checkExceptions();
return;
}
assert(!"wip");
return;
} else if (IR::Subscript *ss = s->source->asSubscript()) {
generateFunctionCall(t, __qmljs_get_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp());
- checkExceptions();
return;
} else if (IR::Unop *u = s->source->asUnop()) {
if (IR::Temp *e = u->expr->asTemp()) {
if (IR::Temp *base = m->base->asTemp()) {
if (IR::Temp *t = s->source->asTemp()) {
generateFunctionCall(Void, __qmljs_set_property, ContextRegister, base, identifier(*m->name), t);
- checkExceptions();
return;
} else {
Q_UNREACHABLE();
} else if (IR::Subscript *ss = s->target->asSubscript()) {
if (IR::Temp *t2 = s->source->asTemp()) {
generateFunctionCall(Void, __qmljs_set_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), t2);
- checkExceptions();
return;
} else {
Q_UNIMPLEMENTED();
}
if (op) {
generateFunctionCallImp(Void, opName, op, t, identifier(*n->id), ContextRegister);
- checkExceptions();
}
return;
}
IR::Temp* base = ss->base->asTemp();
IR::Temp* index = ss->index->asTemp();
generateFunctionCallImp(Void, opName, op, base, index, t, ContextRegister);
- checkExceptions();
}
return;
}
IR::Temp* base = m->base->asTemp();
String* member = identifier(*m->name);
generateFunctionCallImp(Void, opName, op, t, base, member, ContextRegister);
- checkExceptions();
}
return;
}
int argc = prepareVariableArguments(args);
generateFunctionCallImp(result, name, method, ContextRegister, identifier(*baseName->id), baseAddressForCallArguments(), TrustedImm32(argc));
- checkExceptions();
}
void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args)
{
int argc = prepareVariableArguments(args);
generateFunctionCallImp(result, name, method, ContextRegister, baseAddressForCallArguments(), TrustedImm32(argc));
- checkExceptions();
}
template <typename Result, typename Source>
void constructProperty(IR::New *ctor, IR::Temp *result);
void callValue(IR::Call *call, IR::Temp *result);
void constructValue(IR::New *call, IR::Temp *result);
- void checkExceptions();
virtual void visitExp(IR::Exp *);
virtual void visitEnter(IR::Enter *);
FunctionPtr externalFunction;
const char* functionName;
};
+ struct CatchBlockToLink {
+ DataLabelPtr ptr;
+ IR::BasicBlock *catchBlock;
+ };
void storeValue(VM::Value value, Address destination)
{
uchar *_codePtr;
QHash<IR::BasicBlock *, QVector<Jump> > _patches;
QHash<IR::BasicBlock *, Label> _addrs;
+ QList<CatchBlockToLink> _catchHandlers;
QList<CallToLink> _callsToLink;
};