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