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