/// <summary> /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.4 /// </summary> /// <param name="forInStatement"></param> /// <returns></returns> public Completion ExecuteForInStatement(ForInStatement forInStatement) { Identifier identifier = forInStatement.Left.Type == SyntaxNodes.VariableDeclaration ? forInStatement.Left.As <VariableDeclaration>().Declarations.First().Id : forInStatement.Left.As <Identifier>(); var varRef = _engine.EvaluateExpression(identifier) as Reference; var exprRef = _engine.EvaluateExpression(forInStatement.Right); var experValue = _engine.GetValue(exprRef); if (experValue == Undefined.Instance || experValue == Null.Instance) { return(new Completion(Completion.Normal, null, null)); } var obj = TypeConverter.ToObject(_engine, experValue); JsValue v = Null.Instance; // keys are constructed using the prototype chain var cursor = obj; var processedKeys = new HashSet <string>(); while (cursor != null) { var keys = _engine.Object.GetOwnPropertyNames(Undefined.Instance, Arguments.From(cursor)).AsArray(); for (var i = 0; i < keys.GetLength(); i++) { var p = keys.GetOwnProperty(i.ToString()).Value.AsString(); if (processedKeys.Contains(p)) { continue; } processedKeys.Add(p); // collection might be modified by inner statement if (cursor.GetOwnProperty(p) == PropertyDescriptor.Undefined) { continue; } var value = cursor.GetOwnProperty(p); if (!value.Enumerable.HasValue || !value.Enumerable.Value) { continue; } _engine.PutValue(varRef, p); var stmt = ExecuteStatement(forInStatement.Body); if (stmt.Value != null) { v = stmt.Value; } if (stmt.Type == Completion.Break) { return(new Completion(Completion.Normal, v, null)); } if (stmt.Type != Completion.Continue) { if (stmt.Type != Completion.Normal) { return(stmt); } } } cursor = cursor.Prototype; } return(new Completion(Completion.Normal, v, null)); }