1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/runtime/runtime-utils.h"
6 
7 #include "src/arguments.h"
8 #include "src/elements.h"
9 #include "src/factory.h"
10 #include "src/isolate-inl.h"
11 #include "src/keys.h"
12 #include "src/objects-inl.h"
13 
14 namespace v8 {
15 namespace internal {
16 
17 namespace {
18 
19 // Returns either a FixedArray or, if the given {receiver} has an enum cache
20 // that contains all enumerable properties of the {receiver} and its prototypes
21 // have none, the map of the {receiver}. This is used to speed up the check for
22 // deletions during a for-in.
Enumerate(Handle<JSReceiver> receiver)23 MaybeHandle<HeapObject> Enumerate(Handle<JSReceiver> receiver) {
24   Isolate* const isolate = receiver->GetIsolate();
25   JSObject::MakePrototypesFast(receiver, kStartAtReceiver, isolate);
26   FastKeyAccumulator accumulator(isolate, receiver,
27                                  KeyCollectionMode::kIncludePrototypes,
28                                  ENUMERABLE_STRINGS);
29   accumulator.set_is_for_in(true);
30   // Test if we have an enum cache for {receiver}.
31   if (!accumulator.is_receiver_simple_enum()) {
32     Handle<FixedArray> keys;
33     ASSIGN_RETURN_ON_EXCEPTION(
34         isolate, keys, accumulator.GetKeys(GetKeysConversion::kKeepNumbers),
35         HeapObject);
36     // Test again, since cache may have been built by GetKeys() calls above.
37     if (!accumulator.is_receiver_simple_enum()) return keys;
38   }
39   return handle(receiver->map(), isolate);
40 }
41 
42 // This is a slight modifcation of JSReceiver::HasProperty, dealing with
43 // the oddities of JSProxy in for-in filter.
HasEnumerableProperty(Isolate * isolate,Handle<JSReceiver> receiver,Handle<Object> key)44 MaybeHandle<Object> HasEnumerableProperty(Isolate* isolate,
45                                           Handle<JSReceiver> receiver,
46                                           Handle<Object> key) {
47   bool success = false;
48   Maybe<PropertyAttributes> result = Just(ABSENT);
49   LookupIterator it =
50       LookupIterator::PropertyOrElement(isolate, receiver, key, &success);
51   if (!success) return isolate->factory()->undefined_value();
52   for (; it.IsFound(); it.Next()) {
53     switch (it.state()) {
54       case LookupIterator::NOT_FOUND:
55       case LookupIterator::TRANSITION:
56         UNREACHABLE();
57       case LookupIterator::JSPROXY: {
58         // For proxies we have to invoke the [[GetOwnProperty]] trap.
59         result = JSProxy::GetPropertyAttributes(&it);
60         if (result.IsNothing()) return MaybeHandle<Object>();
61         if (result.FromJust() == ABSENT) {
62           // Continue lookup on the proxy's prototype.
63           Handle<JSProxy> proxy = it.GetHolder<JSProxy>();
64           Handle<Object> prototype;
65           ASSIGN_RETURN_ON_EXCEPTION(isolate, prototype,
66                                      JSProxy::GetPrototype(proxy), Object);
67           if (prototype->IsNull(isolate)) {
68             return isolate->factory()->undefined_value();
69           }
70           // We already have a stack-check in JSProxy::GetPrototype.
71           return HasEnumerableProperty(
72               isolate, Handle<JSReceiver>::cast(prototype), key);
73         } else if (result.FromJust() & DONT_ENUM) {
74           return isolate->factory()->undefined_value();
75         } else {
76           return it.GetName();
77         }
78       }
79       case LookupIterator::INTERCEPTOR: {
80         result = JSObject::GetPropertyAttributesWithInterceptor(&it);
81         if (result.IsNothing()) return MaybeHandle<Object>();
82         if (result.FromJust() != ABSENT) return it.GetName();
83         continue;
84       }
85       case LookupIterator::ACCESS_CHECK: {
86         if (it.HasAccess()) continue;
87         result = JSObject::GetPropertyAttributesWithFailedAccessCheck(&it);
88         if (result.IsNothing()) return MaybeHandle<Object>();
89         if (result.FromJust() != ABSENT) return it.GetName();
90         return isolate->factory()->undefined_value();
91       }
92       case LookupIterator::INTEGER_INDEXED_EXOTIC:
93         // TypedArray out-of-bounds access.
94         return isolate->factory()->undefined_value();
95       case LookupIterator::ACCESSOR:
96       case LookupIterator::DATA:
97         return it.GetName();
98     }
99   }
100   return isolate->factory()->undefined_value();
101 }
102 
103 }  // namespace
104 
105 
RUNTIME_FUNCTION(Runtime_ForInEnumerate)106 RUNTIME_FUNCTION(Runtime_ForInEnumerate) {
107   HandleScope scope(isolate);
108   DCHECK_EQ(1, args.length());
109   CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
110   RETURN_RESULT_OR_FAILURE(isolate, Enumerate(receiver));
111 }
112 
113 
RUNTIME_FUNCTION_RETURN_TRIPLE(Runtime_ForInPrepare)114 RUNTIME_FUNCTION_RETURN_TRIPLE(Runtime_ForInPrepare) {
115   HandleScope scope(isolate);
116   DCHECK_EQ(1, args.length());
117   Handle<JSReceiver> receiver = args.at<JSReceiver>(0);
118   Handle<Object> cache_type;
119   if (!Enumerate(receiver).ToHandle(&cache_type)) {
120     return MakeTriple(isolate->heap()->exception(), nullptr, nullptr);
121   }
122   Handle<FixedArray> cache_array;
123   int cache_length;
124   if (cache_type->IsMap()) {
125     Handle<Map> cache_map = Handle<Map>::cast(cache_type);
126     Handle<DescriptorArray> descriptors(cache_map->instance_descriptors(),
127                                         isolate);
128     cache_length = cache_map->EnumLength();
129     if (cache_length && descriptors->HasEnumCache()) {
130       cache_array = handle(descriptors->GetEnumCache(), isolate);
131     } else {
132       cache_array = isolate->factory()->empty_fixed_array();
133       cache_length = 0;
134     }
135   } else {
136     cache_array = Handle<FixedArray>::cast(cache_type);
137     cache_length = cache_array->length();
138     cache_type = handle(Smi::FromInt(1), isolate);
139   }
140   return MakeTriple(*cache_type, *cache_array, Smi::FromInt(cache_length));
141 }
142 
RUNTIME_FUNCTION(Runtime_ForInHasProperty)143 RUNTIME_FUNCTION(Runtime_ForInHasProperty) {
144   HandleScope scope(isolate);
145   DCHECK_EQ(2, args.length());
146   CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
147   CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
148   Handle<Object> result;
149   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
150       isolate, result, HasEnumerableProperty(isolate, receiver, key));
151   return isolate->heap()->ToBoolean(!result->IsUndefined(isolate));
152 }
153 
RUNTIME_FUNCTION(Runtime_ForInFilter)154 RUNTIME_FUNCTION(Runtime_ForInFilter) {
155   HandleScope scope(isolate);
156   DCHECK_EQ(2, args.length());
157   CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
158   CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
159   RETURN_RESULT_OR_FAILURE(isolate,
160                            HasEnumerableProperty(isolate, receiver, key));
161 }
162 
163 
RUNTIME_FUNCTION(Runtime_ForInNext)164 RUNTIME_FUNCTION(Runtime_ForInNext) {
165   HandleScope scope(isolate);
166   DCHECK_EQ(4, args.length());
167   CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
168   CONVERT_ARG_HANDLE_CHECKED(FixedArray, cache_array, 1);
169   CONVERT_ARG_HANDLE_CHECKED(Object, cache_type, 2);
170   CONVERT_SMI_ARG_CHECKED(index, 3);
171   Handle<Object> key = handle(cache_array->get(index), isolate);
172   // Don't need filtering if expected map still matches that of the receiver.
173   if (receiver->map() == *cache_type) {
174     return *key;
175   }
176   RETURN_RESULT_OR_FAILURE(isolate,
177                            HasEnumerableProperty(isolate, receiver, key));
178 }
179 
180 }  // namespace internal
181 }  // namespace v8
182