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
11 namespace v8 {
12 namespace internal {
13
14 namespace {
15
16 // Returns the holder JSObject if the function can legally be called with this
17 // receiver. Returns nullptr if the call is illegal.
18 // TODO(dcarney): CallOptimization duplicates this logic, merge.
GetCompatibleReceiver(Isolate * isolate,FunctionTemplateInfo * info,JSObject * receiver)19 JSObject* GetCompatibleReceiver(Isolate* isolate, FunctionTemplateInfo* info,
20 JSObject* receiver) {
21 Object* recv_type = info->signature();
22 // No signature, return holder.
23 if (!recv_type->IsFunctionTemplateInfo()) return receiver;
24 FunctionTemplateInfo* signature = FunctionTemplateInfo::cast(recv_type);
25
26 // Check the receiver. Fast path for receivers with no hidden prototypes.
27 if (signature->IsTemplateFor(receiver)) return receiver;
28 if (!receiver->map()->has_hidden_prototype()) return nullptr;
29 for (PrototypeIterator iter(isolate, receiver, kStartAtPrototype,
30 PrototypeIterator::END_AT_NON_HIDDEN);
31 !iter.IsAtEnd(); iter.Advance()) {
32 JSObject* current = iter.GetCurrent<JSObject>();
33 if (signature->IsTemplateFor(current)) return current;
34 }
35 return nullptr;
36 }
37
38 template <bool is_construct>
HandleApiCallHelper(Isolate * isolate,Handle<HeapObject> function,Handle<HeapObject> new_target,Handle<FunctionTemplateInfo> fun_data,Handle<Object> receiver,BuiltinArguments args)39 MUST_USE_RESULT MaybeHandle<Object> HandleApiCallHelper(
40 Isolate* isolate, Handle<HeapObject> function,
41 Handle<HeapObject> new_target, Handle<FunctionTemplateInfo> fun_data,
42 Handle<Object> receiver, BuiltinArguments args) {
43 Handle<JSObject> js_receiver;
44 JSObject* raw_holder;
45 if (is_construct) {
46 DCHECK(args.receiver()->IsTheHole(isolate));
47 if (fun_data->instance_template()->IsUndefined(isolate)) {
48 v8::Local<ObjectTemplate> templ =
49 ObjectTemplate::New(reinterpret_cast<v8::Isolate*>(isolate),
50 ToApiHandle<v8::FunctionTemplate>(fun_data));
51 fun_data->set_instance_template(*Utils::OpenHandle(*templ));
52 }
53 Handle<ObjectTemplateInfo> instance_template(
54 ObjectTemplateInfo::cast(fun_data->instance_template()), isolate);
55 ASSIGN_RETURN_ON_EXCEPTION(
56 isolate, js_receiver,
57 ApiNatives::InstantiateObject(instance_template,
58 Handle<JSReceiver>::cast(new_target)),
59 Object);
60 args[0] = *js_receiver;
61 DCHECK_EQ(*js_receiver, *args.receiver());
62
63 raw_holder = *js_receiver;
64 } else {
65 DCHECK(receiver->IsJSReceiver());
66
67 if (!receiver->IsJSObject()) {
68 // This function cannot be called with the given receiver. Abort!
69 THROW_NEW_ERROR(
70 isolate, NewTypeError(MessageTemplate::kIllegalInvocation), Object);
71 }
72
73 js_receiver = Handle<JSObject>::cast(receiver);
74
75 if (!fun_data->accept_any_receiver() &&
76 js_receiver->IsAccessCheckNeeded() &&
77 !isolate->MayAccess(handle(isolate->context()), js_receiver)) {
78 isolate->ReportFailedAccessCheck(js_receiver);
79 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
80 }
81
82 raw_holder = GetCompatibleReceiver(isolate, *fun_data, *js_receiver);
83
84 if (raw_holder == nullptr) {
85 // This function cannot be called with the given receiver. Abort!
86 THROW_NEW_ERROR(
87 isolate, NewTypeError(MessageTemplate::kIllegalInvocation), Object);
88 }
89 }
90
91 Object* raw_call_data = fun_data->call_code();
92 if (!raw_call_data->IsUndefined(isolate)) {
93 DCHECK(raw_call_data->IsCallHandlerInfo());
94 CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);
95 Object* callback_obj = call_data->callback();
96 v8::FunctionCallback callback =
97 v8::ToCData<v8::FunctionCallback>(callback_obj);
98 Object* data_obj = call_data->data();
99
100 LOG(isolate, ApiObjectAccess("call", JSObject::cast(*js_receiver)));
101
102 FunctionCallbackArguments custom(isolate, data_obj, *function, raw_holder,
103 *new_target, &args[0] - 1,
104 args.length() - 1);
105
106 Handle<Object> result = custom.Call(callback);
107
108 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
109 if (result.is_null()) {
110 if (is_construct) return js_receiver;
111 return isolate->factory()->undefined_value();
112 }
113 // Rebox the result.
114 result->VerifyApiCallResultType();
115 if (!is_construct || result->IsJSObject()) return handle(*result, isolate);
116 }
117
118 return js_receiver;
119 }
120
121 } // anonymous namespace
122
BUILTIN(HandleApiCall)123 BUILTIN(HandleApiCall) {
124 HandleScope scope(isolate);
125 Handle<JSFunction> function = args.target();
126 Handle<Object> receiver = args.receiver();
127 Handle<HeapObject> new_target = args.new_target();
128 Handle<FunctionTemplateInfo> fun_data(function->shared()->get_api_func_data(),
129 isolate);
130 if (new_target->IsJSReceiver()) {
131 RETURN_RESULT_OR_FAILURE(
132 isolate, HandleApiCallHelper<true>(isolate, function, new_target,
133 fun_data, receiver, args));
134 } else {
135 RETURN_RESULT_OR_FAILURE(
136 isolate, HandleApiCallHelper<false>(isolate, function, new_target,
137 fun_data, receiver, args));
138 }
139 }
140
141 namespace {
142
143 class RelocatableArguments : public BuiltinArguments, public Relocatable {
144 public:
RelocatableArguments(Isolate * isolate,int length,Object ** arguments)145 RelocatableArguments(Isolate* isolate, int length, Object** arguments)
146 : BuiltinArguments(length, arguments), Relocatable(isolate) {}
147
IterateInstance(ObjectVisitor * v)148 virtual inline void IterateInstance(ObjectVisitor* v) {
149 if (length() == 0) return;
150 v->VisitPointers(lowest_address(), highest_address() + 1);
151 }
152
153 private:
154 DISALLOW_COPY_AND_ASSIGN(RelocatableArguments);
155 };
156
157 } // namespace
158
InvokeApiFunction(Isolate * isolate,bool is_construct,Handle<HeapObject> function,Handle<Object> receiver,int argc,Handle<Object> args[],Handle<HeapObject> new_target)159 MaybeHandle<Object> Builtins::InvokeApiFunction(Isolate* isolate,
160 bool is_construct,
161 Handle<HeapObject> function,
162 Handle<Object> receiver,
163 int argc, Handle<Object> args[],
164 Handle<HeapObject> new_target) {
165 DCHECK(function->IsFunctionTemplateInfo() ||
166 (function->IsJSFunction() &&
167 JSFunction::cast(*function)->shared()->IsApiFunction()));
168
169 // Do proper receiver conversion for non-strict mode api functions.
170 if (!is_construct && !receiver->IsJSReceiver()) {
171 if (function->IsFunctionTemplateInfo() ||
172 is_sloppy(JSFunction::cast(*function)->shared()->language_mode())) {
173 ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
174 Object::ConvertReceiver(isolate, receiver),
175 Object);
176 }
177 }
178
179 Handle<FunctionTemplateInfo> fun_data =
180 function->IsFunctionTemplateInfo()
181 ? Handle<FunctionTemplateInfo>::cast(function)
182 : handle(JSFunction::cast(*function)->shared()->get_api_func_data(),
183 isolate);
184 // Construct BuiltinArguments object:
185 // new target, function, arguments reversed, receiver.
186 const int kBufferSize = 32;
187 Object* small_argv[kBufferSize];
188 Object** argv;
189 const int frame_argc = argc + BuiltinArguments::kNumExtraArgsWithReceiver;
190 if (frame_argc <= kBufferSize) {
191 argv = small_argv;
192 } else {
193 argv = new Object*[frame_argc];
194 }
195 int cursor = frame_argc - 1;
196 argv[cursor--] = *receiver;
197 for (int i = 0; i < argc; ++i) {
198 argv[cursor--] = *args[i];
199 }
200 DCHECK(cursor == BuiltinArguments::kArgcOffset);
201 argv[BuiltinArguments::kArgcOffset] = Smi::FromInt(frame_argc);
202 argv[BuiltinArguments::kTargetOffset] = *function;
203 argv[BuiltinArguments::kNewTargetOffset] = *new_target;
204 MaybeHandle<Object> result;
205 {
206 RelocatableArguments arguments(isolate, frame_argc, &argv[frame_argc - 1]);
207 if (is_construct) {
208 result = HandleApiCallHelper<true>(isolate, function, new_target,
209 fun_data, receiver, arguments);
210 } else {
211 result = HandleApiCallHelper<false>(isolate, function, new_target,
212 fun_data, receiver, arguments);
213 }
214 }
215 if (argv != small_argv) delete[] argv;
216 return result;
217 }
218
219 // Helper function to handle calls to non-function objects created through the
220 // API. The object can be called as either a constructor (using new) or just as
221 // a function (without new).
HandleApiCallAsFunctionOrConstructor(Isolate * isolate,bool is_construct_call,BuiltinArguments args)222 MUST_USE_RESULT static Object* HandleApiCallAsFunctionOrConstructor(
223 Isolate* isolate, bool is_construct_call, BuiltinArguments args) {
224 Handle<Object> receiver = args.receiver();
225
226 // Get the object called.
227 JSObject* obj = JSObject::cast(*receiver);
228
229 // Set the new target.
230 HeapObject* new_target;
231 if (is_construct_call) {
232 // TODO(adamk): This should be passed through in args instead of
233 // being patched in here. We need to set a non-undefined value
234 // for v8::FunctionCallbackInfo::IsConstructCall() to get the
235 // right answer.
236 new_target = obj;
237 } else {
238 new_target = isolate->heap()->undefined_value();
239 }
240
241 // Get the invocation callback from the function descriptor that was
242 // used to create the called object.
243 DCHECK(obj->map()->is_callable());
244 JSFunction* constructor = JSFunction::cast(obj->map()->GetConstructor());
245 // TODO(ishell): turn this back to a DCHECK.
246 CHECK(constructor->shared()->IsApiFunction());
247 Object* handler =
248 constructor->shared()->get_api_func_data()->instance_call_handler();
249 DCHECK(!handler->IsUndefined(isolate));
250 // TODO(ishell): remove this debugging code.
251 CHECK(handler->IsCallHandlerInfo());
252 CallHandlerInfo* call_data = CallHandlerInfo::cast(handler);
253 Object* callback_obj = call_data->callback();
254 v8::FunctionCallback callback =
255 v8::ToCData<v8::FunctionCallback>(callback_obj);
256
257 // Get the data for the call and perform the callback.
258 Object* result;
259 {
260 HandleScope scope(isolate);
261 LOG(isolate, ApiObjectAccess("call non-function", obj));
262
263 FunctionCallbackArguments custom(isolate, call_data->data(), constructor,
264 obj, new_target, &args[0] - 1,
265 args.length() - 1);
266 Handle<Object> result_handle = custom.Call(callback);
267 if (result_handle.is_null()) {
268 result = isolate->heap()->undefined_value();
269 } else {
270 result = *result_handle;
271 }
272 }
273 // Check for exceptions and return result.
274 RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
275 return result;
276 }
277
278 // Handle calls to non-function objects created through the API. This delegate
279 // function is used when the call is a normal function call.
BUILTIN(HandleApiCallAsFunction)280 BUILTIN(HandleApiCallAsFunction) {
281 return HandleApiCallAsFunctionOrConstructor(isolate, false, args);
282 }
283
284 // Handle calls to non-function objects created through the API. This delegate
285 // function is used when the call is a construct call.
BUILTIN(HandleApiCallAsConstructor)286 BUILTIN(HandleApiCallAsConstructor) {
287 return HandleApiCallAsFunctionOrConstructor(isolate, true, args);
288 }
289
290 } // namespace internal
291 } // namespace v8
292