| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a4d6543 commit f489c67
17 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1639,25 +1639,36 @@ If it is called more than once an error will be returned. | |||
| 1639 | 1639 | ||
| 1640 | 1640 | This API can be called even if there is a pending JavaScript exception. | |
| 1641 | 1641 | ||
| 1642 | - ### References to objects with a lifespan longer than that of the native method | ||
| 1642 | + ### References to values with a lifespan longer than that of the native method | ||
| 1643 | 1643 | ||
| 1644 | - In some cases an addon will need to be able to create and reference objects | ||
| 1644 | + In some cases, an addon will need to be able to create and reference values | ||
| 1645 | 1645 | with a lifespan longer than that of a single native method invocation. For | |
| 1646 | 1646 | example, to create a constructor and later use that constructor | |
| 1647 | - in a request to creates instances, it must be possible to reference | ||
| 1647 | + in a request to create instances, it must be possible to reference | ||
| 1648 | 1648 | the constructor object across many different instance creation requests. This | |
| 1649 | 1649 | would not be possible with a normal handle returned as a `napi_value` as | |
| 1650 | 1650 | described in the earlier section. The lifespan of a normal handle is | |
| 1651 | 1651 | managed by scopes and all scopes must be closed before the end of a native | |
| 1652 | 1652 | method. | |
| 1653 | 1653 | ||
| 1654 | - Node-API provides methods to create persistent references to an object. | ||
| 1655 | - Each persistent reference has an associated count with a value of 0 | ||
| 1656 | - or higher. The count determines if the reference will keep | ||
| 1657 | - the corresponding object live. References with a count of 0 do not | ||
| 1658 | - prevent the object from being collected and are often called 'weak' | ||
| 1659 | - references. Any count greater than 0 will prevent the object | ||
| 1660 | - from being collected. | ||
| 1654 | + Node-API provides methods for creating persistent references to values. | ||
| 1655 | + Each reference has an associated count with a value of 0 or higher, | ||
| 1656 | + which determines whether the reference will keep the corresponding value alive. | ||
| 1657 | + References with a count of 0 do not prevent values from being collected. | ||
| 1658 | + Values of object (object, function, external) and symbol types are becoming | ||
| 1659 | + 'weak' references and can still be accessed while they are not collected. | ||
| 1660 | + Values of other types are released when the count becomes 0 | ||
| 1661 | + and cannot be accessed from the reference any more. | ||
| 1662 | + Any count greater than 0 will prevent the values from being collected. | ||
| 1663 | + | ||
| 1664 | + Symbol values have different flavors. The true weak reference behavior is | ||
| 1665 | + only supported by local symbols created with the `Symbol()` constructor call. | ||
| 1666 | + Globally registered symbols created with the `Symbol.for()` call remain | ||
| 1667 | + always strong references because the garbage collector does not collect them. | ||
| 1668 | + The same is true for well-known symbols such as `Symbol.iterator`. They are | ||
| 1669 | + also never collected by the garbage collector. JavaScript's `WeakRef` and | ||
| 1670 | + `WeakMap` types return an error when registered symbols are used, | ||
| 1671 | + but they succeed for local and well-known symbols. | ||
| 1661 | 1672 | ||
| 1662 | 1673 | References can be created with an initial reference count. The count can | |
| 1663 | 1674 | then be modified through [`napi_reference_ref`][] and | |
@@ -1668,6 +1679,11 @@ will return `NULL` for the returned `napi_value`. An attempt to call | |||
| 1668 | 1679 | [`napi_reference_ref`][] for a reference whose object has been collected | |
| 1669 | 1680 | results in an error. | |
| 1670 | 1681 | ||
| 1682 | + Node-API versions 8 and earlier only allow references to be created for a | ||
| 1683 | + limited set of value types, including object, external, function, and symbol. | ||
| 1684 | + However, in newer Node-API versions, references can be created for any | ||
| 1685 | + value type. | ||
| 1686 | + | ||
| 1671 | 1687 | References must be deleted once they are no longer required by the addon. When | |
| 1672 | 1688 | a reference is deleted, it will no longer prevent the corresponding object from | |
| 1673 | 1689 | being collected. Failure to delete a persistent reference results in | |
@@ -1700,15 +1716,18 @@ NAPI_EXTERN napi_status napi_create_reference(napi_env env, | |||
| 1700 | 1716 | ``` | |
| 1701 | 1717 | ||
| 1702 | 1718 | * `[in] env`: The environment that the API is invoked under. | |
| 1703 | - * `[in] value`: `napi_value` representing the `Object` to which we want a | ||
| 1704 | - reference. | ||
| 1719 | + * `[in] value`: The `napi_value` for which a reference is being created. | ||
| 1705 | 1720 | * `[in] initial_refcount`: Initial reference count for the new reference. | |
| 1706 | 1721 | * `[out] result`: `napi_ref` pointing to the new reference. | |
| 1707 | 1722 | ||
| 1708 | 1723 | Returns `napi_ok` if the API succeeded. | |
| 1709 | 1724 | ||
| 1710 | 1725 | This API creates a new reference with the specified reference count | |
| 1711 | - to the `Object` passed in. | ||
| 1726 | + to the value passed in. | ||
| 1727 | + | ||
| 1728 | + In Node-API version 8 and earlier, a reference could only be created for | ||
| 1729 | + object, function, external, and symbol value types. However, in newer Node-API | ||
| 1730 | + versions, a reference can be created for any value type. | ||
| 1712 | 1731 | ||
| 1713 | 1732 | #### `napi_delete_reference` | |
| 1714 | 1733 | ||
@@ -1787,18 +1806,15 @@ NAPI_EXTERN napi_status napi_get_reference_value(napi_env env, | |||
| 1787 | 1806 | napi_value* result); | |
| 1788 | 1807 | ``` | |
| 1789 | 1808 | ||
| 1790 | - the `napi_value passed` in or out of these methods is a handle to the | ||
| 1791 | - object to which the reference is related. | ||
| 1792 | - | ||
| 1793 | 1809 | * `[in] env`: The environment that the API is invoked under. | |
| 1794 | - * `[in] ref`: `napi_ref` for which we requesting the corresponding `Object`. | ||
| 1795 | - * `[out] result`: The `napi_value` for the `Object` referenced by the | ||
| 1796 | - `napi_ref`. | ||
| 1810 | + * `[in] ref`: The `napi_ref` for which the corresponding value is | ||
| 1811 | + being requested. | ||
| 1812 | + * `[out] result`: The `napi_value` referenced by the `napi_ref`. | ||
| 1797 | 1813 | ||
| 1798 | 1814 | Returns `napi_ok` if the API succeeded. | |
| 1799 | 1815 | ||
| 1800 | 1816 | If still valid, this API returns the `napi_value` representing the | |
| 1801 | - JavaScript `Object` associated with the `napi_ref`. Otherwise, result | ||
| 1817 | + JavaScript value associated with the `napi_ref`. Otherwise, result | ||
| 1802 | 1818 | will be `NULL`. | |
| 1803 | 1819 | ||
| 1804 | 1820 | ### Cleanup on exit of the current Node.js environment | |
@@ -5069,9 +5085,8 @@ napi_status napi_define_class(napi_env env, | |||
| 5069 | 5085 | ``` | |
| 5070 | 5086 | ||
| 5071 | 5087 | * `[in] env`: The environment that the API is invoked under. | |
| 5072 | - * `[in] utf8name`: Name of the JavaScript constructor function; When wrapping a | ||
| 5073 | - C++ class, we recommend for clarity that this name be the same as that of | ||
| 5074 | - the C++ class. | ||
| 5088 | + * `[in] utf8name`: Name of the JavaScript constructor function. For clarity, | ||
| 5089 | + it is recommended to use the C++ class name when wrapping a C++ class. | ||
| 5075 | 5090 | * `[in] length`: The length of the `utf8name` in bytes, or `NAPI_AUTO_LENGTH` | |
| 5076 | 5091 | if it is null-terminated. | |
| 5077 | 5092 | * `[in] constructor`: Callback function that handles constructing instances | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -872,26 +872,18 @@ void AddLinkedBinding(Environment* env, | |||
| 872 | 872 | ||
| 873 | 873 | void AddLinkedBinding(Environment* env, | |
| 874 | 874 | const char* name, | |
| 875 | - napi_addon_register_func fn) { | ||
| 875 | + napi_addon_register_func fn, | ||
| 876 | + int32_t module_api_version) { | ||
| 876 | 877 | node_module mod = { | |
| 877 | - -1, | ||
| 878 | - NM_F_LINKED, | ||
| 879 | - nullptr, // nm_dso_handle | ||
| 880 | - nullptr, // nm_filename | ||
| 881 | - nullptr, // nm_register_func | ||
| 882 | - [](v8::Local<v8::Object> exports, | ||
| 883 | - v8::Local<v8::Value> module, | ||
| 884 | - v8::Local<v8::Context> context, | ||
| 885 | - void* priv) { | ||
| 886 | - napi_module_register_by_symbol( | ||
| 887 | - exports, | ||
| 888 | - module, | ||
| 889 | - context, | ||
| 890 | - reinterpret_cast<napi_addon_register_func>(priv)); | ||
| 891 | - }, | ||
| 892 | - name, | ||
| 893 | - reinterpret_cast<void*>(fn), | ||
| 894 | - nullptr // nm_link | ||
| 878 | + -1, // nm_version for Node-API | ||
| 879 | + NM_F_LINKED, // nm_flags | ||
| 880 | + nullptr, // nm_dso_handle | ||
| 881 | + nullptr, // nm_filename | ||
| 882 | + nullptr, // nm_register_func | ||
| 883 | + get_node_api_context_register_func(env, name, module_api_version), | ||
| 884 | + name, // nm_modname | ||
| 885 | + reinterpret_cast<void*>(fn), // nm_priv | ||
| 886 | + nullptr // nm_link | ||
| 895 | 887 | }; | |
| 896 | 888 | AddLinkedBinding(env, mod); | |
| 897 | 889 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -457,6 +457,18 @@ inline napi_status Wrap(napi_env env, | |||
| 457 | 457 | return GET_RETURN_STATUS(env); | |
| 458 | 458 | } | |
| 459 | 459 | ||
| 460 | + // In JavaScript, weak references can be created for object types (Object, | ||
| 461 | + // Function, and external Object) and for local symbols that are created with | ||
| 462 | + // the `Symbol` function call. Global symbols created with the `Symbol.for` | ||
| 463 | + // method cannot be weak references because they are never collected. | ||
| 464 | + // | ||
| 465 | + // Currently, V8 has no API to detect if a symbol is local or global. | ||
| 466 | + // Until we have a V8 API for it, we consider that all symbols can be weak. | ||
| 467 | + // This matches the current Node-API behavior. | ||
| 468 | + inline bool CanBeHeldWeakly(v8::Local<v8::Value> value) { | ||
| 469 | + return value->IsObject() || value->IsSymbol(); | ||
| 470 | + } | ||
| 471 | + | ||
| 460 | 472 | } // end of anonymous namespace | |
| 461 | 473 | ||
| 462 | 474 | void Finalizer::ResetFinalizer() { | |
@@ -551,7 +563,8 @@ void RefBase::Finalize() { | |||
| 551 | 563 | template <typename... Args> | |
| 552 | 564 | Reference::Reference(napi_env env, v8::Local<v8::Value> value, Args&&... args) | |
| 553 | 565 | : RefBase(env, std::forward<Args>(args)...), | |
| 554 | - persistent_(env->isolate, value) { | ||
| 566 | + persistent_(env->isolate, value), | ||
| 567 | + can_be_weak_(CanBeHeldWeakly(value)) { | ||
| 555 | 568 | if (RefCount() == 0) { | |
| 556 | 569 | SetWeak(); | |
| 557 | 570 | } | |
@@ -585,7 +598,7 @@ uint32_t Reference::Ref() { | |||
| 585 | 598 | return 0; | |
| 586 | 599 | } | |
| 587 | 600 | uint32_t refcount = RefBase::Ref(); | |
| 588 | - if (refcount == 1) { | ||
| 601 | + if (refcount == 1 && can_be_weak_) { | ||
| 589 | 602 | persistent_.ClearWeak(); | |
| 590 | 603 | } | |
| 591 | 604 | return refcount; | |
@@ -625,7 +638,11 @@ void Reference::Finalize() { | |||
| 625 | 638 | // Mark the reference as weak and eligible for collection | |
| 626 | 639 | // by the gc. | |
| 627 | 640 | void Reference::SetWeak() { | |
| 628 | - persistent_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); | ||
| 641 | + if (can_be_weak_) { | ||
| 642 | + persistent_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); | ||
| 643 | + } else { | ||
| 644 | + persistent_.Reset(); | ||
| 645 | + } | ||
| 629 | 646 | } | |
| 630 | 647 | ||
| 631 | 648 | // The N-API finalizer callback may make calls into the engine. V8's heap is | |
@@ -2419,9 +2436,11 @@ napi_status NAPI_CDECL napi_create_reference(napi_env env, | |||
| 2419 | 2436 | CHECK_ARG(env, result); | |
| 2420 | 2437 | ||
| 2421 | 2438 | v8::Local<v8::Value> v8_value = v8impl::V8LocalValueFromJsValue(value); | |
| 2422 | - if (!(v8_value->IsObject() || v8_value->IsFunction() || | ||
| 2423 | - v8_value->IsSymbol())) { | ||
| 2424 | - return napi_set_last_error(env, napi_invalid_arg); | ||
| 2439 | + if (env->module_api_version <= 8) { | ||
| 2440 | + if (!(v8_value->IsObject() || v8_value->IsFunction() || | ||
| 2441 | + v8_value->IsSymbol())) { | ||
| 2442 | + return napi_set_last_error(env, napi_invalid_arg); | ||
| 2443 | + } | ||
| 2425 | 2444 | } | |
| 2426 | 2445 | ||
| 2427 | 2446 | v8impl::Reference* reference = v8impl::Reference::New( | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -51,8 +51,11 @@ class Finalizer; | |||
| 51 | 51 | } // end of namespace v8impl | |
| 52 | 52 | ||
| 53 | 53 | struct napi_env__ { | |
| 54 | - explicit napi_env__(v8::Local<v8::Context> context) | ||
| 55 | - : isolate(context->GetIsolate()), context_persistent(isolate, context) { | ||
| 54 | + explicit napi_env__(v8::Local<v8::Context> context, | ||
| 55 | + int32_t module_api_version) | ||
| 56 | + : isolate(context->GetIsolate()), | ||
| 57 | + context_persistent(isolate, context), | ||
| 58 | + module_api_version(module_api_version) { | ||
| 56 | 59 | napi_clear_last_error(this); | |
| 57 | 60 | } | |
| 58 | 61 | ||
@@ -144,6 +147,7 @@ struct napi_env__ { | |||
| 144 | 147 | int open_callback_scopes = 0; | |
| 145 | 148 | int refs = 1; | |
| 146 | 149 | void* instance_data = nullptr; | |
| 150 | + int32_t module_api_version = NODE_API_DEFAULT_MODULE_API_VERSION; | ||
| 147 | 151 | ||
| 148 | 152 | protected: | |
| 149 | 153 | // Should not be deleted directly. Delete with `napi_env__::DeleteMe()` | |
@@ -419,6 +423,7 @@ class Reference : public RefBase { | |||
| 419 | 423 | void SetWeak(); | |
| 420 | 424 | ||
| 421 | 425 | v8impl::Persistent<v8::Value> persistent_; | |
| 426 | + bool can_be_weak_; | ||
| 422 | 427 | }; | |
| 423 | 428 | ||
| 424 | 429 | } // end of namespace v8impl | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1237,9 +1237,11 @@ NODE_EXTERN void AddLinkedBinding(Environment* env, | |||
| 1237 | 1237 | const char* name, | |
| 1238 | 1238 | addon_context_register_func fn, | |
| 1239 | 1239 | void* priv); | |
| 1240 | - NODE_EXTERN void AddLinkedBinding(Environment* env, | ||
| 1241 | - const char* name, | ||
| 1242 | - napi_addon_register_func fn); | ||
| 1240 | + NODE_EXTERN void AddLinkedBinding( | ||
| 1241 | + Environment* env, | ||
| 1242 | + const char* name, | ||
| 1243 | + napi_addon_register_func fn, | ||
| 1244 | + int32_t module_api_version = NODE_API_DEFAULT_MODULE_API_VERSION); | ||
| 1243 | 1245 | ||
| 1244 | 1246 | /* Registers a callback with the passed-in Environment instance. The callback | |
| 1245 | 1247 | * is called after the event loop exits, but before the VM is disposed. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -20,8 +20,9 @@ | |||
| 20 | 20 | #include <memory> | |
| 21 | 21 | ||
| 22 | 22 | node_napi_env__::node_napi_env__(v8::Local<v8::Context> context, | |
| 23 | - const std::string& module_filename) | ||
| 24 | - : napi_env__(context), filename(module_filename) { | ||
| 23 | + const std::string& module_filename, | ||
| 24 | + int32_t module_api_version) | ||
| 25 | + : napi_env__(context, module_api_version), filename(module_filename) { | ||
| 25 | 26 | CHECK_NOT_NULL(node_env()); | |
| 26 | 27 | } | |
| 27 | 28 | ||
@@ -151,11 +152,36 @@ class BufferFinalizer : private Finalizer { | |||
| 151 | 152 | ~BufferFinalizer() { env_->Unref(); } | |
| 152 | 153 | }; | |
| 153 | 154 | ||
| 155 | + void ThrowNodeApiVersionError(node::Environment* node_env, | ||
| 156 | + const char* module_name, | ||
| 157 | + int32_t module_api_version) { | ||
| 158 | + std::string error_message; | ||
| 159 | + error_message += module_name; | ||
| 160 | + error_message += " requires Node-API version "; | ||
| 161 | + error_message += std::to_string(module_api_version); | ||
| 162 | + error_message += ", but this version of Node.js only supports version "; | ||
| 163 | + error_message += NODE_STRINGIFY(NAPI_VERSION) " add-ons."; | ||
| 164 | + node_env->ThrowError(error_message.c_str()); | ||
| 165 | + } | ||
| 166 | + | ||
| 154 | 167 | inline napi_env NewEnv(v8::Local<v8::Context> context, | |
| 155 | - const std::string& module_filename) { | ||
| 168 | + const std::string& module_filename, | ||
| 169 | + int32_t module_api_version) { | ||
| 156 | 170 | node_napi_env result; | |
| 157 | 171 | ||
| 158 | - result = new node_napi_env__(context, module_filename); | ||
| 172 | + // Validate module_api_version. | ||
| 173 | + if (module_api_version < NODE_API_DEFAULT_MODULE_API_VERSION) { | ||
| 174 | + module_api_version = NODE_API_DEFAULT_MODULE_API_VERSION; | ||
| 175 | + } else if (module_api_version > NAPI_VERSION && | ||
| 176 | + module_api_version != NAPI_VERSION_EXPERIMENTAL) { | ||
| 177 | + node::Environment* node_env = node::Environment::GetCurrent(context); | ||
| 178 | + CHECK_NOT_NULL(node_env); | ||
| 179 | + ThrowNodeApiVersionError( | ||
| 180 | + node_env, module_filename.c_str(), module_api_version); | ||
| 181 | + return nullptr; | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + result = new node_napi_env__(context, module_filename, module_api_version); | ||
| 159 | 185 | // TODO(addaleax): There was previously code that tried to delete the | |
| 160 | 186 | // napi_env when its v8::Context was garbage collected; | |
| 161 | 187 | // However, as long as N-API addons using this napi_env are in place, | |
@@ -623,10 +649,48 @@ static void napi_module_register_cb(v8::Local<v8::Object> exports, | |||
| 623 | 649 | static_cast<const napi_module*>(priv)->nm_register_func); | |
| 624 | 650 | } | |
| 625 | 651 | ||
| 652 | + template <int32_t module_api_version> | ||
| 653 | + static void node_api_context_register_func(v8::Local<v8::Object> exports, | ||
| 654 | + v8::Local<v8::Value> module, | ||
| 655 | + v8::Local<v8::Context> context, | ||
| 656 | + void* priv) { | ||
| 657 | + napi_module_register_by_symbol( | ||
| 658 | + exports, | ||
| 659 | + module, | ||
| 660 | + context, | ||
| 661 | + reinterpret_cast<napi_addon_register_func>(priv), | ||
| 662 | + module_api_version); | ||
| 663 | + } | ||
| 664 | + | ||
| 665 | + // This function must be augmented for each new Node API version. | ||
| 666 | + // The key role of this function is to encode module_api_version in the function | ||
| 667 | + // pointer. We are not going to have many Node API versions and having one | ||
| 668 | + // function per version is relatively cheap. It avoids dynamic memory | ||
| 669 | + // allocations or implementing more expensive changes to module registration. | ||
| 670 | + // Currently AddLinkedBinding is the only user of this function. | ||
| 671 | + node::addon_context_register_func get_node_api_context_register_func( | ||
| 672 | + node::Environment* node_env, | ||
| 673 | + const char* module_name, | ||
| 674 | + int32_t module_api_version) { | ||
| 675 | + static_assert( | ||
| 676 | + NAPI_VERSION == 8, | ||
| 677 | + "New version of Node-API requires adding another else-if statement below " | ||
| 678 | + "for the new version and updating this assert condition."); | ||
| 679 | + if (module_api_version <= NODE_API_DEFAULT_MODULE_API_VERSION) { | ||
| 680 | + return node_api_context_register_func<NODE_API_DEFAULT_MODULE_API_VERSION>; | ||
| 681 | + } else if (module_api_version == NAPI_VERSION_EXPERIMENTAL) { | ||
| 682 | + return node_api_context_register_func<NAPI_VERSION_EXPERIMENTAL>; | ||
| 683 | + } else { | ||
| 684 | + v8impl::ThrowNodeApiVersionError(node_env, module_name, module_api_version); | ||
| 685 | + return nullptr; | ||
| 686 | + } | ||
| 687 | + } | ||
| 688 | + | ||
| 626 | 689 | void napi_module_register_by_symbol(v8::Local<v8::Object> exports, | |
| 627 | 690 | v8::Local<v8::Value> module, | |
| 628 | 691 | v8::Local<v8::Context> context, | |
| 629 | - napi_addon_register_func init) { | ||
| 692 | + napi_addon_register_func init, | ||
| 693 | + int32_t module_api_version) { | ||
| 630 | 694 | node::Environment* node_env = node::Environment::GetCurrent(context); | |
| 631 | 695 | std::string module_filename = ""; | |
| 632 | 696 | if (init == nullptr) { | |
@@ -654,7 +718,7 @@ void napi_module_register_by_symbol(v8::Local<v8::Object> exports, | |||
| 654 | 718 | } | |
| 655 | 719 | ||
| 656 | 720 | // Create a new napi_env for this specific module. | |
| 657 | - napi_env env = v8impl::NewEnv(context, module_filename); | ||
| 721 | + napi_env env = v8impl::NewEnv(context, module_filename, module_api_version); | ||
| 658 | 722 | ||
| 659 | 723 | napi_value _exports = nullptr; | |
| 660 | 724 | env->CallIntoModule([&](napi_env env) { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments