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.
GetCompatibleReceiver(Isolate * isolate,FunctionTemplateInfo * info,JSReceiver * receiver)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>
HandleApiCallHelper(Isolate * isolate,Handle<HeapObject> function,Handle<HeapObject> new_target,Handle<FunctionTemplateInfo> fun_data,Handle<Object> receiver,BuiltinArguments args)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
BUILTIN(HandleApiCall)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:
RelocatableArguments(Isolate * isolate,int length,Object ** arguments)149 RelocatableArguments(Isolate* isolate, int length, Object** arguments)
150 : BuiltinArguments(length, arguments), Relocatable(isolate) {}
151
IterateInstance(RootVisitor * v)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
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 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).
HandleApiCallAsFunctionOrConstructor(Isolate * isolate,bool is_construct_call,BuiltinArguments args)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.
BUILTIN(HandleApiCallAsFunction)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.
BUILTIN(HandleApiCallAsConstructor)306 BUILTIN(HandleApiCallAsConstructor) {
307 return HandleApiCallAsFunctionOrConstructor(isolate, true, args);
308 }
309
310 } // namespace internal
311 } // namespace v8
312