blob: bc71e3ef90db8b9e9d3501d068fab670b66169df [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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.h"
8#include "src/isolate-inl.h"
9#include "src/lookup.h"
10#include "src/messages.h"
11
12namespace v8 {
13namespace internal {
14
15
16namespace {
17
18MaybeHandle<JSObject> InstantiateObject(Isolate* isolate,
19 Handle<ObjectTemplateInfo> data);
20
21
22MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate,
23 Handle<FunctionTemplateInfo> data,
24 Handle<Name> name = Handle<Name>());
25
26
27MaybeHandle<Object> Instantiate(Isolate* isolate, Handle<Object> data,
28 Handle<Name> name = Handle<Name>()) {
29 if (data->IsFunctionTemplateInfo()) {
30 return InstantiateFunction(isolate,
31 Handle<FunctionTemplateInfo>::cast(data), name);
32 } else if (data->IsObjectTemplateInfo()) {
33 return InstantiateObject(isolate, Handle<ObjectTemplateInfo>::cast(data));
34 } else {
35 return data;
36 }
37}
38
39
40MaybeHandle<Object> DefineAccessorProperty(Isolate* isolate,
41 Handle<JSObject> object,
42 Handle<Name> name,
43 Handle<Object> getter,
44 Handle<Object> setter,
45 PropertyAttributes attributes) {
46 if (!getter->IsUndefined()) {
47 ASSIGN_RETURN_ON_EXCEPTION(
48 isolate, getter,
49 InstantiateFunction(isolate,
50 Handle<FunctionTemplateInfo>::cast(getter)),
51 Object);
52 }
53 if (!setter->IsUndefined()) {
54 ASSIGN_RETURN_ON_EXCEPTION(
55 isolate, setter,
56 InstantiateFunction(isolate,
57 Handle<FunctionTemplateInfo>::cast(setter)),
58 Object);
59 }
60 RETURN_ON_EXCEPTION(isolate, JSObject::DefineAccessor(object, name, getter,
61 setter, attributes),
62 Object);
63 return object;
64}
65
66
67MaybeHandle<Object> DefineDataProperty(Isolate* isolate,
68 Handle<JSObject> object,
69 Handle<Name> name,
70 Handle<Object> prop_data,
71 PropertyAttributes attributes) {
72 Handle<Object> value;
73 ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
74 Instantiate(isolate, prop_data, name), Object);
75
76 LookupIterator it = LookupIterator::PropertyOrElement(
77 isolate, object, name, LookupIterator::OWN_SKIP_INTERCEPTOR);
78
79#ifdef DEBUG
80 Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it);
81 DCHECK(maybe.IsJust());
82 if (it.IsFound()) {
83 THROW_NEW_ERROR(
84 isolate,
85 NewTypeError(MessageTemplate::kDuplicateTemplateProperty, name),
86 Object);
87 }
88#endif
89
90 MAYBE_RETURN_NULL(
91 Object::AddDataProperty(&it, value, attributes, Object::THROW_ON_ERROR,
92 Object::CERTAINLY_NOT_STORE_FROM_KEYED));
93 return value;
94}
95
96
97void DisableAccessChecks(Isolate* isolate, Handle<JSObject> object) {
98 Handle<Map> old_map(object->map());
99 // Copy map so it won't interfere constructor's initial map.
100 Handle<Map> new_map = Map::Copy(old_map, "DisableAccessChecks");
101 new_map->set_is_access_check_needed(false);
102 JSObject::MigrateToMap(Handle<JSObject>::cast(object), new_map);
103}
104
105
106void EnableAccessChecks(Isolate* isolate, Handle<JSObject> object) {
107 Handle<Map> old_map(object->map());
108 // Copy map so it won't interfere constructor's initial map.
109 Handle<Map> new_map = Map::Copy(old_map, "EnableAccessChecks");
110 new_map->set_is_access_check_needed(true);
111 JSObject::MigrateToMap(object, new_map);
112}
113
114
115class AccessCheckDisableScope {
116 public:
117 AccessCheckDisableScope(Isolate* isolate, Handle<JSObject> obj)
118 : isolate_(isolate),
119 disabled_(obj->map()->is_access_check_needed()),
120 obj_(obj) {
121 if (disabled_) {
122 DisableAccessChecks(isolate_, obj_);
123 }
124 }
125 ~AccessCheckDisableScope() {
126 if (disabled_) {
127 EnableAccessChecks(isolate_, obj_);
128 }
129 }
130
131 private:
132 Isolate* isolate_;
133 const bool disabled_;
134 Handle<JSObject> obj_;
135};
136
137
138Object* GetIntrinsic(Isolate* isolate, v8::Intrinsic intrinsic) {
139 Handle<Context> native_context = isolate->native_context();
140 DCHECK(!native_context.is_null());
141 switch (intrinsic) {
142#define GET_INTRINSIC_VALUE(name, iname) \
143 case v8::k##name: \
144 return native_context->iname();
145 V8_INTRINSICS_LIST(GET_INTRINSIC_VALUE)
146#undef GET_INTRINSIC_VALUE
147 }
148 return nullptr;
149}
150
151
152MaybeHandle<JSObject> ConfigureInstance(Isolate* isolate, Handle<JSObject> obj,
153 Handle<TemplateInfo> data) {
154 auto property_list = handle(data->property_list(), isolate);
155 if (property_list->IsUndefined()) return obj;
156 // TODO(dcarney): just use a FixedArray here.
157 NeanderArray properties(property_list);
158 if (properties.length() == 0) return obj;
159 HandleScope scope(isolate);
160 // Disable access checks while instantiating the object.
161 AccessCheckDisableScope access_check_scope(isolate, obj);
162
163 int i = 0;
164 for (int c = 0; c < data->number_of_properties(); c++) {
165 auto name = handle(Name::cast(properties.get(i++)), isolate);
166 auto bit = handle(properties.get(i++), isolate);
167 if (bit->IsSmi()) {
168 PropertyDetails details(Smi::cast(*bit));
169 PropertyAttributes attributes = details.attributes();
170 PropertyKind kind = details.kind();
171
172 if (kind == kData) {
173 auto prop_data = handle(properties.get(i++), isolate);
174
175 RETURN_ON_EXCEPTION(isolate, DefineDataProperty(isolate, obj, name,
176 prop_data, attributes),
177 JSObject);
178 } else {
179 auto getter = handle(properties.get(i++), isolate);
180 auto setter = handle(properties.get(i++), isolate);
181 RETURN_ON_EXCEPTION(isolate,
182 DefineAccessorProperty(isolate, obj, name, getter,
183 setter, attributes),
184 JSObject);
185 }
186 } else {
187 // Intrinsic data property --- Get appropriate value from the current
188 // context.
189 PropertyDetails details(Smi::cast(properties.get(i++)));
190 PropertyAttributes attributes = details.attributes();
191 DCHECK_EQ(kData, details.kind());
192
193 v8::Intrinsic intrinsic =
194 static_cast<v8::Intrinsic>(Smi::cast(properties.get(i++))->value());
195 auto prop_data = handle(GetIntrinsic(isolate, intrinsic), isolate);
196
197 RETURN_ON_EXCEPTION(isolate, DefineDataProperty(isolate, obj, name,
198 prop_data, attributes),
199 JSObject);
200 }
201 }
202 return obj;
203}
204
205
206MaybeHandle<JSObject> InstantiateObject(Isolate* isolate,
207 Handle<ObjectTemplateInfo> data) {
208 // Enter a new scope. Recursion could otherwise create a lot of handles.
209 HandleScope scope(isolate);
210 // Fast path.
211 Handle<JSObject> result;
212 auto info = Handle<ObjectTemplateInfo>::cast(data);
213 auto constructor = handle(info->constructor(), isolate);
214 Handle<JSFunction> cons;
215 if (constructor->IsUndefined()) {
216 cons = isolate->object_function();
217 } else {
218 auto cons_templ = Handle<FunctionTemplateInfo>::cast(constructor);
219 ASSIGN_RETURN_ON_EXCEPTION(
220 isolate, cons, InstantiateFunction(isolate, cons_templ), JSFunction);
221 }
222 auto object = isolate->factory()->NewJSObject(cons);
223 ASSIGN_RETURN_ON_EXCEPTION(
224 isolate, result, ConfigureInstance(isolate, object, info), JSFunction);
225 // TODO(dcarney): is this necessary?
226 JSObject::MigrateSlowToFast(result, 0, "ApiNatives::InstantiateObject");
227 return scope.CloseAndEscape(result);
228}
229
230
231void CacheFunction(Isolate* isolate, Handle<Smi> serial_number,
232 Handle<JSFunction> function) {
233 auto cache = isolate->function_cache();
234 auto new_cache = ObjectHashTable::Put(cache, serial_number, function);
235 isolate->native_context()->set_function_cache(*new_cache);
236}
237
238
239void UncacheFunction(Isolate* isolate, Handle<Smi> serial_number) {
240 auto cache = isolate->function_cache();
241 bool was_present = false;
242 auto new_cache = ObjectHashTable::Remove(cache, serial_number, &was_present);
243 DCHECK(was_present);
244 isolate->native_context()->set_function_cache(*new_cache);
245}
246
247
248MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate,
249 Handle<FunctionTemplateInfo> data,
250 Handle<Name> name) {
251 auto serial_number = handle(Smi::cast(data->serial_number()), isolate);
252 // Probe cache.
253 if (!data->do_not_cache()) {
254 auto cache = isolate->function_cache();
255 Object* element = cache->Lookup(serial_number);
256 if (element->IsJSFunction()) {
257 return handle(JSFunction::cast(element), isolate);
258 }
259 }
260 // Enter a new scope. Recursion could otherwise create a lot of handles.
261 HandleScope scope(isolate);
262 Handle<JSObject> prototype;
263 if (!data->remove_prototype()) {
264 auto prototype_templ = handle(data->prototype_template(), isolate);
265 if (prototype_templ->IsUndefined()) {
266 prototype = isolate->factory()->NewJSObject(isolate->object_function());
267 } else {
268 ASSIGN_RETURN_ON_EXCEPTION(
269 isolate, prototype,
270 InstantiateObject(isolate,
271 Handle<ObjectTemplateInfo>::cast(prototype_templ)),
272 JSFunction);
273 }
274 auto parent = handle(data->parent_template(), isolate);
275 if (!parent->IsUndefined()) {
276 Handle<JSFunction> parent_instance;
277 ASSIGN_RETURN_ON_EXCEPTION(
278 isolate, parent_instance,
279 InstantiateFunction(isolate,
280 Handle<FunctionTemplateInfo>::cast(parent)),
281 JSFunction);
282 // TODO(dcarney): decide what to do here.
283 Handle<Object> parent_prototype;
284 ASSIGN_RETURN_ON_EXCEPTION(
285 isolate, parent_prototype,
286 JSObject::GetProperty(parent_instance,
287 isolate->factory()->prototype_string()),
288 JSFunction);
289 MAYBE_RETURN(JSObject::SetPrototype(prototype, parent_prototype, false,
290 Object::THROW_ON_ERROR),
291 MaybeHandle<JSFunction>());
292 }
293 }
294 auto function = ApiNatives::CreateApiFunction(
295 isolate, data, prototype, ApiNatives::JavaScriptObjectType);
296 if (!name.is_null() && name->IsString()) {
297 function->shared()->set_name(*name);
298 }
299 if (!data->do_not_cache()) {
300 // Cache the function.
301 CacheFunction(isolate, serial_number, function);
302 }
303 auto result = ConfigureInstance(isolate, function, data);
304 if (result.is_null()) {
305 // Uncache on error.
306 if (!data->do_not_cache()) {
307 UncacheFunction(isolate, serial_number);
308 }
309 return MaybeHandle<JSFunction>();
310 }
311 return scope.CloseAndEscape(function);
312}
313
314
315class InvokeScope {
316 public:
317 explicit InvokeScope(Isolate* isolate)
318 : isolate_(isolate), save_context_(isolate) {}
319 ~InvokeScope() {
320 bool has_exception = isolate_->has_pending_exception();
321 if (has_exception) {
322 isolate_->ReportPendingMessages();
323 } else {
324 isolate_->clear_pending_message();
325 }
326 }
327
328 private:
329 Isolate* isolate_;
330 SaveContext save_context_;
331};
332
333
334void AddPropertyToPropertyList(Isolate* isolate, Handle<TemplateInfo> templ,
335 int length, Handle<Object>* data) {
336 auto list = handle(templ->property_list(), isolate);
337 if (list->IsUndefined()) {
338 list = NeanderArray(isolate).value();
339 templ->set_property_list(*list);
340 }
341 templ->set_number_of_properties(templ->number_of_properties() + 1);
342 NeanderArray array(list);
343 for (int i = 0; i < length; i++) {
344 Handle<Object> value =
345 data[i].is_null()
346 ? Handle<Object>::cast(isolate->factory()->undefined_value())
347 : data[i];
348 array.add(isolate, value);
349 }
350}
351
352} // namespace
353
354
355MaybeHandle<JSFunction> ApiNatives::InstantiateFunction(
356 Handle<FunctionTemplateInfo> data) {
357 Isolate* isolate = data->GetIsolate();
358 InvokeScope invoke_scope(isolate);
359 return ::v8::internal::InstantiateFunction(isolate, data);
360}
361
362
363MaybeHandle<JSObject> ApiNatives::InstantiateObject(
364 Handle<ObjectTemplateInfo> data) {
365 Isolate* isolate = data->GetIsolate();
366 InvokeScope invoke_scope(isolate);
367 return ::v8::internal::InstantiateObject(isolate, data);
368}
369
370
371MaybeHandle<FunctionTemplateInfo> ApiNatives::ConfigureInstance(
372 Isolate* isolate, Handle<FunctionTemplateInfo> desc,
373 Handle<JSObject> instance) {
374 // Configure the instance by adding the properties specified by the
375 // instance template.
376 if (desc->instance_template()->IsUndefined()) return desc;
377 InvokeScope invoke_scope(isolate);
378 Handle<ObjectTemplateInfo> instance_template(
379 ObjectTemplateInfo::cast(desc->instance_template()), isolate);
380 RETURN_ON_EXCEPTION(isolate, ::v8::internal::ConfigureInstance(
381 isolate, instance, instance_template),
382 FunctionTemplateInfo);
383 return desc;
384}
385
386
387void ApiNatives::AddDataProperty(Isolate* isolate, Handle<TemplateInfo> info,
388 Handle<Name> name, Handle<Object> value,
389 PropertyAttributes attributes) {
390 const int kSize = 3;
391 PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell);
392 auto details_handle = handle(details.AsSmi(), isolate);
393 Handle<Object> data[kSize] = {name, details_handle, value};
394 AddPropertyToPropertyList(isolate, info, kSize, data);
395}
396
397
398void ApiNatives::AddDataProperty(Isolate* isolate, Handle<TemplateInfo> info,
399 Handle<Name> name, v8::Intrinsic intrinsic,
400 PropertyAttributes attributes) {
401 const int kSize = 4;
402 auto value = handle(Smi::FromInt(intrinsic), isolate);
403 auto intrinsic_marker = isolate->factory()->true_value();
404 PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell);
405 auto details_handle = handle(details.AsSmi(), isolate);
406 Handle<Object> data[kSize] = {name, intrinsic_marker, details_handle, value};
407 AddPropertyToPropertyList(isolate, info, kSize, data);
408}
409
410
411void ApiNatives::AddAccessorProperty(Isolate* isolate,
412 Handle<TemplateInfo> info,
413 Handle<Name> name,
414 Handle<FunctionTemplateInfo> getter,
415 Handle<FunctionTemplateInfo> setter,
416 PropertyAttributes attributes) {
417 const int kSize = 4;
418 PropertyDetails details(attributes, ACCESSOR, 0, PropertyCellType::kNoCell);
419 auto details_handle = handle(details.AsSmi(), isolate);
420 Handle<Object> data[kSize] = {name, details_handle, getter, setter};
421 AddPropertyToPropertyList(isolate, info, kSize, data);
422}
423
424
425void ApiNatives::AddNativeDataProperty(Isolate* isolate,
426 Handle<TemplateInfo> info,
427 Handle<AccessorInfo> property) {
428 auto list = handle(info->property_accessors(), isolate);
429 if (list->IsUndefined()) {
430 list = NeanderArray(isolate).value();
431 info->set_property_accessors(*list);
432 }
433 NeanderArray array(list);
434 array.add(isolate, property);
435}
436
437
438Handle<JSFunction> ApiNatives::CreateApiFunction(
439 Isolate* isolate, Handle<FunctionTemplateInfo> obj,
440 Handle<Object> prototype, ApiInstanceType instance_type) {
441 Handle<Code> code;
442 if (obj->call_code()->IsCallHandlerInfo() &&
443 CallHandlerInfo::cast(obj->call_code())->fast_handler()->IsCode()) {
444 code = isolate->builtins()->HandleFastApiCall();
445 } else {
446 code = isolate->builtins()->HandleApiCall();
447 }
448 Handle<Code> construct_stub =
449 prototype.is_null() ? isolate->builtins()->ConstructedNonConstructable()
450 : isolate->builtins()->JSConstructStubApi();
451
452 obj->set_instantiated(true);
453 Handle<JSFunction> result;
454 if (obj->remove_prototype()) {
455 result = isolate->factory()->NewFunctionWithoutPrototype(
456 isolate->factory()->empty_string(), code);
457 } else {
458 int internal_field_count = 0;
459 if (!obj->instance_template()->IsUndefined()) {
460 Handle<ObjectTemplateInfo> instance_template = Handle<ObjectTemplateInfo>(
461 ObjectTemplateInfo::cast(obj->instance_template()));
462 internal_field_count =
463 Smi::cast(instance_template->internal_field_count())->value();
464 }
465
466 // TODO(svenpanne) Kill ApiInstanceType and refactor things by generalizing
467 // JSObject::GetHeaderSize.
468 int instance_size = kPointerSize * internal_field_count;
469 InstanceType type;
470 switch (instance_type) {
471 case JavaScriptObjectType:
472 type = JS_OBJECT_TYPE;
473 instance_size += JSObject::kHeaderSize;
474 break;
475 case GlobalObjectType:
476 type = JS_GLOBAL_OBJECT_TYPE;
477 instance_size += JSGlobalObject::kSize;
478 break;
479 case GlobalProxyType:
480 type = JS_GLOBAL_PROXY_TYPE;
481 instance_size += JSGlobalProxy::kSize;
482 break;
483 default:
484 UNREACHABLE();
485 type = JS_OBJECT_TYPE; // Keep the compiler happy.
486 break;
487 }
488
489 result = isolate->factory()->NewFunction(
490 isolate->factory()->empty_string(), code, prototype, type,
491 instance_size, obj->read_only_prototype(), true);
492 }
493
494 result->shared()->set_length(obj->length());
495 Handle<Object> class_name(obj->class_name(), isolate);
496 if (class_name->IsString()) {
497 result->shared()->set_instance_class_name(*class_name);
498 result->shared()->set_name(*class_name);
499 }
500 result->shared()->set_function_data(*obj);
501 result->shared()->set_construct_stub(*construct_stub);
502 result->shared()->DontAdaptArguments();
503
504 if (obj->remove_prototype()) {
505 DCHECK(result->shared()->IsApiFunction());
506 DCHECK(!result->has_initial_map());
507 DCHECK(!result->has_prototype());
508 return result;
509 }
510
511#ifdef DEBUG
512 LookupIterator it(handle(JSObject::cast(result->prototype())),
513 isolate->factory()->constructor_string(),
514 LookupIterator::OWN_SKIP_INTERCEPTOR);
515 MaybeHandle<Object> maybe_prop = Object::GetProperty(&it);
516 DCHECK(it.IsFound());
517 DCHECK(maybe_prop.ToHandleChecked().is_identical_to(result));
518#endif
519
520 // Down from here is only valid for API functions that can be used as a
521 // constructor (don't set the "remove prototype" flag).
522
523 Handle<Map> map(result->initial_map());
524
525 // Mark as undetectable if needed.
526 if (obj->undetectable()) {
527 map->set_is_undetectable();
528 }
529
530 // Mark as hidden for the __proto__ accessor if needed.
531 if (obj->hidden_prototype()) {
532 map->set_is_hidden_prototype();
533 }
534
535 // Mark as needs_access_check if needed.
536 if (obj->needs_access_check()) {
537 map->set_is_access_check_needed(true);
538 }
539
540 // Set interceptor information in the map.
541 if (!obj->named_property_handler()->IsUndefined()) {
542 map->set_has_named_interceptor();
543 }
544 if (!obj->indexed_property_handler()->IsUndefined()) {
545 map->set_has_indexed_interceptor();
546 }
547
548 // Mark instance as callable in the map.
549 if (!obj->instance_call_handler()->IsUndefined()) {
550 map->set_is_callable();
551 map->set_is_constructor();
552 }
553
554 // Recursively copy parent instance templates' accessors,
555 // 'data' may be modified.
556 int max_number_of_additional_properties = 0;
557 int max_number_of_static_properties = 0;
558 FunctionTemplateInfo* info = *obj;
559 while (true) {
560 if (!info->instance_template()->IsUndefined()) {
561 Object* props = ObjectTemplateInfo::cast(info->instance_template())
562 ->property_accessors();
563 if (!props->IsUndefined()) {
564 Handle<Object> props_handle(props, isolate);
565 NeanderArray props_array(props_handle);
566 max_number_of_additional_properties += props_array.length();
567 }
568 }
569 if (!info->property_accessors()->IsUndefined()) {
570 Object* props = info->property_accessors();
571 if (!props->IsUndefined()) {
572 Handle<Object> props_handle(props, isolate);
573 NeanderArray props_array(props_handle);
574 max_number_of_static_properties += props_array.length();
575 }
576 }
577 Object* parent = info->parent_template();
578 if (parent->IsUndefined()) break;
579 info = FunctionTemplateInfo::cast(parent);
580 }
581
582 Map::EnsureDescriptorSlack(map, max_number_of_additional_properties);
583
584 // Use a temporary FixedArray to acculumate static accessors
585 int valid_descriptors = 0;
586 Handle<FixedArray> array;
587 if (max_number_of_static_properties > 0) {
588 array = isolate->factory()->NewFixedArray(max_number_of_static_properties);
589 }
590
591 while (true) {
592 // Install instance descriptors
593 if (!obj->instance_template()->IsUndefined()) {
594 Handle<ObjectTemplateInfo> instance = Handle<ObjectTemplateInfo>(
595 ObjectTemplateInfo::cast(obj->instance_template()), isolate);
596 Handle<Object> props =
597 Handle<Object>(instance->property_accessors(), isolate);
598 if (!props->IsUndefined()) {
599 Map::AppendCallbackDescriptors(map, props);
600 }
601 }
602 // Accumulate static accessors
603 if (!obj->property_accessors()->IsUndefined()) {
604 Handle<Object> props = Handle<Object>(obj->property_accessors(), isolate);
605 valid_descriptors =
606 AccessorInfo::AppendUnique(props, array, valid_descriptors);
607 }
608 // Climb parent chain
609 Handle<Object> parent = Handle<Object>(obj->parent_template(), isolate);
610 if (parent->IsUndefined()) break;
611 obj = Handle<FunctionTemplateInfo>::cast(parent);
612 }
613
614 // Install accumulated static accessors
615 for (int i = 0; i < valid_descriptors; i++) {
616 Handle<AccessorInfo> accessor(AccessorInfo::cast(array->get(i)));
617 JSObject::SetAccessor(result, accessor).Assert();
618 }
619
620 DCHECK(result->shared()->IsApiFunction());
621 return result;
622}
623
624} // namespace internal
625} // namespace v8