11f20efe862d87011f911fc7e856038a4a0804a5
[platform/upstream/nodejs.git] / deps / v8 / src / api-natives.cc
1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/api-natives.h"
6 #include "src/isolate-inl.h"
7
8 namespace v8 {
9 namespace internal {
10
11 namespace {
12
13 MaybeHandle<JSObject> InstantiateObject(Isolate* isolate,
14                                         Handle<ObjectTemplateInfo> data);
15
16
17 MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate,
18                                             Handle<FunctionTemplateInfo> data,
19                                             Handle<Name> name = Handle<Name>());
20
21
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));
29   } else {
30     return data;
31   }
32 }
33
34
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(
42         isolate, getter,
43         InstantiateFunction(isolate,
44                             Handle<FunctionTemplateInfo>::cast(getter)),
45         Object);
46   }
47   if (!setter->IsUndefined()) {
48     ASSIGN_RETURN_ON_EXCEPTION(
49         isolate, setter,
50         InstantiateFunction(isolate,
51                             Handle<FunctionTemplateInfo>::cast(setter)),
52         Object);
53   }
54   RETURN_ON_EXCEPTION(isolate,
55                       JSObject::DefineAccessor(
56                           object, name, getter, setter,
57                           static_cast<PropertyAttributes>(attributes->value())),
58                       Object);
59   return object;
60 }
61
62
63 MaybeHandle<Object> DefineDataProperty(Isolate* isolate,
64                                        Handle<JSObject> object,
65                                        Handle<Name> key,
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());
73
74   Handle<Object> value;
75   ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
76                              Instantiate(isolate, prop_data, key), Object);
77
78 #ifdef DEBUG
79   bool duplicate;
80   if (key->IsName()) {
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();
86   } else {
87     uint32_t index = 0;
88     key->ToArrayIndex(&index);
89     Maybe<bool> maybe = JSReceiver::HasOwnElement(object, index);
90     if (!maybe.has_value) return MaybeHandle<Object>();
91     duplicate = maybe.value;
92   }
93   if (duplicate) {
94     Handle<Object> args[1] = {key};
95     THROW_NEW_ERROR(isolate, NewTypeError("duplicate_template_property",
96                                           HandleVector(args, 1)),
97                     Object);
98   }
99 #endif
100
101   RETURN_ON_EXCEPTION(
102       isolate, Runtime::DefineObjectProperty(object, key, value, attributes),
103       Object);
104   return object;
105 }
106
107
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);
114 }
115
116
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);
123 }
124
125
126 class AccessCheckDisableScope {
127  public:
128   AccessCheckDisableScope(Isolate* isolate, Handle<JSObject> obj)
129       : isolate_(isolate),
130         disabled_(obj->map()->is_access_check_needed()),
131         obj_(obj) {
132     if (disabled_) {
133       DisableAccessChecks(isolate_, obj_);
134     }
135   }
136   ~AccessCheckDisableScope() {
137     if (disabled_) {
138       EnableAccessChecks(isolate_, obj_);
139     }
140   }
141
142  private:
143   Isolate* isolate_;
144   const bool disabled_;
145   Handle<JSObject> obj_;
146 };
147
148
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();
161     if (length == 3) {
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),
167                           JSObject);
168     } else {
169       DCHECK(length == 4);
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,
176                                                  setter, attributes),
177                           JSObject);
178     }
179     i += length + 1;
180   }
181   return obj;
182 }
183
184
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);
189   // Fast path.
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();
196   } else {
197     auto cons_templ = Handle<FunctionTemplateInfo>::cast(constructor);
198     ASSIGN_RETURN_ON_EXCEPTION(
199         isolate, cons, InstantiateFunction(isolate, cons_templ), JSFunction);
200   }
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);
207 }
208
209
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);
215 }
216
217
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);
222   DCHECK(was_present);
223   isolate->native_context()->set_function_cache(*new_cache);
224 }
225
226
227 MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate,
228                                             Handle<FunctionTemplateInfo> data,
229                                             Handle<Name> name) {
230   auto serial_number = handle(Smi::cast(data->serial_number()), isolate);
231   // Probe cache.
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);
237     }
238   }
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());
246     } else {
247       ASSIGN_RETURN_ON_EXCEPTION(
248           isolate, prototype,
249           InstantiateObject(isolate,
250                             Handle<ObjectTemplateInfo>::cast(prototype_templ)),
251           JSFunction);
252     }
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)),
260           JSFunction);
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()),
267           JSFunction);
268       RETURN_ON_EXCEPTION(
269           isolate, JSObject::SetPrototype(prototype, parent_prototype, false),
270           JSFunction);
271     }
272   }
273   auto function = ApiNatives::CreateApiFunction(
274       isolate, data, prototype, ApiNatives::JavaScriptObjectType);
275   if (!name.is_null() && name->IsString()) {
276     function->shared()->set_name(*name);
277   }
278   if (!data->do_not_cache()) {
279     // Cache the function.
280     CacheFunction(isolate, serial_number, function);
281   }
282   auto result = ConfigureInstance(isolate, function, data);
283   if (result.is_null()) {
284     // Uncache on error.
285     if (!data->do_not_cache()) {
286       UncacheFunction(isolate, serial_number);
287     }
288     return MaybeHandle<JSFunction>();
289   }
290   return scope.CloseAndEscape(function);
291 }
292
293
294 class InvokeScope {
295  public:
296   explicit InvokeScope(Isolate* isolate)
297       : isolate_(isolate), save_context_(isolate) {}
298   ~InvokeScope() {
299     bool has_exception = isolate_->has_pending_exception();
300     if (has_exception) {
301       isolate_->ReportPendingMessages();
302     } else {
303       isolate_->clear_pending_message();
304     }
305   }
306
307  private:
308   Isolate* isolate_;
309   SaveContext save_context_;
310 };
311
312
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);
319   }
320   NeanderArray array(list);
321   array.add(isolate, isolate->factory()->NewNumberFromInt(length));
322   for (int i = 0; i < length; i++) {
323     Handle<Object> value =
324         data[i].is_null()
325             ? Handle<Object>::cast(isolate->factory()->undefined_value())
326             : data[i];
327     array.add(isolate, value);
328   }
329 }
330
331 }  // namespace
332
333
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);
339 }
340
341
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);
347 }
348
349
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);
362   return desc;
363 }
364
365
366 void ApiNatives::AddDataProperty(Isolate* isolate, Handle<TemplateInfo> info,
367                                  Handle<Name> name, Handle<Object> value,
368                                  PropertyAttributes attributes) {
369   const int kSize = 3;
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);
375 }
376
377
378 void ApiNatives::AddAccessorProperty(Isolate* isolate,
379                                      Handle<TemplateInfo> info,
380                                      Handle<Name> name,
381                                      Handle<FunctionTemplateInfo> getter,
382                                      Handle<FunctionTemplateInfo> setter,
383                                      PropertyAttributes attributes) {
384   const int kSize = 4;
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);
390 }
391
392
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);
400   }
401   NeanderArray array(list);
402   array.add(isolate, property);
403 }
404
405
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();
411
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);
417   } else {
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();
424     }
425
426     // TODO(svenpanne) Kill ApiInstanceType and refactor things by generalizing
427     // JSObject::GetHeaderSize.
428     int instance_size = kPointerSize * internal_field_count;
429     InstanceType type;
430     switch (instance_type) {
431       case JavaScriptObjectType:
432         type = JS_OBJECT_TYPE;
433         instance_size += JSObject::kHeaderSize;
434         break;
435       case GlobalObjectType:
436         type = JS_GLOBAL_OBJECT_TYPE;
437         instance_size += JSGlobalObject::kSize;
438         break;
439       case GlobalProxyType:
440         type = JS_GLOBAL_PROXY_TYPE;
441         instance_size += JSGlobalProxy::kSize;
442         break;
443       default:
444         UNREACHABLE();
445         type = JS_OBJECT_TYPE;  // Keep the compiler happy.
446         break;
447     }
448
449     result = isolate->factory()->NewFunction(
450         isolate->factory()->empty_string(), code, prototype, type,
451         instance_size, obj->read_only_prototype(), true);
452   }
453
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);
459   }
460   result->shared()->set_function_data(*obj);
461   result->shared()->set_construct_stub(*construct_stub);
462   result->shared()->DontAdaptArguments();
463
464   if (obj->remove_prototype()) {
465     DCHECK(result->shared()->IsApiFunction());
466     DCHECK(!result->has_initial_map());
467     DCHECK(!result->has_prototype());
468     return result;
469   }
470
471 #ifdef DEBUG
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));
478 #endif
479
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).
482
483   Handle<Map> map(result->initial_map());
484
485   // Mark as undetectable if needed.
486   if (obj->undetectable()) {
487     map->set_is_undetectable();
488   }
489
490   // Mark as hidden for the __proto__ accessor if needed.
491   if (obj->hidden_prototype()) {
492     map->set_is_hidden_prototype();
493   }
494
495   // Mark as needs_access_check if needed.
496   if (obj->needs_access_check()) {
497     map->set_is_access_check_needed(true);
498   }
499
500   // Set interceptor information in the map.
501   if (!obj->named_property_handler()->IsUndefined()) {
502     map->set_has_named_interceptor();
503   }
504   if (!obj->indexed_property_handler()->IsUndefined()) {
505     map->set_has_indexed_interceptor();
506   }
507
508   // Set instance call-as-function information in the map.
509   if (!obj->instance_call_handler()->IsUndefined()) {
510     map->set_has_instance_call_handler();
511   }
512
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;
518   while (true) {
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();
526       }
527     }
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();
534       }
535     }
536     Object* parent = info->parent_template();
537     if (parent->IsUndefined()) break;
538     info = FunctionTemplateInfo::cast(parent);
539   }
540
541   Map::EnsureDescriptorSlack(map, max_number_of_additional_properties);
542
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);
548   }
549
550   while (true) {
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);
559       }
560     }
561     // Accumulate static accessors
562     if (!obj->property_accessors()->IsUndefined()) {
563       Handle<Object> props = Handle<Object>(obj->property_accessors(), isolate);
564       valid_descriptors =
565           AccessorInfo::AppendUnique(props, array, valid_descriptors);
566     }
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);
571   }
572
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();
577   }
578
579   DCHECK(result->shared()->IsApiFunction());
580   return result;
581 }
582
583 }  // namespace internal
584 }  // namespace v8