Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 16 | namespace { |
| 17 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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. |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 21 | bool GetPropertyIfPresent(Handle<JSReceiver> receiver, Handle<String> name, |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 22 | Handle<Object>* value) { |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 23 | LookupIterator it(receiver, name, receiver); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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). |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 32 | if (!Object::GetProperty(&it).ToHandle(value)) return false; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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! |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 42 | bool ToPropertyDescriptorFastPath(Isolate* isolate, Handle<JSReceiver> obj, |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 106 | void CreateDataProperty(Isolate* isolate, Handle<JSObject> object, |
| 107 | Handle<String> name, Handle<Object> value) { |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 108 | LookupIterator it(object, name, object, LookupIterator::OWN_SKIP_INTERCEPTOR); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 109 | Maybe<bool> result = JSObject::CreateDataProperty(&it, value); |
| 110 | CHECK(result.IsJust() && result.FromJust()); |
| 111 | } |
| 112 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 113 | } // namespace |
| 114 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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(); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 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 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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 | |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 193 | Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(obj); |
| 194 | if (ToPropertyDescriptorFastPath(isolate, receiver, desc)) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 195 | return true; |
| 196 | } |
| 197 | |
| 198 | // enumerable? |
| 199 | Handle<Object> enumerable; |
| 200 | // 4 through 6b. |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 201 | if (!GetPropertyIfPresent(receiver, isolate->factory()->enumerable_string(), |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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. |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 213 | if (!GetPropertyIfPresent(receiver, isolate->factory()->configurable_string(), |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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. |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 225 | if (!GetPropertyIfPresent(receiver, isolate->factory()->value_string(), |
| 226 | &value)) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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. |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 235 | if (!GetPropertyIfPresent(receiver, isolate->factory()->writable_string(), |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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. |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 245 | if (!GetPropertyIfPresent(receiver, isolate->factory()->get_string(), |
| 246 | &getter)) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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. |
Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 252 | if (!getter->IsCallable() && !getter->IsUndefined(isolate)) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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. |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 263 | if (!GetPropertyIfPresent(receiver, isolate->factory()->set_string(), |
| 264 | &setter)) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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. |
Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 270 | if (!setter->IsCallable() && !setter->IsUndefined(isolate)) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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 |