Home | History | Annotate | Download | only in src
      1 // Copyright 2011 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/property-descriptor.h"
      6 
      7 #include "src/bootstrapper.h"
      8 #include "src/factory.h"
      9 #include "src/isolate-inl.h"
     10 #include "src/lookup.h"
     11 #include "src/objects-inl.h"
     12 
     13 namespace v8 {
     14 namespace internal {
     15 
     16 namespace {
     17 
     18 // Helper function for ToPropertyDescriptor. Comments describe steps for
     19 // "enumerable", other properties are handled the same way.
     20 // Returns false if an exception was thrown.
     21 bool GetPropertyIfPresent(Handle<JSReceiver> receiver, Handle<String> name,
     22                           Handle<Object>* value) {
     23   LookupIterator it(receiver, name, receiver);
     24   // 4. Let hasEnumerable be HasProperty(Obj, "enumerable").
     25   Maybe<bool> has_property = JSReceiver::HasProperty(&it);
     26   // 5. ReturnIfAbrupt(hasEnumerable).
     27   if (has_property.IsNothing()) return false;
     28   // 6. If hasEnumerable is true, then
     29   if (has_property.FromJust() == true) {
     30     // 6a. Let enum be ToBoolean(Get(Obj, "enumerable")).
     31     // 6b. ReturnIfAbrupt(enum).
     32     if (!Object::GetProperty(&it).ToHandle(value)) return false;
     33   }
     34   return true;
     35 }
     36 
     37 
     38 // Helper function for ToPropertyDescriptor. Handles the case of "simple"
     39 // objects: nothing on the prototype chain, just own fast data properties.
     40 // Must not have observable side effects, because the slow path will restart
     41 // the entire conversion!
     42 bool ToPropertyDescriptorFastPath(Isolate* isolate, Handle<JSReceiver> obj,
     43                                   PropertyDescriptor* desc) {
     44   if (!obj->IsJSObject()) return false;
     45   Map* map = Handle<JSObject>::cast(obj)->map();
     46   if (map->instance_type() != JS_OBJECT_TYPE) return false;
     47   if (map->is_access_check_needed()) return false;
     48   if (map->prototype() != *isolate->initial_object_prototype()) return false;
     49   // During bootstrapping, the object_function_prototype_map hasn't been
     50   // set up yet.
     51   if (isolate->bootstrapper()->IsActive()) return false;
     52   if (JSObject::cast(map->prototype())->map() !=
     53       isolate->native_context()->object_function_prototype_map()) {
     54     return false;
     55   }
     56   // TODO(jkummerow): support dictionary properties?
     57   if (map->is_dictionary_map()) return false;
     58   Handle<DescriptorArray> descs =
     59       Handle<DescriptorArray>(map->instance_descriptors());
     60   for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) {
     61     PropertyDetails details = descs->GetDetails(i);
     62     Name* key = descs->GetKey(i);
     63     Handle<Object> value;
     64     switch (details.type()) {
     65       case DATA:
     66         value = JSObject::FastPropertyAt(Handle<JSObject>::cast(obj),
     67                                          details.representation(),
     68                                          FieldIndex::ForDescriptor(map, i));
     69         break;
     70       case DATA_CONSTANT:
     71         value = handle(descs->GetConstant(i), isolate);
     72         break;
     73       case ACCESSOR:
     74       case ACCESSOR_CONSTANT:
     75         // Bail out to slow path.
     76         return false;
     77     }
     78     Heap* heap = isolate->heap();
     79     if (key == heap->enumerable_string()) {
     80       desc->set_enumerable(value->BooleanValue());
     81     } else if (key == heap->configurable_string()) {
     82       desc->set_configurable(value->BooleanValue());
     83     } else if (key == heap->value_string()) {
     84       desc->set_value(value);
     85     } else if (key == heap->writable_string()) {
     86       desc->set_writable(value->BooleanValue());
     87     } else if (key == heap->get_string()) {
     88       // Bail out to slow path to throw an exception if necessary.
     89       if (!value->IsCallable()) return false;
     90       desc->set_get(value);
     91     } else if (key == heap->set_string()) {
     92       // Bail out to slow path to throw an exception if necessary.
     93       if (!value->IsCallable()) return false;
     94       desc->set_set(value);
     95     }
     96   }
     97   if ((desc->has_get() || desc->has_set()) &&
     98       (desc->has_value() || desc->has_writable())) {
     99     // Bail out to slow path to throw an exception.
    100     return false;
    101   }
    102   return true;
    103 }
    104 
    105 
    106 void CreateDataProperty(Isolate* isolate, Handle<JSObject> object,
    107                         Handle<String> name, Handle<Object> value) {
    108   LookupIterator it(object, name, object, LookupIterator::OWN_SKIP_INTERCEPTOR);
    109   Maybe<bool> result = JSObject::CreateDataProperty(&it, value);
    110   CHECK(result.IsJust() && result.FromJust());
    111 }
    112 
    113 }  // namespace
    114 
    115 
    116 // ES6 6.2.4.4 "FromPropertyDescriptor"
    117 Handle<Object> PropertyDescriptor::ToObject(Isolate* isolate) {
    118   DCHECK(!(PropertyDescriptor::IsAccessorDescriptor(this) &&
    119            PropertyDescriptor::IsDataDescriptor(this)));
    120   Factory* factory = isolate->factory();
    121   if (IsRegularAccessorProperty()) {
    122     // Fast case for regular accessor properties.
    123     Handle<JSObject> result = factory->NewJSObjectFromMap(
    124         isolate->accessor_property_descriptor_map());
    125     result->InObjectPropertyAtPut(JSAccessorPropertyDescriptor::kGetIndex,
    126                                   *get());
    127     result->InObjectPropertyAtPut(JSAccessorPropertyDescriptor::kSetIndex,
    128                                   *set());
    129     result->InObjectPropertyAtPut(
    130         JSAccessorPropertyDescriptor::kEnumerableIndex,
    131         isolate->heap()->ToBoolean(enumerable()));
    132     result->InObjectPropertyAtPut(
    133         JSAccessorPropertyDescriptor::kConfigurableIndex,
    134         isolate->heap()->ToBoolean(configurable()));
    135     return result;
    136   }
    137   if (IsRegularDataProperty()) {
    138     // Fast case for regular data properties.
    139     Handle<JSObject> result =
    140         factory->NewJSObjectFromMap(isolate->data_property_descriptor_map());
    141     result->InObjectPropertyAtPut(JSDataPropertyDescriptor::kValueIndex,
    142                                   *value());
    143     result->InObjectPropertyAtPut(JSDataPropertyDescriptor::kWritableIndex,
    144                                   isolate->heap()->ToBoolean(writable()));
    145     result->InObjectPropertyAtPut(JSDataPropertyDescriptor::kEnumerableIndex,
    146                                   isolate->heap()->ToBoolean(enumerable()));
    147     result->InObjectPropertyAtPut(JSDataPropertyDescriptor::kConfigurableIndex,
    148                                   isolate->heap()->ToBoolean(configurable()));
    149     return result;
    150   }
    151   Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
    152   if (has_value()) {
    153     CreateDataProperty(isolate, result, factory->value_string(), value());
    154   }
    155   if (has_writable()) {
    156     CreateDataProperty(isolate, result, factory->writable_string(),
    157                        factory->ToBoolean(writable()));
    158   }
    159   if (has_get()) {
    160     CreateDataProperty(isolate, result, factory->get_string(), get());
    161   }
    162   if (has_set()) {
    163     CreateDataProperty(isolate, result, factory->set_string(), set());
    164   }
    165   if (has_enumerable()) {
    166     CreateDataProperty(isolate, result, factory->enumerable_string(),
    167                        factory->ToBoolean(enumerable()));
    168   }
    169   if (has_configurable()) {
    170     CreateDataProperty(isolate, result, factory->configurable_string(),
    171                        factory->ToBoolean(configurable()));
    172   }
    173   return result;
    174 }
    175 
    176 
    177 // ES6 6.2.4.5
    178 // Returns false in case of exception.
    179 // static
    180 bool PropertyDescriptor::ToPropertyDescriptor(Isolate* isolate,
    181                                               Handle<Object> obj,
    182                                               PropertyDescriptor* desc) {
    183   // 1. ReturnIfAbrupt(Obj).
    184   // 2. If Type(Obj) is not Object, throw a TypeError exception.
    185   if (!obj->IsJSReceiver()) {
    186     isolate->Throw(*isolate->factory()->NewTypeError(
    187         MessageTemplate::kPropertyDescObject, obj));
    188     return false;
    189   }
    190   // 3. Let desc be a new Property Descriptor that initially has no fields.
    191   DCHECK(desc->is_empty());
    192 
    193   Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(obj);
    194   if (ToPropertyDescriptorFastPath(isolate, receiver, desc)) {
    195     return true;
    196   }
    197 
    198   // enumerable?
    199   Handle<Object> enumerable;
    200   // 4 through 6b.
    201   if (!GetPropertyIfPresent(receiver, isolate->factory()->enumerable_string(),
    202                             &enumerable)) {
    203     return false;
    204   }
    205   // 6c. Set the [[Enumerable]] field of desc to enum.
    206   if (!enumerable.is_null()) {
    207     desc->set_enumerable(enumerable->BooleanValue());
    208   }
    209 
    210   // configurable?
    211   Handle<Object> configurable;
    212   // 7 through 9b.
    213   if (!GetPropertyIfPresent(receiver, isolate->factory()->configurable_string(),
    214                             &configurable)) {
    215     return false;
    216   }
    217   // 9c. Set the [[Configurable]] field of desc to conf.
    218   if (!configurable.is_null()) {
    219     desc->set_configurable(configurable->BooleanValue());
    220   }
    221 
    222   // value?
    223   Handle<Object> value;
    224   // 10 through 12b.
    225   if (!GetPropertyIfPresent(receiver, isolate->factory()->value_string(),
    226                             &value)) {
    227     return false;
    228   }
    229   // 12c. Set the [[Value]] field of desc to value.
    230   if (!value.is_null()) desc->set_value(value);
    231 
    232   // writable?
    233   Handle<Object> writable;
    234   // 13 through 15b.
    235   if (!GetPropertyIfPresent(receiver, isolate->factory()->writable_string(),
    236                             &writable)) {
    237     return false;
    238   }
    239   // 15c. Set the [[Writable]] field of desc to writable.
    240   if (!writable.is_null()) desc->set_writable(writable->BooleanValue());
    241 
    242   // getter?
    243   Handle<Object> getter;
    244   // 16 through 18b.
    245   if (!GetPropertyIfPresent(receiver, isolate->factory()->get_string(),
    246                             &getter)) {
    247     return false;
    248   }
    249   if (!getter.is_null()) {
    250     // 18c. If IsCallable(getter) is false and getter is not undefined,
    251     // throw a TypeError exception.
    252     if (!getter->IsCallable() && !getter->IsUndefined(isolate)) {
    253       isolate->Throw(*isolate->factory()->NewTypeError(
    254           MessageTemplate::kObjectGetterCallable, getter));
    255       return false;
    256     }
    257     // 18d. Set the [[Get]] field of desc to getter.
    258     desc->set_get(getter);
    259   }
    260   // setter?
    261   Handle<Object> setter;
    262   // 19 through 21b.
    263   if (!GetPropertyIfPresent(receiver, isolate->factory()->set_string(),
    264                             &setter)) {
    265     return false;
    266   }
    267   if (!setter.is_null()) {
    268     // 21c. If IsCallable(setter) is false and setter is not undefined,
    269     // throw a TypeError exception.
    270     if (!setter->IsCallable() && !setter->IsUndefined(isolate)) {
    271       isolate->Throw(*isolate->factory()->NewTypeError(
    272           MessageTemplate::kObjectSetterCallable, setter));
    273       return false;
    274     }
    275     // 21d. Set the [[Set]] field of desc to setter.
    276     desc->set_set(setter);
    277   }
    278 
    279   // 22. If either desc.[[Get]] or desc.[[Set]] is present, then
    280   // 22a. If either desc.[[Value]] or desc.[[Writable]] is present,
    281   // throw a TypeError exception.
    282   if ((desc->has_get() || desc->has_set()) &&
    283       (desc->has_value() || desc->has_writable())) {
    284     isolate->Throw(*isolate->factory()->NewTypeError(
    285         MessageTemplate::kValueAndAccessor, obj));
    286     return false;
    287   }
    288 
    289   // 23. Return desc.
    290   return true;
    291 }
    292 
    293 
    294 // ES6 6.2.4.6
    295 // static
    296 void PropertyDescriptor::CompletePropertyDescriptor(Isolate* isolate,
    297                                                     PropertyDescriptor* desc) {
    298   // 1. ReturnIfAbrupt(Desc).
    299   // 2. Assert: Desc is a Property Descriptor.
    300   // 3. Let like be Record{
    301   //        [[Value]]: undefined, [[Writable]]: false,
    302   //        [[Get]]: undefined, [[Set]]: undefined,
    303   //        [[Enumerable]]: false, [[Configurable]]: false}.
    304   // 4. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true,
    305   // then:
    306   if (!IsAccessorDescriptor(desc)) {
    307     // 4a. If Desc does not have a [[Value]] field, set Desc.[[Value]] to
    308     //     like.[[Value]].
    309     if (!desc->has_value()) {
    310       desc->set_value(isolate->factory()->undefined_value());
    311     }
    312     // 4b. If Desc does not have a [[Writable]] field, set Desc.[[Writable]]
    313     //     to like.[[Writable]].
    314     if (!desc->has_writable()) desc->set_writable(false);
    315   } else {
    316     // 5. Else,
    317     // 5a. If Desc does not have a [[Get]] field, set Desc.[[Get]] to
    318     //     like.[[Get]].
    319     if (!desc->has_get()) {
    320       desc->set_get(isolate->factory()->undefined_value());
    321     }
    322     // 5b. If Desc does not have a [[Set]] field, set Desc.[[Set]] to
    323     //     like.[[Set]].
    324     if (!desc->has_set()) {
    325       desc->set_set(isolate->factory()->undefined_value());
    326     }
    327   }
    328   // 6. If Desc does not have an [[Enumerable]] field, set
    329   //    Desc.[[Enumerable]] to like.[[Enumerable]].
    330   if (!desc->has_enumerable()) desc->set_enumerable(false);
    331   // 7. If Desc does not have a [[Configurable]] field, set
    332   //    Desc.[[Configurable]] to like.[[Configurable]].
    333   if (!desc->has_configurable()) desc->set_configurable(false);
    334   // 8. Return Desc.
    335 }
    336 
    337 }  // namespace internal
    338 }  // namespace v8
    339