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