blob: 243a9faac3a76cd33f1d5563cb065b5b5e34c772 [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
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.
19bool 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!
40bool 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
104static 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"
113Handle<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
146bool 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
258void 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