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.
5 #include "src/api-natives.h"
6 #include "src/isolate-inl.h"
13 MaybeHandle<JSObject> InstantiateObject(Isolate* isolate,
14 Handle<ObjectTemplateInfo> data);
17 MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate,
18 Handle<FunctionTemplateInfo> data,
19 Handle<Name> name = Handle<Name>());
22 MaybeHandle<Object> Instantiate(Isolate* isolate, Handle<Object> data,
23 Handle<Name> name = Handle<Name>()) {
24 if (data->IsFunctionTemplateInfo()) {
25 return InstantiateFunction(isolate,
26 Handle<FunctionTemplateInfo>::cast(data), name);
27 } else if (data->IsObjectTemplateInfo()) {
28 return InstantiateObject(isolate, Handle<ObjectTemplateInfo>::cast(data));
35 MaybeHandle<Object> DefineAccessorProperty(
36 Isolate* isolate, Handle<JSObject> object, Handle<Name> name,
37 Handle<Object> getter, Handle<Object> setter, Smi* attributes) {
38 DCHECK(PropertyDetails::AttributesField::is_valid(
39 static_cast<PropertyAttributes>(attributes->value())));
40 if (!getter->IsUndefined()) {
41 ASSIGN_RETURN_ON_EXCEPTION(
43 InstantiateFunction(isolate,
44 Handle<FunctionTemplateInfo>::cast(getter)),
47 if (!setter->IsUndefined()) {
48 ASSIGN_RETURN_ON_EXCEPTION(
50 InstantiateFunction(isolate,
51 Handle<FunctionTemplateInfo>::cast(setter)),
54 RETURN_ON_EXCEPTION(isolate,
55 JSObject::DefineAccessor(
56 object, name, getter, setter,
57 static_cast<PropertyAttributes>(attributes->value())),
63 MaybeHandle<Object> DefineDataProperty(Isolate* isolate,
64 Handle<JSObject> object,
66 Handle<Object> prop_data,
67 Smi* unchecked_attributes) {
68 DCHECK((unchecked_attributes->value() &
69 ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
70 // Compute attributes.
71 PropertyAttributes attributes =
72 static_cast<PropertyAttributes>(unchecked_attributes->value());
75 ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
76 Instantiate(isolate, prop_data, key), Object);
81 LookupIterator it(object, Handle<Name>::cast(key),
82 LookupIterator::OWN_SKIP_INTERCEPTOR);
83 Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it);
84 DCHECK(maybe.has_value);
85 duplicate = it.IsFound();
88 key->ToArrayIndex(&index);
89 Maybe<bool> maybe = JSReceiver::HasOwnElement(object, index);
90 if (!maybe.has_value) return MaybeHandle<Object>();
91 duplicate = maybe.value;
94 Handle<Object> args[1] = {key};
95 THROW_NEW_ERROR(isolate, NewTypeError("duplicate_template_property",
96 HandleVector(args, 1)),
102 isolate, Runtime::DefineObjectProperty(object, key, value, attributes),
108 void DisableAccessChecks(Isolate* isolate, Handle<JSObject> object) {
109 Handle<Map> old_map(object->map());
110 // Copy map so it won't interfere constructor's initial map.
111 Handle<Map> new_map = Map::Copy(old_map, "DisableAccessChecks");
112 new_map->set_is_access_check_needed(false);
113 JSObject::MigrateToMap(Handle<JSObject>::cast(object), new_map);
117 void EnableAccessChecks(Isolate* isolate, Handle<JSObject> object) {
118 Handle<Map> old_map(object->map());
119 // Copy map so it won't interfere constructor's initial map.
120 Handle<Map> new_map = Map::Copy(old_map, "EnableAccessChecks");
121 new_map->set_is_access_check_needed(true);
122 JSObject::MigrateToMap(object, new_map);
126 class AccessCheckDisableScope {
128 AccessCheckDisableScope(Isolate* isolate, Handle<JSObject> obj)
130 disabled_(obj->map()->is_access_check_needed()),
133 DisableAccessChecks(isolate_, obj_);
136 ~AccessCheckDisableScope() {
138 EnableAccessChecks(isolate_, obj_);
144 const bool disabled_;
145 Handle<JSObject> obj_;
149 MaybeHandle<JSObject> ConfigureInstance(Isolate* isolate, Handle<JSObject> obj,
150 Handle<TemplateInfo> data) {
151 auto property_list = handle(data->property_list(), isolate);
152 if (property_list->IsUndefined()) return obj;
153 // TODO(dcarney): just use a FixedArray here.
154 NeanderArray properties(property_list);
155 if (properties.length() == 0) return obj;
156 HandleScope scope(isolate);
157 // Disable access checks while instantiating the object.
158 AccessCheckDisableScope access_check_scope(isolate, obj);
159 for (int i = 0; i < properties.length();) {
160 int length = Smi::cast(properties.get(i))->value();
162 auto name = handle(Name::cast(properties.get(i + 1)), isolate);
163 auto prop_data = handle(properties.get(i + 2), isolate);
164 auto attributes = Smi::cast(properties.get(i + 3));
165 RETURN_ON_EXCEPTION(isolate, DefineDataProperty(isolate, obj, name,
166 prop_data, attributes),
170 auto name = handle(Name::cast(properties.get(i + 1)), isolate);
171 auto getter = handle(properties.get(i + 2), isolate);
172 auto setter = handle(properties.get(i + 3), isolate);
173 auto attributes = Smi::cast(properties.get(i + 4));
174 RETURN_ON_EXCEPTION(isolate,
175 DefineAccessorProperty(isolate, obj, name, getter,
185 MaybeHandle<JSObject> InstantiateObject(Isolate* isolate,
186 Handle<ObjectTemplateInfo> data) {
187 // Enter a new scope. Recursion could otherwise create a lot of handles.
188 HandleScope scope(isolate);
190 Handle<JSObject> result;
191 auto info = Handle<ObjectTemplateInfo>::cast(data);
192 auto constructor = handle(info->constructor(), isolate);
193 Handle<JSFunction> cons;
194 if (constructor->IsUndefined()) {
195 cons = isolate->object_function();
197 auto cons_templ = Handle<FunctionTemplateInfo>::cast(constructor);
198 ASSIGN_RETURN_ON_EXCEPTION(
199 isolate, cons, InstantiateFunction(isolate, cons_templ), JSFunction);
201 auto object = isolate->factory()->NewJSObject(cons);
202 ASSIGN_RETURN_ON_EXCEPTION(
203 isolate, result, ConfigureInstance(isolate, object, info), JSFunction);
204 // TODO(dcarney): is this necessary?
205 JSObject::MigrateSlowToFast(result, 0, "ApiNatives::InstantiateObject");
206 return scope.CloseAndEscape(result);
210 void CacheFunction(Isolate* isolate, Handle<Smi> serial_number,
211 Handle<JSFunction> function) {
212 auto cache = isolate->function_cache();
213 auto new_cache = ObjectHashTable::Put(cache, serial_number, function);
214 isolate->native_context()->set_function_cache(*new_cache);
218 void UncacheFunction(Isolate* isolate, Handle<Smi> serial_number) {
219 auto cache = isolate->function_cache();
220 bool was_present = false;
221 auto new_cache = ObjectHashTable::Remove(cache, serial_number, &was_present);
223 isolate->native_context()->set_function_cache(*new_cache);
227 MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate,
228 Handle<FunctionTemplateInfo> data,
230 auto serial_number = handle(Smi::cast(data->serial_number()), isolate);
232 if (!data->do_not_cache()) {
233 auto cache = isolate->function_cache();
234 Object* element = cache->Lookup(serial_number);
235 if (element->IsJSFunction()) {
236 return handle(JSFunction::cast(element), isolate);
239 // Enter a new scope. Recursion could otherwise create a lot of handles.
240 HandleScope scope(isolate);
241 Handle<JSObject> prototype;
242 if (!data->remove_prototype()) {
243 auto prototype_templ = handle(data->prototype_template(), isolate);
244 if (prototype_templ->IsUndefined()) {
245 prototype = isolate->factory()->NewJSObject(isolate->object_function());
247 ASSIGN_RETURN_ON_EXCEPTION(
249 InstantiateObject(isolate,
250 Handle<ObjectTemplateInfo>::cast(prototype_templ)),
253 auto parent = handle(data->parent_template(), isolate);
254 if (!parent->IsUndefined()) {
255 Handle<JSFunction> parent_instance;
256 ASSIGN_RETURN_ON_EXCEPTION(
257 isolate, parent_instance,
258 InstantiateFunction(isolate,
259 Handle<FunctionTemplateInfo>::cast(parent)),
261 // TODO(dcarney): decide what to do here.
262 Handle<Object> parent_prototype;
263 ASSIGN_RETURN_ON_EXCEPTION(
264 isolate, parent_prototype,
265 JSObject::GetProperty(parent_instance,
266 isolate->factory()->prototype_string()),
269 isolate, JSObject::SetPrototype(prototype, parent_prototype, false),
273 auto function = ApiNatives::CreateApiFunction(
274 isolate, data, prototype, ApiNatives::JavaScriptObjectType);
275 if (!name.is_null() && name->IsString()) {
276 function->shared()->set_name(*name);
278 if (!data->do_not_cache()) {
279 // Cache the function.
280 CacheFunction(isolate, serial_number, function);
282 auto result = ConfigureInstance(isolate, function, data);
283 if (result.is_null()) {
285 if (!data->do_not_cache()) {
286 UncacheFunction(isolate, serial_number);
288 return MaybeHandle<JSFunction>();
290 return scope.CloseAndEscape(function);
296 explicit InvokeScope(Isolate* isolate)
297 : isolate_(isolate), save_context_(isolate) {}
299 bool has_exception = isolate_->has_pending_exception();
301 isolate_->ReportPendingMessages();
303 isolate_->clear_pending_message();
309 SaveContext save_context_;
313 void AddPropertyToPropertyList(Isolate* isolate, Handle<TemplateInfo> templ,
314 int length, Handle<Object>* data) {
315 auto list = handle(templ->property_list(), isolate);
316 if (list->IsUndefined()) {
317 list = NeanderArray(isolate).value();
318 templ->set_property_list(*list);
320 NeanderArray array(list);
321 array.add(isolate, isolate->factory()->NewNumberFromInt(length));
322 for (int i = 0; i < length; i++) {
323 Handle<Object> value =
325 ? Handle<Object>::cast(isolate->factory()->undefined_value())
327 array.add(isolate, value);
334 MaybeHandle<JSFunction> ApiNatives::InstantiateFunction(
335 Handle<FunctionTemplateInfo> data) {
336 Isolate* isolate = data->GetIsolate();
337 InvokeScope invoke_scope(isolate);
338 return ::v8::internal::InstantiateFunction(isolate, data);
342 MaybeHandle<JSObject> ApiNatives::InstantiateObject(
343 Handle<ObjectTemplateInfo> data) {
344 Isolate* isolate = data->GetIsolate();
345 InvokeScope invoke_scope(isolate);
346 return ::v8::internal::InstantiateObject(isolate, data);
350 MaybeHandle<FunctionTemplateInfo> ApiNatives::ConfigureInstance(
351 Isolate* isolate, Handle<FunctionTemplateInfo> desc,
352 Handle<JSObject> instance) {
353 // Configure the instance by adding the properties specified by the
354 // instance template.
355 if (desc->instance_template()->IsUndefined()) return desc;
356 InvokeScope invoke_scope(isolate);
357 Handle<ObjectTemplateInfo> instance_template(
358 ObjectTemplateInfo::cast(desc->instance_template()), isolate);
359 RETURN_ON_EXCEPTION(isolate, ::v8::internal::ConfigureInstance(
360 isolate, instance, instance_template),
361 FunctionTemplateInfo);
366 void ApiNatives::AddDataProperty(Isolate* isolate, Handle<TemplateInfo> info,
367 Handle<Name> name, Handle<Object> value,
368 PropertyAttributes attributes) {
370 DCHECK(Smi::IsValid(static_cast<int>(attributes)));
371 auto attribute_handle =
372 handle(Smi::FromInt(static_cast<int>(attributes)), isolate);
373 Handle<Object> data[kSize] = {name, value, attribute_handle};
374 AddPropertyToPropertyList(isolate, info, kSize, data);
378 void ApiNatives::AddAccessorProperty(Isolate* isolate,
379 Handle<TemplateInfo> info,
381 Handle<FunctionTemplateInfo> getter,
382 Handle<FunctionTemplateInfo> setter,
383 PropertyAttributes attributes) {
385 DCHECK(Smi::IsValid(static_cast<int>(attributes)));
386 auto attribute_handle =
387 handle(Smi::FromInt(static_cast<int>(attributes)), isolate);
388 Handle<Object> data[kSize] = {name, getter, setter, attribute_handle};
389 AddPropertyToPropertyList(isolate, info, kSize, data);
393 void ApiNatives::AddNativeDataProperty(Isolate* isolate,
394 Handle<TemplateInfo> info,
395 Handle<AccessorInfo> property) {
396 auto list = handle(info->property_accessors(), isolate);
397 if (list->IsUndefined()) {
398 list = NeanderArray(isolate).value();
399 info->set_property_accessors(*list);
401 NeanderArray array(list);
402 array.add(isolate, property);
406 Handle<JSFunction> ApiNatives::CreateApiFunction(
407 Isolate* isolate, Handle<FunctionTemplateInfo> obj,
408 Handle<Object> prototype, ApiInstanceType instance_type) {
409 Handle<Code> code = isolate->builtins()->HandleApiCall();
410 Handle<Code> construct_stub = isolate->builtins()->JSConstructStubApi();
412 obj->set_instantiated(true);
413 Handle<JSFunction> result;
414 if (obj->remove_prototype()) {
415 result = isolate->factory()->NewFunctionWithoutPrototype(
416 isolate->factory()->empty_string(), code);
418 int internal_field_count = 0;
419 if (!obj->instance_template()->IsUndefined()) {
420 Handle<ObjectTemplateInfo> instance_template = Handle<ObjectTemplateInfo>(
421 ObjectTemplateInfo::cast(obj->instance_template()));
422 internal_field_count =
423 Smi::cast(instance_template->internal_field_count())->value();
426 // TODO(svenpanne) Kill ApiInstanceType and refactor things by generalizing
427 // JSObject::GetHeaderSize.
428 int instance_size = kPointerSize * internal_field_count;
430 switch (instance_type) {
431 case JavaScriptObjectType:
432 type = JS_OBJECT_TYPE;
433 instance_size += JSObject::kHeaderSize;
435 case GlobalObjectType:
436 type = JS_GLOBAL_OBJECT_TYPE;
437 instance_size += JSGlobalObject::kSize;
439 case GlobalProxyType:
440 type = JS_GLOBAL_PROXY_TYPE;
441 instance_size += JSGlobalProxy::kSize;
445 type = JS_OBJECT_TYPE; // Keep the compiler happy.
449 result = isolate->factory()->NewFunction(
450 isolate->factory()->empty_string(), code, prototype, type,
451 instance_size, obj->read_only_prototype(), true);
454 result->shared()->set_length(obj->length());
455 Handle<Object> class_name(obj->class_name(), isolate);
456 if (class_name->IsString()) {
457 result->shared()->set_instance_class_name(*class_name);
458 result->shared()->set_name(*class_name);
460 result->shared()->set_function_data(*obj);
461 result->shared()->set_construct_stub(*construct_stub);
462 result->shared()->DontAdaptArguments();
464 if (obj->remove_prototype()) {
465 DCHECK(result->shared()->IsApiFunction());
466 DCHECK(!result->has_initial_map());
467 DCHECK(!result->has_prototype());
472 LookupIterator it(handle(JSObject::cast(result->prototype())),
473 isolate->factory()->constructor_string(),
474 LookupIterator::OWN_SKIP_INTERCEPTOR);
475 MaybeHandle<Object> maybe_prop = Object::GetProperty(&it);
476 DCHECK(it.IsFound());
477 DCHECK(maybe_prop.ToHandleChecked().is_identical_to(result));
480 // Down from here is only valid for API functions that can be used as a
481 // constructor (don't set the "remove prototype" flag).
483 Handle<Map> map(result->initial_map());
485 // Mark as undetectable if needed.
486 if (obj->undetectable()) {
487 map->set_is_undetectable();
490 // Mark as hidden for the __proto__ accessor if needed.
491 if (obj->hidden_prototype()) {
492 map->set_is_hidden_prototype();
495 // Mark as needs_access_check if needed.
496 if (obj->needs_access_check()) {
497 map->set_is_access_check_needed(true);
500 // Set interceptor information in the map.
501 if (!obj->named_property_handler()->IsUndefined()) {
502 map->set_has_named_interceptor();
504 if (!obj->indexed_property_handler()->IsUndefined()) {
505 map->set_has_indexed_interceptor();
508 // Set instance call-as-function information in the map.
509 if (!obj->instance_call_handler()->IsUndefined()) {
510 map->set_has_instance_call_handler();
513 // Recursively copy parent instance templates' accessors,
514 // 'data' may be modified.
515 int max_number_of_additional_properties = 0;
516 int max_number_of_static_properties = 0;
517 FunctionTemplateInfo* info = *obj;
519 if (!info->instance_template()->IsUndefined()) {
520 Object* props = ObjectTemplateInfo::cast(info->instance_template())
521 ->property_accessors();
522 if (!props->IsUndefined()) {
523 Handle<Object> props_handle(props, isolate);
524 NeanderArray props_array(props_handle);
525 max_number_of_additional_properties += props_array.length();
528 if (!info->property_accessors()->IsUndefined()) {
529 Object* props = info->property_accessors();
530 if (!props->IsUndefined()) {
531 Handle<Object> props_handle(props, isolate);
532 NeanderArray props_array(props_handle);
533 max_number_of_static_properties += props_array.length();
536 Object* parent = info->parent_template();
537 if (parent->IsUndefined()) break;
538 info = FunctionTemplateInfo::cast(parent);
541 Map::EnsureDescriptorSlack(map, max_number_of_additional_properties);
543 // Use a temporary FixedArray to acculumate static accessors
544 int valid_descriptors = 0;
545 Handle<FixedArray> array;
546 if (max_number_of_static_properties > 0) {
547 array = isolate->factory()->NewFixedArray(max_number_of_static_properties);
551 // Install instance descriptors
552 if (!obj->instance_template()->IsUndefined()) {
553 Handle<ObjectTemplateInfo> instance = Handle<ObjectTemplateInfo>(
554 ObjectTemplateInfo::cast(obj->instance_template()), isolate);
555 Handle<Object> props =
556 Handle<Object>(instance->property_accessors(), isolate);
557 if (!props->IsUndefined()) {
558 Map::AppendCallbackDescriptors(map, props);
561 // Accumulate static accessors
562 if (!obj->property_accessors()->IsUndefined()) {
563 Handle<Object> props = Handle<Object>(obj->property_accessors(), isolate);
565 AccessorInfo::AppendUnique(props, array, valid_descriptors);
567 // Climb parent chain
568 Handle<Object> parent = Handle<Object>(obj->parent_template(), isolate);
569 if (parent->IsUndefined()) break;
570 obj = Handle<FunctionTemplateInfo>::cast(parent);
573 // Install accumulated static accessors
574 for (int i = 0; i < valid_descriptors; i++) {
575 Handle<AccessorInfo> accessor(AccessorInfo::cast(array->get(i)));
576 JSObject::SetAccessor(result, accessor).Assert();
579 DCHECK(result->shared()->IsApiFunction());
583 } // namespace internal