blob: f22a2630e21505ce8d21c20b12641c3cf1879f00 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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
13namespace v8 {
14namespace internal {
15
Ben Murdoch097c5b22016-05-18 11:27:45 +010016namespace {
17
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000018// 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 Murdochda12d292016-06-02 14:46:10 +010021bool GetPropertyIfPresent(Handle<JSReceiver> receiver, Handle<String> name,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000022 Handle<Object>* value) {
Ben Murdochda12d292016-06-02 14:46:10 +010023 LookupIterator it(receiver, name, receiver);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000024 // 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 Murdochda12d292016-06-02 14:46:10 +010032 if (!Object::GetProperty(&it).ToHandle(value)) return false;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000033 }
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 Murdochda12d292016-06-02 14:46:10 +010042bool ToPropertyDescriptorFastPath(Isolate* isolate, Handle<JSReceiver> obj,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000043 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 Murdoch097c5b22016-05-18 11:27:45 +0100106void CreateDataProperty(Isolate* isolate, Handle<JSObject> object,
107 Handle<String> name, Handle<Object> value) {
Ben Murdochda12d292016-06-02 14:46:10 +0100108 LookupIterator it(object, name, object, LookupIterator::OWN_SKIP_INTERCEPTOR);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000109 Maybe<bool> result = JSObject::CreateDataProperty(&it, value);
110 CHECK(result.IsJust() && result.FromJust());
111}
112
Ben Murdoch097c5b22016-05-18 11:27:45 +0100113} // namespace
114
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000115
116// ES6 6.2.4.4 "FromPropertyDescriptor"
117Handle<Object> PropertyDescriptor::ToObject(Isolate* isolate) {
118 DCHECK(!(PropertyDescriptor::IsAccessorDescriptor(this) &&
119 PropertyDescriptor::IsDataDescriptor(this)));
120 Factory* factory = isolate->factory();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100121 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 Murdoch4a90d5f2016-03-22 12:00:34 +0000151 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
180bool 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 Murdochda12d292016-06-02 14:46:10 +0100193 Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(obj);
194 if (ToPropertyDescriptorFastPath(isolate, receiver, desc)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000195 return true;
196 }
197
198 // enumerable?
199 Handle<Object> enumerable;
200 // 4 through 6b.
Ben Murdochda12d292016-06-02 14:46:10 +0100201 if (!GetPropertyIfPresent(receiver, isolate->factory()->enumerable_string(),
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000202 &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 Murdochda12d292016-06-02 14:46:10 +0100213 if (!GetPropertyIfPresent(receiver, isolate->factory()->configurable_string(),
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000214 &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 Murdochda12d292016-06-02 14:46:10 +0100225 if (!GetPropertyIfPresent(receiver, isolate->factory()->value_string(),
226 &value)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000227 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 Murdochda12d292016-06-02 14:46:10 +0100235 if (!GetPropertyIfPresent(receiver, isolate->factory()->writable_string(),
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000236 &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 Murdochda12d292016-06-02 14:46:10 +0100245 if (!GetPropertyIfPresent(receiver, isolate->factory()->get_string(),
246 &getter)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000247 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 Murdoch61f157c2016-09-16 13:49:30 +0100252 if (!getter->IsCallable() && !getter->IsUndefined(isolate)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000253 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 Murdochda12d292016-06-02 14:46:10 +0100263 if (!GetPropertyIfPresent(receiver, isolate->factory()->set_string(),
264 &setter)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000265 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 Murdoch61f157c2016-09-16 13:49:30 +0100270 if (!setter->IsCallable() && !setter->IsUndefined(isolate)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000271 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
296void 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