OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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/api-natives.h" | |
6 #include "src/isolate-inl.h" | |
7 | |
8 namespace v8 { | |
9 namespace internal { | |
10 | |
11 namespace { | |
12 | |
13 // Transform getter or setter into something DefineAccessor can handle. | |
14 Handle<Object> InstantiateAccessorComponent(Isolate* isolate, | |
15 Handle<Object> component) { | |
16 if (component->IsUndefined()) return isolate->factory()->undefined_value(); | |
17 Handle<FunctionTemplateInfo> info = | |
18 Handle<FunctionTemplateInfo>::cast(component); | |
19 // TODO(dcarney): instantiate directly. | |
20 return Utils::OpenHandle(*Utils::ToLocal(info)->GetFunction()); | |
21 } | |
22 | |
23 | |
24 Object* DefineApiAccessorProperty(Isolate* isolate, Handle<JSObject> object, | |
25 Handle<Name> name, Handle<Object> getter, | |
26 Handle<Object> setter, Smi* attribute) { | |
27 DCHECK(PropertyDetails::AttributesField::is_valid( | |
28 static_cast<PropertyAttributes>(attribute->value()))); | |
29 RETURN_FAILURE_ON_EXCEPTION( | |
30 isolate, JSObject::DefineAccessor( | |
31 object, name, InstantiateAccessorComponent(isolate, getter), | |
32 InstantiateAccessorComponent(isolate, setter), | |
33 static_cast<PropertyAttributes>(attribute->value()))); | |
34 return *object; | |
35 } | |
36 | |
37 | |
38 Object* AddPropertyForTemplate(Isolate* isolate, Handle<JSObject> object, | |
39 Handle<Object> key, Handle<Object> value, | |
40 Smi* unchecked_attributes) { | |
41 DCHECK((unchecked_attributes->value() & | |
42 ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0); | |
43 // Compute attributes. | |
44 PropertyAttributes attributes = | |
45 static_cast<PropertyAttributes>(unchecked_attributes->value()); | |
46 | |
47 #ifdef DEBUG | |
48 bool duplicate; | |
49 if (key->IsName()) { | |
50 LookupIterator it(object, Handle<Name>::cast(key), | |
51 LookupIterator::OWN_SKIP_INTERCEPTOR); | |
52 Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it); | |
53 DCHECK(maybe.has_value); | |
54 duplicate = it.IsFound(); | |
55 } else { | |
56 uint32_t index = 0; | |
57 key->ToArrayIndex(&index); | |
58 Maybe<bool> maybe = JSReceiver::HasOwnElement(object, index); | |
59 if (!maybe.has_value) return isolate->heap()->exception(); | |
60 duplicate = maybe.value; | |
61 } | |
62 if (duplicate) { | |
63 Handle<Object> args[1] = {key}; | |
64 THROW_NEW_ERROR_RETURN_FAILURE( | |
65 isolate, | |
66 NewTypeError("duplicate_template_property", HandleVector(args, 1))); | |
67 } | |
68 #endif | |
69 | |
70 Handle<Object> result; | |
71 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
72 isolate, result, | |
73 Runtime::DefineObjectProperty(object, key, value, attributes)); | |
74 return *result; | |
75 } | |
76 | |
77 | |
78 void DisableAccessChecks(Isolate* isolate, Handle<JSObject> object) { | |
79 Handle<Map> old_map(object->map()); | |
80 // Copy map so it won't interfere constructor's initial map. | |
81 Handle<Map> new_map = Map::Copy(old_map, "DisableAccessChecks"); | |
82 new_map->set_is_access_check_needed(false); | |
83 JSObject::MigrateToMap(Handle<JSObject>::cast(object), new_map); | |
84 } | |
85 | |
86 | |
87 void EnableAccessChecks(Isolate* isolate, Handle<JSObject> object) { | |
88 Handle<Map> old_map(object->map()); | |
89 // Copy map so it won't interfere constructor's initial map. | |
90 Handle<Map> new_map = Map::Copy(old_map, "EnableAccessChecks"); | |
91 new_map->set_is_access_check_needed(true); | |
92 JSObject::MigrateToMap(object, new_map); | |
93 } | |
94 | |
95 | |
96 MaybeHandle<JSObject> InstantiateObject(Isolate* isolate, | |
97 Handle<ObjectTemplateInfo> data); | |
98 | |
99 | |
100 MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate, | |
101 Handle<FunctionTemplateInfo> data, | |
102 Handle<Name> name = Handle<Name>()); | |
103 | |
104 | |
105 MaybeHandle<Object> Instantiate(Isolate* isolate, Handle<Object> data, | |
106 Handle<Name> name = Handle<Name>()) { | |
107 if (data->IsFunctionTemplateInfo()) { | |
108 return InstantiateFunction(isolate, | |
109 Handle<FunctionTemplateInfo>::cast(data), name); | |
110 } else if (data->IsObjectTemplateInfo()) { | |
111 return InstantiateObject(isolate, Handle<ObjectTemplateInfo>::cast(data)); | |
112 } else { | |
113 // TODO(dcarney): CHECK data is JSObject or Primitive. | |
114 return data; | |
115 } | |
116 } | |
117 | |
118 | |
119 MaybeHandle<JSObject> ConfigureInstance(Isolate* isolate, Handle<JSObject> obj, | |
120 Handle<TemplateInfo> data) { | |
121 auto property_list = handle(data->property_list(), isolate); | |
122 if (property_list->IsUndefined()) return obj; | |
123 // TODO(dcarney): just use a FixedArray here. | |
124 NeanderArray properties(property_list); | |
125 if (properties.length() == 0) return obj; | |
126 HandleScope scope(isolate); | |
127 // // Disable access checks while instantiating the object. | |
128 bool requires_access_checks = obj->map()->is_access_check_needed(); | |
129 if (requires_access_checks) { | |
130 DisableAccessChecks(isolate, obj); | |
131 } | |
132 // TODO(dcarney): put iteration in own function so exception checking is more | |
133 // standard. | |
Toon Verwaest
2015/02/04 12:04:04
Would indeed be nice if you'd just move the Disabl
| |
134 MaybeHandle<JSObject> result = obj; | |
135 for (int i = 0; i < properties.length();) { | |
136 int length = Smi::cast(properties.get(i))->value(); | |
137 if (length == 3) { | |
138 auto name = handle(Name::cast(properties.get(i + 1)), isolate); | |
139 auto prop_data = handle(properties.get(i + 2), isolate); | |
140 auto attributes = Smi::cast(properties.get(i + 3)); | |
141 auto instantiate_result = Instantiate(isolate, prop_data, name); | |
142 Handle<Object> value; | |
143 if (!instantiate_result.ToHandle(&value)) { | |
144 result = MaybeHandle<JSObject>(); | |
145 break; | |
146 } | |
147 auto add_result = | |
148 AddPropertyForTemplate(isolate, obj, name, value, attributes); | |
Toon Verwaest
2015/02/04 12:04:04
Can you change this to return a MaybeHandle<> rath
| |
149 if (add_result->IsException()) { | |
150 result = MaybeHandle<JSObject>(); | |
151 break; | |
152 } | |
153 } else { | |
154 DCHECK(length == 4 || length == 5); | |
155 // TODO(verwaest): The 5th value used to be access_control. Remove once | |
156 // the bindings are updated. | |
157 auto name = handle(Name::cast(properties.get(i + 1)), isolate); | |
158 auto getter = handle(properties.get(i + 2), isolate); | |
159 auto setter = handle(properties.get(i + 3), isolate); | |
160 auto attributes = Smi::cast(properties.get(i + 4)); | |
161 auto define_result = DefineApiAccessorProperty(isolate, obj, name, getter, | |
162 setter, attributes); | |
Toon Verwaest
2015/02/04 12:04:04
Same here
| |
163 if (define_result->IsException()) { | |
164 result = MaybeHandle<JSObject>(); | |
165 break; | |
166 } | |
167 } | |
168 i += length + 1; | |
169 } | |
170 if (requires_access_checks) { | |
171 EnableAccessChecks(isolate, obj); | |
172 } | |
173 // TODO(dcarney): return a Object* here? | |
174 return result; | |
175 } | |
176 | |
177 | |
178 MaybeHandle<JSObject> InstantiateObject(Isolate* isolate, | |
179 Handle<ObjectTemplateInfo> data) { | |
180 // Enter a new scope. Recursion could otherwise create a lot of handles. | |
181 HandleScope scope(isolate); | |
182 // Fast path. | |
183 Handle<JSObject> result; | |
184 auto info = Handle<ObjectTemplateInfo>::cast(data); | |
185 auto constructor = handle(info->constructor(), isolate); | |
186 Handle<JSFunction> cons; | |
187 if (constructor->IsUndefined()) { | |
188 cons = isolate->object_function(); | |
189 } else { | |
190 auto cons_templ = Handle<FunctionTemplateInfo>::cast(constructor); | |
191 ASSIGN_RETURN_ON_EXCEPTION( | |
192 isolate, cons, InstantiateFunction(isolate, cons_templ), JSFunction); | |
193 } | |
194 auto object = isolate->factory()->NewJSObject(cons); | |
195 ASSIGN_RETURN_ON_EXCEPTION( | |
196 isolate, result, ConfigureInstance(isolate, object, info), JSFunction); | |
197 // TODO(dcarney): is this necessary? | |
198 JSObject::MigrateSlowToFast(result, 0, "ApiNatives::InstantiateObject"); | |
199 return scope.CloseAndEscape(result); | |
200 } | |
201 | |
202 | |
203 void InstallInCache(Isolate* isolate, int serial_number, | |
204 Handle<JSFunction> function) { | |
205 Handle<FixedArray> cache(isolate->native_context()->function_cache()); | |
Toon Verwaest
2015/02/04 12:04:04
auto cache = isolate->function_cache();
| |
206 if (isolate->native_context()->function_cache()->length() <= serial_number) { | |
Toon Verwaest
2015/02/04 12:04:04
cache->length()
| |
207 int new_size; | |
208 if (isolate->next_serial_number() < 50) { | |
209 new_size = 100; | |
210 } else { | |
211 new_size = 3 * isolate->next_serial_number() / 2; | |
212 } | |
213 cache = FixedArray::CopySize(cache, new_size); | |
214 isolate->native_context()->set_function_cache(*cache); | |
215 } | |
216 cache->set(serial_number, *function); | |
217 } | |
218 | |
219 | |
220 MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate, | |
221 Handle<FunctionTemplateInfo> data, | |
222 Handle<Name> name) { | |
223 int serial_number = Smi::cast(data->serial_number())->value(); | |
224 // Probe cache. | |
225 if (!data->do_not_cache()) { | |
226 Handle<FixedArray> cache(isolate->native_context()->function_cache()); | |
Toon Verwaest
2015/02/04 12:04:04
auto cache = isolate->function_cache()
| |
227 // Fast case: see if the function has already been instantiated | |
228 if (serial_number < cache->length()) { | |
229 Handle<Object> element = FixedArray::get(cache, serial_number); | |
230 if (element->IsJSFunction()) { | |
231 return Handle<JSFunction>::cast(element); | |
232 } | |
233 } | |
234 } | |
235 // Enter a new scope. Recursion could otherwise create a lot of handles. | |
236 HandleScope scope(isolate); | |
237 Handle<JSObject> prototype; | |
238 if (!data->remove_prototype()) { | |
239 auto prototype_templ = handle(data->prototype_template(), isolate); | |
240 if (prototype_templ->IsUndefined()) { | |
241 prototype = isolate->factory()->NewJSObject(isolate->object_function()); | |
242 } else { | |
243 ASSIGN_RETURN_ON_EXCEPTION( | |
244 isolate, prototype, | |
245 InstantiateObject(isolate, | |
246 Handle<ObjectTemplateInfo>::cast(prototype_templ)), | |
247 JSFunction); | |
248 } | |
249 auto parent = handle(data->parent_template(), isolate); | |
250 if (!parent->IsUndefined()) { | |
251 Handle<JSFunction> parent_instance; | |
252 ASSIGN_RETURN_ON_EXCEPTION( | |
253 isolate, parent_instance, | |
254 InstantiateFunction(isolate, | |
255 Handle<FunctionTemplateInfo>::cast(parent)), | |
256 JSFunction); | |
257 // TODO(dcarney): decide what to do here. | |
258 Handle<Object> parent_prototype; | |
259 ASSIGN_RETURN_ON_EXCEPTION( | |
260 isolate, parent_prototype, | |
261 JSObject::GetProperty(parent_instance, | |
262 isolate->factory()->prototype_string()), | |
263 JSFunction); | |
264 RETURN_ON_EXCEPTION( | |
265 isolate, JSObject::SetPrototype(prototype, parent_prototype, false), | |
266 JSFunction); | |
267 } | |
268 } | |
269 auto function = ApiNatives::CreateApiFunction( | |
270 isolate, data, prototype, ApiNatives::JavaScriptObjectType); | |
271 if (!name.is_null() && name->IsString()) { | |
272 function->shared()->set_name(*name); | |
273 } | |
274 if (!data->do_not_cache()) { | |
275 // Cache the function to limit recursion. | |
276 InstallInCache(isolate, serial_number, function); | |
277 } | |
278 auto result = ConfigureInstance(isolate, function, data); | |
279 if (result.is_null()) { | |
280 // uncache on error. | |
281 if (!data->do_not_cache()) { | |
282 Handle<FixedArray> cache(isolate->native_context()->function_cache()); | |
283 cache->set(serial_number, isolate->heap()->undefined_value()); | |
284 } | |
285 return MaybeHandle<JSFunction>(); | |
286 } | |
287 return scope.CloseAndEscape(function); | |
288 } | |
289 | |
290 | |
291 class InvokeScope { | |
292 public: | |
293 explicit InvokeScope(Isolate* isolate) | |
294 : isolate_(isolate), save_context_(isolate) {} | |
295 ~InvokeScope() { | |
296 bool has_exception = isolate_->has_pending_exception(); | |
297 if (has_exception) { | |
298 isolate_->ReportPendingMessages(); | |
299 } else { | |
300 isolate_->clear_pending_message(); | |
301 } | |
302 } | |
303 | |
304 private: | |
305 Isolate* isolate_; | |
306 SaveContext save_context_; | |
307 }; | |
308 | |
309 } // namespace | |
310 | |
311 | |
312 MaybeHandle<JSFunction> ApiNatives::InstantiateFunction( | |
313 Handle<FunctionTemplateInfo> data) { | |
314 Isolate* isolate = data->GetIsolate(); | |
315 InvokeScope invoke_scope(isolate); | |
316 return ::v8::internal::InstantiateFunction(isolate, data); | |
317 } | |
318 | |
319 | |
320 MaybeHandle<JSObject> ApiNatives::InstantiateObject( | |
321 Handle<ObjectTemplateInfo> data) { | |
322 Isolate* isolate = data->GetIsolate(); | |
323 InvokeScope invoke_scope(isolate); | |
324 return ::v8::internal::InstantiateObject(isolate, data); | |
325 } | |
326 | |
327 | |
328 MaybeHandle<FunctionTemplateInfo> ApiNatives::ConfigureInstance( | |
329 Isolate* isolate, Handle<FunctionTemplateInfo> desc, | |
330 Handle<JSObject> instance) { | |
331 // Configure the instance by adding the properties specified by the | |
332 // instance template. | |
333 if (desc->instance_template()->IsUndefined()) return desc; | |
334 InvokeScope invoke_scope(isolate); | |
335 Handle<ObjectTemplateInfo> instance_template( | |
336 ObjectTemplateInfo::cast(desc->instance_template()), isolate); | |
337 RETURN_ON_EXCEPTION(isolate, ::v8::internal::ConfigureInstance( | |
338 isolate, instance, instance_template), | |
339 FunctionTemplateInfo); | |
340 return desc; | |
341 } | |
342 | |
343 | |
344 Handle<JSFunction> ApiNatives::CreateApiFunction( | |
345 Isolate* isolate, Handle<FunctionTemplateInfo> obj, | |
346 Handle<Object> prototype, ApiInstanceType instance_type) { | |
347 Handle<Code> code = isolate->builtins()->HandleApiCall(); | |
348 Handle<Code> construct_stub = isolate->builtins()->JSConstructStubApi(); | |
349 | |
350 obj->set_instantiated(true); | |
351 Handle<JSFunction> result; | |
352 if (obj->remove_prototype()) { | |
353 result = isolate->factory()->NewFunctionWithoutPrototype( | |
354 isolate->factory()->empty_string(), code); | |
355 } else { | |
356 int internal_field_count = 0; | |
357 if (!obj->instance_template()->IsUndefined()) { | |
358 Handle<ObjectTemplateInfo> instance_template = Handle<ObjectTemplateInfo>( | |
359 ObjectTemplateInfo::cast(obj->instance_template())); | |
360 internal_field_count = | |
361 Smi::cast(instance_template->internal_field_count())->value(); | |
362 } | |
363 | |
364 // TODO(svenpanne) Kill ApiInstanceType and refactor things by generalizing | |
365 // JSObject::GetHeaderSize. | |
366 int instance_size = kPointerSize * internal_field_count; | |
367 InstanceType type; | |
368 switch (instance_type) { | |
369 case JavaScriptObjectType: | |
370 type = JS_OBJECT_TYPE; | |
371 instance_size += JSObject::kHeaderSize; | |
372 break; | |
373 case GlobalObjectType: | |
374 type = JS_GLOBAL_OBJECT_TYPE; | |
375 instance_size += JSGlobalObject::kSize; | |
376 break; | |
377 case GlobalProxyType: | |
378 type = JS_GLOBAL_PROXY_TYPE; | |
379 instance_size += JSGlobalProxy::kSize; | |
380 break; | |
381 default: | |
382 UNREACHABLE(); | |
383 type = JS_OBJECT_TYPE; // Keep the compiler happy. | |
384 break; | |
385 } | |
386 | |
387 result = isolate->factory()->NewFunction( | |
388 isolate->factory()->empty_string(), code, prototype, type, | |
389 instance_size, obj->read_only_prototype(), true); | |
390 } | |
391 | |
392 result->shared()->set_length(obj->length()); | |
393 Handle<Object> class_name(obj->class_name(), isolate); | |
394 if (class_name->IsString()) { | |
395 result->shared()->set_instance_class_name(*class_name); | |
396 result->shared()->set_name(*class_name); | |
397 } | |
398 result->shared()->set_function_data(*obj); | |
399 result->shared()->set_construct_stub(*construct_stub); | |
400 result->shared()->DontAdaptArguments(); | |
401 | |
402 if (obj->remove_prototype()) { | |
403 DCHECK(result->shared()->IsApiFunction()); | |
404 DCHECK(!result->has_initial_map()); | |
405 DCHECK(!result->has_prototype()); | |
406 return result; | |
407 } | |
408 | |
409 #ifdef DEBUG | |
410 LookupIterator it(handle(JSObject::cast(result->prototype())), | |
411 isolate->factory()->constructor_string(), | |
412 LookupIterator::OWN_SKIP_INTERCEPTOR); | |
413 MaybeHandle<Object> maybe_prop = Object::GetProperty(&it); | |
414 DCHECK(it.IsFound()); | |
415 DCHECK(maybe_prop.ToHandleChecked().is_identical_to(result)); | |
416 #endif | |
417 | |
418 // Down from here is only valid for API functions that can be used as a | |
419 // constructor (don't set the "remove prototype" flag). | |
420 | |
421 Handle<Map> map(result->initial_map()); | |
422 | |
423 // Mark as undetectable if needed. | |
424 if (obj->undetectable()) { | |
425 map->set_is_undetectable(); | |
426 } | |
427 | |
428 // Mark as hidden for the __proto__ accessor if needed. | |
429 if (obj->hidden_prototype()) { | |
430 map->set_is_hidden_prototype(); | |
431 } | |
432 | |
433 // Mark as needs_access_check if needed. | |
434 if (obj->needs_access_check()) { | |
435 map->set_is_access_check_needed(true); | |
436 } | |
437 | |
438 // Set interceptor information in the map. | |
439 if (!obj->named_property_handler()->IsUndefined()) { | |
440 map->set_has_named_interceptor(); | |
441 } | |
442 if (!obj->indexed_property_handler()->IsUndefined()) { | |
443 map->set_has_indexed_interceptor(); | |
444 } | |
445 | |
446 // Set instance call-as-function information in the map. | |
447 if (!obj->instance_call_handler()->IsUndefined()) { | |
448 map->set_has_instance_call_handler(); | |
449 } | |
450 | |
451 // Recursively copy parent instance templates' accessors, | |
452 // 'data' may be modified. | |
453 int max_number_of_additional_properties = 0; | |
454 int max_number_of_static_properties = 0; | |
455 FunctionTemplateInfo* info = *obj; | |
456 while (true) { | |
457 if (!info->instance_template()->IsUndefined()) { | |
458 Object* props = ObjectTemplateInfo::cast(info->instance_template()) | |
459 ->property_accessors(); | |
460 if (!props->IsUndefined()) { | |
461 Handle<Object> props_handle(props, isolate); | |
462 NeanderArray props_array(props_handle); | |
463 max_number_of_additional_properties += props_array.length(); | |
464 } | |
465 } | |
466 if (!info->property_accessors()->IsUndefined()) { | |
467 Object* props = info->property_accessors(); | |
468 if (!props->IsUndefined()) { | |
469 Handle<Object> props_handle(props, isolate); | |
470 NeanderArray props_array(props_handle); | |
471 max_number_of_static_properties += props_array.length(); | |
472 } | |
473 } | |
474 Object* parent = info->parent_template(); | |
475 if (parent->IsUndefined()) break; | |
476 info = FunctionTemplateInfo::cast(parent); | |
477 } | |
478 | |
479 Map::EnsureDescriptorSlack(map, max_number_of_additional_properties); | |
480 | |
481 // Use a temporary FixedArray to acculumate static accessors | |
482 int valid_descriptors = 0; | |
483 Handle<FixedArray> array; | |
484 if (max_number_of_static_properties > 0) { | |
485 array = isolate->factory()->NewFixedArray(max_number_of_static_properties); | |
486 } | |
487 | |
488 while (true) { | |
489 // Install instance descriptors | |
490 if (!obj->instance_template()->IsUndefined()) { | |
491 Handle<ObjectTemplateInfo> instance = Handle<ObjectTemplateInfo>( | |
492 ObjectTemplateInfo::cast(obj->instance_template()), isolate); | |
493 Handle<Object> props = | |
494 Handle<Object>(instance->property_accessors(), isolate); | |
495 if (!props->IsUndefined()) { | |
496 Map::AppendCallbackDescriptors(map, props); | |
497 } | |
498 } | |
499 // Accumulate static accessors | |
500 if (!obj->property_accessors()->IsUndefined()) { | |
501 Handle<Object> props = Handle<Object>(obj->property_accessors(), isolate); | |
502 valid_descriptors = | |
503 AccessorInfo::AppendUnique(props, array, valid_descriptors); | |
504 } | |
505 // Climb parent chain | |
506 Handle<Object> parent = Handle<Object>(obj->parent_template(), isolate); | |
507 if (parent->IsUndefined()) break; | |
508 obj = Handle<FunctionTemplateInfo>::cast(parent); | |
509 } | |
510 | |
511 // Install accumulated static accessors | |
512 for (int i = 0; i < valid_descriptors; i++) { | |
513 Handle<AccessorInfo> accessor(AccessorInfo::cast(array->get(i))); | |
514 JSObject::SetAccessor(result, accessor).Assert(); | |
515 } | |
516 | |
517 DCHECK(result->shared()->IsApiFunction()); | |
518 return result; | |
519 } | |
520 | |
521 } // namespace internal | |
522 } // namespace v8 | |
OLD | NEW |