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/objects/property-descriptor.h" 6 7#include "src/execution/isolate-inl.h" 8#include "src/heap/factory.h" 9#include "src/heap/heap-inl.h" // For ToBoolean. TODO(jkummerow): Drop. 10#include "src/init/bootstrapper.h" 11#include "src/objects/lookup.h" 12#include "src/objects/objects-inl.h" 13#include "src/objects/property-descriptor-object-inl.h" 14 15namespace v8 { 16namespace internal { 17 18namespace { 19 20// Helper function for ToPropertyDescriptor. Comments describe steps for 21// "enumerable", other properties are handled the same way. 22// Returns false if an exception was thrown. 23bool GetPropertyIfPresent(Handle<JSReceiver> receiver, Handle<String> name, 24 Handle<Object>* value) { 25 LookupIterator it(receiver->GetIsolate(), receiver, name, receiver); 26 // 4. Let hasEnumerable be HasProperty(Obj, "enumerable"). 27 Maybe<bool> has_property = JSReceiver::HasProperty(&it); 28 // 5. ReturnIfAbrupt(hasEnumerable). 29 if (has_property.IsNothing()) return false; 30 // 6. If hasEnumerable is true, then 31 if (has_property.FromJust() == true) { 32 // 6a. Let enum be ToBoolean(Get(Obj, "enumerable")). 33 // 6b. ReturnIfAbrupt(enum). 34 if (!Object::GetProperty(&it).ToHandle(value)) return false; 35 } 36 return true; 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! 43bool ToPropertyDescriptorFastPath(Isolate* isolate, Handle<JSReceiver> obj, 44 PropertyDescriptor* desc) { 45 if (!obj->IsJSObject()) return false; 46 Handle<Map> map(Handle<JSObject>::cast(obj)->map(), isolate); 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), isolate); 61 for (InternalIndex i : map->IterateOwnDescriptors()) { 62 PropertyDetails details = descs->GetDetails(i); 63 Handle<Object> value; 64 if (details.location() == PropertyLocation::kField) { 65 if (details.kind() == PropertyKind::kData) { 66 value = JSObject::FastPropertyAt(isolate, Handle<JSObject>::cast(obj), 67 details.representation(), 68 FieldIndex::ForDescriptor(*map, i)); 69 } else { 70 DCHECK_EQ(PropertyKind::kAccessor, details.kind()); 71 // Bail out to slow path. 72 return false; 73 } 74 75 } else { 76 DCHECK_EQ(PropertyLocation::kDescriptor, details.location()); 77 if (details.kind() == PropertyKind::kData) { 78 value = handle(descs->GetStrongValue(i), isolate); 79 } else { 80 DCHECK_EQ(PropertyKind::kAccessor, details.kind()); 81 // Bail out to slow path. 82 return false; 83 } 84 } 85 Name key = descs->GetKey(i); 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 113void CreateDataProperty(Handle<JSObject> object, Handle<String> name, 114 Handle<Object> value) { 115 LookupIterator it(object->GetIsolate(), object, name, object, 116 LookupIterator::OWN_SKIP_INTERCEPTOR); 117 Maybe<bool> result = JSObject::CreateDataProperty(&it, value); 118 CHECK(result.IsJust() && result.FromJust()); 119} 120 121} // namespace 122 123// ES6 6.2.4.4 "FromPropertyDescriptor" 124Handle<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// ES6 6.2.4.5 184// Returns false in case of exception. 185// static 186bool PropertyDescriptor::ToPropertyDescriptor(Isolate* isolate, 187 Handle<Object> obj, 188 PropertyDescriptor* desc) { 189 // 1. ReturnIfAbrupt(Obj). 190 // 2. If Type(Obj) is not Object, throw a TypeError exception. 191 if (!obj->IsJSReceiver()) { 192 isolate->Throw(*isolate->factory()->NewTypeError( 193 MessageTemplate::kPropertyDescObject, obj)); 194 return false; 195 } 196 // 3. Let desc be a new Property Descriptor that initially has no fields. 197 DCHECK(desc->is_empty()); 198 199 Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(obj); 200 if (ToPropertyDescriptorFastPath(isolate, receiver, desc)) { 201 return true; 202 } 203 204 // enumerable? 205 Handle<Object> enumerable; 206 // 4 through 6b. 207 if (!GetPropertyIfPresent(receiver, isolate->factory()->enumerable_string(), 208 &enumerable)) { 209 return false; 210 } 211 // 6c. Set the [[Enumerable]] field of desc to enum. 212 if (!enumerable.is_null()) { 213 desc->set_enumerable(enumerable->BooleanValue(isolate)); 214 } 215 216 // configurable? 217 Handle<Object> configurable; 218 // 7 through 9b. 219 if (!GetPropertyIfPresent(receiver, isolate->factory()->configurable_string(), 220 &configurable)) { 221 return false; 222 } 223 // 9c. Set the [[Configurable]] field of desc to conf. 224 if (!configurable.is_null()) { 225 desc->set_configurable(configurable->BooleanValue(isolate)); 226 } 227 228 // value? 229 Handle<Object> value; 230 // 10 through 12b. 231 if (!GetPropertyIfPresent(receiver, isolate->factory()->value_string(), 232 &value)) { 233 return false; 234 } 235 // 12c. Set the [[Value]] field of desc to value. 236 if (!value.is_null()) desc->set_value(value); 237 238 // writable? 239 Handle<Object> writable; 240 // 13 through 15b. 241 if (!GetPropertyIfPresent(receiver, isolate->factory()->writable_string(), 242 &writable)) { 243 return false; 244 } 245 // 15c. Set the [[Writable]] field of desc to writable. 246 if (!writable.is_null()) desc->set_writable(writable->BooleanValue(isolate)); 247 248 // getter? 249 Handle<Object> getter; 250 // 16 through 18b. 251 if (!GetPropertyIfPresent(receiver, isolate->factory()->get_string(), 252 &getter)) { 253 return false; 254 } 255 if (!getter.is_null()) { 256 // 18c. If IsCallable(getter) is false and getter is not undefined, 257 // throw a TypeError exception. 258 if (!getter->IsCallable() && !getter->IsUndefined(isolate)) { 259 isolate->Throw(*isolate->factory()->NewTypeError( 260 MessageTemplate::kObjectGetterCallable, getter)); 261 return false; 262 } 263 // 18d. Set the [[Get]] field of desc to getter. 264 desc->set_get(getter); 265 } 266 // setter? 267 Handle<Object> setter; 268 // 19 through 21b. 269 if (!GetPropertyIfPresent(receiver, isolate->factory()->set_string(), 270 &setter)) { 271 return false; 272 } 273 if (!setter.is_null()) { 274 // 21c. If IsCallable(setter) is false and setter is not undefined, 275 // throw a TypeError exception. 276 if (!setter->IsCallable() && !setter->IsUndefined(isolate)) { 277 isolate->Throw(*isolate->factory()->NewTypeError( 278 MessageTemplate::kObjectSetterCallable, setter)); 279 return false; 280 } 281 // 21d. Set the [[Set]] field of desc to setter. 282 desc->set_set(setter); 283 } 284 285 // 22. If either desc.[[Get]] or desc.[[Set]] is present, then 286 // 22a. If either desc.[[Value]] or desc.[[Writable]] is present, 287 // throw a TypeError exception. 288 if ((desc->has_get() || desc->has_set()) && 289 (desc->has_value() || desc->has_writable())) { 290 isolate->Throw(*isolate->factory()->NewTypeError( 291 MessageTemplate::kValueAndAccessor, obj)); 292 return false; 293 } 294 295 // 23. Return desc. 296 return true; 297} 298 299// ES6 6.2.4.6 300// static 301void PropertyDescriptor::CompletePropertyDescriptor(Isolate* isolate, 302 PropertyDescriptor* desc) { 303 // 1. ReturnIfAbrupt(Desc). 304 // 2. Assert: Desc is a Property Descriptor. 305 // 3. Let like be Record{ 306 // [[Value]]: undefined, [[Writable]]: false, 307 // [[Get]]: undefined, [[Set]]: undefined, 308 // [[Enumerable]]: false, [[Configurable]]: false}. 309 // 4. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, 310 // then: 311 if (!IsAccessorDescriptor(desc)) { 312 // 4a. If Desc does not have a [[Value]] field, set Desc.[[Value]] to 313 // like.[[Value]]. 314 if (!desc->has_value()) { 315 desc->set_value(isolate->factory()->undefined_value()); 316 } 317 // 4b. If Desc does not have a [[Writable]] field, set Desc.[[Writable]] 318 // to like.[[Writable]]. 319 if (!desc->has_writable()) desc->set_writable(false); 320 } else { 321 // 5. Else, 322 // 5a. If Desc does not have a [[Get]] field, set Desc.[[Get]] to 323 // like.[[Get]]. 324 if (!desc->has_get()) { 325 desc->set_get(isolate->factory()->undefined_value()); 326 } 327 // 5b. If Desc does not have a [[Set]] field, set Desc.[[Set]] to 328 // like.[[Set]]. 329 if (!desc->has_set()) { 330 desc->set_set(isolate->factory()->undefined_value()); 331 } 332 } 333 // 6. If Desc does not have an [[Enumerable]] field, set 334 // Desc.[[Enumerable]] to like.[[Enumerable]]. 335 if (!desc->has_enumerable()) desc->set_enumerable(false); 336 // 7. If Desc does not have a [[Configurable]] field, set 337 // Desc.[[Configurable]] to like.[[Configurable]]. 338 if (!desc->has_configurable()) desc->set_configurable(false); 339 // 8. Return Desc. 340} 341 342Handle<PropertyDescriptorObject> PropertyDescriptor::ToPropertyDescriptorObject( 343 Isolate* isolate) { 344 Handle<PropertyDescriptorObject> obj = 345 isolate->factory()->NewPropertyDescriptorObject(); 346 347 int flags = 348 PropertyDescriptorObject::IsEnumerableBit::encode(enumerable_) | 349 PropertyDescriptorObject::HasEnumerableBit::encode(has_enumerable_) | 350 PropertyDescriptorObject::IsConfigurableBit::encode(configurable_) | 351 PropertyDescriptorObject::HasConfigurableBit::encode(has_configurable_) | 352 PropertyDescriptorObject::IsWritableBit::encode(writable_) | 353 PropertyDescriptorObject::HasWritableBit::encode(has_writable_) | 354 PropertyDescriptorObject::HasValueBit::encode(has_value()) | 355 PropertyDescriptorObject::HasGetBit::encode(has_get()) | 356 PropertyDescriptorObject::HasSetBit::encode(has_set()); 357 358 obj->set_flags(flags); 359 360 if (has_value()) obj->set_value(*value_); 361 if (has_get()) obj->set_get(*get_); 362 if (has_set()) obj->set_set(*set_); 363 364 return obj; 365} 366 367} // namespace internal 368} // namespace v8 369