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