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 | |
| 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 |