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/api-natives.h"
6
7 #include "src/api/api-inl.h"
8 #include "src/common/message-template.h"
9 #include "src/execution/isolate-inl.h"
10 #include "src/heap/heap-inl.h"
11 #include "src/logging/runtime-call-stats-scope.h"
12 #include "src/objects/api-callbacks.h"
13 #include "src/objects/hash-table-inl.h"
14 #include "src/objects/lookup.h"
15 #include "src/objects/property-cell.h"
16 #include "src/objects/templates.h"
17
18 namespace v8 {
19 namespace internal {
20
21 namespace {
22
23 class V8_NODISCARD InvokeScope {
24 public:
InvokeScope(Isolate * isolate)25 explicit InvokeScope(Isolate* isolate)
26 : isolate_(isolate), save_context_(isolate) {}
~InvokeScope()27 ~InvokeScope() {
28 bool has_exception = isolate_->has_pending_exception();
29 if (has_exception) {
30 isolate_->ReportPendingMessages();
31 } else {
32 isolate_->clear_pending_message();
33 }
34 }
35
36 private:
37 Isolate* isolate_;
38 SaveContext save_context_;
39 };
40
41 MaybeHandle<JSObject> InstantiateObject(Isolate* isolate,
42 Handle<ObjectTemplateInfo> data,
43 Handle<JSReceiver> new_target,
44 bool is_prototype);
45
46 MaybeHandle<JSFunction> InstantiateFunction(
47 Isolate* isolate, Handle<NativeContext> native_context,
48 Handle<FunctionTemplateInfo> data,
49 MaybeHandle<Name> maybe_name = MaybeHandle<Name>());
50
InstantiateFunction(Isolate * isolate,Handle<FunctionTemplateInfo> data,MaybeHandle<Name> maybe_name=MaybeHandle<Name> ())51 MaybeHandle<JSFunction> InstantiateFunction(
52 Isolate* isolate, Handle<FunctionTemplateInfo> data,
53 MaybeHandle<Name> maybe_name = MaybeHandle<Name>()) {
54 return InstantiateFunction(isolate, isolate->native_context(), data,
55 maybe_name);
56 }
57
Instantiate(Isolate * isolate,Handle<Object> data,MaybeHandle<Name> maybe_name=MaybeHandle<Name> ())58 MaybeHandle<Object> Instantiate(
59 Isolate* isolate, Handle<Object> data,
60 MaybeHandle<Name> maybe_name = MaybeHandle<Name>()) {
61 if (data->IsFunctionTemplateInfo()) {
62 return InstantiateFunction(
63 isolate, Handle<FunctionTemplateInfo>::cast(data), maybe_name);
64 } else if (data->IsObjectTemplateInfo()) {
65 return InstantiateObject(isolate, Handle<ObjectTemplateInfo>::cast(data),
66 Handle<JSReceiver>(), false);
67 } else {
68 return data;
69 }
70 }
71
DefineAccessorProperty(Isolate * isolate,Handle<JSObject> object,Handle<Name> name,Handle<Object> getter,Handle<Object> setter,PropertyAttributes attributes)72 MaybeHandle<Object> DefineAccessorProperty(Isolate* isolate,
73 Handle<JSObject> object,
74 Handle<Name> name,
75 Handle<Object> getter,
76 Handle<Object> setter,
77 PropertyAttributes attributes) {
78 DCHECK(!getter->IsFunctionTemplateInfo() ||
79 FunctionTemplateInfo::cast(*getter).should_cache());
80 DCHECK(!setter->IsFunctionTemplateInfo() ||
81 FunctionTemplateInfo::cast(*setter).should_cache());
82 if (getter->IsFunctionTemplateInfo() &&
83 FunctionTemplateInfo::cast(*getter).BreakAtEntry()) {
84 ASSIGN_RETURN_ON_EXCEPTION(
85 isolate, getter,
86 InstantiateFunction(isolate,
87 Handle<FunctionTemplateInfo>::cast(getter)),
88 Object);
89 }
90 if (setter->IsFunctionTemplateInfo() &&
91 FunctionTemplateInfo::cast(*setter).BreakAtEntry()) {
92 ASSIGN_RETURN_ON_EXCEPTION(
93 isolate, setter,
94 InstantiateFunction(isolate,
95 Handle<FunctionTemplateInfo>::cast(setter)),
96 Object);
97 }
98 RETURN_ON_EXCEPTION(
99 isolate,
100 JSObject::DefineAccessor(object, name, getter, setter, attributes),
101 Object);
102 return object;
103 }
104
DefineDataProperty(Isolate * isolate,Handle<JSObject> object,Handle<Name> name,Handle<Object> prop_data,PropertyAttributes attributes)105 MaybeHandle<Object> DefineDataProperty(Isolate* isolate,
106 Handle<JSObject> object,
107 Handle<Name> name,
108 Handle<Object> prop_data,
109 PropertyAttributes attributes) {
110 Handle<Object> value;
111 ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
112 Instantiate(isolate, prop_data, name), Object);
113
114 PropertyKey key(isolate, name);
115 LookupIterator it(isolate, object, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
116
117 #ifdef DEBUG
118 Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it);
119 DCHECK(maybe.IsJust());
120 if (it.IsFound()) {
121 THROW_NEW_ERROR(
122 isolate,
123 NewTypeError(MessageTemplate::kDuplicateTemplateProperty, name),
124 Object);
125 }
126 #endif
127
128 MAYBE_RETURN_NULL(Object::AddDataProperty(&it, value, attributes,
129 Just(ShouldThrow::kThrowOnError),
130 StoreOrigin::kNamed));
131 return value;
132 }
133
DisableAccessChecks(Isolate * isolate,Handle<JSObject> object)134 void DisableAccessChecks(Isolate* isolate, Handle<JSObject> object) {
135 Handle<Map> old_map(object->map(), isolate);
136 // Copy map so it won't interfere constructor's initial map.
137 Handle<Map> new_map = Map::Copy(isolate, old_map, "DisableAccessChecks");
138 new_map->set_is_access_check_needed(false);
139 JSObject::MigrateToMap(isolate, Handle<JSObject>::cast(object), new_map);
140 }
141
EnableAccessChecks(Isolate * isolate,Handle<JSObject> object)142 void EnableAccessChecks(Isolate* isolate, Handle<JSObject> object) {
143 Handle<Map> old_map(object->map(), isolate);
144 // Copy map so it won't interfere constructor's initial map.
145 Handle<Map> new_map = Map::Copy(isolate, old_map, "EnableAccessChecks");
146 new_map->set_is_access_check_needed(true);
147 new_map->set_may_have_interesting_symbols(true);
148 JSObject::MigrateToMap(isolate, object, new_map);
149 }
150
151 class V8_NODISCARD AccessCheckDisableScope {
152 public:
AccessCheckDisableScope(Isolate * isolate,Handle<JSObject> obj)153 AccessCheckDisableScope(Isolate* isolate, Handle<JSObject> obj)
154 : isolate_(isolate),
155 disabled_(obj->map().is_access_check_needed()),
156 obj_(obj) {
157 if (disabled_) {
158 DisableAccessChecks(isolate_, obj_);
159 }
160 }
~AccessCheckDisableScope()161 ~AccessCheckDisableScope() {
162 if (disabled_) {
163 EnableAccessChecks(isolate_, obj_);
164 }
165 }
166
167 private:
168 Isolate* isolate_;
169 const bool disabled_;
170 Handle<JSObject> obj_;
171 };
172
GetIntrinsic(Isolate * isolate,v8::Intrinsic intrinsic)173 Object GetIntrinsic(Isolate* isolate, v8::Intrinsic intrinsic) {
174 Handle<Context> native_context = isolate->native_context();
175 DCHECK(!native_context.is_null());
176 switch (intrinsic) {
177 #define GET_INTRINSIC_VALUE(name, iname) \
178 case v8::k##name: \
179 return native_context->iname();
180 V8_INTRINSICS_LIST(GET_INTRINSIC_VALUE)
181 #undef GET_INTRINSIC_VALUE
182 }
183 return Object();
184 }
185
186 template <typename TemplateInfoT>
ConfigureInstance(Isolate * isolate,Handle<JSObject> obj,Handle<TemplateInfoT> data)187 MaybeHandle<JSObject> ConfigureInstance(Isolate* isolate, Handle<JSObject> obj,
188 Handle<TemplateInfoT> data) {
189 RCS_SCOPE(isolate, RuntimeCallCounterId::kConfigureInstance);
190 HandleScope scope(isolate);
191 // Disable access checks while instantiating the object.
192 AccessCheckDisableScope access_check_scope(isolate, obj);
193
194 // Walk the inheritance chain and copy all accessors to current object.
195 int max_number_of_properties = 0;
196 TemplateInfoT info = *data;
197 while (!info.is_null()) {
198 Object props = info.property_accessors();
199 if (!props.IsUndefined(isolate)) {
200 max_number_of_properties += TemplateList::cast(props).length();
201 }
202 info = info.GetParent(isolate);
203 }
204
205 if (max_number_of_properties > 0) {
206 int valid_descriptors = 0;
207 // Use a temporary FixedArray to accumulate unique accessors.
208 Handle<FixedArray> array =
209 isolate->factory()->NewFixedArray(max_number_of_properties);
210
211 for (Handle<TemplateInfoT> temp(*data, isolate); !temp->is_null();
212 temp = handle(temp->GetParent(isolate), isolate)) {
213 // Accumulate accessors.
214 Object maybe_properties = temp->property_accessors();
215 if (!maybe_properties.IsUndefined(isolate)) {
216 valid_descriptors = AccessorInfo::AppendUnique(
217 isolate, handle(maybe_properties, isolate), array,
218 valid_descriptors);
219 }
220 }
221
222 // Install accumulated accessors.
223 for (int i = 0; i < valid_descriptors; i++) {
224 Handle<AccessorInfo> accessor(AccessorInfo::cast(array->get(i)), isolate);
225 Handle<Name> name(Name::cast(accessor->name()), isolate);
226 JSObject::SetAccessor(obj, name, accessor,
227 accessor->initial_property_attributes())
228 .Assert();
229 }
230 }
231
232 Object maybe_property_list = data->property_list();
233 if (maybe_property_list.IsUndefined(isolate)) return obj;
234 Handle<TemplateList> properties(TemplateList::cast(maybe_property_list),
235 isolate);
236 if (properties->length() == 0) return obj;
237
238 int i = 0;
239 for (int c = 0; c < data->number_of_properties(); c++) {
240 auto name = handle(Name::cast(properties->get(i++)), isolate);
241 Object bit = properties->get(i++);
242 if (bit.IsSmi()) {
243 PropertyDetails details(Smi::cast(bit));
244 PropertyAttributes attributes = details.attributes();
245 PropertyKind kind = details.kind();
246
247 if (kind == PropertyKind::kData) {
248 auto prop_data = handle(properties->get(i++), isolate);
249 RETURN_ON_EXCEPTION(
250 isolate,
251 DefineDataProperty(isolate, obj, name, prop_data, attributes),
252 JSObject);
253 } else {
254 auto getter = handle(properties->get(i++), isolate);
255 auto setter = handle(properties->get(i++), isolate);
256 RETURN_ON_EXCEPTION(isolate,
257 DefineAccessorProperty(isolate, obj, name, getter,
258 setter, attributes),
259 JSObject);
260 }
261 } else {
262 // Intrinsic data property --- Get appropriate value from the current
263 // context.
264 PropertyDetails details(Smi::cast(properties->get(i++)));
265 PropertyAttributes attributes = details.attributes();
266 DCHECK_EQ(PropertyKind::kData, details.kind());
267
268 v8::Intrinsic intrinsic =
269 static_cast<v8::Intrinsic>(Smi::ToInt(properties->get(i++)));
270 auto prop_data = handle(GetIntrinsic(isolate, intrinsic), isolate);
271
272 RETURN_ON_EXCEPTION(
273 isolate,
274 DefineDataProperty(isolate, obj, name, prop_data, attributes),
275 JSObject);
276 }
277 }
278 return obj;
279 }
280
281 // Whether or not to cache every instance: when we materialize a getter or
282 // setter from an lazy AccessorPair, we rely on this cache to be able to always
283 // return the same getter or setter. However, objects will be cloned anyways,
284 // so it's not observable if we didn't cache an instance. Furthermore, a badly
285 // behaved embedder might create an unlimited number of objects, so we limit
286 // the cache for those cases.
287 enum class CachingMode { kLimited, kUnlimited };
288
ProbeInstantiationsCache(Isolate * isolate,Handle<NativeContext> native_context,int serial_number,CachingMode caching_mode)289 MaybeHandle<JSObject> ProbeInstantiationsCache(
290 Isolate* isolate, Handle<NativeContext> native_context, int serial_number,
291 CachingMode caching_mode) {
292 DCHECK_NE(serial_number, TemplateInfo::kDoNotCache);
293 if (serial_number == TemplateInfo::kUncached) {
294 return {};
295 }
296
297 if (serial_number < TemplateInfo::kFastTemplateInstantiationsCacheSize) {
298 FixedArray fast_cache =
299 native_context->fast_template_instantiations_cache();
300 Handle<Object> object{fast_cache.get(serial_number), isolate};
301 if (object->IsTheHole(isolate)) return {};
302 return Handle<JSObject>::cast(object);
303 }
304 if (caching_mode == CachingMode::kUnlimited ||
305 (serial_number < TemplateInfo::kSlowTemplateInstantiationsCacheSize)) {
306 SimpleNumberDictionary slow_cache =
307 native_context->slow_template_instantiations_cache();
308 InternalIndex entry = slow_cache.FindEntry(isolate, serial_number);
309 if (entry.is_found()) {
310 return handle(JSObject::cast(slow_cache.ValueAt(entry)), isolate);
311 }
312 }
313 return {};
314 }
315
CacheTemplateInstantiation(Isolate * isolate,Handle<NativeContext> native_context,Handle<TemplateInfo> data,CachingMode caching_mode,Handle<JSObject> object)316 void CacheTemplateInstantiation(Isolate* isolate,
317 Handle<NativeContext> native_context,
318 Handle<TemplateInfo> data,
319 CachingMode caching_mode,
320 Handle<JSObject> object) {
321 DCHECK_NE(TemplateInfo::kDoNotCache, data->serial_number());
322
323 int serial_number = data->serial_number();
324 if (serial_number == TemplateInfo::kUncached) {
325 serial_number = isolate->heap()->GetNextTemplateSerialNumber();
326 }
327
328 if (serial_number < TemplateInfo::kFastTemplateInstantiationsCacheSize) {
329 Handle<FixedArray> fast_cache =
330 handle(native_context->fast_template_instantiations_cache(), isolate);
331 Handle<FixedArray> new_cache =
332 FixedArray::SetAndGrow(isolate, fast_cache, serial_number, object);
333 if (*new_cache != *fast_cache) {
334 native_context->set_fast_template_instantiations_cache(*new_cache);
335 }
336 data->set_serial_number(serial_number);
337 } else if (caching_mode == CachingMode::kUnlimited ||
338 (serial_number <
339 TemplateInfo::kSlowTemplateInstantiationsCacheSize)) {
340 Handle<SimpleNumberDictionary> cache =
341 handle(native_context->slow_template_instantiations_cache(), isolate);
342 auto new_cache =
343 SimpleNumberDictionary::Set(isolate, cache, serial_number, object);
344 if (*new_cache != *cache) {
345 native_context->set_slow_template_instantiations_cache(*new_cache);
346 }
347 data->set_serial_number(serial_number);
348 } else {
349 // we've overflowed the cache limit, no more caching
350 data->set_serial_number(TemplateInfo::kDoNotCache);
351 }
352 }
353
UncacheTemplateInstantiation(Isolate * isolate,Handle<NativeContext> native_context,Handle<TemplateInfo> data,CachingMode caching_mode)354 void UncacheTemplateInstantiation(Isolate* isolate,
355 Handle<NativeContext> native_context,
356 Handle<TemplateInfo> data,
357 CachingMode caching_mode) {
358 int serial_number = data->serial_number();
359 if (serial_number < 0) return;
360
361 if (serial_number < TemplateInfo::kFastTemplateInstantiationsCacheSize) {
362 FixedArray fast_cache =
363 native_context->fast_template_instantiations_cache();
364 DCHECK(!fast_cache.get(serial_number).IsUndefined(isolate));
365 fast_cache.set_undefined(serial_number);
366 data->set_serial_number(TemplateInfo::kUncached);
367 } else if (caching_mode == CachingMode::kUnlimited ||
368 (serial_number <
369 TemplateInfo::kSlowTemplateInstantiationsCacheSize)) {
370 Handle<SimpleNumberDictionary> cache =
371 handle(native_context->slow_template_instantiations_cache(), isolate);
372 InternalIndex entry = cache->FindEntry(isolate, serial_number);
373 DCHECK(entry.is_found());
374 cache = SimpleNumberDictionary::DeleteEntry(isolate, cache, entry);
375 native_context->set_slow_template_instantiations_cache(*cache);
376 data->set_serial_number(TemplateInfo::kUncached);
377 }
378 }
379
IsSimpleInstantiation(Isolate * isolate,ObjectTemplateInfo info,JSReceiver new_target)380 bool IsSimpleInstantiation(Isolate* isolate, ObjectTemplateInfo info,
381 JSReceiver new_target) {
382 DisallowGarbageCollection no_gc;
383
384 if (!new_target.IsJSFunction()) return false;
385 JSFunction fun = JSFunction::cast(new_target);
386 if (fun.shared().function_data(kAcquireLoad) != info.constructor())
387 return false;
388 if (info.immutable_proto()) return false;
389 return fun.native_context() == isolate->raw_native_context();
390 }
391
InstantiateObject(Isolate * isolate,Handle<ObjectTemplateInfo> info,Handle<JSReceiver> new_target,bool is_prototype)392 MaybeHandle<JSObject> InstantiateObject(Isolate* isolate,
393 Handle<ObjectTemplateInfo> info,
394 Handle<JSReceiver> new_target,
395 bool is_prototype) {
396 RCS_SCOPE(isolate, RuntimeCallCounterId::kInstantiateObject);
397 Handle<JSFunction> constructor;
398 bool should_cache = info->should_cache();
399 if (!new_target.is_null()) {
400 if (IsSimpleInstantiation(isolate, *info, *new_target)) {
401 constructor = Handle<JSFunction>::cast(new_target);
402 } else {
403 // Disable caching for subclass instantiation.
404 should_cache = false;
405 }
406 }
407 // Fast path.
408 Handle<JSObject> result;
409 if (should_cache && info->is_cached()) {
410 if (ProbeInstantiationsCache(isolate, isolate->native_context(),
411 info->serial_number(), CachingMode::kLimited)
412 .ToHandle(&result)) {
413 return isolate->factory()->CopyJSObject(result);
414 }
415 }
416
417 if (constructor.is_null()) {
418 Object maybe_constructor_info = info->constructor();
419 if (maybe_constructor_info.IsUndefined(isolate)) {
420 constructor = isolate->object_function();
421 } else {
422 // Enter a new scope. Recursion could otherwise create a lot of handles.
423 HandleScope scope(isolate);
424 Handle<FunctionTemplateInfo> cons_templ(
425 FunctionTemplateInfo::cast(maybe_constructor_info), isolate);
426 Handle<JSFunction> tmp_constructor;
427 ASSIGN_RETURN_ON_EXCEPTION(isolate, tmp_constructor,
428 InstantiateFunction(isolate, cons_templ),
429 JSObject);
430 constructor = scope.CloseAndEscape(tmp_constructor);
431 }
432
433 if (new_target.is_null()) new_target = constructor;
434 }
435
436 Handle<JSObject> object;
437 ASSIGN_RETURN_ON_EXCEPTION(
438 isolate, object,
439 JSObject::New(constructor, new_target, Handle<AllocationSite>::null()),
440 JSObject);
441
442 if (is_prototype) JSObject::OptimizeAsPrototype(object);
443
444 ASSIGN_RETURN_ON_EXCEPTION(
445 isolate, result, ConfigureInstance(isolate, object, info), JSObject);
446 if (info->immutable_proto()) {
447 JSObject::SetImmutableProto(object);
448 }
449 if (!is_prototype) {
450 // Keep prototypes in slow-mode. Let them be lazily turned fast later on.
451 // TODO(dcarney): is this necessary?
452 JSObject::MigrateSlowToFast(result, 0, "ApiNatives::InstantiateObject");
453 // Don't cache prototypes.
454 if (should_cache) {
455 CacheTemplateInstantiation(isolate, isolate->native_context(), info,
456 CachingMode::kLimited, result);
457 result = isolate->factory()->CopyJSObject(result);
458 }
459 }
460
461 return result;
462 }
463
464 namespace {
GetInstancePrototype(Isolate * isolate,Handle<Object> function_template)465 MaybeHandle<Object> GetInstancePrototype(Isolate* isolate,
466 Handle<Object> function_template) {
467 // Enter a new scope. Recursion could otherwise create a lot of handles.
468 HandleScope scope(isolate);
469 Handle<JSFunction> parent_instance;
470 ASSIGN_RETURN_ON_EXCEPTION(
471 isolate, parent_instance,
472 InstantiateFunction(
473 isolate, Handle<FunctionTemplateInfo>::cast(function_template)),
474 JSFunction);
475 Handle<Object> instance_prototype;
476 // TODO(cbruni): decide what to do here.
477 ASSIGN_RETURN_ON_EXCEPTION(
478 isolate, instance_prototype,
479 JSObject::GetProperty(isolate, parent_instance,
480 isolate->factory()->prototype_string()),
481 JSFunction);
482 return scope.CloseAndEscape(instance_prototype);
483 }
484 } // namespace
485
InstantiateFunction(Isolate * isolate,Handle<NativeContext> native_context,Handle<FunctionTemplateInfo> data,MaybeHandle<Name> maybe_name)486 MaybeHandle<JSFunction> InstantiateFunction(
487 Isolate* isolate, Handle<NativeContext> native_context,
488 Handle<FunctionTemplateInfo> data, MaybeHandle<Name> maybe_name) {
489 RCS_SCOPE(isolate, RuntimeCallCounterId::kInstantiateFunction);
490 bool should_cache = data->should_cache();
491 if (should_cache && data->is_cached()) {
492 Handle<JSObject> result;
493 if (ProbeInstantiationsCache(isolate, native_context, data->serial_number(),
494 CachingMode::kUnlimited)
495 .ToHandle(&result)) {
496 return Handle<JSFunction>::cast(result);
497 }
498 }
499 Handle<Object> prototype;
500 if (!data->remove_prototype()) {
501 Handle<Object> prototype_templ(data->GetPrototypeTemplate(), isolate);
502 if (prototype_templ->IsUndefined(isolate)) {
503 Handle<Object> protoype_provider_templ(
504 data->GetPrototypeProviderTemplate(), isolate);
505 if (protoype_provider_templ->IsUndefined(isolate)) {
506 prototype = isolate->factory()->NewJSObject(isolate->object_function());
507 } else {
508 ASSIGN_RETURN_ON_EXCEPTION(
509 isolate, prototype,
510 GetInstancePrototype(isolate, protoype_provider_templ), JSFunction);
511 }
512 } else {
513 ASSIGN_RETURN_ON_EXCEPTION(
514 isolate, prototype,
515 InstantiateObject(isolate,
516 Handle<ObjectTemplateInfo>::cast(prototype_templ),
517 Handle<JSReceiver>(), true),
518 JSFunction);
519 }
520 Handle<Object> parent(data->GetParentTemplate(), isolate);
521 if (!parent->IsUndefined(isolate)) {
522 Handle<Object> parent_prototype;
523 ASSIGN_RETURN_ON_EXCEPTION(isolate, parent_prototype,
524 GetInstancePrototype(isolate, parent),
525 JSFunction);
526 CHECK(parent_prototype->IsHeapObject());
527 JSObject::ForceSetPrototype(isolate, Handle<JSObject>::cast(prototype),
528 Handle<HeapObject>::cast(parent_prototype));
529 }
530 }
531 InstanceType function_type = JS_SPECIAL_API_OBJECT_TYPE;
532 if (!data->needs_access_check() &&
533 data->GetNamedPropertyHandler().IsUndefined(isolate) &&
534 data->GetIndexedPropertyHandler().IsUndefined(isolate)) {
535 function_type = FLAG_embedder_instance_types && data->HasInstanceType()
536 ? static_cast<InstanceType>(data->InstanceType())
537 : JS_API_OBJECT_TYPE;
538 }
539
540 Handle<JSFunction> function = ApiNatives::CreateApiFunction(
541 isolate, native_context, data, prototype, function_type, maybe_name);
542 if (should_cache) {
543 // Cache the function.
544 CacheTemplateInstantiation(isolate, native_context, data,
545 CachingMode::kUnlimited, function);
546 }
547 MaybeHandle<JSObject> result = ConfigureInstance(isolate, function, data);
548 if (result.is_null()) {
549 // Uncache on error.
550 UncacheTemplateInstantiation(isolate, native_context, data,
551 CachingMode::kUnlimited);
552 return MaybeHandle<JSFunction>();
553 }
554 data->set_published(true);
555 return function;
556 }
557
AddPropertyToPropertyList(Isolate * isolate,Handle<TemplateInfo> templ,int length,Handle<Object> * data)558 void AddPropertyToPropertyList(Isolate* isolate, Handle<TemplateInfo> templ,
559 int length, Handle<Object>* data) {
560 Object maybe_list = templ->property_list();
561 Handle<TemplateList> list;
562 if (maybe_list.IsUndefined(isolate)) {
563 list = TemplateList::New(isolate, length);
564 } else {
565 list = handle(TemplateList::cast(maybe_list), isolate);
566 }
567 templ->set_number_of_properties(templ->number_of_properties() + 1);
568 for (int i = 0; i < length; i++) {
569 Handle<Object> value =
570 data[i].is_null()
571 ? Handle<Object>::cast(isolate->factory()->undefined_value())
572 : data[i];
573 list = TemplateList::Add(isolate, list, value);
574 }
575 templ->set_property_list(*list);
576 }
577
578 } // namespace
579
InstantiateFunction(Isolate * isolate,Handle<NativeContext> native_context,Handle<FunctionTemplateInfo> data,MaybeHandle<Name> maybe_name)580 MaybeHandle<JSFunction> ApiNatives::InstantiateFunction(
581 Isolate* isolate, Handle<NativeContext> native_context,
582 Handle<FunctionTemplateInfo> data, MaybeHandle<Name> maybe_name) {
583 InvokeScope invoke_scope(isolate);
584 return ::v8::internal::InstantiateFunction(isolate, native_context, data,
585 maybe_name);
586 }
587
InstantiateFunction(Handle<FunctionTemplateInfo> data,MaybeHandle<Name> maybe_name)588 MaybeHandle<JSFunction> ApiNatives::InstantiateFunction(
589 Handle<FunctionTemplateInfo> data, MaybeHandle<Name> maybe_name) {
590 Isolate* isolate = data->GetIsolate();
591 InvokeScope invoke_scope(isolate);
592 return ::v8::internal::InstantiateFunction(isolate, data, maybe_name);
593 }
594
InstantiateObject(Isolate * isolate,Handle<ObjectTemplateInfo> data,Handle<JSReceiver> new_target)595 MaybeHandle<JSObject> ApiNatives::InstantiateObject(
596 Isolate* isolate, Handle<ObjectTemplateInfo> data,
597 Handle<JSReceiver> new_target) {
598 InvokeScope invoke_scope(isolate);
599 return ::v8::internal::InstantiateObject(isolate, data, new_target, false);
600 }
601
InstantiateRemoteObject(Handle<ObjectTemplateInfo> data)602 MaybeHandle<JSObject> ApiNatives::InstantiateRemoteObject(
603 Handle<ObjectTemplateInfo> data) {
604 Isolate* isolate = data->GetIsolate();
605 InvokeScope invoke_scope(isolate);
606
607 Handle<FunctionTemplateInfo> constructor(
608 FunctionTemplateInfo::cast(data->constructor()), isolate);
609 Handle<Map> object_map = isolate->factory()->NewMap(
610 JS_SPECIAL_API_OBJECT_TYPE,
611 JSObject::kHeaderSize +
612 data->embedder_field_count() * kEmbedderDataSlotSize,
613 TERMINAL_FAST_ELEMENTS_KIND);
614 object_map->SetConstructor(*constructor);
615 object_map->set_is_access_check_needed(true);
616 object_map->set_may_have_interesting_symbols(true);
617
618 Handle<JSObject> object = isolate->factory()->NewJSObjectFromMap(object_map);
619 JSObject::ForceSetPrototype(isolate, object,
620 isolate->factory()->null_value());
621
622 return object;
623 }
624
AddDataProperty(Isolate * isolate,Handle<TemplateInfo> info,Handle<Name> name,Handle<Object> value,PropertyAttributes attributes)625 void ApiNatives::AddDataProperty(Isolate* isolate, Handle<TemplateInfo> info,
626 Handle<Name> name, Handle<Object> value,
627 PropertyAttributes attributes) {
628 PropertyDetails details(PropertyKind::kData, attributes,
629 PropertyConstness::kMutable);
630 auto details_handle = handle(details.AsSmi(), isolate);
631 Handle<Object> data[] = {name, details_handle, value};
632 AddPropertyToPropertyList(isolate, info, arraysize(data), data);
633 }
634
AddDataProperty(Isolate * isolate,Handle<TemplateInfo> info,Handle<Name> name,v8::Intrinsic intrinsic,PropertyAttributes attributes)635 void ApiNatives::AddDataProperty(Isolate* isolate, Handle<TemplateInfo> info,
636 Handle<Name> name, v8::Intrinsic intrinsic,
637 PropertyAttributes attributes) {
638 auto value = handle(Smi::FromInt(intrinsic), isolate);
639 auto intrinsic_marker = isolate->factory()->true_value();
640 PropertyDetails details(PropertyKind::kData, attributes,
641 PropertyConstness::kMutable);
642 auto details_handle = handle(details.AsSmi(), isolate);
643 Handle<Object> data[] = {name, intrinsic_marker, details_handle, value};
644 AddPropertyToPropertyList(isolate, info, arraysize(data), data);
645 }
646
AddAccessorProperty(Isolate * isolate,Handle<TemplateInfo> info,Handle<Name> name,Handle<FunctionTemplateInfo> getter,Handle<FunctionTemplateInfo> setter,PropertyAttributes attributes)647 void ApiNatives::AddAccessorProperty(Isolate* isolate,
648 Handle<TemplateInfo> info,
649 Handle<Name> name,
650 Handle<FunctionTemplateInfo> getter,
651 Handle<FunctionTemplateInfo> setter,
652 PropertyAttributes attributes) {
653 if (!getter.is_null()) getter->set_published(true);
654 if (!setter.is_null()) setter->set_published(true);
655 PropertyDetails details(PropertyKind::kAccessor, attributes,
656 PropertyConstness::kMutable);
657 auto details_handle = handle(details.AsSmi(), isolate);
658 Handle<Object> data[] = {name, details_handle, getter, setter};
659 AddPropertyToPropertyList(isolate, info, arraysize(data), data);
660 }
661
AddNativeDataProperty(Isolate * isolate,Handle<TemplateInfo> info,Handle<AccessorInfo> property)662 void ApiNatives::AddNativeDataProperty(Isolate* isolate,
663 Handle<TemplateInfo> info,
664 Handle<AccessorInfo> property) {
665 Object maybe_list = info->property_accessors();
666 Handle<TemplateList> list;
667 if (maybe_list.IsUndefined(isolate)) {
668 list = TemplateList::New(isolate, 1);
669 } else {
670 list = handle(TemplateList::cast(maybe_list), isolate);
671 }
672 list = TemplateList::Add(isolate, list, property);
673 info->set_property_accessors(*list);
674 }
675
CreateApiFunction(Isolate * isolate,Handle<NativeContext> native_context,Handle<FunctionTemplateInfo> obj,Handle<Object> prototype,InstanceType type,MaybeHandle<Name> maybe_name)676 Handle<JSFunction> ApiNatives::CreateApiFunction(
677 Isolate* isolate, Handle<NativeContext> native_context,
678 Handle<FunctionTemplateInfo> obj, Handle<Object> prototype,
679 InstanceType type, MaybeHandle<Name> maybe_name) {
680 RCS_SCOPE(isolate, RuntimeCallCounterId::kCreateApiFunction);
681 Handle<SharedFunctionInfo> shared =
682 FunctionTemplateInfo::GetOrCreateSharedFunctionInfo(isolate, obj,
683 maybe_name);
684 // To simplify things, API functions always have shared name.
685 DCHECK(shared->HasSharedName());
686
687 Handle<JSFunction> result =
688 Factory::JSFunctionBuilder{isolate, shared, native_context}.Build();
689
690 if (obj->remove_prototype()) {
691 DCHECK(prototype.is_null());
692 DCHECK(result->shared().IsApiFunction());
693 DCHECK(!result->IsConstructor());
694 DCHECK(!result->has_prototype_slot());
695 return result;
696 }
697
698 // Down from here is only valid for API functions that can be used as a
699 // constructor (don't set the "remove prototype" flag).
700 DCHECK(result->has_prototype_slot());
701
702 if (obj->read_only_prototype()) {
703 result->set_map(*isolate->sloppy_function_with_readonly_prototype_map());
704 }
705
706 if (prototype->IsTheHole(isolate)) {
707 prototype = isolate->factory()->NewFunctionPrototype(result);
708 } else if (obj->GetPrototypeProviderTemplate().IsUndefined(isolate)) {
709 JSObject::AddProperty(isolate, Handle<JSObject>::cast(prototype),
710 isolate->factory()->constructor_string(), result,
711 DONT_ENUM);
712 }
713
714 int embedder_field_count = 0;
715 bool immutable_proto = false;
716 if (!obj->GetInstanceTemplate().IsUndefined(isolate)) {
717 Handle<ObjectTemplateInfo> GetInstanceTemplate = Handle<ObjectTemplateInfo>(
718 ObjectTemplateInfo::cast(obj->GetInstanceTemplate()), isolate);
719 embedder_field_count = GetInstanceTemplate->embedder_field_count();
720 immutable_proto = GetInstanceTemplate->immutable_proto();
721 }
722
723 // JSFunction requires information about the prototype slot.
724 DCHECK(!InstanceTypeChecker::IsJSFunction(type));
725 int instance_size = JSObject::GetHeaderSize(type) +
726 kEmbedderDataSlotSize * embedder_field_count;
727
728 Handle<Map> map = isolate->factory()->NewMap(type, instance_size,
729 TERMINAL_FAST_ELEMENTS_KIND);
730
731 // Mark as undetectable if needed.
732 if (obj->undetectable()) {
733 // We only allow callable undetectable receivers here, since this whole
734 // undetectable business is only to support document.all, which is both
735 // undetectable and callable. If we ever see the need to have an object
736 // that is undetectable but not callable, we need to update the types.h
737 // to allow encoding this.
738 CHECK(!obj->GetInstanceCallHandler().IsUndefined(isolate));
739 map->set_is_undetectable(true);
740 }
741
742 // Mark as needs_access_check if needed.
743 if (obj->needs_access_check()) {
744 map->set_is_access_check_needed(true);
745 map->set_may_have_interesting_symbols(true);
746 }
747
748 // Set interceptor information in the map.
749 if (!obj->GetNamedPropertyHandler().IsUndefined(isolate)) {
750 map->set_has_named_interceptor(true);
751 map->set_may_have_interesting_symbols(true);
752 }
753 if (!obj->GetIndexedPropertyHandler().IsUndefined(isolate)) {
754 map->set_has_indexed_interceptor(true);
755 }
756
757 // Mark instance as callable in the map.
758 if (!obj->GetInstanceCallHandler().IsUndefined(isolate)) {
759 map->set_is_callable(true);
760 map->set_is_constructor(!obj->undetectable());
761 }
762
763 if (immutable_proto) map->set_is_immutable_proto(true);
764
765 JSFunction::SetInitialMap(isolate, result, map,
766 Handle<JSObject>::cast(prototype));
767 return result;
768 }
769
770 } // namespace internal
771 } // namespace v8
772