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