blob: 4b558d124f5db765d3c51d9b7a20ee934019224f [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 Murdochda12d292016-06-02 14:46:10 +010025 FastKeyAccumulator accumulator(isolate, receiver, INCLUDE_PROTOS,
26 ENUMERABLE_STRINGS);
27 accumulator.set_filter_proxy_keys(false);
Ben Murdoch097c5b22016-05-18 11:27:45 +010028 // Test if we have an enum cache for {receiver}.
Ben Murdochda12d292016-06-02 14:46:10 +010029 if (!accumulator.is_receiver_simple_enum()) {
Ben Murdoch097c5b22016-05-18 11:27:45 +010030 Handle<FixedArray> keys;
Ben Murdochda12d292016-06-02 14:46:10 +010031 ASSIGN_RETURN_ON_EXCEPTION(isolate, keys, accumulator.GetKeys(KEEP_NUMBERS),
32 HeapObject);
Ben Murdoch097c5b22016-05-18 11:27:45 +010033 // Test again, since cache may have been built by GetKeys() calls above.
Ben Murdochda12d292016-06-02 14:46:10 +010034 if (!accumulator.is_receiver_simple_enum()) return keys;
Ben Murdoch097c5b22016-05-18 11:27:45 +010035 }
36 return handle(receiver->map(), isolate);
37}
38
Ben Murdochda12d292016-06-02 14:46:10 +010039// This is a slight modifcation of JSReceiver::HasProperty, dealing with
40// the oddities of JSProxy in for-in filter.
41MaybeHandle<Object> HasEnumerableProperty(Isolate* isolate,
42 Handle<JSReceiver> receiver,
43 Handle<Object> key) {
44 bool success = false;
45 Maybe<PropertyAttributes> result = Just(ABSENT);
46 LookupIterator it =
47 LookupIterator::PropertyOrElement(isolate, receiver, key, &success);
48 if (!success) return isolate->factory()->undefined_value();
49 for (; it.IsFound(); it.Next()) {
50 switch (it.state()) {
51 case LookupIterator::NOT_FOUND:
52 case LookupIterator::TRANSITION:
53 UNREACHABLE();
54 case LookupIterator::JSPROXY: {
55 // For proxies we have to invoke the [[GetOwnProperty]] trap.
56 result = JSProxy::GetPropertyAttributes(&it);
57 if (result.IsNothing()) return MaybeHandle<Object>();
58 if (result.FromJust() == ABSENT) {
59 // Continue lookup on the proxy's prototype.
60 Handle<JSProxy> proxy = it.GetHolder<JSProxy>();
61 Handle<Object> prototype;
62 ASSIGN_RETURN_ON_EXCEPTION(isolate, prototype,
63 JSProxy::GetPrototype(proxy), Object);
64 if (prototype->IsNull()) break;
65 // We already have a stack-check in JSProxy::GetPrototype.
66 return HasEnumerableProperty(
67 isolate, Handle<JSReceiver>::cast(prototype), key);
68 } else if (result.FromJust() & DONT_ENUM) {
69 return isolate->factory()->undefined_value();
70 } else {
71 return it.GetName();
72 }
73 }
74 case LookupIterator::INTERCEPTOR: {
75 result = JSObject::GetPropertyAttributesWithInterceptor(&it);
76 if (result.IsNothing()) return MaybeHandle<Object>();
77 if (result.FromJust() != ABSENT) return it.GetName();
78 continue;
79 }
80 case LookupIterator::ACCESS_CHECK: {
81 if (it.HasAccess()) continue;
82 result = JSObject::GetPropertyAttributesWithFailedAccessCheck(&it);
83 if (result.IsNothing()) return MaybeHandle<Object>();
84 if (result.FromJust() != ABSENT) return it.GetName();
85 return isolate->factory()->undefined_value();
86 }
87 case LookupIterator::INTEGER_INDEXED_EXOTIC:
88 // TypedArray out-of-bounds access.
89 return isolate->factory()->undefined_value();
90 case LookupIterator::ACCESSOR:
91 case LookupIterator::DATA:
92 return it.GetName();
93 }
94 }
95 return isolate->factory()->undefined_value();
96}
Ben Murdoch097c5b22016-05-18 11:27:45 +010097
98MaybeHandle<Object> Filter(Handle<JSReceiver> receiver, Handle<Object> key) {
99 Isolate* const isolate = receiver->GetIsolate();
Ben Murdochda12d292016-06-02 14:46:10 +0100100 return HasEnumerableProperty(isolate, receiver, key);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100101}
102
103} // namespace
104
105
106RUNTIME_FUNCTION(Runtime_ForInEnumerate) {
107 HandleScope scope(isolate);
108 DCHECK_EQ(1, args.length());
109 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
110 Handle<HeapObject> result;
111 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, Enumerate(receiver));
112 return *result;
113}
114
115
116RUNTIME_FUNCTION_RETURN_TRIPLE(Runtime_ForInPrepare) {
117 HandleScope scope(isolate);
118 DCHECK_EQ(1, args.length());
119 Handle<JSReceiver> receiver = args.at<JSReceiver>(0);
120 Handle<Object> cache_type;
121 if (!Enumerate(receiver).ToHandle(&cache_type)) {
122 return MakeTriple(isolate->heap()->exception(), nullptr, nullptr);
123 }
124 Handle<FixedArray> cache_array;
125 int cache_length;
126 if (cache_type->IsMap()) {
127 Handle<Map> cache_map = Handle<Map>::cast(cache_type);
128 Handle<DescriptorArray> descriptors(cache_map->instance_descriptors(),
129 isolate);
130 cache_length = cache_map->EnumLength();
131 if (cache_length && descriptors->HasEnumCache()) {
132 cache_array = handle(descriptors->GetEnumCache(), isolate);
133 } else {
134 cache_array = isolate->factory()->empty_fixed_array();
135 cache_length = 0;
136 }
137 } else {
138 cache_array = Handle<FixedArray>::cast(cache_type);
139 cache_length = cache_array->length();
140 cache_type = handle(Smi::FromInt(1), isolate);
141 }
142 return MakeTriple(*cache_type, *cache_array, Smi::FromInt(cache_length));
143}
144
145
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000146RUNTIME_FUNCTION(Runtime_ForInDone) {
147 SealHandleScope scope(isolate);
148 DCHECK_EQ(2, args.length());
149 CONVERT_SMI_ARG_CHECKED(index, 0);
150 CONVERT_SMI_ARG_CHECKED(length, 1);
151 DCHECK_LE(0, index);
152 DCHECK_LE(index, length);
153 return isolate->heap()->ToBoolean(index == length);
154}
155
156
157RUNTIME_FUNCTION(Runtime_ForInFilter) {
158 HandleScope scope(isolate);
159 DCHECK_EQ(2, args.length());
160 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
161 CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100162 Handle<Object> result;
163 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, Filter(receiver, key));
164 return *result;
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 Murdoch097c5b22016-05-18 11:27:45 +0100180 Handle<Object> result;
181 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, Filter(receiver, key));
182 return *result;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000183}
184
185
186RUNTIME_FUNCTION(Runtime_ForInStep) {
187 SealHandleScope scope(isolate);
188 DCHECK_EQ(1, args.length());
189 CONVERT_SMI_ARG_CHECKED(index, 0);
190 DCHECK_LE(0, index);
191 DCHECK_LT(index, Smi::kMaxValue);
192 return Smi::FromInt(index + 1);
193}
194
195} // namespace internal
196} // namespace v8