• 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/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.
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 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>
HandleApiCallHelper(Isolate * isolate,Handle<HeapObject> function,Handle<HeapObject> new_target,Handle<FunctionTemplateInfo> fun_data,Handle<Object> receiver,BuiltinArguments args)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->instance_template()->IsUndefined(isolate)) {
57       v8::Local<ObjectTemplate> templ =
58           ObjectTemplate::New(reinterpret_cast<v8::Isolate*>(isolate),
59                               ToApiHandle<v8::FunctionTemplate>(fun_data));
60       fun_data->set_instance_template(*Utils::OpenHandle(*templ));
61     }
62     Handle<ObjectTemplateInfo> instance_template(
63         ObjectTemplateInfo::cast(fun_data->instance_template()), isolate);
64     ASSIGN_RETURN_ON_EXCEPTION(
65         isolate, js_receiver,
66         ApiNatives::InstantiateObject(isolate, instance_template,
67                                       Handle<JSReceiver>::cast(new_target)),
68         Object);
69     args[0] = *js_receiver;
70     DCHECK_EQ(*js_receiver, *args.receiver());
71 
72     raw_holder = *js_receiver;
73   } else {
74     DCHECK(receiver->IsJSReceiver());
75     js_receiver = Handle<JSReceiver>::cast(receiver);
76 
77     if (!fun_data->accept_any_receiver() &&
78         js_receiver->IsAccessCheckNeeded()) {
79       // Proxies never need access checks.
80       DCHECK(js_receiver->IsJSObject());
81       Handle<JSObject> js_obj_receiver = Handle<JSObject>::cast(js_receiver);
82       if (!isolate->MayAccess(handle(isolate->context(), isolate),
83                               js_obj_receiver)) {
84         isolate->ReportFailedAccessCheck(js_obj_receiver);
85         RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
86         return isolate->factory()->undefined_value();
87       }
88     }
89 
90     raw_holder = GetCompatibleReceiver(isolate, *fun_data, *js_receiver);
91 
92     if (raw_holder == nullptr) {
93       // This function cannot be called with the given receiver.  Abort!
94       THROW_NEW_ERROR(
95           isolate, NewTypeError(MessageTemplate::kIllegalInvocation), Object);
96     }
97   }
98 
99   Object* raw_call_data = fun_data->call_code();
100   if (!raw_call_data->IsUndefined(isolate)) {
101     DCHECK(raw_call_data->IsCallHandlerInfo());
102     CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);
103     Object* data_obj = call_data->data();
104 
105 
106     FunctionCallbackArguments custom(isolate, data_obj, *function, raw_holder,
107                                      *new_target, &args[0] - 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 
BUILTIN(HandleApiCall)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:
RelocatableArguments(Isolate * isolate,int length,Object ** arguments)149   RelocatableArguments(Isolate* isolate, int length, Object** arguments)
150       : BuiltinArguments(length, arguments), Relocatable(isolate) {}
151 
IterateInstance(RootVisitor * v)152   virtual inline void IterateInstance(RootVisitor* v) {
153     if (length() == 0) return;
154     v->VisitRootPointers(Root::kRelocatable, nullptr, lowest_address(),
155                          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   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   Object* small_argv[kBufferSize];
212   Object** argv;
213   const int frame_argc = argc + BuiltinArguments::kNumExtraArgsWithReceiver;
214   if (frame_argc <= kBufferSize) {
215     argv = small_argv;
216   } else {
217     argv = new Object*[frame_argc];
218   }
219   int cursor = frame_argc - 1;
220   argv[cursor--] = *receiver;
221   for (int i = 0; i < argc; ++i) {
222     argv[cursor--] = *args[i];
223   }
224   DCHECK_EQ(cursor, BuiltinArguments::kPaddingOffset);
225   argv[BuiltinArguments::kPaddingOffset] =
226       ReadOnlyRoots(isolate).the_hole_value();
227   argv[BuiltinArguments::kArgcOffset] = Smi::FromInt(frame_argc);
228   argv[BuiltinArguments::kTargetOffset] = *function;
229   argv[BuiltinArguments::kNewTargetOffset] = *new_target;
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).
HandleApiCallAsFunctionOrConstructor(Isolate * isolate,bool is_construct_call,BuiltinArguments args)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()->instance_call_handler();
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[0] - 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.
BUILTIN(HandleApiCallAsFunction)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.
BUILTIN(HandleApiCallAsConstructor)306 BUILTIN(HandleApiCallAsConstructor) {
307   return HandleApiCallAsFunctionOrConstructor(isolate, true, args);
308 }
309 
310 }  // namespace internal
311 }  // namespace v8
312