Home | History | Annotate | Download | only in builtins
      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.
     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>
     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 
    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:
    149   RelocatableArguments(Isolate* isolate, int length, Object** arguments)
    150       : BuiltinArguments(length, arguments), Relocatable(isolate) {}
    151 
    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 
    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).
    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.
    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.
    306 BUILTIN(HandleApiCallAsConstructor) {
    307   return HandleApiCallAsFunctionOrConstructor(isolate, true, args);
    308 }
    309 
    310 }  // namespace internal
    311 }  // namespace v8
    312