diff --git a/README.md b/README.md index bd53f75f..cfa42bb0 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ Other options can be found in [Build Napa.js](https://github.com/Microsoft/napaj ## Quick Start ```js -var napa = require('napajs'); -var zone1 = napa.zone.create('zone1', { workers: 4 }); +const napa = require('napajs'); +const zone1 = napa.zone.create('zone1', { workers: 4 }); // Broadcast code to all 4 workers in 'zone1'. zone1.broadcast('console.log("hello world");'); @@ -37,11 +37,11 @@ More examples: * [Synchronized loading](./examples/tutorial/synchronized-loading) ## Features -- Multi-threaded JavaScript runtime -- Node.js compatible module architecture with NPM support -- API for object transportation, object sharing and synchronization across JavaScript threads -- API for pluggable logging, metric and memory allocator -- Distributed as a Node.js module, as well as supporting embed scenarios +- Multi-threaded JavaScript runtime. +- Node.js compatible module architecture with NPM support. +- API for object transportation, object sharing and synchronization across JavaScript threads. +- API for pluggable logging, metric and memory allocator. +- Distributed as a Node.js module, as well as supporting embed scenarios. ## Documentation - [Napa.js Home](https://github.com/Microsoft/napajs/wiki) @@ -56,7 +56,7 @@ You can contribute to Napa.js in following ways: * Contribute to core module compatibility with Node. * Contribute bug fixes. -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact opencode@microsoft.com with any additional questions or comments. +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact opencode@microsoft.com with any additional questions or comments. # License Copyright (c) Microsoft Corporation. All rights reserved. diff --git a/benchmark/README.md b/benchmark/README.md index 02d07edb..ac250575 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -11,7 +11,7 @@ We got this report on environment below: | Name | Value | |-------------------|---------------------------------------------------------------------------------------| -|**Processor** |Intel(R) Xeon(R) CPU L5640 @ 2.27GHz, 8 virtual procesors | +|**Processor** |Intel(R) Xeon(R) CPU L5640 @ 2.27GHz, 8 virtual processors | |**System Type** |x64-based PC | |**Physical Memory**|16.0 GB | |**OS version** |Microsoft Windows Server 2012 R2 | @@ -36,12 +36,12 @@ Please refer to [execute-scalability.ts](./execute-scalability.ts) for test deta ## Execute overhead The overhead of `zone.execute` includes -1. marshalling cost of arguments in caller thread. -2. queuing time before a worker can execute. -3. unmarshalling cost of arguments in target worker. -4. marshalling cost of return value from target worker. -5. queuing time before caller callback is notified. -6. unmarshalling cost of return value in caller thread. +1. Marshalling cost of arguments in caller thread. +2. Queuing time before a worker can execute. +3. Unmarshalling cost of arguments in target worker. +4. Marshalling cost of return value from target worker. +5. Queuing time before caller callback is notified. +6. Unmarshalling cost of return value in caller thread. In this section we will examine #2 and #5. So we use empty function with no arguments and no return value. @@ -90,7 +90,7 @@ Average overhead is around 0.06ms to 0.12ms for `zone.execute`. ## Transport overhead The overhead of `transport.marshall` includes -1. overhead of needing replacer callback during JSON.stringify. (even empty callback will slowdown JSON.stringfiy significantly) +1. overhead of needing replacer callback during JSON.stringify. (even an empty callback will slow down JSON.stringify significantly) 2. traverse every value during JSON.stringify, to check value type and get `cid` to put into payload. - a. If value doesn't need special care. - b. If value is a transportable object that needs special care. @@ -132,12 +132,12 @@ Please refer to [transport-overhead.ts](./transport-overhead.ts) for test detail ## Store access overhead The overhead of `store.set` includes -1. overhead of calling `transport.marshall` on value. -2. overhead of put marshalled data and transport context into C++ map (with exclusive_lock). +1. Overhead of calling `transport.marshall` on value. +2. Overhead of put marshalled data and transport context into C++ map (with exclusive_lock). The overhead of `store.get` includes -1. overhead of getting marshalled data and transport context from C++ map (with shared_lock). -2. overhead of calling `transport.unmarshall` on marshalled data. +1. Overhead of getting marshalled data and transport context from C++ map (with shared_lock). +2. Overhead of calling `transport.unmarshall` on marshalled data. For `store.set`, numbers below indicates the cost beyond marshall is around 0.07~0.4ms varies per payload size. (10B to 18KB). `store.get` takes a bit more: 0.06~0.9ms with the same payload size variance. If the value in store is not updated frequently, it's always good to cache it in JavaScript world. diff --git a/docs/api/log.md b/docs/api/log.md index af5a95f4..46735f4a 100644 --- a/docs/api/log.md +++ b/docs/api/log.md @@ -17,7 +17,7 @@ ## Introduction Logging is a basic requirement for building services. `napajs` logging API enables developers to integrate their own logging capabilities in both JavaScript and C++ (addon) world. -A log row may contain following information: +A log row may contain the following information: - (Optional) Section: Useful field to filter log rows. Treatment is defined by logging providers. - (Optional) Trace ID: Useful field to join logs in the same transaction or request. - (Required) Message: Log message. @@ -54,7 +54,7 @@ void MyFunction() { ### log(message: string): void It logs a message. Using info level. -*`log` is a shortcut for `log.info`.* +* A `log` is a shortcut for `log.info`.* Example: ```js @@ -78,16 +78,16 @@ Example: napa.log('request', 'A1B2C3D4', 'request received'); ``` ### log.err(...) -It logs an error message. Three variation of arguments are the same with `log`. +It logs an error message. Three variations of arguments are the same with the `log`. ### log.warn(...) -It logs a warning message. Three variation of arguments are the same with `log`. +It logs a warning message. Three variations of arguments are the same with the `log`. ### log.info(...) -It logs an info message. Three variation of arguments are the same with `log`. +It logs an info message. Three variations of arguments are the same with the `log`. ### log.debug(...) -It logs a debug message. Three combinations of arguments are the same with `log`. +It logs a debug message. Three combinations of arguments are the same with the `log`. ## Using custom logging providers Developers can hook up custom logging provider by calling the following before creation of any zones: diff --git a/docs/api/memory.md b/docs/api/memory.md index fdfb7280..7ccab620 100644 --- a/docs/api/memory.md +++ b/docs/api/memory.md @@ -72,7 +72,7 @@ Memory allocation in C++ addon is tricky. A common pitfall is to allocate memory There are also advanced scenarios that user want to customize memory allocation. Napa.js provides APIs for customizing memory allocator as well. -### Recommended way of allocate memory. +### Recommended way of allocate memory TBD ### Customize memory allocation diff --git a/docs/api/metric.md b/docs/api/metric.md index 115fa1db..abff44cf 100644 --- a/docs/api/metric.md +++ b/docs/api/metric.md @@ -23,9 +23,9 @@ A metric has its identity containing following information: - **Section**: The group or category of the metric. - **Name**: Name of the metric. Section/Name combination should be unique in the system. - **Type**: Type of the metric, which can be - - *Number*: A absolute number, e.g: PrivateBytes. + - *Number*: An absolute number, e.g: PrivateBytes. - *Rate*: A flowing volume in number, e.g: QueryPerSecond. - - *Percentile*: A absolute number that needs to be sampled by percentiles, e.g: SuccessLatency. + - *Percentile*: An absolute number that needs to be sampled by percentiles, e.g: SuccessLatency. - **Dimensions**: A metric can have multiple dimensions, each dimension can bind with a string value at runtime. e.g: IncomingRequestRate can have 2 dimensions: ['client-id', 'request-type']. Metrics are process-wise objects, which can be used across [zones](./zone.md#intro). @@ -40,7 +40,7 @@ Metrics are process-wise objects, which can be used across [zones](./zone.md#int Percentile, }; - /// Interface to represents a multi-dimensional metric with a maximum dimensionality of 64. + /// Interface to represent a multi-dimensional metric with a maximum dimensionality of 64. class Metric { public: diff --git a/docs/api/module.md b/docs/api/module.md index e5944a08..3fa3dfba 100644 --- a/docs/api/module.md +++ b/docs/api/module.md @@ -27,7 +27,7 @@ Napa.js follows [Node.js' convention](https://nodejs.org/api/modules.html) to su 4) API of creating C++ modules (addons) are similar. Napa.JS introduced macros that the same source code can be compiled to produce both Napa.js addon and Node.js addon. But there are also differences: -1) C++ module that is designed/implemented for Napa.js can run on Node.JS (need different compile flags to produce '.napa' and '.node'). But not vice versal. +1) C++ module that is designed/implemented for Napa.js can run on Node.JS (need different compile flags to produce '.napa' and '.node'). But not vice versa. 2) Napa.js doesn't support all Node.js API. Node API are supported [incrementally](./node-api.md) on the motivation of adding Node.js built-ins and core modules that are needed for computation heavy tasks. You can access full capabilities of Node exposed via [Node zone](./zone.md#node-zone). 3) Napa.js doesn't provide `uv` functionalities, thus built-ins and core modules have its own implementation. To write async function in addon, methods `DoAsyncWork`/`PostAsyncWork` are introduced to work for both Napa.js and Node.js. 4) Napa.js supports embed mode. C++ modules need separate compilation between Node mode and embed mode. diff --git a/docs/api/store.md b/docs/api/store.md index a4d7ee21..9c68e410 100644 --- a/docs/api/store.md +++ b/docs/api/store.md @@ -23,7 +23,7 @@ Though very convenient, it's not recommended to use store to pass values within Following APIs are exposed to create, get and operate upon stores. ### create(id: string): Store -It creates a store by a string identifer that can be used to get the store later. When all references to the store from all JavaScript VMs are cleared, the store will be destroyed. Thus always keep a reference at global or module scope is usually a good practice using `Store`. Error will be thrown if the id already exists. +It creates a store by a string identifier that can be used to get the store later. When all references to the store from all JavaScript VMs are cleared, the store will be destroyed. Thus always keep a reference at global or module scope is usually a good practice using `Store`. Error will be thrown if the id already exists. Example: ```js diff --git a/docs/api/transport.md b/docs/api/transport.md index d212c691..e80c1270 100644 --- a/docs/api/transport.md +++ b/docs/api/transport.md @@ -40,7 +40,7 @@ Transportable types are: - JavaScript primitive types: undefined, null, boolean, number, string - Object (TypeScript class) that implement [`Transportable`](#transportable) interface - Function without referencing closures. -- JavaScript standard built-In objects in this whitelist. +- JavaScript standard built-in objects in this whitelist. * ArrayBuffer * Float32Array * Float64Array @@ -54,23 +54,23 @@ Transportable types are: - Array or plain JavaScript object that is composite pattern of above. ### Constructor ID (cid) -For user classes that implement [`Transportable`](#transportable) interface, Napa uses Constructor ID (`cid`) to lookup constructors for creating a right object from a string payload. `cid` is marshalled as a part of the payload. During unmarshalling, transport layer will extract the `cid`, create an object instance using the constructor associated with it, and then call unmarshall on the object. +For user classes that implement the [`Transportable`](#transportable) interface, Napa uses Constructor ID (`cid`) to lookup constructors for creating a right object from a string payload. `cid` is marshalled as a part of the payload. During unmarshalling, the transport layer will extract the `cid`, create an object instance using the constructor associated with it, and then call unmarshall on the object. -It's class developer's responsibility to choose the right `cid` for your class. To avoid conflict, we suggest to use the combination of module.id and class name as `cid`. Developer can use class decorator [`cid`](#decorator-cid) to register a user Transportable class automatically, when using TypeScript with decorator feature enabled. Or call [`transport.register`](#register) manually during module initialization. +It's the class developer's responsibility to choose the right `cid` for your class. To avoid conflict, we suggest to use the combination of module.id and class name as `cid`. Developer can use class decorator [`cid`](#decorator-cid) to register a user Transportable class automatically, when using TypeScript with decorator feature enabled. Or call [`transport.register`](#register) manually during module initialization. ### Transport context -There are states that cannot be saved or loaded in serialized form (like std::shared_ptr), or it's very inefficient to serialize (like JavaScript function). Transport context is introduced to help in these scenarios. TransportContext objects can be passed from one JavaScript VM to another, or stored in native world, so lifecycle of shared native objects extended by using TransportContext. An example of `Transportable` implementation using TransportContext is [`ShareableWrap`](./../../inc/napa/module/shareable-wrap.h). +There are states that cannot be saved or loaded in serialized form (like std::shared_ptr), or it's very inefficient to serialize (like JavaScript function). Transport context is introduced to help in these scenarios. TransportContext objects can be passed from one JavaScript VM to another, or stored in the native world, so lifecycle of shared native objects extended by using TransportContext. An example of the `Transportable` implementation using TransportContext is [`ShareableWrap`](./../../inc/napa/module/shareable-wrap.h). ### Transporting functions JavaScript function is a special transportable type, through marshalling its definition into a [store](./store.md#intro), and generate a new function from its definition on target thread. Highlights on transporting functions are: - For the same function, marshall/unmarshall is an one-time cost on each JavaScript thread. Once a function is transported for the first time, later transportation of the same function to previous JavaScript thread can be regarded as free. -- Closure cannot be transported, but you won't get error when transporting a function. Instead, you will get runtime error complaining a variable (from closure) is undefined when you can the function later. -- `__dirname` / `__filename` can be accessed in transported function, which is determined by `origin` property of function. By default `origin` property is set to current working directory. +- Closure cannot be transported, but you won't get an error when transporting a function. Instead, you will get runtime error complaining a variable (from closure) is undefined when you can the function later. +- `__dirname` / `__filename` can be accessed in transported function, which is determined by `origin` property of the function. By default, `origin` property is set to the current working directory. ### Transporting JavaScript built-in objects -JavaScript standard built-In objects in [the whitelist](#built-in-whitelist) can be transported among napa workers transparently. JavaScript Objects with properties in these types are also able to be transported. Please refer to [unit tests](./../../test/transport-test.ts) for detail. +JavaScript standard built-in objects in [the whitelist](#built-in-whitelist) can be transported among napa workers transparently. JavaScript Objects with properties in these types are also able to be transported. Please refer to [unit tests](./../../test/transport-test.ts) for detail. An example [Parallel Quick Sort](./../../examples/tutorial/parallel-quick-sort) demonstrated transporting TypedArray (created from SharedArrayBuffer) among multiple Napa workers for efficient data sharing. @@ -105,7 +105,7 @@ class B { assert(!transport.isTransportable(new B())); ``` ### register(transportableClass: new(...args: any[]) => any): void -Register a `Transportable` class before transport layer can marshall/unmarshall its instances. +Register a `Transportable` class before the transport layer can marshall/unmarshall its instances. User can also use class decorator [`@cid`](#cid-decorator) for class registration. Example: @@ -121,7 +121,7 @@ class A extends transport.AutoTransportable { transport.register(A); ``` ### marshall(jsValue: any, context: TransportContext): string -Marshall a [transportable](#transportable-types) JavaScript value into a JSON payload with a [`TransportContext`](#transport-context). Error will be thrown if the value is not transportable. +Marshall a [transportable](#transportable-types) JavaScript value into a JSON payload with a [`TransportContext`](#transport-context).An Error will be thrown if the value is not transportable. Example: ```js @@ -132,7 +132,7 @@ var jsonPayload = transport.marshall( console.log(jsonPayload); ``` ### unmarshall(json: string, context: TransportContext): any -Unmarshall an [transportable](#transportable-types) JavaScript value from a JSON payload with a [`TransportContext`](#transport-context). Error will be thrown if `cid` property is found and not registered with transport layer. +Unmarshall a [transportable](#transportable-types) JavaScript value from a JSON payload with a [`TransportContext`](#transport-context).An Error will be thrown if `cid` property is found and not registered with the transport layer. Example: ```js @@ -148,18 +148,18 @@ Save a shareable object in context. Load a shareable object from handle. ### context.sharedCount: number -Count of shareable objects saved in current context. +Count of shareable objects saved in the current context. ## Interface `Transportable` -Interface for Transportable object. +Interface for the Transportable object. ### transportable.cid: string -Get accessor for [Constructor ID](#constructor-id). It is used to lookup constructor for payload of current class. +Get accessor for [Constructor ID](#constructor-id). It is used to lookup constructor for the payload of the current class. ### transportable.marshall(context: TransportContext): object -Marshall transform this object into a plain JavaScript object with the help of [TransportContext](#transport-context). +Marshall transforms this object into a plain JavaScript object with the help of [TransportContext](#transport-context). ### transportable.unmarshall(payload: object, context: TransportContext): void -Unmarshall transform marshalled payload into current object. +Unmarshall transforms marshalled payload into current object. ## Abstract class `TransportableObject` TBD diff --git a/docs/api/zone.md b/docs/api/zone.md index f26c8d96..cd59e0f5 100644 --- a/docs/api/zone.md +++ b/docs/api/zone.md @@ -43,7 +43,7 @@ There are two types of zone: ### Zone operations There are two operations, designed to reinforce the symmetry of workers within a zone: - 1) **Broadcast** - run code that changes worker state on all workers, returning a promise for the pending operation. Through the promise, we can only know if the operation succeed or failed. Usually we use `broadcast` to bootstrap the application, pre-cache objects, or change application settings. Function `broadcastSync` is also offered as a synchronized version of broadcast operations. + 1) **Broadcast** - run code that changes worker state on all workers, returning a promise for the pending operation. Through the promise, we can only know if the operation succeeded or failed. Usually we use `broadcast` to bootstrap the application, pre-cache objects, or change application settings. Function `broadcastSync` is also offered as a synchronized version of broadcast operations. 2) **Execute** - run code that doesn't change worker state on an arbitrary worker, returning a promise of getting the result. Execute is designed for doing the real work. Zone operations are on a basis of first-come-first-serve, while `broadcast` takes higher priority over `execute`. @@ -51,7 +51,7 @@ There are two operations, designed to reinforce the symmetry of workers within a ## API ### create(id: string, settings: ZoneSettings): Zone -It creates a Napa zone with a string id. If zone with the id is already created, error will be thrown. [`ZoneSettings`](#zone-settings) can be specified for creating zones. +It creates a Napa zone with a string id. If a zone with the id is already created, an error will be thrown. [`ZoneSettings`](#zone-settings) can be specified for creating zones. Example 1: Create a zone with id 'zone1', using default ZoneSettings. ```js diff --git a/docs/design/transport-js-builtins.md b/docs/design/transport-js-builtins.md index 84e1fd48..0e9c8d80 100644 --- a/docs/design/transport-js-builtins.md +++ b/docs/design/transport-js-builtins.md @@ -1,22 +1,22 @@ # Transport JavaScript standard built-in objects ## Incentives -The abstraction of 'Transportable' lies in the center of napa.js to efficiently share objects between JavaScript VMs (napa workers). Except JavaScript primitive types, an object needs to implement 'Transportable' interface to make it transportable. It means [Javascript standard built-in objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) are not transportable unless wrappers or equivalent implementations for them are implemented by extending 'Transportable' interface. Developing cost for all those objects is not trivial, and new abstraction layer (wrappers or equivalent implementations) will bring barriers for users to learn and adopt these new stuffs. Moreover, developers also need to deal with the interaction between JavaScript standards objects and those wrappers or equivalent implementations. +The abstraction of 'Transportable' lies in the center of Napa.js to efficiently share objects between JavaScript VMs (Napa workers). Except JavaScript primitive types, an object needs to implement 'Transportable' interface to make it transportable. It means [JavaScript standard built-in objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) are not transportable unless wrappers or equivalent implementations for them are implemented by extending 'Transportable' interface. The development cost for these objects is not trivial, and new abstraction layer (wrappers or equivalent implementations) will create barriers for users to learn and adopt these new things. Moreover, developers also need to deal with the interaction between JavaScript standards objects and those wrappers or equivalent implementations. The incentive of this design is to provide a solution to make JavaScript standard built-in objects transportable with requirements listed in the Goals section. -At the first stage, we will focus on an efficient solution to share data between napa workers. Basically, it is about making SharedArrayBuffer / TypedArray / DataView transportable. +At the first stage, we will focus on an efficient solution to share data between Napa workers. Basically, it is about making *SharedArrayBuffer / TypedArray / DataView* transportable. ## Goals Make Javascript standard built-in objects transportable with -- an efficient way to share structured data, like SharedArrayBuffer, among napa workers -- consistent APIs with ECMA standards -- no new abstraction layers for the simplest usage -- the least new concepts for advanced usage -- a scalable solution to make all Javascript standard built-in objects transportable, avoiding to make them transportable one by one. +- An efficient way to share structured data, like *SharedArrayBuffer*, among Napa workers +- Consistent APIs with ECMA standards +- No new abstraction layers for the simplest usage +- The least new concepts for advanced usage +- A scalable solution to make all JavaScript standard built-in objects transportable, avoiding to make them transportable one by one. ## Example -The below example shows how SharedArrayBuffer object is transported across multiple napa workers. It will print the TypedArray 'ta' created from a SharedArrayBuffer, with all its elements set to 100 from different napa workers. +The below example shows how *SharedArrayBuffer* object is transported across multiple Napa workers. It will print the TypedArray 'ta' created from a *SharedArrayBuffer*, with all its elements set to 100 from different Napa workers. ```js var napa = require("napajs"); var zone = napa.zone.create('zone', { workers: 4 }); @@ -45,28 +45,28 @@ run(); ``` ## Solution -Here we just give a high level description of the solution. Its api will go to docs/api/transport. +Here we just give a high-level description of the solution. Its API will go to [docs/api/transport](https://github.com/Microsoft/napajs/blob/master/docs/api/transport.md). - V8 provides its value-serialization mechanism by ValueSerializer and ValueDeserializer, which is compatible with the HTML structured clone algorithm. It is a horizontal solution to serialize / deserialize JavaScript objects. ValueSerializer::Delegate and ValueDeserializer::Delegate are their inner class. They work as base classes from which developers can deprive to customize some special handling of external / shared resources, like memory used by a SharedArrayBuffer object. -- napa::v8_extensions::ExternalizedContents -> 1. It holds the externalized contents (memory) of a SharedArrayBuffer instance once it is serialized via napa::v8_extensions::Utils::SerializeValue(). -> 2. Only 1 instance of ExternalizedContents wil be generated for each SharedArrayBuffer. If a SharedArrayBuffer had been externalized, it will reuse the ExternalizedContents instance created before in napa::v8_extensions::Utils::SerializeValue() +- **napa::v8_extensions::ExternalizedContents** + - It holds the externalized contents (memory) of a SharedArrayBuffer instance once it is serialized via *napa::v8_extensions::Utils::SerializeValue()*. + - Only 1 instance of ExternalizedContents will be generated for each SharedArrayBuffer. If a SharedArrayBuffer had been externalized, it will reuse the ExternalizedContents instance created before in *napa::v8_extensions::Utils::SerializeValue()*. -- napa::v8_extensions::SerializedData -> 1. It is generated by napa::v8_extensions::Utils::SerializeValue(). It holds the serialized data of a JavaScript object, which is required during its deserialization. +- **napa::v8_extensions::SerializedData** + - It is generated by *napa::v8_extensions::Utils::SerializeValue()*. It holds the serialized data of a JavaScript object, which is required during its deserialization. -- BuiltInObjectTransporter -> 1. napa::v8_extensions::Serializer, derived from v8::ValueSerializer::Delegate -> 2. napa::v8_extensions::Deserializer, derived from v8::ValueDeserializer::Delegate -> 3. static std::shared_ptr v8_extensions::Utils::SerializeValue(Isolate* isolate, Local value); ->>> Generate the SerializedData instance given an input value. ->>> If any SharedArrayBuffer instances exist in the input value, their ExternalizedContents instances will be generated and attached to the ShareArrayBuffer instances respectively. -> 4. static MaybeLocal v8_extensions::Utils::DeserializeValue(Isolate* isolate, std::shared_ptr data); ->>> Restore a JavaScript value from its SerializedData instance generated by v8_extensions::Utils::SerializeValue() before. +- **BuiltInObjectTransporter** + - **napa::v8_extensions::Serializer, derived from v8::ValueSerializer::Delegate** + - **napa::v8_extensions::Deserializer, derived from v8::ValueDeserializer::Delegate** + - **static std::shared_ptr\ v8_extensions::Utils::SerializeValue(Isolate\* isolate, Local\ value);** + - Generate the *SerializedData* instance given an input value. + - If any *SharedArrayBuffer* instances exist in the input value, their *ExternalizedContents* instances will be generated and attached to the *ShareArrayBuffer* instances respectively. + - **static MaybeLocal\ v8_extensions::Utils::DeserializeValue(Isolate\* isolate, std::shared_ptr\ data);** + - Restore a JavaScript value from its SerializedData instance generated by *v8_extensions::Utils::SerializeValue()* before. -- Currently, napa relies on Transportable API and a registered constructor to make an object transportable. In [marshallTransform](https://github.com/Microsoft/napajs/blob/master/lib/transport/transport.ts), when a JavaScript object is detected to have a registered constructor, it will go with napa way to marshall this object with the help of a TransportContext object, otherwise a non-transportable error is thrown. +- Currently, Napa relies on Transportable API and a registered constructor to make an object transportable. In [marshallTransform](https://github.com/Microsoft/napajs/blob/master/lib/transport/transport.ts), when a JavaScript object is detected to have a registered constructor, it will go with Napa way to marshall this object with the help of a **TransportContext** object, otherwise a non-transportable error is thrown. -- Instead of throwing an Error when no registered constructor is detected, the above mentioned BuiltInObjectTransporter can jump in to help marshall this object. We can use a whitelist of object types to restrict this solution to those verified types at first. +- Instead of throwing an error when no registered constructor is detected, the **BuiltInObjectTransporter** can help handle this object. We can use a whitelist of object types to restrict this solution to those verified types at first. ```js export function marshallTransform(jsValue: any, context: transportable.TransportContext): any { if (jsValue != null && typeof jsValue === 'object' && !Array.isArray(jsValue)) { @@ -111,25 +111,23 @@ function unmarshallTransform(payload: any, context: transportable.TransportConte } ``` -- Life cycle of SharedArrayBuffer (SAB) -> 1. When a SAB participates transportation among napa workers, its life cycle will be extended till the last reference this SAB. The reference of a SAB could be ->>> 1) a SAB object in its original isolate. +#### Lifecycle of SharedArrayBuffer (SAB) +- When a SAB participates transportation among Napa workers, its life cycle will be extended till the last reference this SAB. The reference of a SAB could be: + - SAB object in its original isolate. + - Received SAB transported from another Napa workers, including node zone of Napa. + - TypedArray or DataView created from the original SAB or a received SAB. ->>> 2) a received SAB transported from another napa workers, including node zone of napa. +- The life cycle extension during transportation is achieved through the *ExternalizedContents* *SharedPtrWrap* of the SAB. + - When a SAB is transported for the first time, it will be externalized and its ExternalizedContents will be stored in its *SerializedData*. At the same time, the *SharedPtrWrap* of the *ExternalizedContents* will be set to the '_externalized' property of the original SAB. ->>> 3) a TypedArray or DataView created from the original SAB or a received SAB. + - When a SAB is transported for the second time or later, it will skip externalization and find its *ExternalizedContents* from its '_externalized' property, and store it to its *SerializedData*. -> 2. The life cycle extension during transportation is achieved through the ExternalizedContents SharedPtrWrap of the SAB. ->>> 1) When a SAB is transported for the first time, it will be externalized and its ExternalizedContents will be stored in its SerializedData. At the same time, the SharedPtrWrap of the ExternalizedContents will be set to the '_externalized' property of the original SAB. + - When a Napa worker tries to restore a transported SAB, it will find the pre-stored *ExternalizedContents*, and create a *SharedPtrWrap* for it, then set it to the to-be-restored SAB. ->>> 2) When a SAB is transported for the second time or later, it wil skip externalization and find its ExternalizedContents from its '_externalized' property, and store it to its SerializedData. - ->>> 3) When a napa worker try to restored a transported SAB, it will find the pre-stored ExternalizedContents, and create a SharedPtrWrap for it, then set it to the to-be-restored SAB. - ->>> 4) The life cycle of the SharedArrayBuffer is extended by the SharedPtrWrap of its ExternalizedContents. + - The life cycle of the *SharedArrayBuffer* is extended by the *SharedPtrWrap* of its *ExternalizedContents*. ## Constraints The above solution is based on the serialization / deserialization mechanism of V8. It may have the following constraints. - Not all [JavaScripts standard built-in objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) are supported by Node (as a dependency of Napa in node mode) or V8 of a given version. We only provide transporting solution for those mature object types. -- Up to present, Node does not explicitly support multiple V8 isolates. There might be inconsistency to transport objects between node zone and napa zones. Extra effort might be required to make it consistent. +- Presently, Node does not explicitly support multiple V8 isolates. There may be inconsistencies in transporting objects between Node zones and Napa zones. Extra effort might be required to make it consistent. diff --git a/examples/modules/README.md b/examples/modules/README.md index 5ed3a76e..96d6fa1a 100644 --- a/examples/modules/README.md +++ b/examples/modules/README.md @@ -27,7 +27,7 @@ The source codes can be organized as the structure below. ## How to build and test ? For the module examples, node-gyp or cmake-js build solution is provided for your reference. -Please make sure [node-gyp](https://github.com/nodejs/node-gyp#installation) or [cmake-js](https://github.com/cmake-js/cmake-js#installation) has been set up correctly at your client, then you could follow the below steps to build and test the module examples. +Please make sure [node-gyp](https://github.com/nodejs/node-gyp#installation) or [cmake-js](https://github.com/cmake-js/cmake-js#installation) has been set up correctly at your client, then you could follow the steps below to build and test the module examples. ``` 1. go to the directory of a module example 2. run "npm install" to build the module diff --git a/examples/modules/async-number/README.md b/examples/modules/async-number/README.md index 51ad06b4..c9327ef1 100644 --- a/examples/modules/async-number/README.md +++ b/examples/modules/async-number/README.md @@ -1,4 +1,4 @@ -# Asynchronus numbering +# Asynchronous numbering ## APIs @@ -69,7 +69,7 @@ namespace { std::atomic _now(0); } -/// It increases a number by a given parameter asynchronously and run a callback at the next execution loop. +/// It increases a number by a given parameter asynchronously and runs a callback at the next execution loop. void Increase(const FunctionCallbackInfo& args) { auto isolate = args.GetIsolate(); @@ -81,12 +81,12 @@ void Increase(const FunctionCallbackInfo& args) { napa::module::PostAsyncWork(Local::Cast(args[1]), [value]() { - // This runs at the separate thread. + // This runs in a separate thread. _now += value; return reinterpret_cast(static_cast(_now.load())); }, [](auto jsCallback, void* result) { - // This runs at the same thread as one Increase() is called. + // This runs in the same thread as the one Increase() is called in. auto isolate = Isolate::GetCurrent(); int32_t argc = 1; @@ -98,7 +98,7 @@ void Increase(const FunctionCallbackInfo& args) { ); } -/// It increases a number by a given parameter synchronously and run a callback at the next execution loop. +/// It increases a number by a given parameter synchronously and runs a callback at the next execution loop. void IncreaseSync(const FunctionCallbackInfo& args) { auto isolate = args.GetIsolate(); @@ -110,12 +110,12 @@ void IncreaseSync(const FunctionCallbackInfo& args) { napa::module::DoAsyncWork(Local::Cast(args[1]), [value](auto complete) { - // This runs at the same thread. + // This runs in the same thread. _now += value; complete(reinterpret_cast(static_cast(_now.load()))); }, [](auto jsCallback, void* result) { - // This runs at the same thread as one IncreaseSync() is called. + // This runs in the same thread as the one IncreaseSync() is called in. auto isolate = Isolate::GetCurrent(); int32_t argc = 1; diff --git a/examples/modules/async-number/napa/addon.cpp b/examples/modules/async-number/napa/addon.cpp index 06904e55..ead2f330 100644 --- a/examples/modules/async-number/napa/addon.cpp +++ b/examples/modules/async-number/napa/addon.cpp @@ -16,7 +16,7 @@ namespace { std::atomic _now(0); } -/// It increases a number by a given parameter asynchronously and run a callback at the next execution loop. +/// It increases a number by a given parameter asynchronously and runs a callback at the next execution loop. void Increase(const FunctionCallbackInfo& args) { auto isolate = args.GetIsolate(); @@ -28,12 +28,12 @@ void Increase(const FunctionCallbackInfo& args) { napa::zone::PostAsyncWork(Local::Cast(args[1]), [value]() { - // This runs at the separate thread. + // This runs in a separate thread. _now += value; return reinterpret_cast(static_cast(_now.load())); }, [](auto jsCallback, void* result) { - // This runs at the same thread as one Increase() is called. + // This runs in the same thread as the one Increase() is called in. auto isolate = Isolate::GetCurrent(); int32_t argc = 1; @@ -45,7 +45,7 @@ void Increase(const FunctionCallbackInfo& args) { ); } -/// It increases a number by a given parameter synchronously and run a callback at the next execution loop. +/// It increases a number by a given parameter synchronously and runs a callback at the next execution loop. void IncreaseSync(const FunctionCallbackInfo& args) { auto isolate = args.GetIsolate(); @@ -57,12 +57,12 @@ void IncreaseSync(const FunctionCallbackInfo& args) { napa::zone::DoAsyncWork(Local::Cast(args[1]), [value](auto complete) { - // This runs at the same thread. + // This runs in the same thread. _now += value; complete(reinterpret_cast(static_cast(_now.load()))); }, [](auto jsCallback, void* result) { - // This runs at the same thread as one IncreaseSync() is called. + // This runs in the same thread as the one IncreaseSync() is called in. auto isolate = Isolate::GetCurrent(); int32_t argc = 1; diff --git a/examples/modules/plus-number/README.md b/examples/modules/plus-number/README.md index f9a340d0..89d5fa5d 100644 --- a/examples/modules/plus-number/README.md +++ b/examples/modules/plus-number/README.md @@ -11,7 +11,7 @@ var obj = addon.createPlusNumber(); ## Wrapped class -*plus-number.h* declares ths class with one constructor and one method, *Add()*. +*plus-number.h* declares the class with one constructor and one method, *Add()*. ```h namespace napa { diff --git a/examples/tutorial/estimate-pi-in-parallel/README.md b/examples/tutorial/estimate-pi-in-parallel/README.md index 45ea9476..a910dd35 100644 --- a/examples/tutorial/estimate-pi-in-parallel/README.md +++ b/examples/tutorial/estimate-pi-in-parallel/README.md @@ -4,9 +4,9 @@ This example implements an algorithm to [estimate PI using Monte Carlo method](h In the implementation, multiple batches of points are evaluated simultaneously in a [napa zone](https://github.com/Microsoft/napajs/wiki/introduction#zone) of 4 workers. Results are aggregated to calculate the final PI after all batches finishes. ## How to run -1. Go to directory of "examples/tutorial/estimate-pi-in-parallel" -2. Run "npm install" to install napajs -3. Run "node estimate-pi-in-parallel.js" +1. Go to directory of `examples/tutorial/estimate-pi-in-parallel` +2. Run `npm install` to install napajs +3. Run `node estimate-pi-in-parallel.js` ## Program output The table below shows results of PI calculated under different settings, each setting emulates 4,000,000 points evaluated by a napa zone of 4 workers. diff --git a/examples/tutorial/max-square-sub-matrix/README.md b/examples/tutorial/max-square-sub-matrix/README.md index c48f7e4e..5ee8ae1f 100644 --- a/examples/tutorial/max-square-sub-matrix/README.md +++ b/examples/tutorial/max-square-sub-matrix/README.md @@ -6,9 +6,9 @@ In this implementation, all units to be evaluated were divided into 2 * size of Please note that this example is to demonstrate the programming paradigm, while itself is NOT performance efficient, since each worker does too little CPU operation and major overhead is on communication. ## How to run -1. Go to directory of "examples/tutorial/max-square-sub-matrix" -2. Run "npm install" to install napajs -3. Run "node max-square-sub-matrix.js" +1. Go to directory of `examples/tutorial/max-square-sub-matrix` +2. Run `npm install` to install napajs +3. Run `node max-square-sub-matrix.js` **Note**: This example uses 'async / await', so Node version that supports ES6 is required. (newer than v7.6.0). diff --git a/examples/tutorial/napa-runner/README.md b/examples/tutorial/napa-runner/README.md index 031ee408..c42c1425 100644 --- a/examples/tutorial/napa-runner/README.md +++ b/examples/tutorial/napa-runner/README.md @@ -2,15 +2,15 @@ Napa runner is an example to embed Napa.JS into a C++ program. It simply runs JavaScript with all Napa capability without Node dependency from command line. ## How to build -1. Go to napajs root directory, run "node build.js embed" to build napa library for embeded mode. +1. Go to napajs root directory and run `node build.js embed` to build napa library for embeded mode. -**NOTE**: This step may take about 30 mins, because it will build V8 libray from Node. We are using node v6.10.3, a stable version can build as a library. It is specified in [embedded.js](https://github.com/Microsoft/napajs/blob/master/scripts/embedded.js) and [napa-runner CMakeLists.txt](https://github.com/Microsoft/napajs/blob/master/examples/tutorial/napa-runner/CMakeLists.txt). Please update both of them if you want to use a different version of Node/V8. +**NOTE**: This step may take about 30 mins, because it will build V8 library from Node. We are using node v6.10.3, a stable version can build as a library. It is specified in [embedded.js](https://github.com/Microsoft/napajs/blob/master/scripts/embedded.js) and [napa-runner CMakeLists.txt](https://github.com/Microsoft/napajs/blob/master/examples/tutorial/napa-runner/CMakeLists.txt). Please update both of them if you want to use a different version of Node/V8. -2. Go to directory of "examples/tutorial/napa-runner", and run "cmake-js build" to build napa runner +2. Go to directory of `examples/tutorial/napa-runner`, and run `cmake-js build` to build napa runner **NOTE**: Build solution of napa-runner is provided only for linux system so far. Windows / Mac OS support will come in near future. ## How to use -1. Run "npm install" to intall required npm modules -2. Run "./bin/napa-runner emstimate-PI.js" +1. Run `npm install` to intall required npm modules +2. Run `./bin/napa-runner emstimate-PI.js` diff --git a/examples/tutorial/parallel-quick-sort/README.md b/examples/tutorial/parallel-quick-sort/README.md index 269bad58..50df74b7 100644 --- a/examples/tutorial/parallel-quick-sort/README.md +++ b/examples/tutorial/parallel-quick-sort/README.md @@ -4,9 +4,9 @@ This example implements a parallel version of quicksort by napa.js, and compares 2. How does napa.js accelerate a computation heavy task by parallelization. ## How to run -1. Go to directory of "examples/tutorial/parallel-quick-sort" -2. Run "npm install" to install napajs -3. Run "node parallel-quick-sort.js" +1. Go to directory of `examples/tutorial/parallel-quick-sort`. +2. Run `npm install` to install napajs. +3. Run `node parallel-quick-sort.js`. **Note**: This example involves TypedArray and SharedArrayBuffer. It requires Node v9.0.0 or newer. diff --git a/examples/tutorial/recursive-fibonacci/README.md b/examples/tutorial/recursive-fibonacci/README.md index 75df8460..ca0771c0 100644 --- a/examples/tutorial/recursive-fibonacci/README.md +++ b/examples/tutorial/recursive-fibonacci/README.md @@ -6,9 +6,9 @@ Recursion is supported on a single thread by JavaScript language, how if we want Please note that this example is to demonstrate the programming paradigm, while itself is *NOT* performance efficient, since each worker does too little CPU operation (simply '+') and major overhead is on communication. ## How to run -1. Go to directory of "examples/tutorial/recursive-fibonacci" -2. Run "npm install" to install napajs -3. Run "node recursive-fibonacci.js" +1. Go to directory of `examples/tutorial/recursive-fibonacci` +2. Run `npm install`to install napajs +3. Run `node recursive-fibonacci.js` ## Program output Table below shows statistics of calculating Nth Fibonacci number by a napa zone with 4 workers. diff --git a/examples/tutorial/synchronized-loading/README.md b/examples/tutorial/synchronized-loading/README.md index e1acf60e..5160f042 100644 --- a/examples/tutorial/synchronized-loading/README.md +++ b/examples/tutorial/synchronized-loading/README.md @@ -4,9 +4,9 @@ This example implements a shared phone book component. The component will not lo The component is implemented using lazy-loading pattern with the use of [`napa.sync.Lock`](./../../../docs/api/sync.md#interface-lock). ## How to run -1. Go to directory of "examples/tutorial/synchronized-loading" -2. Run "npm install" to install napajs -3. Run "node synchronized-loading.js" +1. Go to directory of `examples/tutorial/synchronized-loading` +2. Run `npm install` to install napajs +3. Run `node synchronized-loading.js` ## Program output The output below shows one possible result. The sequence of the output may be different but the data loading will always run once only. diff --git a/package.json b/package.json index ba2314bc..a305c5b0 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "semver": "^5.5.0" }, "devDependencies": { + "cmake-js": "^3.7.3", "node-pre-gyp-github": "^1.3.1", "mocha": "^3.5.0", "typescript": "^2.4.2", diff --git a/scripts/embedded.js b/scripts/embedded.js index bba1f875..5c7f139c 100644 --- a/scripts/embedded.js +++ b/scripts/embedded.js @@ -47,7 +47,7 @@ exports.build = function (buildType) { } else { // TODO (asib): support other platforms - console.log("\x1b[1m\x1b[32m", "Napa build solution for embeded mode is not provided for ", os.platform(),'\x1b[0m'); + console.log("\x1b[1m\x1b[32m", "Napa build solution for embedded mode is not provided for ", os.platform(),'\x1b[0m'); return; } diff --git a/src/api/capi.cpp b/src/api/capi.cpp index 2431c45a..83807b9e 100644 --- a/src/api/capi.cpp +++ b/src/api/capi.cpp @@ -25,7 +25,7 @@ using namespace napa; static std::atomic _initialized(false); static settings::PlatformSettings _platformSettings; -/// a simple wrapper around Zone for managing lifetime using shared_ptr. +/// A simple wrapper around Zone for managing lifetime using shared_ptr. struct napa_zone { std::string id; std::shared_ptr zone; @@ -40,7 +40,7 @@ napa_zone_handle napa_zone_create(napa_string_ref id) { napa_zone_handle napa_zone_get(napa_string_ref id) { NAPA_ASSERT(_initialized, "Napa wasn't initialized"); - + auto zoneId = NAPA_STRING_REF_TO_STD_STRING(id); std::shared_ptr zone; if (zoneId == "node") { @@ -105,7 +105,7 @@ napa_result_code napa_zone_release(napa_zone_handle handle) { napa_string_ref napa_zone_get_id(napa_zone_handle handle) { NAPA_ASSERT(_initialized, "Napa platform wasn't initialized"); NAPA_ASSERT(handle, "Zone handle is null"); - + return STD_STRING_TO_NAPA_STRING_REF(handle->id); } @@ -120,14 +120,14 @@ void napa_zone_broadcast(napa_zone_handle handle, FunctionSpec req; req.module = spec.module; req.function = spec.function; - + req.arguments.reserve(spec.arguments_count); for (size_t i = 0; i < spec.arguments_count; i++) { req.arguments.emplace_back(spec.arguments[i]); } req.options = spec.options; - + // Assume ownership of transport context req.transportContext.reset(reinterpret_cast(spec.transport_context)); @@ -137,7 +137,7 @@ void napa_zone_broadcast(napa_zone_handle handle, res.code = result.code; res.error_message = STD_STRING_TO_NAPA_STRING_REF(result.errorMessage); res.return_value = STD_STRING_TO_NAPA_STRING_REF(result.returnValue); - + // Release ownership of transport context res.transport_context = reinterpret_cast(result.transportContext.release()); @@ -156,14 +156,14 @@ void napa_zone_execute(napa_zone_handle handle, FunctionSpec req; req.module = spec.module; req.function = spec.function; - + req.arguments.reserve(spec.arguments_count); for (size_t i = 0; i < spec.arguments_count; i++) { req.arguments.emplace_back(spec.arguments[i]); } req.options = spec.options; - + // Assume ownership of transport context req.transportContext.reset(reinterpret_cast(spec.transport_context)); @@ -172,7 +172,7 @@ void napa_zone_execute(napa_zone_handle handle, res.code = result.code; res.error_message = STD_STRING_TO_NAPA_STRING_REF(result.errorMessage); res.return_value = STD_STRING_TO_NAPA_STRING_REF(result.returnValue); - + // Release ownership of transport context res.transport_context = reinterpret_cast(result.transportContext.release()); @@ -264,9 +264,9 @@ namespace { } // namespace void napa_allocator_set( - napa_allocate_callback allocate_callback, + napa_allocate_callback allocate_callback, napa_deallocate_callback deallocate_callback) { - + NAPA_ASSERT(allocate_callback != nullptr, "'allocate_callback' should be a valid function."); NAPA_ASSERT(deallocate_callback != nullptr, "'deallocate_callback' should be a valid function."); diff --git a/src/module/loader/javascript-module-loader.cpp b/src/module/loader/javascript-module-loader.cpp index 290d32f4..33329462 100644 --- a/src/module/loader/javascript-module-loader.cpp +++ b/src/module/loader/javascript-module-loader.cpp @@ -49,7 +49,16 @@ bool JavascriptModuleLoader::TryGet(const std::string& path, v8::Local wrappedSource = v8::String::Concat( + v8_helpers::MakeV8String(isolate, "(function(){"), + v8::String::Concat( + source, + v8_helpers::MakeV8String(isolate, "}).apply(module.exports);") + ) + ); + auto script = v8::Script::Compile(wrappedSource, &origin); if (script.IsEmpty() || tryCatch.HasCaught()) { tryCatch.ReThrow(); return false; diff --git a/src/v8-extensions/deserializer.cpp b/src/v8-extensions/deserializer.cpp index c24071af..c15a1056 100644 --- a/src/v8-extensions/deserializer.cpp +++ b/src/v8-extensions/deserializer.cpp @@ -26,6 +26,8 @@ MaybeLocal Deserializer::ReadValue() { return MaybeLocal(); } +#if !V8_VERSION_EQUALS_TO_OR_NEWER_THAN(6, 6) + uint32_t index = 0; Local key = v8_helpers::MakeV8String(_isolate, "_externalized"); for (const auto& contents : _data->GetExternalizedSharedArrayBufferContents()) { @@ -40,10 +42,38 @@ MaybeLocal Deserializer::ReadValue() { _deserializer.TransferSharedArrayBuffer(index++, sharedArrayBuffers); } +#endif + return _deserializer.ReadValue(context); } -Deserializer* Deserializer::NewDeserializer(v8::Isolate* isolate, std::shared_ptr data) { +#if V8_VERSION_EQUALS_TO_OR_NEWER_THAN(6, 6) + +MaybeLocal Deserializer::GetSharedArrayBufferFromId( + Isolate* isolate, uint32_t cloneId) { + if (_data && cloneId < _data->GetExternalizedSharedArrayBufferContents().size()) { + auto externalizedSharedArrayBufferContents = _data->GetExternalizedSharedArrayBufferContents().at(cloneId); + SharedArrayBuffer::Contents contents = externalizedSharedArrayBufferContents.first; + auto sharedArrayBuffer = SharedArrayBuffer::New(isolate, contents.Data(), contents.ByteLength()); + + // After deserialization of a SharedArrayBuffer from its SerializedData, + // set its '_externalized' property to a ShareableWrap of its ExternalizedContents. + // This extends the lifecycle of the ExternalizedContents + // by the lifetime of the restored SharedArrayBuffer object. + Local context = _isolate->GetCurrentContext(); + Local key = v8_helpers::MakeV8String(_isolate, "_externalized"); + auto shareableWrap = napa::module::binding::CreateShareableWrap(externalizedSharedArrayBufferContents.second); + sharedArrayBuffer->CreateDataProperty(context, key, shareableWrap); + return sharedArrayBuffer; + } + else { + return MaybeLocal(); + } +} + +#endif + +Deserializer* Deserializer::NewDeserializer(Isolate* isolate, std::shared_ptr data) { return new Deserializer(isolate, data); } diff --git a/src/v8-extensions/deserializer.h b/src/v8-extensions/deserializer.h index 38e9c4ee..406c40c2 100644 --- a/src/v8-extensions/deserializer.h +++ b/src/v8-extensions/deserializer.h @@ -24,7 +24,15 @@ namespace v8_extensions { v8::MaybeLocal ReadValue(); - static Deserializer* NewDeserializer(v8::Isolate* isolate, std::shared_ptr data); +#if V8_VERSION_EQUALS_TO_OR_NEWER_THAN(6, 6) + + v8::MaybeLocal GetSharedArrayBufferFromId( + v8::Isolate* isolate, uint32_t cloneId) override; + +#endif + + static Deserializer* NewDeserializer( + v8::Isolate* isolate, std::shared_ptr data); private: v8::Isolate* _isolate; diff --git a/test/module-test.ts b/test/module-test.ts index 1a2e57e1..e809cd8b 100644 --- a/test/module-test.ts +++ b/test/module-test.ts @@ -177,7 +177,7 @@ describe('napajs/module', function () { var fs = require('fs'); assert(fs.existsSync(__dirname + '/module/jsmodule.js')); - assert.ifError(fs.existsSync(__dirname + '/non-existing-file.txt')); + assert(!fs.existsSync(__dirname + '/non-existing-file.txt')); }); }); diff --git a/test/transport-test.ts b/test/transport-test.ts index 924293f5..87bdbf2a 100644 --- a/test/transport-test.ts +++ b/test/transport-test.ts @@ -341,12 +341,11 @@ describe('napajs/transport', () => { let builtinTestGroup = 'Transport built-in objects'; let nodeVersionMajor = parseInt(process.versions.node.split('.')[0]); - - // TODO #223: Fix transportation support for builtin types, which is broken due to breaking changes of v8 introduced from node v10.0.0. - if (nodeVersionMajor == 9) { + + if (nodeVersionMajor >= 9) { describe(builtinTestGroup, transportBuiltinObjects); } else { describe.skip(builtinTestGroup, transportBuiltinObjects); - require('npmlog').warn(builtinTestGroup, 'This test group is skipped since it requires node v9.0.0 .'); + require('npmlog').warn(builtinTestGroup, 'This test group is skipped since it requires node v9.0.0 or above.'); } });