From 577cbfa2eed1abe677216f3d57b41a8496ceb18b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 28 Oct 2012 16:24:06 +0100 Subject: [PATCH] Added basic support for 'for (var x in y)' statement Change-Id: I8f3c8add78bebf92e0073348d1ecbdf3f328af6d Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 26 +++++++++++++++++++++++ qmljs_objects.h | 13 ++++++++++++ qmljs_runtime.cpp | 21 +++++++++++++++++++ qmljs_runtime.h | 4 ++++ qv4codegen.cpp | 63 ++++++++++++++++++++++++++++++++++++++++--------------- qv4codegen_p.h | 2 ++ qv4ir.cpp | 4 ++++ qv4ir_p.h | 4 +++- qv4isel_masm.cpp | 11 ++++++++++ 9 files changed, 130 insertions(+), 18 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index e4c0d3d..eccee64 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -150,6 +150,27 @@ void Object::defineOwnProperty(Context *ctx, const Value &getter, const Value &s ctx->throwUnimplemented(QStringLiteral("defineOwnProperty")); } +String *ForEachIteratorObject::nextPropertyName() +{ + Property *p = 0; + while (1) { + if (!current) + return 0; + + // ### index array data as well + ++tableIndex; + if (!current->members || tableIndex > current->members->_propertyCount) { + current = current->prototype; + tableIndex = -1; + continue; + } + p = current->members->_properties[tableIndex]; + // ### check that it's not a repeated attribute + if (p /*&& !(p->attributes & DontEnumAttribute)*/) + return p->name; + } +} + Value ArrayObject::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) { if (name->isEqualTo(ctx->engine->id_length)) @@ -541,3 +562,8 @@ Object *ExecutionEngine::newActivationObject(Context *ctx) { return new ActivationObject(ctx); } + +Object *ExecutionEngine::newForEachIteratorObject(Object *o) +{ + return new ForEachIteratorObject(o); +} diff --git a/qmljs_objects.h b/qmljs_objects.h index a98b640..61f43b1 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -264,6 +264,7 @@ private: } private: + friend struct ForEachIteratorObject; Property **_properties; Property **_buckets; Property *_freeList; @@ -314,6 +315,16 @@ struct Object { void setProperty(Context *ctx, const QString &name, void (*code)(Context *), int count = 0); }; +struct ForEachIteratorObject: Object { + Object *object; + Object *current; // inside the prototype chain + int tableIndex; + ForEachIteratorObject(Object *o) : object(o), current(o), tableIndex(-1) {} + virtual QString className() { return QStringLiteral("__ForEachIteratorObject"); } + + String *nextPropertyName(); +}; + struct BooleanObject: Object { Value value; BooleanObject(const Value &value): value(value) {} @@ -505,6 +516,8 @@ struct ExecutionEngine Object *newErrorObject(const Value &value); Object *newMathObject(Context *ctx); Object *newActivationObject(Context *ctx); + + Object *newForEachIteratorObject(Object *o); }; } // namespace VM diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index faae161..2abaf11 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -1024,6 +1024,27 @@ void __qmljs_set_element(Context *ctx, Value object, Value index, Value value) object.objectValue()->setProperty(ctx, name, value, /*flags*/ 0); } +Value __qmljs_foreach_iterator_object(Value in, Context *ctx) +{ + in = __qmljs_to_object(in, ctx); + Object *it = ctx->engine->newForEachIteratorObject(in.objectValue()); + return Value::fromObject(it); +} + +Value __qmljs_foreach_next_property_name(Value foreach_iterator) +{ + assert(foreach_iterator.isObject()); + + ForEachIteratorObject *it = static_cast(foreach_iterator.objectValue()); + assert(it->className() == QLatin1String("__ForEachIteratorObject")); + + String *s = it->nextPropertyName(); + if (!s) + return Value::nullValue(); + return Value::fromString(s); +} + + void __qmljs_set_activation_property(Context *ctx, String *name, Value value) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 4427ede..508396d 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -308,6 +308,10 @@ Value __qmljs_get_activation_property(Context *ctx, String *name); Value __qmljs_get_element(Context *ctx, Value object, Value index); void __qmljs_set_element(Context *ctx, Value object, Value index, Value value); +// For each +Value __qmljs_foreach_iterator_object(Value in, Context *ctx); +Value __qmljs_foreach_next_property_name(Value foreach_iterator); + // context Value __qmljs_get_thisObject(Context *ctx); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 9f2ddd5..4e9c9cf 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1081,32 +1081,32 @@ bool Codegen::visit(FunctionExpression *ast) return false; } -bool Codegen::visit(IdentifierExpression *ast) +IR::Expr *Codegen::identifier(const QString &name, int line, int col) { - int index = _env->findMember(ast->name.toString()); + int index = _env->findMember(name); if (! _function->hasDirectEval && _env->parent) { if (index != -1) { - _expr.code = _block->TEMP(index); - return false; + return _block->TEMP(index); } - index = indexOfArgument(ast->name); + index = indexOfArgument(&name); if (index != -1) { - _expr.code = _block->TEMP(-(index + 1)); - return false; + return _block->TEMP(-(index + 1)); } } if (index >= _env->vars.size()) { // named local variable, e.g. in a catch statement - _expr.code = _block->TEMP(index); - return false; + return _block->TEMP(index); } - _expr.code = _block->NAME(ast->name.toString(), - ast->identifierToken.startLine, - ast->identifierToken.startColumn); + return _block->NAME(name, line, col); +} + +bool Codegen::visit(IdentifierExpression *ast) +{ + _expr.code = identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn); return false; } @@ -1532,9 +1532,6 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, _block->JUMP(_exitBlock); - // ### should not be required - _throwBlock->JUMP(_exitBlock); - qSwap(_function, function); qSwap(_block, entryBlock); qSwap(_exitBlock, exitBlock); @@ -1746,9 +1743,41 @@ bool Codegen::visit(LabelledStatement *ast) return false; } -bool Codegen::visit(LocalForEachStatement *) +bool Codegen::visit(LocalForEachStatement *ast) { - assert(!"not implemented"); + IR::BasicBlock *foreachin = _function->newBasicBlock(); + IR::BasicBlock *foreachbody = _function->newBasicBlock(); + IR::BasicBlock *foreachend = _function->newBasicBlock(); + + enterLoop(ast, foreachend, foreachin); + + variableDeclaration(ast->declaration); + + int iterator = _block->newTemp(); + move(_block->TEMP(iterator), *expression(ast->expression)); + IR::ExprList *args = _function->New(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + + _block->JUMP(foreachin); + + _block = foreachbody; + int temp = _block->newTemp(); + move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); + statement(ast->statement); + _block->JUMP(foreachin); + + _block = foreachin; + + args = _function->New(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args)); + int null = _block->newTemp(); + move(_block->TEMP(null), _block->CONST(IR::NullType, 0)); + cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); + _block = foreachend; + + leaveLoop(); return false; } diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 95fedb5..1f5b87f 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -203,6 +203,8 @@ protected: void variableDeclaration(AST::VariableDeclaration *ast); void variableDeclarationList(AST::VariableDeclarationList *ast); + IR::Expr *identifier(const QString &name, int line = 0, int col = 0); + // nodes virtual bool visit(AST::ArgumentList *ast); virtual bool visit(AST::CaseBlock *ast); diff --git a/qv4ir.cpp b/qv4ir.cpp index 40c85c8..21b0fe7 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -230,6 +230,10 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_delete_exception_handler"; case Name::builtin_get_exception: return "builtin_get_exception"; + case IR::Name::builtin_foreach_iterator_object: + return "builtin_foreach_iterator_object"; + case IR::Name::builtin_foreach_next_property_name: + return "builtin_foreach_next_property_name"; } return "builtin_(###FIXME)"; }; diff --git a/qv4ir_p.h b/qv4ir_p.h index 7853cc2..9e004a5 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -274,7 +274,9 @@ struct Name: Expr { builtin_throw, builtin_create_exception_handler, builtin_delete_exception_handler, - builtin_get_exception + builtin_get_exception, + builtin_foreach_iterator_object, + builtin_foreach_next_property_name }; const QString *id; diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 3662b88..54a0b4c 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -223,6 +223,17 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu break; case IR::Name::builtin_get_exception: generateFunctionCall(result, __qmljs_get_exception, ContextRegister); + case IR::Name::builtin_foreach_iterator_object: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + generateFunctionCall(result, __qmljs_foreach_iterator_object, arg, ContextRegister); + } + break; + case IR::Name::builtin_foreach_next_property_name: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + generateFunctionCall(result, __qmljs_foreach_next_property_name, arg); + } break; } } -- 2.7.4