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