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. 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_filter_proxy_keys(false); 30 accumulator.set_is_for_in(true); 31 // Test if we have an enum cache for {receiver}. 32 if (!accumulator.is_receiver_simple_enum()) { 33 Handle<FixedArray> keys; 34 ASSIGN_RETURN_ON_EXCEPTION( 35 isolate, keys, accumulator.GetKeys(GetKeysConversion::kKeepNumbers), 36 HeapObject); 37 // Test again, since cache may have been built by GetKeys() calls above. 38 if (!accumulator.is_receiver_simple_enum()) return keys; 39 } 40 return handle(receiver->map(), isolate); 41 } 42 43 // This is a slight modifcation of JSReceiver::HasProperty, dealing with 44 // the oddities of JSProxy in for-in filter. 45 MaybeHandle<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); 68 if (prototype->IsNull(isolate)) break; 69 // 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 } 101 102 MaybeHandle<Object> Filter(Handle<JSReceiver> receiver, Handle<Object> key) { 103 Isolate* const isolate = receiver->GetIsolate(); 104 return HasEnumerableProperty(isolate, receiver, key); 105 } 106 107 } // namespace 108 109 110 RUNTIME_FUNCTION(Runtime_ForInEnumerate) { 111 HandleScope scope(isolate); 112 DCHECK_EQ(1, args.length()); 113 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0); 114 RETURN_RESULT_OR_FAILURE(isolate, Enumerate(receiver)); 115 } 116 117 118 RUNTIME_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 148 RUNTIME_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 159 RUNTIME_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); 164 RETURN_RESULT_OR_FAILURE(isolate, Filter(receiver, key)); 165 } 166 167 168 RUNTIME_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); 176 // Don't need filtering if expected map still matches that of the receiver. 177 if (receiver->map() == *cache_type) { 178 return *key; 179 } 180 RETURN_RESULT_OR_FAILURE(isolate, Filter(receiver, key)); 181 } 182 183 184 RUNTIME_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 195