builtins-api.cc 11.4 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
#include "src/api/api-arguments-inl.h"
#include "src/api/api-natives.h"
7 8
#include "src/builtins/builtins-utils-inl.h"
#include "src/builtins/builtins.h"
9 10
#include "src/logging/counters.h"
#include "src/logging/log.h"
11
#include "src/objects/objects-inl.h"
12
#include "src/objects/prototype.h"
13
#include "src/objects/templates.h"
14
#include "src/objects/visitors.h"
15 16 17 18 19 20 21 22 23

namespace v8 {
namespace internal {

namespace {

// Returns the holder JSObject if the function can legally be called with this
// receiver.  Returns nullptr if the call is illegal.
// TODO(dcarney): CallOptimization duplicates this logic, merge.
24
JSReceiver GetCompatibleReceiver(Isolate* isolate, FunctionTemplateInfo info,
25
                                 JSReceiver receiver) {
26
  RCS_SCOPE(isolate, RuntimeCallCounterId::kGetCompatibleReceiver);
27
  Object recv_type = info.signature();
28
  // No signature, return holder.
29
  if (!recv_type.IsFunctionTemplateInfo()) return receiver;
30
  // A Proxy cannot have been created from the signature template.
31
  if (!receiver.IsJSObject()) return JSReceiver();
32

33
  JSObject js_obj_receiver = JSObject::cast(receiver);
34
  FunctionTemplateInfo signature = FunctionTemplateInfo::cast(recv_type);
35

36
  // Check the receiver.
37
  if (signature.IsTemplateFor(js_obj_receiver)) return receiver;
38 39 40 41 42 43 44 45

  // The JSGlobalProxy might have a hidden prototype.
  if (V8_UNLIKELY(js_obj_receiver.IsJSGlobalProxy())) {
    HeapObject prototype = js_obj_receiver.map().prototype();
    if (!prototype.IsNull(isolate)) {
      JSObject js_obj_prototype = JSObject::cast(prototype);
      if (signature.IsTemplateFor(js_obj_prototype)) return js_obj_prototype;
    }
46
  }
47
  return JSReceiver();
48 49 50
}

template <bool is_construct>
51
V8_WARN_UNUSED_RESULT MaybeHandle<Object> HandleApiCallHelper(
52 53 54
    Isolate* isolate, Handle<HeapObject> function,
    Handle<HeapObject> new_target, Handle<FunctionTemplateInfo> fun_data,
    Handle<Object> receiver, BuiltinArguments args) {
55
  Handle<JSReceiver> js_receiver;
56
  JSReceiver raw_holder;
57 58
  if (is_construct) {
    DCHECK(args.receiver()->IsTheHole(isolate));
59
    if (fun_data->GetInstanceTemplate().IsUndefined(isolate)) {
60 61 62
      v8::Local<ObjectTemplate> templ =
          ObjectTemplate::New(reinterpret_cast<v8::Isolate*>(isolate),
                              ToApiHandle<v8::FunctionTemplate>(fun_data));
63 64
      FunctionTemplateInfo::SetInstanceTemplate(isolate, fun_data,
                                                Utils::OpenHandle(*templ));
65 66
    }
    Handle<ObjectTemplateInfo> instance_template(
67
        ObjectTemplateInfo::cast(fun_data->GetInstanceTemplate()), isolate);
68 69
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, js_receiver,
70
        ApiNatives::InstantiateObject(isolate, instance_template,
71 72
                                      Handle<JSReceiver>::cast(new_target)),
        Object);
73
    args.set_at(0, *js_receiver);
74 75 76 77 78
    DCHECK_EQ(*js_receiver, *args.receiver());

    raw_holder = *js_receiver;
  } else {
    DCHECK(receiver->IsJSReceiver());
79
    js_receiver = Handle<JSReceiver>::cast(receiver);
80 81

    if (!fun_data->accept_any_receiver() &&
82 83 84 85
        js_receiver->IsAccessCheckNeeded()) {
      // Proxies never need access checks.
      DCHECK(js_receiver->IsJSObject());
      Handle<JSObject> js_obj_receiver = Handle<JSObject>::cast(js_receiver);
86 87
      if (!isolate->MayAccess(handle(isolate->context(), isolate),
                              js_obj_receiver)) {
88 89 90 91
        isolate->ReportFailedAccessCheck(js_obj_receiver);
        RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
        return isolate->factory()->undefined_value();
      }
92 93 94 95
    }

    raw_holder = GetCompatibleReceiver(isolate, *fun_data, *js_receiver);

96
    if (raw_holder.is_null()) {
97 98 99 100 101 102
      // This function cannot be called with the given receiver.  Abort!
      THROW_NEW_ERROR(
          isolate, NewTypeError(MessageTemplate::kIllegalInvocation), Object);
    }
  }

103
  Object raw_call_data = fun_data->call_code(kAcquireLoad);
104 105
  if (!raw_call_data.IsUndefined(isolate)) {
    DCHECK(raw_call_data.IsCallHandlerInfo());
106
    CallHandlerInfo call_data = CallHandlerInfo::cast(raw_call_data);
107
    Object data_obj = call_data.data();
108

109 110 111
    FunctionCallbackArguments custom(
        isolate, data_obj, *function, raw_holder, *new_target,
        args.address_of_first_argument(), args.length() - 1);
112
    Handle<Object> result = custom.Call(call_data);
113 114 115 116 117 118 119 120

    RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
    if (result.is_null()) {
      if (is_construct) return js_receiver;
      return isolate->factory()->undefined_value();
    }
    // Rebox the result.
    result->VerifyApiCallResultType();
121 122
    if (!is_construct || result->IsJSReceiver())
      return handle(*result, isolate);
123 124 125 126 127 128 129 130 131
  }

  return js_receiver;
}

}  // anonymous namespace

BUILTIN(HandleApiCall) {
  HandleScope scope(isolate);
132
  Handle<JSFunction> function = args.target();
133 134
  Handle<Object> receiver = args.receiver();
  Handle<HeapObject> new_target = args.new_target();
135
  Handle<FunctionTemplateInfo> fun_data(function->shared().get_api_func_data(),
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
                                        isolate);
  if (new_target->IsJSReceiver()) {
    RETURN_RESULT_OR_FAILURE(
        isolate, HandleApiCallHelper<true>(isolate, function, new_target,
                                           fun_data, receiver, args));
  } else {
    RETURN_RESULT_OR_FAILURE(
        isolate, HandleApiCallHelper<false>(isolate, function, new_target,
                                            fun_data, receiver, args));
  }
}

namespace {

class RelocatableArguments : public BuiltinArguments, public Relocatable {
 public:
152
  RelocatableArguments(Isolate* isolate, int length, Address* arguments)
153 154
      : BuiltinArguments(length, arguments), Relocatable(isolate) {}

155 156 157
  RelocatableArguments(const RelocatableArguments&) = delete;
  RelocatableArguments& operator=(const RelocatableArguments&) = delete;

158
  inline void IterateInstance(RootVisitor* v) override {
159
    if (length() == 0) return;
160 161
    v->VisitRootPointers(Root::kRelocatable, nullptr, first_slot(),
                         last_slot() + 1);
162 163 164 165 166 167
  }
};

}  // namespace

MaybeHandle<Object> Builtins::InvokeApiFunction(Isolate* isolate,
168
                                                bool is_construct,
169 170
                                                Handle<HeapObject> function,
                                                Handle<Object> receiver,
171 172
                                                int argc, Handle<Object> args[],
                                                Handle<HeapObject> new_target) {
173
  RCS_SCOPE(isolate, RuntimeCallCounterId::kInvokeApiFunction);
174 175
  DCHECK(function->IsFunctionTemplateInfo() ||
         (function->IsJSFunction() &&
176
          JSFunction::cast(*function).shared().IsApiFunction()));
177 178

  // Do proper receiver conversion for non-strict mode api functions.
179
  if (!is_construct && !receiver->IsJSReceiver()) {
180
    if (function->IsFunctionTemplateInfo() ||
181
        is_sloppy(JSFunction::cast(*function).shared().language_mode())) {
182 183 184 185 186 187
      ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
                                 Object::ConvertReceiver(isolate, receiver),
                                 Object);
    }
  }

188 189 190 191
  // We assume that all lazy accessor pairs have been instantiated when setting
  // a break point on any API function.
  DCHECK_IMPLIES(function->IsFunctionTemplateInfo(),
                 !Handle<FunctionTemplateInfo>::cast(function)->BreakAtEntry());
192

193 194 195
  Handle<FunctionTemplateInfo> fun_data =
      function->IsFunctionTemplateInfo()
          ? Handle<FunctionTemplateInfo>::cast(function)
196
          : handle(JSFunction::cast(*function).shared().get_api_func_data(),
197 198 199 200
                   isolate);
  // Construct BuiltinArguments object:
  // new target, function, arguments reversed, receiver.
  const int kBufferSize = 32;
201 202
  Address small_argv[kBufferSize];
  Address* argv;
203 204 205 206
  const int frame_argc = argc + BuiltinArguments::kNumExtraArgsWithReceiver;
  if (frame_argc <= kBufferSize) {
    argv = small_argv;
  } else {
207
    argv = new Address[frame_argc];
208
  }
209 210 211 212 213 214 215 216 217 218
  argv[BuiltinArguments::kNewTargetOffset] = new_target->ptr();
  argv[BuiltinArguments::kTargetOffset] = function->ptr();
  argv[BuiltinArguments::kArgcOffset] = Smi::FromInt(frame_argc).ptr();
  argv[BuiltinArguments::kPaddingOffset] =
      ReadOnlyRoots(isolate).the_hole_value().ptr();
  int cursor = BuiltinArguments::kNumExtraArgs;
  argv[cursor++] = receiver->ptr();
  for (int i = 0; i < argc; ++i) {
    argv[cursor++] = args[i]->ptr();
  }
219 220 221
  MaybeHandle<Object> result;
  {
    RelocatableArguments arguments(isolate, frame_argc, &argv[frame_argc - 1]);
222 223 224 225 226 227 228
    if (is_construct) {
      result = HandleApiCallHelper<true>(isolate, function, new_target,
                                         fun_data, receiver, arguments);
    } else {
      result = HandleApiCallHelper<false>(isolate, function, new_target,
                                          fun_data, receiver, arguments);
    }
229 230 231 232 233 234 235 236
  }
  if (argv != small_argv) delete[] argv;
  return result;
}

// Helper function to handle calls to non-function objects created through the
// API. The object can be called as either a constructor (using new) or just as
// a function (without new).
237
V8_WARN_UNUSED_RESULT static Object HandleApiCallAsFunctionOrConstructor(
238 239 240 241
    Isolate* isolate, bool is_construct_call, BuiltinArguments args) {
  Handle<Object> receiver = args.receiver();

  // Get the object called.
242
  JSObject obj = JSObject::cast(*receiver);
243 244

  // Set the new target.
245
  HeapObject new_target;
246 247 248 249 250 251 252
  if (is_construct_call) {
    // TODO(adamk): This should be passed through in args instead of
    // being patched in here. We need to set a non-undefined value
    // for v8::FunctionCallbackInfo::IsConstructCall() to get the
    // right answer.
    new_target = obj;
  } else {
253
    new_target = ReadOnlyRoots(isolate).undefined_value();
254 255 256 257
  }

  // Get the invocation callback from the function descriptor that was
  // used to create the called object.
258 259 260
  DCHECK(obj.map().is_callable());
  JSFunction constructor = JSFunction::cast(obj.map().GetConstructor());
  DCHECK(constructor.shared().IsApiFunction());
261
  Object handler =
262 263
      constructor.shared().get_api_func_data().GetInstanceCallHandler();
  DCHECK(!handler.IsUndefined(isolate));
264
  CallHandlerInfo call_data = CallHandlerInfo::cast(handler);
265 266

  // Get the data for the call and perform the callback.
267
  Object result;
268 269 270
  {
    HandleScope scope(isolate);
    LOG(isolate, ApiObjectAccess("call non-function", obj));
271 272 273
    FunctionCallbackArguments custom(
        isolate, call_data.data(), constructor, obj, new_target,
        args.address_of_first_argument(), args.length() - 1);
274
    Handle<Object> result_handle = custom.Call(call_data);
275
    if (result_handle.is_null()) {
276
      result = ReadOnlyRoots(isolate).undefined_value();
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
    } else {
      result = *result_handle;
    }
  }
  // Check for exceptions and return result.
  RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
  return result;
}

// Handle calls to non-function objects created through the API. This delegate
// function is used when the call is a normal function call.
BUILTIN(HandleApiCallAsFunction) {
  return HandleApiCallAsFunctionOrConstructor(isolate, false, args);
}

// Handle calls to non-function objects created through the API. This delegate
// function is used when the call is a construct call.
BUILTIN(HandleApiCallAsConstructor) {
  return HandleApiCallAsFunctionOrConstructor(isolate, true, args);
}

}  // namespace internal
}  // namespace v8