struct ExceptionHandler {
ExecutionContext *context;
+ DeclarativeEnvironment::With *with;
const uchar *code; // Interpreter state
int targetTempIndex; // Interpreter state
jmp_buf stackFrame;
arguments = 0;
argumentCount = 0;
locals = 0;
- activation = 0;
formals = 0;
formalCount = 0;
vars = 0;
varCount = 0;
+ activation = 0;
+ withObject = 0;
}
DeclarativeEnvironment::DeclarativeEnvironment(FunctionObject *f, Value *args, uint argc)
outer = f->scope;
engine = outer->engine;
- if (f->needsActivation)
- activation = engine->newActivationObject(this);
- else
- activation = 0;
-
formals = f->formalParameterList;
formalCount = f->formalParameterCount;
arguments = args;
locals = varCount ? new Value[varCount] : 0;
if (varCount)
std::fill(locals, locals + varCount, Value::undefinedValue());
+
+ if (f->needsActivation)
+ activation = engine->newActivationObject(this);
+ else
+ activation = 0;
+
+ withObject = 0;
}
bool DeclarativeEnvironment::hasBinding(String *name) const
if (__qmljs_string_equal(formals[i], name))
return true;
}
- return deletableLocals.contains(name->toQString());
+ if (!deletableLocals)
+ return false;
+ return deletableLocals->contains(name->toQString());
}
void DeclarativeEnvironment::createMutableBinding(String *name, bool deletable)
assert(deletable);
assert(!hasBinding(name));
- deletableLocals.insert(name->toQString(), Value::undefinedValue());
+ if (!deletableLocals)
+ deletableLocals = new QHash<QString, Value>();
+ deletableLocals->insert(name->toQString(), Value::undefinedValue());
}
void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool strict)
return;
}
}
- QHash<QString, Value>::iterator it = deletableLocals.find(name->toQString());
- if (it != deletableLocals.end()) {
+ assert(deletableLocals);
+ QHash<QString, Value>::iterator it = deletableLocals->find(name->toQString());
+ if (it != deletableLocals->end()) {
*it = value;
return;
}
if (__qmljs_string_equal(formals[i], name))
return arguments[i];
}
- QHash<QString, Value>::const_iterator it = deletableLocals.find(name->toQString());
- if (it != deletableLocals.end())
+ assert(deletableLocals);
+ QHash<QString, Value>::const_iterator it = deletableLocals->find(name->toQString());
+ if (it != deletableLocals->end())
return *it;
assert(false);
bool DeclarativeEnvironment::deleteBinding(String *name)
{
- QHash<QString, Value>::iterator it = deletableLocals.find(name->toQString());
- if (it != deletableLocals.end()) {
- deletableLocals.erase(it);
- return true;
+ if (deletableLocals) {
+ QHash<QString, Value>::iterator it = deletableLocals->find(name->toQString());
+ if (it != deletableLocals->end()) {
+ deletableLocals->erase(it);
+ return true;
+ }
}
return !hasBinding(name);
}
+void DeclarativeEnvironment::pushWithObject(Object *with)
+{
+ With *w = new With;
+ w->next = withObject;
+ w->object = with;
+ withObject = w;
+}
+
+void DeclarativeEnvironment::popWithObject()
+{
+ assert(withObject);
+
+ With *w = withObject;
+ withObject = w->next;
+ delete w;
+}
+
void ExecutionContext::init(ExecutionEngine *eng)
{
PropertyDescriptor *ExecutionContext::lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp)
{
for (DeclarativeEnvironment *ctx = lexicalEnvironment; ctx; ctx = ctx->outer) {
+ if (ctx->withObject) {
+ DeclarativeEnvironment::With *w = ctx->withObject;
+ while (w) {
+ if (PropertyDescriptor *pd = w->object->__getPropertyDescriptor__(this, name, tmp))
+ return pd;
+ w = w->next;
+ }
+ }
if (ctx->activation) {
if (PropertyDescriptor *pd = ctx->activation->__getPropertyDescriptor__(this, name, tmp))
return pd;
ExecutionEngine *engine;
DeclarativeEnvironment *outer;
- Object *activation;
Value *arguments;
unsigned int argumentCount;
Value *locals;
String **vars;
unsigned int varCount;
+ Object *activation;
+ struct With {
+ Object *object;
+ With *next;
+ } *withObject;
+
// these get used for createMutableBinding(..., true).
// the only place this is being used is eval(...)
- QHash<QString, Value> deletableLocals;
+ QHash<QString, Value> *deletableLocals;
DeclarativeEnvironment(ExecutionEngine *e);
DeclarativeEnvironment(FunctionObject *f, Value *args, uint argc);
+ ~DeclarativeEnvironment() { delete deletableLocals; }
bool hasBinding(String *name) const;
void createMutableBinding(String *name, bool deletable);
void setMutableBinding(String *name, Value value, bool strict);
Value getBindingValue(String *name, bool strict) const;
bool deleteBinding(String *name);
+
+ // ### needs a bit of work in exception handlers
+ void pushWithObject(Object *with);
+ void popWithObject();
};
struct ExecutionContext
context->leaveCallContext();
context = context->parent;
}
+ DeclarativeEnvironment *env = context->lexicalEnvironment;
+ while (env->withObject != handler.with) {
+ DeclarativeEnvironment::With *w = env->withObject;
+ env->withObject = w->next;
+ delete w;
+ }
context->engine->exception = value;
context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler());
ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last();
handler.context = context;
+ handler.with = context->lexicalEnvironment->withObject;
return handler.stackFrame;
}
__qmljs_throw(val, context);
}
+void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx)
+{
+ Object *obj = __qmljs_to_object(o, ctx).asObject();
+ ctx->lexicalEnvironment->pushWithObject(obj);
+}
+
+void __qmljs_builtin_pop_with(ExecutionContext *ctx)
+{
+ ctx->lexicalEnvironment->popWithObject();
+}
+
} // extern "C"
Value __qmljs_builtin_typeof(Value val, ExecutionContext *context);
void __qmljs_builtin_throw(Value val, ExecutionContext *context);
+void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx);
+void __qmljs_builtin_pop_with(ExecutionContext *ctx);
// constructors
Value __qmljs_init_closure(IR::Function *clos, ExecutionContext *ctx);
if (! initializer)
initializer = _block->CONST(IR::UndefinedType, 0);
- if (! _env->parent) {
+ if (! _env->parent || _function->insideWith) {
// it's global code.
move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), initializer);
} else {
{
int index = _env->findMember(name);
- if (! _function->hasDirectEval && _env->parent) {
+ if (! _function->hasDirectEval && !_function->insideWith && _env->parent) {
if (index != -1) {
return _block->TEMP(index);
}
return false;
}
-bool Codegen::visit(WithStatement *)
+bool Codegen::visit(WithStatement *ast)
{
- assert(!"with not implemented");
+ IR::BasicBlock *withBlock = _function->newBasicBlock();
+
+ _block->JUMP(withBlock);
+ _block = withBlock;
+ int withObject = _block->newTemp();
+ _block->MOVE(_block->TEMP(withObject), *expression(ast->expression));
+ IR::ExprList *args = _function->New<IR::ExprList>();
+ args->init(_block->TEMP(withObject));
+ _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_with, 0, 0), args));
+ ++_function->insideWith;
+ statement(ast->statement);
+ --_function->insideWith;
+ _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_with, 0, 0), 0));
+
+ IR::BasicBlock *next = _function->newBasicBlock();
+ _block->JUMP(next);
+ _block = next;
+
return false;
}
return "builtin_foreach_iterator_object";
case IR::Name::builtin_foreach_next_property_name:
return "builtin_foreach_next_property_name";
+ case IR::Name::builtin_push_with:
+ return "builtin_push_with";
+ case IR::Name::builtin_pop_with:
+ return "builtin_pop_with";
}
return "builtin_(###FIXME)";
};
builtin_delete_exception_handler,
builtin_get_exception,
builtin_foreach_iterator_object,
- builtin_foreach_next_property_name
+ builtin_foreach_next_property_name,
+ builtin_push_with,
+ builtin_pop_with
};
const QString *id;
bool hasDirectEval: 1;
bool hasNestedFunctions: 1;
+ int insideWith;
template <typename _Tp> _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); }
, codeData(0)
, hasDirectEval(false)
, hasNestedFunctions(false)
+ , insideWith(0)
{ this->name = newString(name); }
~Function();
generateFunctionCall(result, __qmljs_foreach_next_property_name, arg);
}
break;
+ case IR::Name::builtin_push_with: {
+ IR::Temp *arg = call->args->expr->asTemp();
+ assert(arg != 0);
+ generateFunctionCall(Void, __qmljs_builtin_push_with, arg, ContextRegister);
+ }
+ break;
+ case IR::Name::builtin_pop_with:
+ generateFunctionCall(Void, __qmljs_builtin_pop_with, ContextRegister);
+ break;
}
}
--- /dev/null
+var o = { "x": 1 }
+var x = 0;
+with(o) {
+ with( { "x": 2 } ) {
+ print(x)
+ }
+ print(x)
+}
+print(x)
+
+
+function foo() {
+ var x = 0;
+ with(o) {
+ with( { "x": 2 } ) {
+ print(x)
+ }
+ print(x)
+ }
+ print(x)
+}
+
+print("\n")
+foo();
+
+
+function bar() {
+ var x = 0;
+ try {
+ with(o) {
+ with( { "x": 2 } ) {
+ print(x)
+ throw 0;
+ }
+ print(x)
+ }
+ }
+ catch(e) {}
+ print(x)
+}
+
+print("\n")
+bar();