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