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/counters.h"
10 #include "src/logging/log.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 
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.
GetCompatibleReceiver(Isolate * isolate,FunctionTemplateInfo info,JSReceiver receiver)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 JSReceiver();
31 
32   JSObject js_obj_receiver = JSObject::cast(receiver);
33   FunctionTemplateInfo signature = FunctionTemplateInfo::cast(recv_type);
34 
35   // Check the receiver.
36   if (signature.IsTemplateFor(js_obj_receiver)) return receiver;
37 
38   // The JSGlobalProxy might have a hidden prototype.
39   if (V8_UNLIKELY(js_obj_receiver.IsJSGlobalProxy())) {
40     HeapObject prototype = js_obj_receiver.map().prototype();
41     if (!prototype.IsNull(isolate)) {
42       JSObject js_obj_prototype = JSObject::cast(prototype);
43       if (signature.IsTemplateFor(js_obj_prototype)) return js_obj_prototype;
44     }
45   }
46   return JSReceiver();
47 }
48 
49 template <bool is_construct>
HandleApiCallHelper(Isolate * isolate,Handle<HeapObject> function,Handle<HeapObject> new_target,Handle<FunctionTemplateInfo> fun_data,Handle<Object> receiver,BuiltinArguments args)50 V8_WARN_UNUSED_RESULT MaybeHandle<Object> HandleApiCallHelper(
51     Isolate* isolate, Handle<HeapObject> function,
52     Handle<HeapObject> new_target, Handle<FunctionTemplateInfo> fun_data,
53     Handle<Object> receiver, BuiltinArguments args) {
54   Handle<JSReceiver> js_receiver;
55   JSReceiver raw_holder;
56   if (is_construct) {
57     DCHECK(args.receiver()->IsTheHole(isolate));
58     if (fun_data->GetInstanceTemplate().IsUndefined(isolate)) {
59       v8::Local<ObjectTemplate> templ =
60           ObjectTemplate::New(reinterpret_cast<v8::Isolate*>(isolate),
61                               ToApiHandle<v8::FunctionTemplate>(fun_data));
62       FunctionTemplateInfo::SetInstanceTemplate(isolate, fun_data,
63                                                 Utils::OpenHandle(*templ));
64     }
65     Handle<ObjectTemplateInfo> instance_template(
66         ObjectTemplateInfo::cast(fun_data->GetInstanceTemplate()), isolate);
67     ASSIGN_RETURN_ON_EXCEPTION(
68         isolate, js_receiver,
69         ApiNatives::InstantiateObject(isolate, instance_template,
70                                       Handle<JSReceiver>::cast(new_target)),
71         Object);
72     args.set_at(0, *js_receiver);
73     DCHECK_EQ(*js_receiver, *args.receiver());
74 
75     raw_holder = *js_receiver;
76   } else {
77     DCHECK(receiver->IsJSReceiver());
78     js_receiver = Handle<JSReceiver>::cast(receiver);
79 
80     if (!fun_data->accept_any_receiver() &&
81         js_receiver->IsAccessCheckNeeded()) {
82       // Proxies never need access checks.
83       DCHECK(js_receiver->IsJSObject());
84       Handle<JSObject> js_obj_receiver = Handle<JSObject>::cast(js_receiver);
85       if (!isolate->MayAccess(handle(isolate->context(), isolate),
86                               js_obj_receiver)) {
87         isolate->ReportFailedAccessCheck(js_obj_receiver);
88         RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
89         return isolate->factory()->undefined_value();
90       }
91     }
92 
93     raw_holder = GetCompatibleReceiver(isolate, *fun_data, *js_receiver);
94 
95     if (raw_holder.is_null()) {
96       // This function cannot be called with the given receiver.  Abort!
97       THROW_NEW_ERROR(
98           isolate, NewTypeError(MessageTemplate::kIllegalInvocation), Object);
99     }
100   }
101 
102   Object raw_call_data = fun_data->call_code();
103   if (!raw_call_data.IsUndefined(isolate)) {
104     DCHECK(raw_call_data.IsCallHandlerInfo());
105     CallHandlerInfo call_data = CallHandlerInfo::cast(raw_call_data);
106     Object data_obj = call_data.data();
107 
108     FunctionCallbackArguments custom(
109         isolate, data_obj, *function, raw_holder, *new_target,
110         args.address_of_first_argument(), args.length() - 1);
111     Handle<Object> result = custom.Call(call_data);
112 
113     RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
114     if (result.is_null()) {
115       if (is_construct) return js_receiver;
116       return isolate->factory()->undefined_value();
117     }
118     // Rebox the result.
119     result->VerifyApiCallResultType();
120     if (!is_construct || result->IsJSReceiver())
121       return handle(*result, isolate);
122   }
123 
124   return js_receiver;
125 }
126 
127 }  // anonymous namespace
128 
BUILTIN(HandleApiCall)129 BUILTIN(HandleApiCall) {
130   HandleScope scope(isolate);
131   Handle<JSFunction> function = args.target();
132   Handle<Object> receiver = args.receiver();
133   Handle<HeapObject> new_target = args.new_target();
134   Handle<FunctionTemplateInfo> fun_data(function->shared().get_api_func_data(),
135                                         isolate);
136   if (new_target->IsJSReceiver()) {
137     RETURN_RESULT_OR_FAILURE(
138         isolate, HandleApiCallHelper<true>(isolate, function, new_target,
139                                            fun_data, receiver, args));
140   } else {
141     RETURN_RESULT_OR_FAILURE(
142         isolate, HandleApiCallHelper<false>(isolate, function, new_target,
143                                             fun_data, receiver, args));
144   }
145 }
146 
147 namespace {
148 
149 class RelocatableArguments : public BuiltinArguments, public Relocatable {
150  public:
RelocatableArguments(Isolate * isolate,int length,Address * arguments)151   RelocatableArguments(Isolate* isolate, int length, Address* arguments)
152       : BuiltinArguments(length, arguments), Relocatable(isolate) {}
153 
IterateInstance(RootVisitor * v)154   inline void IterateInstance(RootVisitor* v) override {
155     if (length() == 0) return;
156     v->VisitRootPointers(Root::kRelocatable, nullptr, first_slot(),
157                          last_slot() + 1);
158   }
159 
160  private:
161   DISALLOW_COPY_AND_ASSIGN(RelocatableArguments);
162 };
163 
164 }  // namespace
165 
InvokeApiFunction(Isolate * isolate,bool is_construct,Handle<HeapObject> function,Handle<Object> receiver,int argc,Handle<Object> args[],Handle<HeapObject> new_target)166 MaybeHandle<Object> Builtins::InvokeApiFunction(Isolate* isolate,
167                                                 bool is_construct,
168                                                 Handle<HeapObject> function,
169                                                 Handle<Object> receiver,
170                                                 int argc, Handle<Object> args[],
171                                                 Handle<HeapObject> new_target) {
172   RuntimeCallTimerScope timer(isolate,
173                               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 #ifdef V8_REVERSE_JSARGS
210   argv[BuiltinArguments::kNewTargetOffset] = new_target->ptr();
211   argv[BuiltinArguments::kTargetOffset] = function->ptr();
212   argv[BuiltinArguments::kArgcOffset] = Smi::FromInt(frame_argc).ptr();
213   argv[BuiltinArguments::kPaddingOffset] =
214       ReadOnlyRoots(isolate).the_hole_value().ptr();
215   int cursor = BuiltinArguments::kNumExtraArgs;
216   argv[cursor++] = receiver->ptr();
217   for (int i = 0; i < argc; ++i) {
218     argv[cursor++] = args[i]->ptr();
219   }
220 #else
221   int cursor = frame_argc - 1;
222   argv[cursor--] = receiver->ptr();
223   for (int i = 0; i < argc; ++i) {
224     argv[cursor--] = args[i]->ptr();
225   }
226   DCHECK_EQ(cursor, BuiltinArguments::kPaddingOffset);
227   argv[BuiltinArguments::kPaddingOffset] =
228       ReadOnlyRoots(isolate).the_hole_value().ptr();
229   argv[BuiltinArguments::kArgcOffset] = Smi::FromInt(frame_argc).ptr();
230   argv[BuiltinArguments::kTargetOffset] = function->ptr();
231   argv[BuiltinArguments::kNewTargetOffset] = new_target->ptr();
232 #endif
233   MaybeHandle<Object> result;
234   {
235     RelocatableArguments arguments(isolate, frame_argc, &argv[frame_argc - 1]);
236     if (is_construct) {
237       result = HandleApiCallHelper<true>(isolate, function, new_target,
238                                          fun_data, receiver, arguments);
239     } else {
240       result = HandleApiCallHelper<false>(isolate, function, new_target,
241                                           fun_data, receiver, arguments);
242     }
243   }
244   if (argv != small_argv) delete[] argv;
245   return result;
246 }
247 
248 // Helper function to handle calls to non-function objects created through the
249 // API. The object can be called as either a constructor (using new) or just as
250 // a function (without new).
HandleApiCallAsFunctionOrConstructor(Isolate * isolate,bool is_construct_call,BuiltinArguments args)251 V8_WARN_UNUSED_RESULT static Object HandleApiCallAsFunctionOrConstructor(
252     Isolate* isolate, bool is_construct_call, BuiltinArguments args) {
253   Handle<Object> receiver = args.receiver();
254 
255   // Get the object called.
256   JSObject obj = JSObject::cast(*receiver);
257 
258   // Set the new target.
259   HeapObject new_target;
260   if (is_construct_call) {
261     // TODO(adamk): This should be passed through in args instead of
262     // being patched in here. We need to set a non-undefined value
263     // for v8::FunctionCallbackInfo::IsConstructCall() to get the
264     // right answer.
265     new_target = obj;
266   } else {
267     new_target = ReadOnlyRoots(isolate).undefined_value();
268   }
269 
270   // Get the invocation callback from the function descriptor that was
271   // used to create the called object.
272   DCHECK(obj.map().is_callable());
273   JSFunction constructor = JSFunction::cast(obj.map().GetConstructor());
274   DCHECK(constructor.shared().IsApiFunction());
275   Object handler =
276       constructor.shared().get_api_func_data().GetInstanceCallHandler();
277   DCHECK(!handler.IsUndefined(isolate));
278   CallHandlerInfo call_data = CallHandlerInfo::cast(handler);
279 
280   // Get the data for the call and perform the callback.
281   Object result;
282   {
283     HandleScope scope(isolate);
284     LOG(isolate, ApiObjectAccess("call non-function", obj));
285     FunctionCallbackArguments custom(
286         isolate, call_data.data(), constructor, obj, new_target,
287         args.address_of_first_argument(), args.length() - 1);
288     Handle<Object> result_handle = custom.Call(call_data);
289     if (result_handle.is_null()) {
290       result = ReadOnlyRoots(isolate).undefined_value();
291     } else {
292       result = *result_handle;
293     }
294   }
295   // Check for exceptions and return result.
296   RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
297   return result;
298 }
299 
300 // Handle calls to non-function objects created through the API. This delegate
301 // function is used when the call is a normal function call.
BUILTIN(HandleApiCallAsFunction)302 BUILTIN(HandleApiCallAsFunction) {
303   return HandleApiCallAsFunctionOrConstructor(isolate, false, args);
304 }
305 
306 // Handle calls to non-function objects created through the API. This delegate
307 // function is used when the call is a construct call.
BUILTIN(HandleApiCallAsConstructor)308 BUILTIN(HandleApiCallAsConstructor) {
309   return HandleApiCallAsFunctionOrConstructor(isolate, true, args);
310 }
311 
312 }  // namespace internal
313 }  // namespace v8
314