blob: 750f948adbe399263ce44f194e82d9d3d8ffde6c [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.
21bool GetPropertyIfPresent(Handle<Object> obj, Handle<String> name,
22 Handle<Object>* value) {
23 LookupIterator it(obj, name);
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).
32 if (!JSObject::GetProperty(&it).ToHandle(value)) return false;
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!
42bool ToPropertyDescriptorFastPath(Isolate* isolate, Handle<Object> obj,
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 Murdoch097c5b22016-05-18 11:27:45 +0100106void CreateDataProperty(Isolate* isolate, Handle<JSObject> object,
107 Handle<String> name, Handle<Object> value) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000108 LookupIterator it(object, name, LookupIterator::OWN_SKIP_INTERCEPTOR);
109 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
193 if (ToPropertyDescriptorFastPath(isolate, obj, desc)) {
194 return true;
195 }
196
197 // enumerable?
198 Handle<Object> enumerable;
199 // 4 through 6b.
200 if (!GetPropertyIfPresent(obj, isolate->factory()->enumerable_string(),
201 &enumerable)) {
202 return false;
203 }
204 // 6c. Set the [[Enumerable]] field of desc to enum.
205 if (!enumerable.is_null()) {
206 desc->set_enumerable(enumerable->BooleanValue());
207 }
208
209 // configurable?
210 Handle<Object> configurable;
211 // 7 through 9b.
212 if (!GetPropertyIfPresent(obj, isolate->factory()->configurable_string(),
213 &configurable)) {
214 return false;
215 }
216 // 9c. Set the [[Configurable]] field of desc to conf.
217 if (!configurable.is_null()) {
218 desc->set_configurable(configurable->BooleanValue());
219 }
220
221 // value?
222 Handle<Object> value;
223 // 10 through 12b.
224 if (!GetPropertyIfPresent(obj, isolate->factory()->value_string(), &value)) {
225 return false;
226 }
227 // 12c. Set the [[Value]] field of desc to value.
228 if (!value.is_null()) desc->set_value(value);
229
230 // writable?
231 Handle<Object> writable;
232 // 13 through 15b.
233 if (!GetPropertyIfPresent(obj, isolate->factory()->writable_string(),
234 &writable)) {
235 return false;
236 }
237 // 15c. Set the [[Writable]] field of desc to writable.
238 if (!writable.is_null()) desc->set_writable(writable->BooleanValue());
239
240 // getter?
241 Handle<Object> getter;
242 // 16 through 18b.
243 if (!GetPropertyIfPresent(obj, isolate->factory()->get_string(), &getter)) {
244 return false;
245 }
246 if (!getter.is_null()) {
247 // 18c. If IsCallable(getter) is false and getter is not undefined,
248 // throw a TypeError exception.
249 if (!getter->IsCallable() && !getter->IsUndefined()) {
250 isolate->Throw(*isolate->factory()->NewTypeError(
251 MessageTemplate::kObjectGetterCallable, getter));
252 return false;
253 }
254 // 18d. Set the [[Get]] field of desc to getter.
255 desc->set_get(getter);
256 }
257 // setter?
258 Handle<Object> setter;
259 // 19 through 21b.
260 if (!GetPropertyIfPresent(obj, isolate->factory()->set_string(), &setter)) {
261 return false;
262 }
263 if (!setter.is_null()) {
264 // 21c. If IsCallable(setter) is false and setter is not undefined,
265 // throw a TypeError exception.
266 if (!setter->IsCallable() && !setter->IsUndefined()) {
267 isolate->Throw(*isolate->factory()->NewTypeError(
268 MessageTemplate::kObjectSetterCallable, setter));
269 return false;
270 }
271 // 21d. Set the [[Set]] field of desc to setter.
272 desc->set_set(setter);
273 }
274
275 // 22. If either desc.[[Get]] or desc.[[Set]] is present, then
276 // 22a. If either desc.[[Value]] or desc.[[Writable]] is present,
277 // throw a TypeError exception.
278 if ((desc->has_get() || desc->has_set()) &&
279 (desc->has_value() || desc->has_writable())) {
280 isolate->Throw(*isolate->factory()->NewTypeError(
281 MessageTemplate::kValueAndAccessor, obj));
282 return false;
283 }
284
285 // 23. Return desc.
286 return true;
287}
288
289
290// ES6 6.2.4.6
291// static
292void PropertyDescriptor::CompletePropertyDescriptor(Isolate* isolate,
293 PropertyDescriptor* desc) {
294 // 1. ReturnIfAbrupt(Desc).
295 // 2. Assert: Desc is a Property Descriptor.
296 // 3. Let like be Record{
297 // [[Value]]: undefined, [[Writable]]: false,
298 // [[Get]]: undefined, [[Set]]: undefined,
299 // [[Enumerable]]: false, [[Configurable]]: false}.
300 // 4. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true,
301 // then:
302 if (!IsAccessorDescriptor(desc)) {
303 // 4a. If Desc does not have a [[Value]] field, set Desc.[[Value]] to
304 // like.[[Value]].
305 if (!desc->has_value()) {
306 desc->set_value(isolate->factory()->undefined_value());
307 }
308 // 4b. If Desc does not have a [[Writable]] field, set Desc.[[Writable]]
309 // to like.[[Writable]].
310 if (!desc->has_writable()) desc->set_writable(false);
311 } else {
312 // 5. Else,
313 // 5a. If Desc does not have a [[Get]] field, set Desc.[[Get]] to
314 // like.[[Get]].
315 if (!desc->has_get()) {
316 desc->set_get(isolate->factory()->undefined_value());
317 }
318 // 5b. If Desc does not have a [[Set]] field, set Desc.[[Set]] to
319 // like.[[Set]].
320 if (!desc->has_set()) {
321 desc->set_set(isolate->factory()->undefined_value());
322 }
323 }
324 // 6. If Desc does not have an [[Enumerable]] field, set
325 // Desc.[[Enumerable]] to like.[[Enumerable]].
326 if (!desc->has_enumerable()) desc->set_enumerable(false);
327 // 7. If Desc does not have a [[Configurable]] field, set
328 // Desc.[[Configurable]] to like.[[Configurable]].
329 if (!desc->has_configurable()) desc->set_configurable(false);
330 // 8. Return Desc.
331}
332
333} // namespace internal
334} // namespace v8