From 8432d6596f5a1a45373da57eb9ded6b89bd1539b Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 13 Jul 2022 05:03:36 +0200 Subject: [PATCH] src: slim down env-inl.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move big and/or infrequently used functions from env-inl.h to env.cc to speed up build times and reduce binary bloat. This commit also touches async_wrap-inl.h and base_object-inl.h because those are closely interwined with env-inl.h. Non-functional change. Refs: https://github.com/nodejs/node/issues/43712 PR-URL: https://github.com/nodejs/node/pull/43745 Reviewed-By: Tobias Nießen Reviewed-By: Colin Ihrig Reviewed-By: Darshan Sen Reviewed-By: Richard Lau --- src/async_wrap-inl.h | 7 - src/base_object-inl.h | 104 --------- src/base_object.h | 16 +- src/env-inl.h | 389 --------------------------------- src/env.cc | 492 ++++++++++++++++++++++++++++++++++++++++++ src/env.h | 99 +++++---- 6 files changed, 548 insertions(+), 559 deletions(-) diff --git a/src/async_wrap-inl.h b/src/async_wrap-inl.h index 03745081f3b09b..08f305da8a3192 100644 --- a/src/async_wrap-inl.h +++ b/src/async_wrap-inl.h @@ -80,13 +80,6 @@ inline v8::MaybeLocal AsyncWrap::MakeCallback( return MakeCallback(cb_v.As(), argc, argv); } - -// Defined here to avoid a circular dependency with env-inl.h. -inline AsyncHooks::DefaultTriggerAsyncIdScope ::DefaultTriggerAsyncIdScope( - AsyncWrap* async_wrap) - : DefaultTriggerAsyncIdScope(async_wrap->env(), - async_wrap->get_async_id()) {} - } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/base_object-inl.h b/src/base_object-inl.h index bb1e8d4b46bce3..ff618f08b5d394 100644 --- a/src/base_object-inl.h +++ b/src/base_object-inl.h @@ -32,40 +32,6 @@ namespace node { -BaseObject::BaseObject(Environment* env, v8::Local object) - : persistent_handle_(env->isolate(), object), env_(env) { - CHECK_EQ(false, object.IsEmpty()); - CHECK_GT(object->InternalFieldCount(), 0); - object->SetAlignedPointerInInternalField( - BaseObject::kSlot, - static_cast(this)); - env->AddCleanupHook(DeleteMe, static_cast(this)); - env->modify_base_object_count(1); -} - -BaseObject::~BaseObject() { - env()->modify_base_object_count(-1); - env()->RemoveCleanupHook(DeleteMe, static_cast(this)); - - if (UNLIKELY(has_pointer_data())) { - PointerData* metadata = pointer_data(); - CHECK_EQ(metadata->strong_ptr_count, 0); - metadata->self = nullptr; - if (metadata->weak_ptr_count == 0) - delete metadata; - } - - if (persistent_handle_.IsEmpty()) { - // This most likely happened because the weak callback below cleared it. - return; - } - - { - v8::HandleScope handle_scope(env()->isolate()); - object()->SetAlignedPointerInInternalField(BaseObject::kSlot, nullptr); - } -} - void BaseObject::Detach() { CHECK_GT(pointer_data()->strong_ptr_count, 0); pointer_data()->is_detached = true; @@ -107,28 +73,6 @@ T* BaseObject::FromJSObject(v8::Local object) { return static_cast(FromJSObject(object)); } - -void BaseObject::MakeWeak() { - if (has_pointer_data()) { - pointer_data()->wants_weak_jsobj = true; - if (pointer_data()->strong_ptr_count > 0) return; - } - - persistent_handle_.SetWeak( - this, - [](const v8::WeakCallbackInfo& data) { - BaseObject* obj = data.GetParameter(); - // Clear the persistent handle so that ~BaseObject() doesn't attempt - // to mess with internal fields, since the JS object may have - // transitioned into an invalid state. - // Refs: https://github.com/nodejs/node/issues/18897 - obj->persistent_handle_.Reset(); - CHECK_IMPLIES(obj->has_pointer_data(), - obj->pointer_data()->strong_ptr_count == 0); - obj->OnGCCollect(); - }, v8::WeakCallbackType::kParameter); -} - void BaseObject::OnGCCollect() { delete this; } @@ -148,23 +92,6 @@ bool BaseObject::IsWeakOrDetached() const { return pd->wants_weak_jsobj || pd->is_detached; } -void BaseObject::LazilyInitializedJSTemplateConstructor( - const v8::FunctionCallbackInfo& args) { - DCHECK(args.IsConstructCall()); - DCHECK_GT(args.This()->InternalFieldCount(), 0); - args.This()->SetAlignedPointerInInternalField(BaseObject::kSlot, nullptr); -} - -v8::Local -BaseObject::MakeLazilyInitializedJSTemplate(Environment* env) { - v8::Local t = - env->NewFunctionTemplate(LazilyInitializedJSTemplateConstructor); - t->Inherit(BaseObject::GetConstructorTemplate(env)); - t->InstanceTemplate()->SetInternalFieldCount( - BaseObject::kInternalFieldCount); - return t; -} - template void BaseObject::InternalFieldGet( v8::Local property, @@ -185,37 +112,6 @@ bool BaseObject::has_pointer_data() const { return pointer_data_ != nullptr; } -BaseObject::PointerData* BaseObject::pointer_data() { - if (!has_pointer_data()) { - PointerData* metadata = new PointerData(); - metadata->wants_weak_jsobj = persistent_handle_.IsWeak(); - metadata->self = this; - pointer_data_ = metadata; - } - CHECK(has_pointer_data()); - return pointer_data_; -} - -void BaseObject::decrease_refcount() { - CHECK(has_pointer_data()); - PointerData* metadata = pointer_data(); - CHECK_GT(metadata->strong_ptr_count, 0); - unsigned int new_refcount = --metadata->strong_ptr_count; - if (new_refcount == 0) { - if (metadata->is_detached) { - OnGCCollect(); - } else if (metadata->wants_weak_jsobj && !persistent_handle_.IsEmpty()) { - MakeWeak(); - } - } -} - -void BaseObject::increase_refcount() { - unsigned int prev_refcount = pointer_data()->strong_ptr_count++; - if (prev_refcount == 0 && !persistent_handle_.IsEmpty()) - persistent_handle_.ClearWeak(); -} - template BaseObject::PointerData* BaseObjectPtrImpl::pointer_data() const { diff --git a/src/base_object.h b/src/base_object.h index 1c63da92fd80c0..842f763a56d75c 100644 --- a/src/base_object.h +++ b/src/base_object.h @@ -44,8 +44,8 @@ class BaseObject : public MemoryRetainer { // Associates this object with `object`. It uses the 0th internal field for // that, and in particular aborts if there is no such field. - inline BaseObject(Environment* env, v8::Local object); - inline ~BaseObject() override; + BaseObject(Environment* env, v8::Local object); + ~BaseObject() override; BaseObject() = delete; @@ -65,7 +65,7 @@ class BaseObject : public MemoryRetainer { // was also passed to the `BaseObject()` constructor initially. // This may return `nullptr` if the C++ object has not been constructed yet, // e.g. when the JS object used `MakeLazilyInitializedJSTemplate`. - static inline void LazilyInitializedJSTemplateConstructor( + static void LazilyInitializedJSTemplateConstructor( const v8::FunctionCallbackInfo& args); static inline BaseObject* FromJSObject(v8::Local object); template @@ -74,7 +74,7 @@ class BaseObject : public MemoryRetainer { // Make the `v8::Global` a weak reference and, `delete` this object once // the JS object has been garbage collected and there are no (strong) // BaseObjectPtr references to it. - inline void MakeWeak(); + void MakeWeak(); // Undo `MakeWeak()`, i.e. turn this into a strong reference that is a GC // root and will not be touched by the garbage collector. @@ -88,7 +88,7 @@ class BaseObject : public MemoryRetainer { // Utility to create a FunctionTemplate with one internal field (used for // the `BaseObject*` pointer) and a constructor that initializes that field // to `nullptr`. - static inline v8::Local MakeLazilyInitializedJSTemplate( + static v8::Local MakeLazilyInitializedJSTemplate( Environment* env); // Setter/Getter pair for internal fields that can be passed to SetAccessor. @@ -202,11 +202,11 @@ class BaseObject : public MemoryRetainer { inline bool has_pointer_data() const; // This creates a PointerData struct if none was associated with this // BaseObject before. - inline PointerData* pointer_data(); + PointerData* pointer_data(); // Functions that adjust the strong pointer count. - inline void decrease_refcount(); - inline void increase_refcount(); + void decrease_refcount(); + void increase_refcount(); Environment* env_; PointerData* pointer_data_ = nullptr; diff --git a/src/env-inl.h b/src/env-inl.h index fdc4f1f7ba4c16..084cb9f83c51af 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -33,7 +33,6 @@ #include "node_perf_common.h" #include "util-inl.h" #include "uv.h" -#include "v8-fast-api-calls.h" #include "v8.h" #include @@ -107,24 +106,6 @@ v8::Local AsyncHooks::native_execution_async_resource(size_t i) { return native_execution_async_resources_[i]; } -inline void AsyncHooks::SetJSPromiseHooks(v8::Local init, - v8::Local before, - v8::Local after, - v8::Local resolve) { - js_promise_hooks_[0].Reset(env()->isolate(), init); - js_promise_hooks_[1].Reset(env()->isolate(), before); - js_promise_hooks_[2].Reset(env()->isolate(), after); - js_promise_hooks_[3].Reset(env()->isolate(), resolve); - for (auto it = contexts_.begin(); it != contexts_.end(); it++) { - if (it->IsEmpty()) { - contexts_.erase(it--); - continue; - } - PersistentToLocal::Weak(env()->isolate(), *it) - ->SetPromiseHooks(init, before, after, resolve); - } -} - inline v8::Local AsyncHooks::provider_string(int idx) { return env()->isolate_data()->async_wrap_provider(idx); } @@ -137,163 +118,6 @@ inline Environment* AsyncHooks::env() { return Environment::ForAsyncHooks(this); } -// Remember to keep this code aligned with pushAsyncContext() in JS. -inline void AsyncHooks::push_async_context(double async_id, - double trigger_async_id, - v8::Local resource) { - // Since async_hooks is experimental, do only perform the check - // when async_hooks is enabled. - if (fields_[kCheck] > 0) { - CHECK_GE(async_id, -1); - CHECK_GE(trigger_async_id, -1); - } - - uint32_t offset = fields_[kStackLength]; - if (offset * 2 >= async_ids_stack_.Length()) - grow_async_ids_stack(); - async_ids_stack_[2 * offset] = async_id_fields_[kExecutionAsyncId]; - async_ids_stack_[2 * offset + 1] = async_id_fields_[kTriggerAsyncId]; - fields_[kStackLength] += 1; - async_id_fields_[kExecutionAsyncId] = async_id; - async_id_fields_[kTriggerAsyncId] = trigger_async_id; - -#ifdef DEBUG - for (uint32_t i = offset; i < native_execution_async_resources_.size(); i++) - CHECK(native_execution_async_resources_[i].IsEmpty()); -#endif - - // When this call comes from JS (as a way of increasing the stack size), - // `resource` will be empty, because JS caches these values anyway. - if (!resource.IsEmpty()) { - native_execution_async_resources_.resize(offset + 1); - // Caveat: This is a v8::Local<> assignment, we do not keep a v8::Global<>! - native_execution_async_resources_[offset] = resource; - } -} - -// Remember to keep this code aligned with popAsyncContext() in JS. -inline bool AsyncHooks::pop_async_context(double async_id) { - // In case of an exception then this may have already been reset, if the - // stack was multiple MakeCallback()'s deep. - if (UNLIKELY(fields_[kStackLength] == 0)) return false; - - // Ask for the async_id to be restored as a check that the stack - // hasn't been corrupted. - if (UNLIKELY(fields_[kCheck] > 0 && - async_id_fields_[kExecutionAsyncId] != async_id)) { - FailWithCorruptedAsyncStack(async_id); - } - - uint32_t offset = fields_[kStackLength] - 1; - async_id_fields_[kExecutionAsyncId] = async_ids_stack_[2 * offset]; - async_id_fields_[kTriggerAsyncId] = async_ids_stack_[2 * offset + 1]; - fields_[kStackLength] = offset; - - if (LIKELY(offset < native_execution_async_resources_.size() && - !native_execution_async_resources_[offset].IsEmpty())) { -#ifdef DEBUG - for (uint32_t i = offset + 1; - i < native_execution_async_resources_.size(); - i++) { - CHECK(native_execution_async_resources_[i].IsEmpty()); - } -#endif - native_execution_async_resources_.resize(offset); - if (native_execution_async_resources_.size() < - native_execution_async_resources_.capacity() / 2 && - native_execution_async_resources_.size() > 16) { - native_execution_async_resources_.shrink_to_fit(); - } - } - - if (UNLIKELY(js_execution_async_resources()->Length() > offset)) { - v8::HandleScope handle_scope(env()->isolate()); - USE(js_execution_async_resources()->Set( - env()->context(), - env()->length_string(), - v8::Integer::NewFromUnsigned(env()->isolate(), offset))); - } - - return fields_[kStackLength] > 0; -} - -void AsyncHooks::clear_async_id_stack() { - v8::Isolate* isolate = env()->isolate(); - v8::HandleScope handle_scope(isolate); - if (!js_execution_async_resources_.IsEmpty()) { - USE(PersistentToLocal::Strong(js_execution_async_resources_)->Set( - env()->context(), - env()->length_string(), - v8::Integer::NewFromUnsigned(isolate, 0))); - } - native_execution_async_resources_.clear(); - native_execution_async_resources_.shrink_to_fit(); - - async_id_fields_[kExecutionAsyncId] = 0; - async_id_fields_[kTriggerAsyncId] = 0; - fields_[kStackLength] = 0; -} - -inline void AsyncHooks::AddContext(v8::Local ctx) { - ctx->SetPromiseHooks( - js_promise_hooks_[0].IsEmpty() ? - v8::Local() : - PersistentToLocal::Strong(js_promise_hooks_[0]), - js_promise_hooks_[1].IsEmpty() ? - v8::Local() : - PersistentToLocal::Strong(js_promise_hooks_[1]), - js_promise_hooks_[2].IsEmpty() ? - v8::Local() : - PersistentToLocal::Strong(js_promise_hooks_[2]), - js_promise_hooks_[3].IsEmpty() ? - v8::Local() : - PersistentToLocal::Strong(js_promise_hooks_[3])); - - size_t id = contexts_.size(); - contexts_.resize(id + 1); - contexts_[id].Reset(env()->isolate(), ctx); - contexts_[id].SetWeak(); -} - -inline void AsyncHooks::RemoveContext(v8::Local ctx) { - v8::Isolate* isolate = env()->isolate(); - v8::HandleScope handle_scope(isolate); - contexts_.erase(std::remove_if(contexts_.begin(), - contexts_.end(), - [&](auto&& el) { return el.IsEmpty(); }), - contexts_.end()); - for (auto it = contexts_.begin(); it != contexts_.end(); it++) { - v8::Local saved_context = - PersistentToLocal::Weak(isolate, *it); - if (saved_context == ctx) { - it->Reset(); - contexts_.erase(it); - break; - } - } -} - -// The DefaultTriggerAsyncIdScope(AsyncWrap*) constructor is defined in -// async_wrap-inl.h to avoid a circular dependency. - -inline AsyncHooks::DefaultTriggerAsyncIdScope ::DefaultTriggerAsyncIdScope( - Environment* env, double default_trigger_async_id) - : async_hooks_(env->async_hooks()) { - if (env->async_hooks()->fields()[AsyncHooks::kCheck] > 0) { - CHECK_GE(default_trigger_async_id, 0); - } - - old_default_trigger_async_id_ = - async_hooks_->async_id_fields()[AsyncHooks::kDefaultTriggerAsyncId]; - async_hooks_->async_id_fields()[AsyncHooks::kDefaultTriggerAsyncId] = - default_trigger_async_id; -} - -inline AsyncHooks::DefaultTriggerAsyncIdScope ::~DefaultTriggerAsyncIdScope() { - async_hooks_->async_id_fields()[AsyncHooks::kDefaultTriggerAsyncId] = - old_default_trigger_async_id_; -} - Environment* Environment::ForAsyncHooks(AsyncHooks* hooks) { return ContainerOf(&Environment::async_hooks_, hooks); } @@ -346,24 +170,6 @@ inline bool TickInfo::has_rejection_to_warn() const { return fields_[kHasRejectionToWarn] == 1; } -inline void Environment::AssignToContext(v8::Local context, - const ContextInfo& info) { - context->SetAlignedPointerInEmbedderData( - ContextEmbedderIndex::kEnvironment, this); - // Used by Environment::GetCurrent to know that we are on a node context. - context->SetAlignedPointerInEmbedderData( - ContextEmbedderIndex::kContextTag, Environment::kNodeContextTagPtr); - // Used to retrieve bindings - context->SetAlignedPointerInEmbedderData( - ContextEmbedderIndex::kBindingListIndex, &(this->bindings_)); - -#if HAVE_INSPECTOR - inspector_agent()->ContextCreated(context, info); -#endif // HAVE_INSPECTOR - - this->async_hooks()->AddContext(context); -} - inline Environment* Environment::GetCurrent(v8::Isolate* isolate) { if (UNLIKELY(!isolate->InContext())) return nullptr; v8::HandleScope handle_scope(isolate); @@ -507,16 +313,6 @@ inline uv_loop_t* Environment::event_loop() const { return isolate_data()->event_loop(); } -inline void Environment::TryLoadAddon( - const char* filename, - int flags, - const std::function& was_loaded) { - loaded_addons_.emplace_back(filename, flags); - if (!was_loaded(&loaded_addons_.back())) { - loaded_addons_.pop_back(); - } -} - #if HAVE_INSPECTOR inline bool Environment::is_in_inspector_console_call() const { return is_in_inspector_console_call_; @@ -668,22 +464,6 @@ inline const std::string& Environment::exec_path() const { return exec_path_; } -inline std::string Environment::GetCwd() { - char cwd[PATH_MAX_BYTES]; - size_t size = PATH_MAX_BYTES; - const int err = uv_cwd(cwd, &size); - - if (err == 0) { - CHECK_GT(size, 0); - return cwd; - } - - // This can fail if the cwd is deleted. In that case, fall back to - // exec_path. - const std::string& exec_path = exec_path_; - return exec_path.substr(0, exec_path.find_last_of(kPathSeparator)); -} - #if HAVE_INSPECTOR inline void Environment::set_coverage_directory(const char* dir) { coverage_directory_ = std::string(dir); @@ -938,15 +718,6 @@ inline void Environment::ForEachWorker(Fn&& iterator) { for (worker::Worker* w : sub_worker_contexts_) iterator(w); } -inline void Environment::add_refs(int64_t diff) { - task_queues_async_refs_ += diff; - CHECK_GE(task_queues_async_refs_, 0); - if (task_queues_async_refs_ == 0) - uv_unref(reinterpret_cast(&task_queues_async_)); - else - uv_ref(reinterpret_cast(&task_queues_async_)); -} - inline bool Environment::is_stopping() const { return is_stopping_.load(); } @@ -981,28 +752,6 @@ inline IsolateData* Environment::isolate_data() const { return isolate_data_; } -inline uv_buf_t Environment::allocate_managed_buffer( - const size_t suggested_size) { - NoArrayBufferZeroFillScope no_zero_fill_scope(isolate_data()); - std::unique_ptr bs = - v8::ArrayBuffer::NewBackingStore(isolate(), suggested_size); - uv_buf_t buf = uv_buf_init(static_cast(bs->Data()), bs->ByteLength()); - released_allocated_buffers_.emplace(buf.base, std::move(bs)); - return buf; -} - -inline std::unique_ptr Environment::release_managed_buffer( - const uv_buf_t& buf) { - std::unique_ptr bs; - if (buf.base != nullptr) { - auto it = released_allocated_buffers_.find(buf.base); - CHECK_NE(it, released_allocated_buffers_.end()); - bs = std::move(it->second); - released_allocated_buffers_.erase(it); - } - return bs; -} - inline void Environment::ThrowError(const char* errmsg) { ThrowError(v8::Exception::Error, errmsg); } @@ -1039,144 +788,6 @@ inline void Environment::ThrowUVException(int errorno, UVException(isolate(), errorno, syscall, message, path, dest)); } -inline v8::Local Environment::NewFunctionTemplate( - v8::FunctionCallback callback, - v8::Local signature, - v8::ConstructorBehavior behavior, - v8::SideEffectType side_effect_type, - const v8::CFunction* c_function) { - return v8::FunctionTemplate::New(isolate(), - callback, - v8::Local(), - signature, - 0, - behavior, - side_effect_type, - c_function); -} - -inline void Environment::SetMethod(v8::Local that, - const char* name, - v8::FunctionCallback callback) { - v8::Local context = isolate()->GetCurrentContext(); - v8::Local function = - NewFunctionTemplate(callback, v8::Local(), - v8::ConstructorBehavior::kThrow, - v8::SideEffectType::kHasSideEffect) - ->GetFunction(context) - .ToLocalChecked(); - // kInternalized strings are created in the old space. - const v8::NewStringType type = v8::NewStringType::kInternalized; - v8::Local name_string = - v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); - that->Set(context, name_string, function).Check(); - function->SetName(name_string); // NODE_SET_METHOD() compatibility. -} - -inline void Environment::SetFastMethod(v8::Local that, - const char* name, - v8::FunctionCallback slow_callback, - const v8::CFunction* c_function) { - v8::Local context = isolate()->GetCurrentContext(); - v8::Local function = - NewFunctionTemplate(slow_callback, - v8::Local(), - v8::ConstructorBehavior::kThrow, - v8::SideEffectType::kHasNoSideEffect, - c_function) - ->GetFunction(context) - .ToLocalChecked(); - const v8::NewStringType type = v8::NewStringType::kInternalized; - v8::Local name_string = - v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); - that->Set(context, name_string, function).Check(); -} - -inline void Environment::SetMethodNoSideEffect(v8::Local that, - const char* name, - v8::FunctionCallback callback) { - v8::Local context = isolate()->GetCurrentContext(); - v8::Local function = - NewFunctionTemplate(callback, v8::Local(), - v8::ConstructorBehavior::kThrow, - v8::SideEffectType::kHasNoSideEffect) - ->GetFunction(context) - .ToLocalChecked(); - // kInternalized strings are created in the old space. - const v8::NewStringType type = v8::NewStringType::kInternalized; - v8::Local name_string = - v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); - that->Set(context, name_string, function).Check(); - function->SetName(name_string); // NODE_SET_METHOD() compatibility. -} - -inline void Environment::SetProtoMethod(v8::Local that, - const char* name, - v8::FunctionCallback callback) { - v8::Local signature = v8::Signature::New(isolate(), that); - v8::Local t = - NewFunctionTemplate(callback, signature, v8::ConstructorBehavior::kThrow, - v8::SideEffectType::kHasSideEffect); - // kInternalized strings are created in the old space. - const v8::NewStringType type = v8::NewStringType::kInternalized; - v8::Local name_string = - v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); - that->PrototypeTemplate()->Set(name_string, t); - t->SetClassName(name_string); // NODE_SET_PROTOTYPE_METHOD() compatibility. -} - -inline void Environment::SetProtoMethodNoSideEffect( - v8::Local that, - const char* name, - v8::FunctionCallback callback) { - v8::Local signature = v8::Signature::New(isolate(), that); - v8::Local t = - NewFunctionTemplate(callback, signature, v8::ConstructorBehavior::kThrow, - v8::SideEffectType::kHasNoSideEffect); - // kInternalized strings are created in the old space. - const v8::NewStringType type = v8::NewStringType::kInternalized; - v8::Local name_string = - v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); - that->PrototypeTemplate()->Set(name_string, t); - t->SetClassName(name_string); // NODE_SET_PROTOTYPE_METHOD() compatibility. -} - -inline void Environment::SetInstanceMethod(v8::Local that, - const char* name, - v8::FunctionCallback callback) { - v8::Local signature = v8::Signature::New(isolate(), that); - v8::Local t = - NewFunctionTemplate(callback, signature, v8::ConstructorBehavior::kThrow, - v8::SideEffectType::kHasSideEffect); - // kInternalized strings are created in the old space. - const v8::NewStringType type = v8::NewStringType::kInternalized; - v8::Local name_string = - v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); - that->InstanceTemplate()->Set(name_string, t); - t->SetClassName(name_string); -} - -inline void Environment::SetConstructorFunction( - v8::Local that, - const char* name, - v8::Local tmpl, - SetConstructorFunctionFlag flag) { - SetConstructorFunction(that, OneByteString(isolate(), name), tmpl, flag); -} - -inline void Environment::SetConstructorFunction( - v8::Local that, - v8::Local name, - v8::Local tmpl, - SetConstructorFunctionFlag flag) { - if (LIKELY(flag == SetConstructorFunctionFlag::SET_CLASS_NAME)) - tmpl->SetClassName(name); - that->Set( - context(), - name, - tmpl->GetFunction(context()).ToLocalChecked()).Check(); -} - void Environment::AddCleanupHook(CleanupCallback fn, void* arg) { auto insertion_info = cleanup_hooks_.emplace(CleanupHookCallback { fn, arg, cleanup_hook_counter_++ diff --git a/src/env.cc b/src/env.cc index 65467587ffcdd8..d078491c356d19 100644 --- a/src/env.cc +++ b/src/env.cc @@ -36,6 +36,7 @@ using v8::Context; using v8::EmbedderGraph; using v8::EscapableHandleScope; using v8::Function; +using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; using v8::HeapSpaceStatistics; @@ -56,12 +57,187 @@ using v8::TracingController; using v8::TryCatch; using v8::Undefined; using v8::Value; +using v8::WeakCallbackInfo; +using v8::WeakCallbackType; using worker::Worker; int const Environment::kNodeContextTag = 0x6e6f64; void* const Environment::kNodeContextTagPtr = const_cast( static_cast(&Environment::kNodeContextTag)); +void AsyncHooks::SetJSPromiseHooks(Local init, + Local before, + Local after, + Local resolve) { + js_promise_hooks_[0].Reset(env()->isolate(), init); + js_promise_hooks_[1].Reset(env()->isolate(), before); + js_promise_hooks_[2].Reset(env()->isolate(), after); + js_promise_hooks_[3].Reset(env()->isolate(), resolve); + for (auto it = contexts_.begin(); it != contexts_.end(); it++) { + if (it->IsEmpty()) { + contexts_.erase(it--); + continue; + } + PersistentToLocal::Weak(env()->isolate(), *it) + ->SetPromiseHooks(init, before, after, resolve); + } +} + +// Remember to keep this code aligned with pushAsyncContext() in JS. +void AsyncHooks::push_async_context(double async_id, + double trigger_async_id, + Local resource) { + // Since async_hooks is experimental, do only perform the check + // when async_hooks is enabled. + if (fields_[kCheck] > 0) { + CHECK_GE(async_id, -1); + CHECK_GE(trigger_async_id, -1); + } + + uint32_t offset = fields_[kStackLength]; + if (offset * 2 >= async_ids_stack_.Length()) grow_async_ids_stack(); + async_ids_stack_[2 * offset] = async_id_fields_[kExecutionAsyncId]; + async_ids_stack_[2 * offset + 1] = async_id_fields_[kTriggerAsyncId]; + fields_[kStackLength] += 1; + async_id_fields_[kExecutionAsyncId] = async_id; + async_id_fields_[kTriggerAsyncId] = trigger_async_id; + +#ifdef DEBUG + for (uint32_t i = offset; i < native_execution_async_resources_.size(); i++) + CHECK(native_execution_async_resources_[i].IsEmpty()); +#endif + + // When this call comes from JS (as a way of increasing the stack size), + // `resource` will be empty, because JS caches these values anyway. + if (!resource.IsEmpty()) { + native_execution_async_resources_.resize(offset + 1); + // Caveat: This is a v8::Local<> assignment, we do not keep a v8::Global<>! + native_execution_async_resources_[offset] = resource; + } +} + +// Remember to keep this code aligned with popAsyncContext() in JS. +bool AsyncHooks::pop_async_context(double async_id) { + // In case of an exception then this may have already been reset, if the + // stack was multiple MakeCallback()'s deep. + if (UNLIKELY(fields_[kStackLength] == 0)) return false; + + // Ask for the async_id to be restored as a check that the stack + // hasn't been corrupted. + if (UNLIKELY(fields_[kCheck] > 0 && + async_id_fields_[kExecutionAsyncId] != async_id)) { + FailWithCorruptedAsyncStack(async_id); + } + + uint32_t offset = fields_[kStackLength] - 1; + async_id_fields_[kExecutionAsyncId] = async_ids_stack_[2 * offset]; + async_id_fields_[kTriggerAsyncId] = async_ids_stack_[2 * offset + 1]; + fields_[kStackLength] = offset; + + if (LIKELY(offset < native_execution_async_resources_.size() && + !native_execution_async_resources_[offset].IsEmpty())) { +#ifdef DEBUG + for (uint32_t i = offset + 1; i < native_execution_async_resources_.size(); + i++) { + CHECK(native_execution_async_resources_[i].IsEmpty()); + } +#endif + native_execution_async_resources_.resize(offset); + if (native_execution_async_resources_.size() < + native_execution_async_resources_.capacity() / 2 && + native_execution_async_resources_.size() > 16) { + native_execution_async_resources_.shrink_to_fit(); + } + } + + if (UNLIKELY(js_execution_async_resources()->Length() > offset)) { + HandleScope handle_scope(env()->isolate()); + USE(js_execution_async_resources()->Set( + env()->context(), + env()->length_string(), + Integer::NewFromUnsigned(env()->isolate(), offset))); + } + + return fields_[kStackLength] > 0; +} + +void AsyncHooks::clear_async_id_stack() { + Isolate* isolate = env()->isolate(); + HandleScope handle_scope(isolate); + if (!js_execution_async_resources_.IsEmpty()) { + USE(PersistentToLocal::Strong(js_execution_async_resources_) + ->Set(env()->context(), + env()->length_string(), + Integer::NewFromUnsigned(isolate, 0))); + } + native_execution_async_resources_.clear(); + native_execution_async_resources_.shrink_to_fit(); + + async_id_fields_[kExecutionAsyncId] = 0; + async_id_fields_[kTriggerAsyncId] = 0; + fields_[kStackLength] = 0; +} + +void AsyncHooks::AddContext(Local ctx) { + ctx->SetPromiseHooks(js_promise_hooks_[0].IsEmpty() + ? Local() + : PersistentToLocal::Strong(js_promise_hooks_[0]), + js_promise_hooks_[1].IsEmpty() + ? Local() + : PersistentToLocal::Strong(js_promise_hooks_[1]), + js_promise_hooks_[2].IsEmpty() + ? Local() + : PersistentToLocal::Strong(js_promise_hooks_[2]), + js_promise_hooks_[3].IsEmpty() + ? Local() + : PersistentToLocal::Strong(js_promise_hooks_[3])); + + size_t id = contexts_.size(); + contexts_.resize(id + 1); + contexts_[id].Reset(env()->isolate(), ctx); + contexts_[id].SetWeak(); +} + +void AsyncHooks::RemoveContext(Local ctx) { + Isolate* isolate = env()->isolate(); + HandleScope handle_scope(isolate); + contexts_.erase(std::remove_if(contexts_.begin(), + contexts_.end(), + [&](auto&& el) { return el.IsEmpty(); }), + contexts_.end()); + for (auto it = contexts_.begin(); it != contexts_.end(); it++) { + Local saved_context = PersistentToLocal::Weak(isolate, *it); + if (saved_context == ctx) { + it->Reset(); + contexts_.erase(it); + break; + } + } +} + +AsyncHooks::DefaultTriggerAsyncIdScope::DefaultTriggerAsyncIdScope( + Environment* env, double default_trigger_async_id) + : async_hooks_(env->async_hooks()) { + if (env->async_hooks()->fields()[AsyncHooks::kCheck] > 0) { + CHECK_GE(default_trigger_async_id, 0); + } + + old_default_trigger_async_id_ = + async_hooks_->async_id_fields()[AsyncHooks::kDefaultTriggerAsyncId]; + async_hooks_->async_id_fields()[AsyncHooks::kDefaultTriggerAsyncId] = + default_trigger_async_id; +} + +AsyncHooks::DefaultTriggerAsyncIdScope::~DefaultTriggerAsyncIdScope() { + async_hooks_->async_id_fields()[AsyncHooks::kDefaultTriggerAsyncId] = + old_default_trigger_async_id_; +} + +AsyncHooks::DefaultTriggerAsyncIdScope::DefaultTriggerAsyncIdScope( + AsyncWrap* async_wrap) + : DefaultTriggerAsyncIdScope(async_wrap->env(), + async_wrap->get_async_id()) {} + std::vector IsolateData::Serialize(SnapshotCreator* creator) { Isolate* isolate = creator->GetIsolate(); std::vector indexes; @@ -253,6 +429,221 @@ void TrackingTraceStateObserver::UpdateTraceCategoryState() { USE(cb->Call(env_->context(), Undefined(isolate), arraysize(args), args)); } +void Environment::AssignToContext(Local context, + const ContextInfo& info) { + context->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kEnvironment, + this); + // Used by Environment::GetCurrent to know that we are on a node context. + context->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kContextTag, + Environment::kNodeContextTagPtr); + // Used to retrieve bindings + context->SetAlignedPointerInEmbedderData( + ContextEmbedderIndex::kBindingListIndex, &(this->bindings_)); + +#if HAVE_INSPECTOR + inspector_agent()->ContextCreated(context, info); +#endif // HAVE_INSPECTOR + + this->async_hooks()->AddContext(context); +} + +void Environment::TryLoadAddon( + const char* filename, + int flags, + const std::function& was_loaded) { + loaded_addons_.emplace_back(filename, flags); + if (!was_loaded(&loaded_addons_.back())) { + loaded_addons_.pop_back(); + } +} + +std::string Environment::GetCwd() { + char cwd[PATH_MAX_BYTES]; + size_t size = PATH_MAX_BYTES; + const int err = uv_cwd(cwd, &size); + + if (err == 0) { + CHECK_GT(size, 0); + return cwd; + } + + // This can fail if the cwd is deleted. In that case, fall back to + // exec_path. + const std::string& exec_path = exec_path_; + return exec_path.substr(0, exec_path.find_last_of(kPathSeparator)); +} + +void Environment::add_refs(int64_t diff) { + task_queues_async_refs_ += diff; + CHECK_GE(task_queues_async_refs_, 0); + if (task_queues_async_refs_ == 0) + uv_unref(reinterpret_cast(&task_queues_async_)); + else + uv_ref(reinterpret_cast(&task_queues_async_)); +} + +uv_buf_t Environment::allocate_managed_buffer(const size_t suggested_size) { + NoArrayBufferZeroFillScope no_zero_fill_scope(isolate_data()); + std::unique_ptr bs = + v8::ArrayBuffer::NewBackingStore(isolate(), suggested_size); + uv_buf_t buf = uv_buf_init(static_cast(bs->Data()), bs->ByteLength()); + released_allocated_buffers_.emplace(buf.base, std::move(bs)); + return buf; +} + +std::unique_ptr Environment::release_managed_buffer( + const uv_buf_t& buf) { + std::unique_ptr bs; + if (buf.base != nullptr) { + auto it = released_allocated_buffers_.find(buf.base); + CHECK_NE(it, released_allocated_buffers_.end()); + bs = std::move(it->second); + released_allocated_buffers_.erase(it); + } + return bs; +} + +Local Environment::NewFunctionTemplate( + v8::FunctionCallback callback, + Local signature, + v8::ConstructorBehavior behavior, + v8::SideEffectType side_effect_type, + const v8::CFunction* c_function) { + return v8::FunctionTemplate::New(isolate(), + callback, + Local(), + signature, + 0, + behavior, + side_effect_type, + c_function); +} + +void Environment::SetMethod(Local that, + const char* name, + v8::FunctionCallback callback) { + Local context = isolate()->GetCurrentContext(); + Local function = + NewFunctionTemplate(callback, + Local(), + v8::ConstructorBehavior::kThrow, + v8::SideEffectType::kHasSideEffect) + ->GetFunction(context) + .ToLocalChecked(); + // kInternalized strings are created in the old space. + const v8::NewStringType type = v8::NewStringType::kInternalized; + Local name_string = + v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); + that->Set(context, name_string, function).Check(); + function->SetName(name_string); // NODE_SET_METHOD() compatibility. +} + +void Environment::SetFastMethod(Local that, + const char* name, + v8::FunctionCallback slow_callback, + const v8::CFunction* c_function) { + Local context = isolate()->GetCurrentContext(); + Local function = + NewFunctionTemplate(slow_callback, + Local(), + v8::ConstructorBehavior::kThrow, + v8::SideEffectType::kHasNoSideEffect, + c_function) + ->GetFunction(context) + .ToLocalChecked(); + const v8::NewStringType type = v8::NewStringType::kInternalized; + Local name_string = + v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); + that->Set(context, name_string, function).Check(); +} + +void Environment::SetMethodNoSideEffect(Local that, + const char* name, + v8::FunctionCallback callback) { + Local context = isolate()->GetCurrentContext(); + Local function = + NewFunctionTemplate(callback, + Local(), + v8::ConstructorBehavior::kThrow, + v8::SideEffectType::kHasNoSideEffect) + ->GetFunction(context) + .ToLocalChecked(); + // kInternalized strings are created in the old space. + const v8::NewStringType type = v8::NewStringType::kInternalized; + Local name_string = + v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); + that->Set(context, name_string, function).Check(); + function->SetName(name_string); // NODE_SET_METHOD() compatibility. +} + +void Environment::SetProtoMethod(Local that, + const char* name, + v8::FunctionCallback callback) { + Local signature = v8::Signature::New(isolate(), that); + Local t = + NewFunctionTemplate(callback, + signature, + v8::ConstructorBehavior::kThrow, + v8::SideEffectType::kHasSideEffect); + // kInternalized strings are created in the old space. + const v8::NewStringType type = v8::NewStringType::kInternalized; + Local name_string = + v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); + that->PrototypeTemplate()->Set(name_string, t); + t->SetClassName(name_string); // NODE_SET_PROTOTYPE_METHOD() compatibility. +} + +void Environment::SetProtoMethodNoSideEffect(Local that, + const char* name, + v8::FunctionCallback callback) { + Local signature = v8::Signature::New(isolate(), that); + Local t = + NewFunctionTemplate(callback, + signature, + v8::ConstructorBehavior::kThrow, + v8::SideEffectType::kHasNoSideEffect); + // kInternalized strings are created in the old space. + const v8::NewStringType type = v8::NewStringType::kInternalized; + Local name_string = + v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); + that->PrototypeTemplate()->Set(name_string, t); + t->SetClassName(name_string); // NODE_SET_PROTOTYPE_METHOD() compatibility. +} + +void Environment::SetInstanceMethod(Local that, + const char* name, + v8::FunctionCallback callback) { + Local signature = v8::Signature::New(isolate(), that); + Local t = + NewFunctionTemplate(callback, + signature, + v8::ConstructorBehavior::kThrow, + v8::SideEffectType::kHasSideEffect); + // kInternalized strings are created in the old space. + const v8::NewStringType type = v8::NewStringType::kInternalized; + Local name_string = + v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); + that->InstanceTemplate()->Set(name_string, t); + t->SetClassName(name_string); +} + +void Environment::SetConstructorFunction(Local that, + const char* name, + Local tmpl, + SetConstructorFunctionFlag flag) { + SetConstructorFunction(that, OneByteString(isolate(), name), tmpl, flag); +} + +void Environment::SetConstructorFunction(Local that, + Local name, + Local tmpl, + SetConstructorFunctionFlag flag) { + if (LIKELY(flag == SetConstructorFunctionFlag::SET_CLASS_NAME)) + tmpl->SetClassName(name); + that->Set(context(), name, tmpl->GetFunction(context()).ToLocalChecked()) + .Check(); +} + void Environment::CreateProperties() { HandleScope handle_scope(isolate_); Local ctx = context(); @@ -1732,6 +2123,107 @@ void Environment::RunWeakRefCleanup() { } // Not really any better place than env.cc at this moment. +BaseObject::BaseObject(Environment* env, Local object) + : persistent_handle_(env->isolate(), object), env_(env) { + CHECK_EQ(false, object.IsEmpty()); + CHECK_GT(object->InternalFieldCount(), 0); + object->SetAlignedPointerInInternalField(BaseObject::kSlot, + static_cast(this)); + env->AddCleanupHook(DeleteMe, static_cast(this)); + env->modify_base_object_count(1); +} + +BaseObject::~BaseObject() { + env()->modify_base_object_count(-1); + env()->RemoveCleanupHook(DeleteMe, static_cast(this)); + + if (UNLIKELY(has_pointer_data())) { + PointerData* metadata = pointer_data(); + CHECK_EQ(metadata->strong_ptr_count, 0); + metadata->self = nullptr; + if (metadata->weak_ptr_count == 0) delete metadata; + } + + if (persistent_handle_.IsEmpty()) { + // This most likely happened because the weak callback below cleared it. + return; + } + + { + HandleScope handle_scope(env()->isolate()); + object()->SetAlignedPointerInInternalField(BaseObject::kSlot, nullptr); + } +} + +void BaseObject::MakeWeak() { + if (has_pointer_data()) { + pointer_data()->wants_weak_jsobj = true; + if (pointer_data()->strong_ptr_count > 0) return; + } + + persistent_handle_.SetWeak( + this, + [](const WeakCallbackInfo& data) { + BaseObject* obj = data.GetParameter(); + // Clear the persistent handle so that ~BaseObject() doesn't attempt + // to mess with internal fields, since the JS object may have + // transitioned into an invalid state. + // Refs: https://github.com/nodejs/node/issues/18897 + obj->persistent_handle_.Reset(); + CHECK_IMPLIES(obj->has_pointer_data(), + obj->pointer_data()->strong_ptr_count == 0); + obj->OnGCCollect(); + }, + WeakCallbackType::kParameter); +} + +void BaseObject::LazilyInitializedJSTemplateConstructor( + const FunctionCallbackInfo& args) { + DCHECK(args.IsConstructCall()); + DCHECK_GT(args.This()->InternalFieldCount(), 0); + args.This()->SetAlignedPointerInInternalField(BaseObject::kSlot, nullptr); +} + +Local BaseObject::MakeLazilyInitializedJSTemplate( + Environment* env) { + Local t = + env->NewFunctionTemplate(LazilyInitializedJSTemplateConstructor); + t->Inherit(BaseObject::GetConstructorTemplate(env)); + t->InstanceTemplate()->SetInternalFieldCount(BaseObject::kInternalFieldCount); + return t; +} + +BaseObject::PointerData* BaseObject::pointer_data() { + if (!has_pointer_data()) { + PointerData* metadata = new PointerData(); + metadata->wants_weak_jsobj = persistent_handle_.IsWeak(); + metadata->self = this; + pointer_data_ = metadata; + } + CHECK(has_pointer_data()); + return pointer_data_; +} + +void BaseObject::decrease_refcount() { + CHECK(has_pointer_data()); + PointerData* metadata = pointer_data(); + CHECK_GT(metadata->strong_ptr_count, 0); + unsigned int new_refcount = --metadata->strong_ptr_count; + if (new_refcount == 0) { + if (metadata->is_detached) { + OnGCCollect(); + } else if (metadata->wants_weak_jsobj && !persistent_handle_.IsEmpty()) { + MakeWeak(); + } + } +} + +void BaseObject::increase_refcount() { + unsigned int prev_refcount = pointer_data()->strong_ptr_count++; + if (prev_refcount == 0 && !persistent_handle_.IsEmpty()) + persistent_handle_.ClearWeak(); +} + void BaseObject::DeleteMe(void* data) { BaseObject* self = static_cast(data); if (self->has_pointer_data() && diff --git a/src/env.h b/src/env.h index 52bfaa3db8d46c..9c6cf21c176bc4 100644 --- a/src/env.h +++ b/src/env.h @@ -730,10 +730,10 @@ class AsyncHooks : public MemoryRetainer { // The `js_execution_async_resources` array contains the value in that case. inline v8::Local native_execution_async_resource(size_t index); - inline void SetJSPromiseHooks(v8::Local init, - v8::Local before, - v8::Local after, - v8::Local resolve); + void SetJSPromiseHooks(v8::Local init, + v8::Local before, + v8::Local after, + v8::Local resolve); inline v8::Local provider_string(int idx); @@ -743,13 +743,14 @@ class AsyncHooks : public MemoryRetainer { // NB: This call does not take (co-)ownership of `execution_async_resource`. // The lifetime of the `v8::Local<>` pointee must last until // `pop_async_context()` or `clear_async_id_stack()` are called. - inline void push_async_context(double async_id, double trigger_async_id, - v8::Local execution_async_resource); - inline bool pop_async_context(double async_id); - inline void clear_async_id_stack(); // Used in fatal exceptions. + void push_async_context(double async_id, + double trigger_async_id, + v8::Local execution_async_resource); + bool pop_async_context(double async_id); + void clear_async_id_stack(); // Used in fatal exceptions. - inline void AddContext(v8::Local ctx); - inline void RemoveContext(v8::Local ctx); + void AddContext(v8::Local ctx); + void RemoveContext(v8::Local ctx); AsyncHooks(const AsyncHooks&) = delete; AsyncHooks& operator=(const AsyncHooks&) = delete; @@ -1129,17 +1130,15 @@ class Environment : public MemoryRetainer { template inline void CloseHandle(T* handle, OnCloseCallback callback); - inline void AssignToContext(v8::Local context, - const ContextInfo& info); + void AssignToContext(v8::Local context, const ContextInfo& info); void StartProfilerIdleNotifier(); inline v8::Isolate* isolate() const; inline uv_loop_t* event_loop() const; - inline void TryLoadAddon( - const char* filename, - int flags, - const std::function& was_loaded); + void TryLoadAddon(const char* filename, + int flags, + const std::function& was_loaded); static inline Environment* from_timer_handle(uv_timer_t* handle); inline uv_timer_t* timer_handle(); @@ -1229,7 +1228,7 @@ class Environment : public MemoryRetainer { // loop alive. // This is used by Workers to manage their own .ref()/.unref() implementation, // as Workers aren't directly associated with their own libuv handles. - inline void add_refs(int64_t diff); + void add_refs(int64_t diff); inline bool has_run_bootstrapping_code() const; inline void DoneBootstrapping(); @@ -1281,7 +1280,7 @@ class Environment : public MemoryRetainer { const char* path = nullptr, const char* dest = nullptr); - inline v8::Local NewFunctionTemplate( + v8::Local NewFunctionTemplate( v8::FunctionCallback callback, v8::Local signature = v8::Local(), v8::ConstructorBehavior behavior = v8::ConstructorBehavior::kAllow, @@ -1289,48 +1288,47 @@ class Environment : public MemoryRetainer { const v8::CFunction* c_function = nullptr); // Convenience methods for NewFunctionTemplate(). - inline void SetMethod(v8::Local that, - const char* name, - v8::FunctionCallback callback); + void SetMethod(v8::Local that, + const char* name, + v8::FunctionCallback callback); - inline void SetFastMethod(v8::Local that, - const char* name, - v8::FunctionCallback slow_callback, - const v8::CFunction* c_function); + void SetFastMethod(v8::Local that, + const char* name, + v8::FunctionCallback slow_callback, + const v8::CFunction* c_function); - inline void SetProtoMethod(v8::Local that, - const char* name, - v8::FunctionCallback callback); - - inline void SetInstanceMethod(v8::Local that, - const char* name, - v8::FunctionCallback callback); + void SetProtoMethod(v8::Local that, + const char* name, + v8::FunctionCallback callback); + void SetInstanceMethod(v8::Local that, + const char* name, + v8::FunctionCallback callback); // Safe variants denote the function has no side effects. - inline void SetMethodNoSideEffect(v8::Local that, - const char* name, - v8::FunctionCallback callback); - inline void SetProtoMethodNoSideEffect(v8::Local that, - const char* name, - v8::FunctionCallback callback); + void SetMethodNoSideEffect(v8::Local that, + const char* name, + v8::FunctionCallback callback); + void SetProtoMethodNoSideEffect(v8::Local that, + const char* name, + v8::FunctionCallback callback); enum class SetConstructorFunctionFlag { NONE, SET_CLASS_NAME, }; - inline void SetConstructorFunction(v8::Local that, - const char* name, - v8::Local tmpl, - SetConstructorFunctionFlag flag = - SetConstructorFunctionFlag::SET_CLASS_NAME); + void SetConstructorFunction(v8::Local that, + const char* name, + v8::Local tmpl, + SetConstructorFunctionFlag flag = + SetConstructorFunctionFlag::SET_CLASS_NAME); - inline void SetConstructorFunction(v8::Local that, - v8::Local name, - v8::Local tmpl, - SetConstructorFunctionFlag flag = - SetConstructorFunctionFlag::SET_CLASS_NAME); + void SetConstructorFunction(v8::Local that, + v8::Local name, + v8::Local tmpl, + SetConstructorFunctionFlag flag = + SetConstructorFunctionFlag::SET_CLASS_NAME); void AtExit(void (*cb)(void* arg), void* arg); void RunAtExitCallbacks(); @@ -1488,9 +1486,8 @@ class Environment : public MemoryRetainer { void RunAndClearNativeImmediates(bool only_refed = false); void RunAndClearInterrupts(); - inline uv_buf_t allocate_managed_buffer(const size_t suggested_size); - inline std::unique_ptr release_managed_buffer( - const uv_buf_t& buf); + uv_buf_t allocate_managed_buffer(const size_t suggested_size); + std::unique_ptr release_managed_buffer(const uv_buf_t& buf); void AddUnmanagedFd(int fd); void RemoveUnmanagedFd(int fd);