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