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