Home | History | Annotate | Download | only in runtime
      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