#include "src/api/api-arguments-inl.h"
#include "src/api/api-natives.h"
#include "src/base/small-vector.h"
#include "src/builtins/builtins-utils-inl.h"
#include "src/builtins/builtins.h"
#include "src/common/assert-scope.h"
#include "src/logging/log.h"
#include "src/logging/runtime-call-stats-scope.h"
#include "src/objects/objects-inl.h"
#include "src/objects/prototype.h"
#include "src/objects/templates.h"
#include "src/objects/visitors.h"
namespace v8 {
namespace internal {
namespace {
bool IsCompatibleReceiver(Isolate* isolate, Tagged<FunctionTemplateInfo> info,
Tagged<JSReceiver> receiver) {
RCS_SCOPE(isolate, RuntimeCallCounterId::kGetCompatibleReceiver);
Tagged<Object> recv_type = info->signature();
if (!IsFunctionTemplateInfo(recv_type)) return true;
if (!IsJSObject(receiver)) return false;
Tagged<JSObject> js_obj_receiver = Cast<JSObject>(receiver);
Tagged<FunctionTemplateInfo> signature =
Cast<FunctionTemplateInfo>(recv_type);
if (signature->IsTemplateFor(js_obj_receiver)) return true;
if (V8_UNLIKELY(IsJSGlobalProxy(js_obj_receiver))) {
Tagged<HeapObject> prototype = js_obj_receiver->map()->prototype();
if (!IsNull(prototype, isolate)) {
Tagged<JSObject> js_obj_prototype = Cast<JSObject>(prototype);
if (signature->IsTemplateFor(js_obj_prototype)) return true;
}
}
return false;
}
template <bool is_construct>
V8_WARN_UNUSED_RESULT MaybeHandle<Object> HandleApiCallHelper(
Isolate* isolate, DirectHandle<HeapObject> new_target,
DirectHandle<FunctionTemplateInfo> fun_data, DirectHandle<Object> receiver,
Address* argv, int argc) {
Handle<JSReceiver> js_receiver;
if (is_construct) {
DCHECK(IsTheHole(*receiver, isolate));
if (IsUndefined(fun_data->GetInstanceTemplate(), isolate)) {
v8::Local<ObjectTemplate> templ =
ObjectTemplate::New(reinterpret_cast<v8::Isolate*>(isolate),
ToApiHandle<v8::FunctionTemplate>(fun_data));
FunctionTemplateInfo::SetInstanceTemplate(
isolate, fun_data, Utils::OpenDirectHandle(*templ));
}
DirectHandle<ObjectTemplateInfo> instance_template(
Cast<ObjectTemplateInfo>(fun_data->GetInstanceTemplate()), isolate);
ASSIGN_RETURN_ON_EXCEPTION(
isolate, js_receiver,
ApiNatives::InstantiateObject(isolate, instance_template,
Cast<JSReceiver>(new_target)));
argv[BuiltinArguments::kReceiverArgsIndex] = js_receiver->ptr();
} else {
DCHECK(IsJSReceiver(*receiver));
js_receiver = indirect_handle(Cast<JSReceiver>(receiver), isolate);
if (!fun_data->accept_any_receiver() && IsAccessCheckNeeded(*js_receiver)) {
DCHECK(IsJSObject(*js_receiver));
DirectHandle<JSObject> js_object = Cast<JSObject>(js_receiver);
if (!isolate->MayAccess(isolate->native_context(), js_object)) {
RETURN_ON_EXCEPTION(isolate,
isolate->ReportFailedAccessCheck(js_object));
UNREACHABLE();
}
}
if (!IsCompatibleReceiver(isolate, *fun_data, *js_receiver)) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kIllegalInvocation));
}
}
if (fun_data->has_callback(isolate)) {
FunctionCallbackArguments custom(isolate, *fun_data, *new_target, argv,
argc);
DirectHandle<Object> result =
custom.CallOrConstruct(*fun_data, is_construct);
RETURN_EXCEPTION_IF_EXCEPTION(isolate);
if (result.is_null()) {
if (is_construct) return js_receiver;
return isolate->factory()->undefined_value();
}
{
DisallowGarbageCollection no_gc;
Tagged<Object> raw_result = *result;
DCHECK(Is<JSAny>(raw_result));
if (!is_construct || IsJSReceiver(raw_result))
return handle(raw_result, isolate);
}
}
return js_receiver;
}
}
BUILTIN(HandleApiConstruct) {
HandleScope scope(isolate);
DirectHandle<Object> receiver = args.receiver();
DirectHandle<HeapObject> new_target = args.new_target();
DCHECK(!IsUndefined(*new_target, isolate));
DirectHandle<FunctionTemplateInfo> fun_data(
args.target()->shared()->api_func_data(), isolate);
int argc = args.length() - 1;
Address* argv = args.address_of_first_argument();
RETURN_RESULT_OR_FAILURE(
isolate, HandleApiCallHelper<true>(isolate, new_target, fun_data,
receiver, argv, argc));
}
namespace {
class RelocatableArguments : public Relocatable {
public:
RelocatableArguments(Isolate* isolate, size_t length, Address* arguments)
: Relocatable(isolate), length_(length), arguments_(arguments) {
DCHECK_LT(0, length_);
}
RelocatableArguments(const RelocatableArguments&) = delete;
RelocatableArguments& operator=(const RelocatableArguments&) = delete;
inline void IterateInstance(RootVisitor* v) override {
v->VisitRootPointers(Root::kRelocatable, nullptr,
FullObjectSlot(&arguments_[0]),
FullObjectSlot(&arguments_[length_]));
}
private:
size_t length_;
Address* arguments_;
};
}
MaybeHandle<Object> Builtins::InvokeApiFunction(
Isolate* isolate, bool is_construct,
DirectHandle<FunctionTemplateInfo> function, DirectHandle<Object> receiver,
base::Vector<const DirectHandle<Object>> args,
DirectHandle<HeapObject> new_target) {
RCS_SCOPE(isolate, RuntimeCallCounterId::kInvokeApiFunction);
if (!is_construct && !IsJSReceiver(*receiver)) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, receiver,
Object::ConvertReceiver(isolate, receiver));
}
DCHECK(!Cast<FunctionTemplateInfo>(function)->BreakAtEntry(isolate));
int argc = static_cast<int>(args.size());
base::SmallVector<Address, 32> argv(argc + 1);
argv[0] = (*receiver).ptr();
for (int i = 0; i < argc; ++i) {
argv[i + 1] = (*args[i]).ptr();
}
RelocatableArguments arguments(isolate, argv.size(), argv.data());
if (is_construct) {
return HandleApiCallHelper<true>(isolate, new_target, function, receiver,
argv.data() + 1, argc);
}
return HandleApiCallHelper<false>(isolate, new_target, function, receiver,
argv.data() + 1, argc);
}
V8_WARN_UNUSED_RESULT static Tagged<Object>
HandleApiCallAsFunctionOrConstructorDelegate(Isolate* isolate,
bool is_construct_call,
BuiltinArguments args) {
DirectHandle<Object> receiver = args.receiver();
Tagged<JSObject> obj = Cast<JSObject>(*receiver);
Tagged<HeapObject> new_target;
if (is_construct_call) {
new_target = obj;
} else {
new_target = ReadOnlyRoots(isolate).undefined_value();
}
DCHECK(obj->map()->is_callable());
Tagged<JSFunction> constructor =
Cast<JSFunction>(obj->map()->GetConstructor());
DCHECK(constructor->shared()->IsApiFunction());
Tagged<Object> handler =
constructor->shared()->api_func_data()->GetInstanceCallHandler();
DCHECK(!IsUndefined(handler, isolate));
Tagged<FunctionTemplateInfo> templ = Cast<FunctionTemplateInfo>(handler);
DCHECK(templ->is_object_template_call_handler());
DCHECK(templ->has_callback(isolate));
Tagged<Object> result;
{
HandleScope scope(isolate);
FunctionCallbackArguments custom(isolate, templ, new_target,
args.address_of_first_argument(),
args.length() - 1);
DirectHandle<Object> result_handle =
custom.CallOrConstruct(templ, is_construct_call);
if (result_handle.is_null()) {
result = ReadOnlyRoots(isolate).undefined_value();
} else {
result = *result_handle;
}
RETURN_FAILURE_IF_EXCEPTION(isolate);
}
return result;
}
BUILTIN(HandleApiCallAsFunctionDelegate) {
isolate->CountUsage(v8::Isolate::UseCounterFeature::kDocumentAllLegacyCall);
return HandleApiCallAsFunctionOrConstructorDelegate(isolate, false, args);
}
BUILTIN(HandleApiCallAsConstructorDelegate) {
isolate->CountUsage(
v8::Isolate::UseCounterFeature::kDocumentAllLegacyConstruct);
return HandleApiCallAsFunctionOrConstructorDelegate(isolate, true, args);
}
}
}