blob: e57016a6d39c847d2ea51bfc9630fc772cdd9f32 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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"
Ben Murdochda12d292016-06-02 14:46:10 +01008#include "src/elements.h"
Ben Murdoch097c5b22016-05-18 11:27:45 +01009#include "src/factory.h"
10#include "src/isolate-inl.h"
Ben Murdochda12d292016-06-02 14:46:10 +010011#include "src/keys.h"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000012#include "src/objects-inl.h"
13
14namespace v8 {
15namespace internal {
16
Ben Murdoch097c5b22016-05-18 11:27:45 +010017namespace {
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.
23MaybeHandle<HeapObject> Enumerate(Handle<JSReceiver> receiver) {
24 Isolate* const isolate = receiver->GetIsolate();
Ben Murdoch61f157c2016-09-16 13:49:30 +010025 JSObject::MakePrototypesFast(receiver, kStartAtReceiver, isolate);
26 FastKeyAccumulator accumulator(isolate, receiver,
27 KeyCollectionMode::kIncludePrototypes,
Ben Murdochda12d292016-06-02 14:46:10 +010028 ENUMERABLE_STRINGS);
29 accumulator.set_filter_proxy_keys(false);
Ben Murdoch61f157c2016-09-16 13:49:30 +010030 accumulator.set_is_for_in(true);
Ben Murdoch097c5b22016-05-18 11:27:45 +010031 // Test if we have an enum cache for {receiver}.
Ben Murdochda12d292016-06-02 14:46:10 +010032 if (!accumulator.is_receiver_simple_enum()) {
Ben Murdoch097c5b22016-05-18 11:27:45 +010033 Handle<FixedArray> keys;
Ben Murdoch61f157c2016-09-16 13:49:30 +010034 ASSIGN_RETURN_ON_EXCEPTION(
35 isolate, keys, accumulator.GetKeys(GetKeysConversion::kKeepNumbers),
36 HeapObject);
Ben Murdoch097c5b22016-05-18 11:27:45 +010037 // Test again, since cache may have been built by GetKeys() calls above.
Ben Murdochda12d292016-06-02 14:46:10 +010038 if (!accumulator.is_receiver_simple_enum()) return keys;
Ben Murdoch097c5b22016-05-18 11:27:45 +010039 }
40 return handle(receiver->map(), isolate);
41}
42
Ben Murdochda12d292016-06-02 14:46:10 +010043// This is a slight modifcation of JSReceiver::HasProperty, dealing with
44// the oddities of JSProxy in for-in filter.
45MaybeHandle<Object> HasEnumerableProperty(Isolate* isolate,
46 Handle<JSReceiver> receiver,
47 Handle<Object> key) {
48 bool success = false;
49 Maybe<PropertyAttributes> result = Just(ABSENT);
50 LookupIterator it =
51 LookupIterator::PropertyOrElement(isolate, receiver, key, &success);
52 if (!success) return isolate->factory()->undefined_value();
53 for (; it.IsFound(); it.Next()) {
54 switch (it.state()) {
55 case LookupIterator::NOT_FOUND:
56 case LookupIterator::TRANSITION:
57 UNREACHABLE();
58 case LookupIterator::JSPROXY: {
59 // For proxies we have to invoke the [[GetOwnProperty]] trap.
60 result = JSProxy::GetPropertyAttributes(&it);
61 if (result.IsNothing()) return MaybeHandle<Object>();
62 if (result.FromJust() == ABSENT) {
63 // Continue lookup on the proxy's prototype.
64 Handle<JSProxy> proxy = it.GetHolder<JSProxy>();
65 Handle<Object> prototype;
66 ASSIGN_RETURN_ON_EXCEPTION(isolate, prototype,
67 JSProxy::GetPrototype(proxy), Object);
Ben Murdoch61f157c2016-09-16 13:49:30 +010068 if (prototype->IsNull(isolate)) break;
Ben Murdochda12d292016-06-02 14:46:10 +010069 // We already have a stack-check in JSProxy::GetPrototype.
70 return HasEnumerableProperty(
71 isolate, Handle<JSReceiver>::cast(prototype), key);
72 } else if (result.FromJust() & DONT_ENUM) {
73 return isolate->factory()->undefined_value();
74 } else {
75 return it.GetName();
76 }
77 }
78 case LookupIterator::INTERCEPTOR: {
79 result = JSObject::GetPropertyAttributesWithInterceptor(&it);
80 if (result.IsNothing()) return MaybeHandle<Object>();
81 if (result.FromJust() != ABSENT) return it.GetName();
82 continue;
83 }
84 case LookupIterator::ACCESS_CHECK: {
85 if (it.HasAccess()) continue;
86 result = JSObject::GetPropertyAttributesWithFailedAccessCheck(&it);
87 if (result.IsNothing()) return MaybeHandle<Object>();
88 if (result.FromJust() != ABSENT) return it.GetName();
89 return isolate->factory()->undefined_value();
90 }
91 case LookupIterator::INTEGER_INDEXED_EXOTIC:
92 // TypedArray out-of-bounds access.
93 return isolate->factory()->undefined_value();
94 case LookupIterator::ACCESSOR:
95 case LookupIterator::DATA:
96 return it.GetName();
97 }
98 }
99 return isolate->factory()->undefined_value();
100}
Ben Murdoch097c5b22016-05-18 11:27:45 +0100101
102MaybeHandle<Object> Filter(Handle<JSReceiver> receiver, Handle<Object> key) {
103 Isolate* const isolate = receiver->GetIsolate();
Ben Murdochda12d292016-06-02 14:46:10 +0100104 return HasEnumerableProperty(isolate, receiver, key);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100105}
106
107} // namespace
108
109
110RUNTIME_FUNCTION(Runtime_ForInEnumerate) {
111 HandleScope scope(isolate);
112 DCHECK_EQ(1, args.length());
113 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
Ben Murdoch61f157c2016-09-16 13:49:30 +0100114 RETURN_RESULT_OR_FAILURE(isolate, Enumerate(receiver));
Ben Murdoch097c5b22016-05-18 11:27:45 +0100115}
116
117
118RUNTIME_FUNCTION_RETURN_TRIPLE(Runtime_ForInPrepare) {
119 HandleScope scope(isolate);
120 DCHECK_EQ(1, args.length());
121 Handle<JSReceiver> receiver = args.at<JSReceiver>(0);
122 Handle<Object> cache_type;
123 if (!Enumerate(receiver).ToHandle(&cache_type)) {
124 return MakeTriple(isolate->heap()->exception(), nullptr, nullptr);
125 }
126 Handle<FixedArray> cache_array;
127 int cache_length;
128 if (cache_type->IsMap()) {
129 Handle<Map> cache_map = Handle<Map>::cast(cache_type);
130 Handle<DescriptorArray> descriptors(cache_map->instance_descriptors(),
131 isolate);
132 cache_length = cache_map->EnumLength();
133 if (cache_length && descriptors->HasEnumCache()) {
134 cache_array = handle(descriptors->GetEnumCache(), isolate);
135 } else {
136 cache_array = isolate->factory()->empty_fixed_array();
137 cache_length = 0;
138 }
139 } else {
140 cache_array = Handle<FixedArray>::cast(cache_type);
141 cache_length = cache_array->length();
142 cache_type = handle(Smi::FromInt(1), isolate);
143 }
144 return MakeTriple(*cache_type, *cache_array, Smi::FromInt(cache_length));
145}
146
147
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000148RUNTIME_FUNCTION(Runtime_ForInDone) {
149 SealHandleScope scope(isolate);
150 DCHECK_EQ(2, args.length());
151 CONVERT_SMI_ARG_CHECKED(index, 0);
152 CONVERT_SMI_ARG_CHECKED(length, 1);
153 DCHECK_LE(0, index);
154 DCHECK_LE(index, length);
155 return isolate->heap()->ToBoolean(index == length);
156}
157
158
159RUNTIME_FUNCTION(Runtime_ForInFilter) {
160 HandleScope scope(isolate);
161 DCHECK_EQ(2, args.length());
162 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
163 CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
Ben Murdoch61f157c2016-09-16 13:49:30 +0100164 RETURN_RESULT_OR_FAILURE(isolate, Filter(receiver, key));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000165}
166
167
168RUNTIME_FUNCTION(Runtime_ForInNext) {
169 HandleScope scope(isolate);
170 DCHECK_EQ(4, args.length());
171 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
172 CONVERT_ARG_HANDLE_CHECKED(FixedArray, cache_array, 1);
173 CONVERT_ARG_HANDLE_CHECKED(Object, cache_type, 2);
174 CONVERT_SMI_ARG_CHECKED(index, 3);
175 Handle<Object> key = handle(cache_array->get(index), isolate);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100176 // Don't need filtering if expected map still matches that of the receiver.
177 if (receiver->map() == *cache_type) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000178 return *key;
179 }
Ben Murdoch61f157c2016-09-16 13:49:30 +0100180 RETURN_RESULT_OR_FAILURE(isolate, Filter(receiver, key));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000181}
182
183
184RUNTIME_FUNCTION(Runtime_ForInStep) {
185 SealHandleScope scope(isolate);
186 DCHECK_EQ(1, args.length());
187 CONVERT_SMI_ARG_CHECKED(index, 0);
188 DCHECK_LE(0, index);
189 DCHECK_LT(index, Smi::kMaxValue);
190 return Smi::FromInt(index + 1);
191}
192
193} // namespace internal
194} // namespace v8