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