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/api-arguments-inl.h" 6#include "src/api/api-natives.h" 7#include "src/builtins/builtins-utils-inl.h" 8#include "src/builtins/builtins.h" 9#include "src/logging/log.h" 10#include "src/logging/runtime-call-stats-scope.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 16namespace v8 { 17namespace internal { 18 19namespace { 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. 24JSReceiver 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 // The JSGlobalProxy might have a hidden prototype. 40 if (V8_UNLIKELY(js_obj_receiver.IsJSGlobalProxy())) { 41 HeapObject prototype = js_obj_receiver.map().prototype(); 42 if (!prototype.IsNull(isolate)) { 43 JSObject js_obj_prototype = JSObject::cast(prototype); 44 if (signature.IsTemplateFor(js_obj_prototype)) return js_obj_prototype; 45 } 46 } 47 return JSReceiver(); 48} 49 50template <bool is_construct> 51V8_WARN_UNUSED_RESULT MaybeHandle<Object> HandleApiCallHelper( 52 Isolate* isolate, Handle<HeapObject> function, 53 Handle<HeapObject> new_target, Handle<FunctionTemplateInfo> fun_data, 54 Handle<Object> receiver, BuiltinArguments args) { 55 Handle<JSReceiver> js_receiver; 56 JSReceiver raw_holder; 57 if (is_construct) { 58 DCHECK(args.receiver()->IsTheHole(isolate)); 59 if (fun_data->GetInstanceTemplate().IsUndefined(isolate)) { 60 v8::Local<ObjectTemplate> templ = 61 ObjectTemplate::New(reinterpret_cast<v8::Isolate*>(isolate), 62 ToApiHandle<v8::FunctionTemplate>(fun_data)); 63 FunctionTemplateInfo::SetInstanceTemplate(isolate, fun_data, 64 Utils::OpenHandle(*templ)); 65 } 66 Handle<ObjectTemplateInfo> instance_template( 67 ObjectTemplateInfo::cast(fun_data->GetInstanceTemplate()), isolate); 68 ASSIGN_RETURN_ON_EXCEPTION( 69 isolate, js_receiver, 70 ApiNatives::InstantiateObject(isolate, instance_template, 71 Handle<JSReceiver>::cast(new_target)), 72 Object); 73 args.set_at(0, *js_receiver); 74 DCHECK_EQ(*js_receiver, *args.receiver()); 75 76 raw_holder = *js_receiver; 77 } else { 78 DCHECK(receiver->IsJSReceiver()); 79 js_receiver = Handle<JSReceiver>::cast(receiver); 80 81 if (!fun_data->accept_any_receiver() && 82 js_receiver->IsAccessCheckNeeded()) { 83 // Proxies never need access checks. 84 DCHECK(js_receiver->IsJSObject()); 85 Handle<JSObject> js_obj_receiver = Handle<JSObject>::cast(js_receiver); 86 if (!isolate->MayAccess(handle(isolate->context(), isolate), 87 js_obj_receiver)) { 88 isolate->ReportFailedAccessCheck(js_obj_receiver); 89 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); 90 return isolate->factory()->undefined_value(); 91 } 92 } 93 94 raw_holder = GetCompatibleReceiver(isolate, *fun_data, *js_receiver); 95 96 if (raw_holder.is_null()) { 97 // This function cannot be called with the given receiver. Abort! 98 THROW_NEW_ERROR( 99 isolate, NewTypeError(MessageTemplate::kIllegalInvocation), Object); 100 } 101 } 102 103 Object raw_call_data = fun_data->call_code(kAcquireLoad); 104 if (!raw_call_data.IsUndefined(isolate)) { 105 DCHECK(raw_call_data.IsCallHandlerInfo()); 106 CallHandlerInfo call_data = CallHandlerInfo::cast(raw_call_data); 107 Object data_obj = call_data.data(); 108 109 FunctionCallbackArguments custom( 110 isolate, data_obj, *function, raw_holder, *new_target, 111 args.address_of_first_argument(), args.length() - 1); 112 Handle<Object> result = custom.Call(call_data); 113 114 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); 115 if (result.is_null()) { 116 if (is_construct) return js_receiver; 117 return isolate->factory()->undefined_value(); 118 } 119 // Rebox the result. 120 result->VerifyApiCallResultType(); 121 if (!is_construct || result->IsJSReceiver()) 122 return handle(*result, isolate); 123 } 124 125 return js_receiver; 126} 127 128} // anonymous namespace 129 130BUILTIN(HandleApiCall) { 131 HandleScope scope(isolate); 132 Handle<JSFunction> function = args.target(); 133 Handle<Object> receiver = args.receiver(); 134 Handle<HeapObject> new_target = args.new_target(); 135 Handle<FunctionTemplateInfo> fun_data(function->shared().get_api_func_data(), 136 isolate); 137 if (new_target->IsJSReceiver()) { 138 RETURN_RESULT_OR_FAILURE( 139 isolate, HandleApiCallHelper<true>(isolate, function, new_target, 140 fun_data, receiver, args)); 141 } else { 142 RETURN_RESULT_OR_FAILURE( 143 isolate, HandleApiCallHelper<false>(isolate, function, new_target, 144 fun_data, receiver, args)); 145 } 146} 147 148namespace { 149 150class RelocatableArguments : public BuiltinArguments, public Relocatable { 151 public: 152 RelocatableArguments(Isolate* isolate, int length, Address* arguments) 153 : BuiltinArguments(length, arguments), Relocatable(isolate) {} 154 155 RelocatableArguments(const RelocatableArguments&) = delete; 156 RelocatableArguments& operator=(const RelocatableArguments&) = delete; 157 158 inline void IterateInstance(RootVisitor* v) override { 159 if (length() == 0) return; 160 v->VisitRootPointers(Root::kRelocatable, nullptr, first_slot(), 161 last_slot() + 1); 162 } 163}; 164 165} // namespace 166 167MaybeHandle<Object> Builtins::InvokeApiFunction(Isolate* isolate, 168 bool is_construct, 169 Handle<HeapObject> function, 170 Handle<Object> receiver, 171 int argc, Handle<Object> args[], 172 Handle<HeapObject> new_target) { 173 RCS_SCOPE(isolate, RuntimeCallCounterId::kInvokeApiFunction); 174 DCHECK(function->IsFunctionTemplateInfo() || 175 (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 ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver, 183 Object::ConvertReceiver(isolate, receiver), 184 Object); 185 } 186 } 187 188 // We assume that all lazy accessor pairs have been instantiated when setting 189 // a break point on any API function. 190 DCHECK_IMPLIES(function->IsFunctionTemplateInfo(), 191 !Handle<FunctionTemplateInfo>::cast(function)->BreakAtEntry()); 192 193 Handle<FunctionTemplateInfo> fun_data = 194 function->IsFunctionTemplateInfo() 195 ? Handle<FunctionTemplateInfo>::cast(function) 196 : handle(JSFunction::cast(*function).shared().get_api_func_data(), 197 isolate); 198 // Construct BuiltinArguments object: 199 // new target, function, arguments reversed, receiver. 200 const int kBufferSize = 32; 201 Address small_argv[kBufferSize]; 202 Address* argv; 203 const int frame_argc = argc + BuiltinArguments::kNumExtraArgsWithReceiver; 204 if (frame_argc <= kBufferSize) { 205 argv = small_argv; 206 } else { 207 argv = new Address[frame_argc]; 208 } 209 argv[BuiltinArguments::kNewTargetOffset] = new_target->ptr(); 210 argv[BuiltinArguments::kTargetOffset] = function->ptr(); 211 argv[BuiltinArguments::kArgcOffset] = Smi::FromInt(frame_argc).ptr(); 212 argv[BuiltinArguments::kPaddingOffset] = 213 ReadOnlyRoots(isolate).the_hole_value().ptr(); 214 int cursor = BuiltinArguments::kNumExtraArgs; 215 argv[cursor++] = receiver->ptr(); 216 for (int i = 0; i < argc; ++i) { 217 argv[cursor++] = args[i]->ptr(); 218 } 219 MaybeHandle<Object> result; 220 { 221 RelocatableArguments arguments(isolate, frame_argc, &argv[frame_argc - 1]); 222 if (is_construct) { 223 result = HandleApiCallHelper<true>(isolate, function, new_target, 224 fun_data, receiver, arguments); 225 } else { 226 result = HandleApiCallHelper<false>(isolate, function, new_target, 227 fun_data, receiver, arguments); 228 } 229 } 230 if (argv != small_argv) delete[] argv; 231 return result; 232} 233 234// Helper function to handle calls to non-function objects created through the 235// API. The object can be called as either a constructor (using new) or just as 236// a function (without new). 237V8_WARN_UNUSED_RESULT static Object HandleApiCallAsFunctionOrConstructor( 238 Isolate* isolate, bool is_construct_call, BuiltinArguments args) { 239 Handle<Object> receiver = args.receiver(); 240 241 // Get the object called. 242 JSObject obj = JSObject::cast(*receiver); 243 244 // Set the new target. 245 HeapObject new_target; 246 if (is_construct_call) { 247 // TODO(adamk): This should be passed through in args instead of 248 // being patched in here. We need to set a non-undefined value 249 // for v8::FunctionCallbackInfo::IsConstructCall() to get the 250 // right answer. 251 new_target = obj; 252 } else { 253 new_target = ReadOnlyRoots(isolate).undefined_value(); 254 } 255 256 // Get the invocation callback from the function descriptor that was 257 // used to create the called object. 258 DCHECK(obj.map().is_callable()); 259 JSFunction constructor = JSFunction::cast(obj.map().GetConstructor()); 260 DCHECK(constructor.shared().IsApiFunction()); 261 Object handler = 262 constructor.shared().get_api_func_data().GetInstanceCallHandler(); 263 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 HandleScope scope(isolate); 270 FunctionCallbackArguments custom( 271 isolate, call_data.data(), constructor, obj, new_target, 272 args.address_of_first_argument(), args.length() - 1); 273 Handle<Object> result_handle = custom.Call(call_data); 274 if (result_handle.is_null()) { 275 result = ReadOnlyRoots(isolate).undefined_value(); 276 } else { 277 result = *result_handle; 278 } 279 } 280 // Check for exceptions and return result. 281 RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); 282 return result; 283} 284 285// Handle calls to non-function objects created through the API. This delegate 286// function is used when the call is a normal function call. 287BUILTIN(HandleApiCallAsFunction) { 288 return HandleApiCallAsFunctionOrConstructor(isolate, false, args); 289} 290 291// Handle calls to non-function objects created through the API. This delegate 292// function is used when the call is a construct call. 293BUILTIN(HandleApiCallAsConstructor) { 294 return HandleApiCallAsFunctionOrConstructor(isolate, true, args); 295} 296 297} // namespace internal 298} // namespace v8 299