V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
builtins-api.cc
1 // Copyright 2016 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-arguments-inl.h"
6 #include "src/api-natives.h"
7 #include "src/builtins/builtins-utils-inl.h"
8 #include "src/builtins/builtins.h"
9 #include "src/counters.h"
10 #include "src/log.h"
11 #include "src/objects-inl.h"
12 #include "src/objects/templates.h"
13 #include "src/prototype.h"
14 #include "src/visitors.h"
15 
16 namespace v8 {
17 namespace internal {
18 
19 namespace {
20 
21 // Returns the holder JSObject if the function can legally be called with this
22 // receiver. Returns nullptr if the call is illegal.
23 // TODO(dcarney): CallOptimization duplicates this logic, merge.
24 JSReceiver* GetCompatibleReceiver(Isolate* isolate, FunctionTemplateInfo* info,
25  JSReceiver* receiver) {
26  Object* recv_type = info->signature();
27  // No signature, return holder.
28  if (!recv_type->IsFunctionTemplateInfo()) return receiver;
29  // A Proxy cannot have been created from the signature template.
30  if (!receiver->IsJSObject()) return nullptr;
31 
32  JSObject* js_obj_receiver = JSObject::cast(receiver);
33  FunctionTemplateInfo* signature = FunctionTemplateInfo::cast(recv_type);
34 
35  // Check the receiver. Fast path for receivers with no hidden prototypes.
36  if (signature->IsTemplateFor(js_obj_receiver)) return receiver;
37  if (!js_obj_receiver->map()->has_hidden_prototype()) return nullptr;
38  for (PrototypeIterator iter(isolate, js_obj_receiver, kStartAtPrototype,
39  PrototypeIterator::END_AT_NON_HIDDEN);
40  !iter.IsAtEnd(); iter.Advance()) {
41  JSObject* current = iter.GetCurrent<JSObject>();
42  if (signature->IsTemplateFor(current)) return current;
43  }
44  return nullptr;
45 }
46 
47 template <bool is_construct>
48 V8_WARN_UNUSED_RESULT MaybeHandle<Object> HandleApiCallHelper(
49  Isolate* isolate, Handle<HeapObject> function,
50  Handle<HeapObject> new_target, Handle<FunctionTemplateInfo> fun_data,
51  Handle<Object> receiver, BuiltinArguments args) {
52  Handle<JSReceiver> js_receiver;
53  JSReceiver* raw_holder;
54  if (is_construct) {
55  DCHECK(args.receiver()->IsTheHole(isolate));
56  if (fun_data->GetInstanceTemplate()->IsUndefined(isolate)) {
58  ObjectTemplate::New(reinterpret_cast<v8::Isolate*>(isolate),
59  ToApiHandle<v8::FunctionTemplate>(fun_data));
60  FunctionTemplateInfo::SetInstanceTemplate(isolate, fun_data,
61  Utils::OpenHandle(*templ));
62  }
63  Handle<ObjectTemplateInfo> instance_template(
64  ObjectTemplateInfo::cast(fun_data->GetInstanceTemplate()), isolate);
65  ASSIGN_RETURN_ON_EXCEPTION(
66  isolate, js_receiver,
67  ApiNatives::InstantiateObject(isolate, instance_template,
68  Handle<JSReceiver>::cast(new_target)),
69  Object);
70  args.set_at(0, *js_receiver);
71  DCHECK_EQ(*js_receiver, *args.receiver());
72 
73  raw_holder = *js_receiver;
74  } else {
75  DCHECK(receiver->IsJSReceiver());
76  js_receiver = Handle<JSReceiver>::cast(receiver);
77 
78  if (!fun_data->accept_any_receiver() &&
79  js_receiver->IsAccessCheckNeeded()) {
80  // Proxies never need access checks.
81  DCHECK(js_receiver->IsJSObject());
82  Handle<JSObject> js_obj_receiver = Handle<JSObject>::cast(js_receiver);
83  if (!isolate->MayAccess(handle(isolate->context(), isolate),
84  js_obj_receiver)) {
85  isolate->ReportFailedAccessCheck(js_obj_receiver);
86  RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
87  return isolate->factory()->undefined_value();
88  }
89  }
90 
91  raw_holder = GetCompatibleReceiver(isolate, *fun_data, *js_receiver);
92 
93  if (raw_holder == nullptr) {
94  // This function cannot be called with the given receiver. Abort!
95  THROW_NEW_ERROR(
96  isolate, NewTypeError(MessageTemplate::kIllegalInvocation), Object);
97  }
98  }
99 
100  Object* raw_call_data = fun_data->call_code();
101  if (!raw_call_data->IsUndefined(isolate)) {
102  DCHECK(raw_call_data->IsCallHandlerInfo());
103  CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);
104  Object* data_obj = call_data->data();
105 
106  FunctionCallbackArguments custom(isolate, data_obj, *function, raw_holder,
107  *new_target, args.address_of_arg_at(1),
108  args.length() - 1);
109  Handle<Object> result = custom.Call(call_data);
110 
111  RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
112  if (result.is_null()) {
113  if (is_construct) return js_receiver;
114  return isolate->factory()->undefined_value();
115  }
116  // Rebox the result.
117  result->VerifyApiCallResultType();
118  if (!is_construct || result->IsJSReceiver())
119  return handle(*result, isolate);
120  }
121 
122  return js_receiver;
123 }
124 
125 } // anonymous namespace
126 
127 BUILTIN(HandleApiCall) {
128  HandleScope scope(isolate);
129  Handle<JSFunction> function = args.target();
130  Handle<Object> receiver = args.receiver();
131  Handle<HeapObject> new_target = args.new_target();
132  Handle<FunctionTemplateInfo> fun_data(function->shared()->get_api_func_data(),
133  isolate);
134  if (new_target->IsJSReceiver()) {
135  RETURN_RESULT_OR_FAILURE(
136  isolate, HandleApiCallHelper<true>(isolate, function, new_target,
137  fun_data, receiver, args));
138  } else {
139  RETURN_RESULT_OR_FAILURE(
140  isolate, HandleApiCallHelper<false>(isolate, function, new_target,
141  fun_data, receiver, args));
142  }
143 }
144 
145 namespace {
146 
147 class RelocatableArguments : public BuiltinArguments, public Relocatable {
148  public:
149  RelocatableArguments(Isolate* isolate, int length, Address* arguments)
150  : BuiltinArguments(length, arguments), Relocatable(isolate) {}
151 
152  inline void IterateInstance(RootVisitor* v) override {
153  if (length() == 0) return;
154  v->VisitRootPointers(Root::kRelocatable, nullptr, first_slot(),
155  last_slot() + 1);
156  }
157 
158  private:
159  DISALLOW_COPY_AND_ASSIGN(RelocatableArguments);
160 };
161 
162 } // namespace
163 
164 MaybeHandle<Object> Builtins::InvokeApiFunction(Isolate* isolate,
165  bool is_construct,
166  Handle<HeapObject> function,
167  Handle<Object> receiver,
168  int argc, Handle<Object> args[],
169  Handle<HeapObject> new_target) {
170  DCHECK(function->IsFunctionTemplateInfo() ||
171  (function->IsJSFunction() &&
172  JSFunction::cast(*function)->shared()->IsApiFunction()));
173 
174  // Do proper receiver conversion for non-strict mode api functions.
175  if (!is_construct && !receiver->IsJSReceiver()) {
176  if (function->IsFunctionTemplateInfo() ||
177  is_sloppy(JSFunction::cast(*function)->shared()->language_mode())) {
178  ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
179  Object::ConvertReceiver(isolate, receiver),
180  Object);
181  }
182  }
183 
184  if (function->IsFunctionTemplateInfo()) {
185  Handle<FunctionTemplateInfo> info =
186  Handle<FunctionTemplateInfo>::cast(function);
187  // If we need to break at function entry, go the long way. Instantiate the
188  // function, use the DebugBreakTrampoline, and call it through JS.
189  if (info->BreakAtEntry()) {
190  DCHECK(!is_construct);
191  DCHECK(new_target->IsUndefined(isolate));
192  Handle<JSFunction> function;
193  ASSIGN_RETURN_ON_EXCEPTION(isolate, function,
194  ApiNatives::InstantiateFunction(
195  info, MaybeHandle<v8::internal::Name>()),
196  Object);
197  Handle<Code> trampoline = BUILTIN_CODE(isolate, DebugBreakTrampoline);
198  function->set_code(*trampoline);
199  return Execution::Call(isolate, function, receiver, argc, args);
200  }
201  }
202 
203  Handle<FunctionTemplateInfo> fun_data =
204  function->IsFunctionTemplateInfo()
205  ? Handle<FunctionTemplateInfo>::cast(function)
206  : handle(JSFunction::cast(*function)->shared()->get_api_func_data(),
207  isolate);
208  // Construct BuiltinArguments object:
209  // new target, function, arguments reversed, receiver.
210  const int kBufferSize = 32;
211  Address small_argv[kBufferSize];
212  Address* argv;
213  const int frame_argc = argc + BuiltinArguments::kNumExtraArgsWithReceiver;
214  if (frame_argc <= kBufferSize) {
215  argv = small_argv;
216  } else {
217  argv = new Address[frame_argc];
218  }
219  int cursor = frame_argc - 1;
220  argv[cursor--] = receiver->ptr();
221  for (int i = 0; i < argc; ++i) {
222  argv[cursor--] = args[i]->ptr();
223  }
224  DCHECK_EQ(cursor, BuiltinArguments::kPaddingOffset);
225  argv[BuiltinArguments::kPaddingOffset] =
226  ReadOnlyRoots(isolate).the_hole_value()->ptr();
227  argv[BuiltinArguments::kArgcOffset] = Smi::FromInt(frame_argc)->ptr();
228  argv[BuiltinArguments::kTargetOffset] = function->ptr();
229  argv[BuiltinArguments::kNewTargetOffset] = new_target->ptr();
230  MaybeHandle<Object> result;
231  {
232  RelocatableArguments arguments(isolate, frame_argc, &argv[frame_argc - 1]);
233  if (is_construct) {
234  result = HandleApiCallHelper<true>(isolate, function, new_target,
235  fun_data, receiver, arguments);
236  } else {
237  result = HandleApiCallHelper<false>(isolate, function, new_target,
238  fun_data, receiver, arguments);
239  }
240  }
241  if (argv != small_argv) delete[] argv;
242  return result;
243 }
244 
245 // Helper function to handle calls to non-function objects created through the
246 // API. The object can be called as either a constructor (using new) or just as
247 // a function (without new).
248 V8_WARN_UNUSED_RESULT static Object* HandleApiCallAsFunctionOrConstructor(
249  Isolate* isolate, bool is_construct_call, BuiltinArguments args) {
250  Handle<Object> receiver = args.receiver();
251 
252  // Get the object called.
253  JSObject* obj = JSObject::cast(*receiver);
254 
255  // Set the new target.
256  HeapObject* new_target;
257  if (is_construct_call) {
258  // TODO(adamk): This should be passed through in args instead of
259  // being patched in here. We need to set a non-undefined value
260  // for v8::FunctionCallbackInfo::IsConstructCall() to get the
261  // right answer.
262  new_target = obj;
263  } else {
264  new_target = ReadOnlyRoots(isolate).undefined_value();
265  }
266 
267  // Get the invocation callback from the function descriptor that was
268  // used to create the called object.
269  DCHECK(obj->map()->is_callable());
270  JSFunction* constructor = JSFunction::cast(obj->map()->GetConstructor());
271  // TODO(ishell): turn this back to a DCHECK.
272  CHECK(constructor->shared()->IsApiFunction());
273  Object* handler =
274  constructor->shared()->get_api_func_data()->GetInstanceCallHandler();
275  DCHECK(!handler->IsUndefined(isolate));
276  CallHandlerInfo* call_data = CallHandlerInfo::cast(handler);
277 
278  // Get the data for the call and perform the callback.
279  Object* result;
280  {
281  HandleScope scope(isolate);
282  LOG(isolate, ApiObjectAccess("call non-function", obj));
283  FunctionCallbackArguments custom(isolate, call_data->data(), constructor,
284  obj, new_target, args.address_of_arg_at(1),
285  args.length() - 1);
286  Handle<Object> result_handle = custom.Call(call_data);
287  if (result_handle.is_null()) {
288  result = ReadOnlyRoots(isolate).undefined_value();
289  } else {
290  result = *result_handle;
291  }
292  }
293  // Check for exceptions and return result.
294  RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
295  return result;
296 }
297 
298 // Handle calls to non-function objects created through the API. This delegate
299 // function is used when the call is a normal function call.
300 BUILTIN(HandleApiCallAsFunction) {
301  return HandleApiCallAsFunctionOrConstructor(isolate, false, args);
302 }
303 
304 // Handle calls to non-function objects created through the API. This delegate
305 // function is used when the call is a construct call.
306 BUILTIN(HandleApiCallAsConstructor) {
307  return HandleApiCallAsFunctionOrConstructor(isolate, true, args);
308 }
309 
310 } // namespace internal
311 } // namespace v8
Definition: v8.h:85
Definition: libplatform.h:13
static Local< ObjectTemplate > New(Isolate *isolate, Local< FunctionTemplate > constructor=Local< FunctionTemplate >())
Definition: api.cc:1616