return Bailout(kForInStatementOptimizationIsDisabled);
}
- if (stmt->for_in_type() != ForInStatement::FAST_FOR_IN) {
- return Bailout(kForInStatementIsNotFastCase);
- }
-
if (!stmt->each()->IsVariableProxy() ||
!stmt->each()->AsVariableProxy()->var()->IsStackLocal()) {
return Bailout(kForInStatementWithNonLocalEachVariable);
CHECK_ALIVE(VisitForValue(stmt->enumerable()));
HValue* enumerable = Top(); // Leave enumerable at the top.
- HInstruction* map = Add<HForInPrepareMap>(enumerable);
- Add<HSimulate>(stmt->PrepareId());
-
- HInstruction* array = Add<HForInCacheArray>(
- enumerable, map, DescriptorArray::kEnumCacheBridgeCacheIndex);
+ HInstruction* map;
+ HInstruction* array;
+ HInstruction* enum_length;
+ bool fast = stmt->for_in_type() == ForInStatement::FAST_FOR_IN;
+ if (fast) {
+ map = Add<HForInPrepareMap>(enumerable);
+ Add<HSimulate>(stmt->PrepareId());
+
+ array = Add<HForInCacheArray>(enumerable, map,
+ DescriptorArray::kEnumCacheBridgeCacheIndex);
+ enum_length = Add<HMapEnumLength>(map);
+
+ HInstruction* index_cache = Add<HForInCacheArray>(
+ enumerable, map, DescriptorArray::kEnumCacheBridgeIndicesCacheIndex);
+ HForInCacheArray::cast(array)
+ ->set_index_cache(HForInCacheArray::cast(index_cache));
+ } else {
+ Add<HSimulate>(stmt->PrepareId());
+ {
+ NoObservableSideEffectsScope no_effects(this);
+ BuildJSObjectCheck(enumerable, 0);
+ }
+ Add<HSimulate>(stmt->ToObjectId());
- HInstruction* enum_length = Add<HMapEnumLength>(map);
+ map = graph()->GetConstant1();
+ Runtime::FunctionId function_id = Runtime::kGetPropertyNamesFast;
+ Add<HPushArguments>(enumerable);
+ array = Add<HCallRuntime>(isolate()->factory()->empty_string(),
+ Runtime::FunctionForId(function_id), 1);
+ Push(array);
+ Add<HSimulate>(stmt->EnumId());
+ Drop(1);
+ Handle<Map> array_map = isolate()->factory()->fixed_array_map();
+ HValue* check = Add<HCheckMaps>(array, array_map);
+ enum_length = AddLoadFixedArrayLength(array, check);
+ }
HInstruction* start_index = Add<HConstant>(0);
Push(enum_length);
Push(start_index);
- HInstruction* index_cache = Add<HForInCacheArray>(
- enumerable, map, DescriptorArray::kEnumCacheBridgeIndicesCacheIndex);
- HForInCacheArray::cast(array)->set_index_cache(
- HForInCacheArray::cast(index_cache));
-
HBasicBlock* loop_entry = BuildLoopEntry(stmt);
+ // Reload the values to ensure we have up-to-date values inside of the loop.
+ // This is relevant especially for OSR where the values don't come from the
+ // computation above, but from the OSR entry block.
+ enumerable = environment()->ExpressionStackAt(4);
HValue* index = environment()->ExpressionStackAt(0);
HValue* limit = environment()->ExpressionStackAt(1);
set_current_block(loop_body);
- HValue* key = Add<HLoadKeyed>(
- environment()->ExpressionStackAt(2), // Enum cache.
- environment()->ExpressionStackAt(0), // Iteration index.
- environment()->ExpressionStackAt(0),
- FAST_ELEMENTS);
+ HValue* key =
+ Add<HLoadKeyed>(environment()->ExpressionStackAt(2), // Enum cache.
+ index, index, FAST_ELEMENTS);
- // Check if the expected map still matches that of the enumerable.
- // If not just deoptimize.
- Add<HCheckMapValue>(environment()->ExpressionStackAt(4),
- environment()->ExpressionStackAt(3));
-
- Bind(each_var, key);
+ if (fast) {
+ // Check if the expected map still matches that of the enumerable.
+ // If not just deoptimize.
+ Add<HCheckMapValue>(enumerable, environment()->ExpressionStackAt(3));
+ Bind(each_var, key);
+ } else {
+ HValue* function = AddLoadJSBuiltin(Builtins::FILTER_KEY);
+ Add<HPushArguments>(enumerable, key);
+ key = Add<HInvokeFunction>(function, 2);
+ Bind(each_var, key);
+ Add<HSimulate>(stmt->AssignmentId());
+ Add<HCheckHeapObject>(key);
+ }
BreakAndContinueInfo break_info(stmt, scope(), 5);
{
--- /dev/null
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --harmony-proxies --allow-natives-syntax
+
+"use strict";
+
+// Test non-JSObject receiver.
+function f(o) {
+ var result = [];
+ for (var i in o) {
+ result.push(i);
+ }
+ return result;
+}
+
+assertEquals(["0"], f("a"));
+assertEquals(["0"], f("a"));
+%OptimizeFunctionOnNextCall(f);
+assertEquals(["0","1","2"], f("bla"));
+
+// Test the lazy deopt points.
+var keys = ["a", "b", "c", "d"];
+var has_keys = [];
+var deopt_has = false;
+var deopt_enum = false;
+
+var handler = {
+ enumerate: function(target) {
+ if (deopt_enum) {
+ %DeoptimizeFunction(f2);
+ deopt_enum = false;
+ }
+ return keys;
+ },
+
+ getPropertyDescriptor: function(k) {
+ if (deopt_has) {
+ %DeoptimizeFunction(f2);
+ deopt_has = false;
+ }
+ has_keys.push(k);
+ return {value: 10, configurable: true, writable: false, enumerable: true};
+ }
+};
+
+
+var proxy = Proxy.create(handler);
+var o = {__proto__: proxy};
+
+function f2(o) {
+ var result = [];
+ for (var i in o) {
+ result.push(i);
+ }
+ return result;
+}
+
+function check_f2() {
+ assertEquals(keys, f2(o));
+ assertEquals(keys, has_keys);
+ has_keys.length = 0;
+}
+
+check_f2();
+check_f2();
+// Test lazy deopt after GetPropertyNamesFast
+%OptimizeFunctionOnNextCall(f2);
+deopt_enum = true;
+check_f2();
+// Test lazy deopt after FILTER_KEY
+%OptimizeFunctionOnNextCall(f2);
+deopt_has = true;
+check_f2();