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/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.
     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>
     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 
    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:
    150   RelocatableArguments(Isolate* isolate, int length, Object** arguments)
    151       : BuiltinArguments(length, arguments), Relocatable(isolate) {}
    152 
    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 
    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).
    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.
    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.
    291 BUILTIN(HandleApiCallAsConstructor) {
    292   return HandleApiCallAsFunctionOrConstructor(isolate, true, args);
    293 }
    294 
    295 }  // namespace internal
    296 }  // namespace v8
    297