• 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