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