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