FFFF nodejs---node/src/node_api.cc at master · bsmr/nodejs---node · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
4140 lines (3362 loc) · 132 KB

File metadata and controls

4140 lines (3362 loc) · 132 KB
const Info& _cbinfo;
CallbackBundle* _bundle;
};
class FunctionCallbackWrapper
: public CallbackWrapperBase<v8::FunctionCallbackInfo<v8::Value>,
&CallbackBundle::function_or_getter> {
public:
static void Invoke(const v8::FunctionCallbackInfo<v8::Value>& info) {
FunctionCallbackWrapper cbwrapper(info);
cbwrapper.InvokeCallback();
}
explicit FunctionCallbackWrapper(
const v8::FunctionCallbackInfo<v8::Value>& cbinfo)
: CallbackWrapperBase(cbinfo, cbinfo.Length()) {}
napi_value GetNewTarget() override {
if (_cbinfo.IsConstructCall()) {
return v8impl::JsValueFromV8LocalValue(_cbinfo.NewTarget());
} else {
return nullptr;
}
}
/*virtual*/
void Args(napi_value* buffer, size_t buffer_length) override {
size_t i = 0;
size_t min = std::min(buffer_length, _args_length);
for (; i < min; i += 1) {
buffer[i] = v8impl::JsValueFromV8LocalValue(_cbinfo[i]);
}
if (i < buffer_length) {
napi_value undefined =
v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate()));
for (; i < buffer_length; i += 1) {
buffer[i] = undefined;
}
}
}
/*virtual*/
void SetReturnValue(napi_value value) override {
v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
_cbinfo.GetReturnValue().Set(val);
}
};
class GetterCallbackWrapper
: public CallbackWrapperBase<v8::PropertyCallbackInfo<v8::Value>,
&CallbackBundle::function_or_getter> {
public:
static void Invoke(v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Value>& info) {
GetterCallbackWrapper cbwrapper(info);
cbwrapper.InvokeCallback();
}
explicit GetterCallbackWrapper(
const v8::PropertyCallbackInfo<v8::Value>& cbinfo)
: CallbackWrapperBase(cbinfo, 0) {}
/*virtual*/
void Args(napi_value* buffer, size_t buffer_length) override {
if (buffer_length > 0) {
napi_value undefined =
v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate()));
for (size_t i = 0; i < buffer_length; i += 1) {
buffer[i] = undefined;
}
}
}
/*virtual*/
void SetReturnValue(napi_value value) override {
v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
_cbinfo.GetReturnValue().Set(val);
}
};
< 6B28 div class="react-code-text react-code-line-contents" style="min-height:auto">
class SetterCallbackWrapper
: public CallbackWrapperBase<v8::PropertyCallbackInfo<void>,
&CallbackBundle::setter> {
public:
static void Invoke(v8::Local<v8::Name> property,
v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<void>& info) {
SetterCallbackWrapper cbwrapper(info, value);
cbwrapper.InvokeCallback();
}
SetterCallbackWrapper(const v8::PropertyCallbackInfo<void>& cbinfo,
const v8::Local<v8::Value>& value)
: CallbackWrapperBase(cbinfo, 1), _value(value) {}
/*virtual*/
void Args(napi_value* buffer, size_t buffer_length) override {
if (buffer_length > 0) {
buffer[0] = v8impl::JsValueFromV8LocalValue(_value);
if (buffer_length > 1) {
napi_value undefined = v8impl::JsValueFromV8LocalValue(
v8::Undefined(_cbinfo.GetIsolate()));
for (size_t i = 1; i < buffer_length; i += 1) {
buffer[i] = undefined;
}
}
}
}
/*virtual*/
void SetReturnValue(napi_value value) override {
// Ignore any value returned from a setter callback.
}
private:
const v8::Local<v8::Value>& _value;
};
// Creates an object to be made available to the static function callback
// wrapper, used to retrieve the native callback function and data pointer.
static
v8::Local<v8::Value> CreateFunctionCallbackData(napi_env env,
napi_callback cb,
void* data) {
CallbackBundle* bundle = new CallbackBundle();
bundle->function_or_getter = cb;
bundle->cb_data = data;
bundle->env = env;
v8::Local<v8::Value> cbdata = v8::External::New(env->isolate, bundle);
bundle->BindLifecycleTo(env->isolate, cbdata);
return cbdata;
}
// Creates an object to be made available to the static getter/setter
// callback wrapper, used to retrieve the native getter/setter callback
// function and data pointer.
static
v8::Local<v8::Value> CreateAccessorCallbackData(napi_env env,
napi_callback getter,
napi_callback setter,
void* data) {
CallbackBundle* bundle = new CallbackBundle();
bundle->function_or_getter = getter;
bundle->setter = setter;
bundle->cb_data = data;
bundle->env = env;
v8::Local<v8::Value> cbdata = v8::External::New(env->isolate, bundle);
bundle->BindLifecycleTo(env->isolate, cbdata);
return cbdata;
}
static void DeleteEnv(napi_env env, void* data, void* hint) {
delete env;
}
static
napi_env GetEnv(v8::Local<v8::Context> context) {
napi_env result;
auto isolate = context->GetIsolate();
auto global = context->Global();
// In the case of the string for which we grab the private and the value of
// the private on the global object we can call .ToLocalChecked() directly
// because we need to stop hard if either of them is empty.
//
// Re https://github.com/nodejs/node/pull/14217#discussion_r128775149
auto value = global->GetPrivate(context, NAPI_PRIVATE_KEY(context, env))
.ToLocalChecked();
if (value->IsExternal()) {
result = static_cast<napi_env>(value.As<v8::External>()->Value());
} else {
result = new napi_env__(isolate, node::GetCurrentEventLoop(isolate));
auto external = v8::External::New(isolate, result);
// We must also stop hard if the result of assigning the env to the global
// is either nothing or false.
CHECK(global->SetPrivate(context, NAPI_PRIVATE_KEY(context, env), external)
.FromJust());
// Create a self-destructing reference to external that will get rid of the
// napi_env when external goes out of scope.
Reference::New(result, external, 0, true, DeleteEnv, nullptr, nullptr);
}
return result;
}
enum UnwrapAction {
KeepWrap,
RemoveWrap
};
static
napi_status Unwrap(napi_env env,
napi_value js_object,
void** result,
UnwrapAction action) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, js_object);
if (action == KeepWrap) {
CHECK_ARG(env, result);
}
v8::Isolate* isolate = env->isolate;
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(js_object);
RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg);
v8::Local<v8::Object> obj = value.As<v8::Object>();
auto val = obj->GetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper))
.ToLocalChecked();
RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg);
Reference* reference =
static_cast<v8impl::Reference*>(val.As<v8::External>()->Value());
if (result) {
*result = reference->Data();
}
if (action == RemoveWrap) {
CHECK(obj->DeletePrivate(context, NAPI_PRIVATE_KEY(context, wrapper))
.FromJust());
Reference::Delete(reference);
}
return GET_RETURN_STATUS(env);
}
static
napi_status ConcludeDeferred(napi_env env,
napi_deferred deferred,
napi_value result,
bool is_resolved) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, result);
v8::Local<v8::Context> context = env->isolate->GetCurrentContext();
node::Persistent<v8::Value>* deferred_ref =
NodePersistentFromJsDeferred(deferred);
v8::Local<v8::Value> v8_deferred =
v8::Local<v8::Value>::New(env->isolate, *deferred_ref);
auto v8_resolver = v8::Local<v8::Promise::Resolver>::Cast(v8_deferred);
v8::Maybe<bool> success = is_resolved ?
v8_resolver->Resolve(context, v8impl::V8LocalValueFromJsValue(result)) :
v8_resolver->Reject(context, v8impl::V8LocalValueFromJsValue(result));
delete deferred_ref;
RETURN_STATUS_IF_FALSE(env, success.FromMaybe(false), napi_generic_failure);
return GET_RETURN_STATUS(env);
}
class ThreadSafeFunction : public node::AsyncResource {
public:
ThreadSafeFunction(v8::Local<v8::Function> func,
v8::Local<v8::Object> resource,
v8::Local<v8::String> name,
size_t thread_count_,
void* context_,
size_t max_queue_size_,
napi_env env_,
void* finalize_data_,
napi_finalize finalize_cb_,
napi_threadsafe_function_call_js call_js_cb_):
AsyncResource(env_->isolate,
resource,
*v8::String::Utf8Value(env_->isolate, name)),
thread_count(thread_count_),
is_closing(false),
context(context_),
max_queue_size(max_queue_size_),
env(env_),
finalize_data(finalize_data_),
finalize_cb(finalize_cb_),
call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_),
handles_closing(false) {
ref.Reset(env->isolate, func);
node::AddEnvironmentCleanupHook(env->isolate, Cleanup, this);
}
~ThreadSafeFunction() {
node::RemoveEnvironmentCleanupHook(env->isolate, Cleanup, this);
}
// These methods can be called from any thread.
napi_status Push(void* data, napi_threadsafe_function_call_mode mode) {
node::Mutex::ScopedLock lock(this->mutex);
while (queue.size() >= max_queue_size &&
max_queue_size > 0 &&
!is_closing) {
if (mode == napi_tsfn_nonblocking) {
return napi_queue_full;
}
cond->Wait(lock);
}
if (is_closing) {
if (thread_count == 0) {
return napi_invalid_arg;
} else {
thread_count--;
return napi_closing;
}
} else {
if (uv_async_send(&async) != 0) {
return napi_generic_failure;
}
queue.push(data);
return napi_ok;
}
}
napi_status Acquire() {
node::Mutex::ScopedLock lock(this->mutex);
if (is_closing) {
return napi_closing;
}
thread_count++;
return napi_ok;
}
napi_status Release(napi_threadsafe_function_release_mode mode) {
node::Mutex::ScopedLock lock(this->mutex);
if (thread_count == 0) {
return napi_invalid_arg;
}
thread_count--;
if (thread_count == 0 || mode == napi_tsfn_abort) {
if (!is_closing) {
is_closing = (mode == napi_tsfn_abort);
if (is_closing && max_queue_size > 0) {
cond->Signal(lock);
}
if (uv_async_send(&async) != 0) {
return napi_generic_failure;
}
}
}
return napi_ok;
}
void EmptyQueueAndDelete() {
for (; !queue.empty() ; queue.pop()) {
call_js_cb(nullptr, nullptr, context, queue.front());
}
delete this;
}
// These methods must only be called from the loop thread.
napi_status Init() {
ThreadSafeFunction* ts_fn = this;
if (uv_async_init(env->loop, &async, AsyncCb) == 0) {
if (max_queue_size > 0) {
cond.reset(new node::ConditionVariable);
}
if ((max_queue_size == 0 || cond.get() != nullptr) &&
uv_idle_init(env->loop, &idle) == 0) {
return napi_ok;
}
node::Environment::GetCurrent(env->isolate)->CloseHandle(
reinterpret_cast<uv_handle_t*>(&async),
[](uv_handle_t* handle) -> void {
ThreadSafeFunction* ts_fn =
node::ContainerOf(&ThreadSafeFunction::async,
reinterpret_cast<uv_async_t*>(handle));
delete ts_fn;
});
// Prevent the thread-safe function from being deleted here, because
// the callback above will delete it.
ts_fn = nullptr;
}
delete ts_fn;
return napi_generic_failure;
}
napi_status Unref() {
uv_unref(reinterpret_cast<uv_handle_t*>(&async));
uv_unref(reinterpret_cast<uv_handle_t*>(&idle));
return napi_ok;
}
napi_status Ref() {
uv_ref(reinterpret_cast<uv_handle_t*>(&async));
uv_ref(reinterpret_cast<uv_handle_t*>(&idle));
return napi_ok;
}
void DispatchOne() {
void* data = nullptr;
bool popped_value = false;
bool idle_stop_failed = false;
{
node::Mutex::ScopedLock lock(this->mutex);
if (is_closing) {
CloseHandlesAndMaybeDelete();
} else {
size_t size = queue.size();
if (size > 0) {
data = queue.front();
queue.pop();
popped_value = true;
if (size == max_queue_size && max_queue_size > 0) {
cond->Signal(lock);
}
size--;
0