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/execution/arguments-inl.h"
8 #include "src/execution/isolate-inl.h"
9 #include "src/heap/factory.h"
10 #include "src/heap/heap-inl.h" // For ToBoolean. TODO(jkummerow): Drop.
11 #include "src/logging/counters.h"
12 #include "src/objects/elements.h"
13 #include "src/objects/keys.h"
14 #include "src/objects/module.h"
15 #include "src/objects/objects-inl.h"
16
17 namespace v8 {
18 namespace internal {
19
20 namespace {
21
22 // Returns either a FixedArray or, if the given {receiver} has an enum cache
23 // that contains all enumerable properties of the {receiver} and its prototypes
24 // have none, the map of the {receiver}. This is used to speed up the check for
25 // deletions during a for-in.
Enumerate(Isolate * isolate,Handle<JSReceiver> receiver)26 MaybeHandle<HeapObject> Enumerate(Isolate* isolate,
27 Handle<JSReceiver> receiver) {
28 JSObject::MakePrototypesFast(receiver, kStartAtReceiver, isolate);
29 FastKeyAccumulator accumulator(isolate, receiver,
30 KeyCollectionMode::kIncludePrototypes,
31 ENUMERABLE_STRINGS, true);
32 // Test if we have an enum cache for {receiver}.
33 if (!accumulator.is_receiver_simple_enum()) {
34 Handle<FixedArray> keys;
35 ASSIGN_RETURN_ON_EXCEPTION(
36 isolate, keys,
37 accumulator.GetKeys(accumulator.may_have_elements()
38 ? GetKeysConversion::kConvertToString
39 : GetKeysConversion::kNoNumbers),
40 HeapObject);
41 // Test again, since cache may have been built by GetKeys() calls above.
42 if (!accumulator.is_receiver_simple_enum()) return keys;
43 }
44 DCHECK(!receiver->IsJSModuleNamespace());
45 return handle(receiver->map(), isolate);
46 }
47
48 // This is a slight modification of JSReceiver::HasProperty, dealing with
49 // the oddities of JSProxy and JSModuleNamespace in for-in filter.
HasEnumerableProperty(Isolate * isolate,Handle<JSReceiver> receiver,Handle<Object> key)50 MaybeHandle<Object> HasEnumerableProperty(Isolate* isolate,
51 Handle<JSReceiver> receiver,
52 Handle<Object> key) {
53 bool success = false;
54 Maybe<PropertyAttributes> result = Just(ABSENT);
55 PropertyKey lookup_key(isolate, key, &success);
56 if (!success) return isolate->factory()->undefined_value();
57 LookupIterator it(isolate, receiver, lookup_key);
58 for (; it.IsFound(); it.Next()) {
59 switch (it.state()) {
60 case LookupIterator::NOT_FOUND:
61 case LookupIterator::TRANSITION:
62 UNREACHABLE();
63 case LookupIterator::JSPROXY: {
64 // For proxies we have to invoke the [[GetOwnProperty]] trap.
65 result = JSProxy::GetPropertyAttributes(&it);
66 if (result.IsNothing()) return MaybeHandle<Object>();
67 if (result.FromJust() == ABSENT) {
68 // Continue lookup on the proxy's prototype.
69 Handle<JSProxy> proxy = it.GetHolder<JSProxy>();
70 Handle<Object> prototype;
71 ASSIGN_RETURN_ON_EXCEPTION(isolate, prototype,
72 JSProxy::GetPrototype(proxy), Object);
73 if (prototype->IsNull(isolate)) {
74 return isolate->factory()->undefined_value();
75 }
76 // We already have a stack-check in JSProxy::GetPrototype.
77 return HasEnumerableProperty(
78 isolate, Handle<JSReceiver>::cast(prototype), key);
79 } else if (result.FromJust() & DONT_ENUM) {
80 return isolate->factory()->undefined_value();
81 } else {
82 return it.GetName();
83 }
84 }
85 case LookupIterator::INTERCEPTOR: {
86 result = JSObject::GetPropertyAttributesWithInterceptor(&it);
87 if (result.IsNothing()) return MaybeHandle<Object>();
88 if (result.FromJust() != ABSENT) return it.GetName();
89 continue;
90 }
91 case LookupIterator::ACCESS_CHECK: {
92 if (it.HasAccess()) continue;
93 result = JSObject::GetPropertyAttributesWithFailedAccessCheck(&it);
94 if (result.IsNothing()) return MaybeHandle<Object>();
95 if (result.FromJust() != ABSENT) return it.GetName();
96 return isolate->factory()->undefined_value();
97 }
98 case LookupIterator::INTEGER_INDEXED_EXOTIC:
99 // TypedArray out-of-bounds access.
100 return isolate->factory()->undefined_value();
101 case LookupIterator::ACCESSOR: {
102 if (it.GetHolder<Object>()->IsJSModuleNamespace()) {
103 result = JSModuleNamespace::GetPropertyAttributes(&it);
104 if (result.IsNothing()) return MaybeHandle<Object>();
105 DCHECK_EQ(0, result.FromJust() & DONT_ENUM);
106 }
107 return it.GetName();
108 }
109 case LookupIterator::DATA:
110 return it.GetName();
111 }
112 }
113 return isolate->factory()->undefined_value();
114 }
115
116 } // namespace
117
118
RUNTIME_FUNCTION(Runtime_ForInEnumerate)119 RUNTIME_FUNCTION(Runtime_ForInEnumerate) {
120 HandleScope scope(isolate);
121 DCHECK_EQ(1, args.length());
122 Handle<JSReceiver> receiver = args.at<JSReceiver>(0);
123 RETURN_RESULT_OR_FAILURE(isolate, Enumerate(isolate, receiver));
124 }
125
126
RUNTIME_FUNCTION(Runtime_ForInHasProperty)127 RUNTIME_FUNCTION(Runtime_ForInHasProperty) {
128 HandleScope scope(isolate);
129 DCHECK_EQ(2, args.length());
130 Handle<JSReceiver> receiver = args.at<JSReceiver>(0);
131 Handle<Object> key = args.at(1);
132 Handle<Object> result;
133 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
134 isolate, result, HasEnumerableProperty(isolate, receiver, key));
135 return isolate->heap()->ToBoolean(!result->IsUndefined(isolate));
136 }
137
138 } // namespace internal
139 } // namespace v8
140