зеркало из https://github.com/microsoft/napajs.git
Merged PR 293569: Implement anonymous function execution.
1. Support function transport. 2. Implement zone.execute on function.
This commit is contained in:
Родитель
f41cb059be
Коммит
b8f58f7ca0
|
@ -1,9 +1,9 @@
|
|||
# Benchmark
|
||||
|
||||
## Summary:
|
||||
## Summary
|
||||
- JavaScript execution in napajs is on par with node, using the same version of V8, which is expected.
|
||||
- `zone.execute` scales linearly on number of workers, which is expected.
|
||||
- The overhead of calling `zone.execute` from node is around 0.1ms after warm-up, `zone.executeSync` is around 0.2ms.
|
||||
- The overhead of calling `zone.execute` from node is around 0.1ms after warm-up, `zone.executeSync` is around 0.2ms. The cost of using anonymous function is neglectable.
|
||||
- `transport.marshall` cost on small plain JavaScript values is about 3x of JSON.stringify.
|
||||
- The overhead of `store.set` and `store.get` is around 0.06ms plus transport overhead on the objecs.
|
||||
|
||||
|
@ -41,7 +41,7 @@ Transport overhead (#1, #3, #4, #7) varies by size and complexity of payload, wi
|
|||
Please refer to [execute-overhead.ts](./execute-overhead.ts) for test details.
|
||||
|
||||
### Overhead after warm-up
|
||||
Average overhead is around 0.06ms to 0.12ms for `zone.execute`, and around 0.16ms for `zone.executeSync`
|
||||
Average overhead is around 0.06ms to 0.12ms for `zone.execute`, and around 0.16ms for `zone.executeSync`.
|
||||
|
||||
| repeat | zone.execute (ms) | zone.executeSync (ms) |
|
||||
|----------|-------------------|-----------------------|
|
||||
|
@ -50,6 +50,8 @@ Average overhead is around 0.06ms to 0.12ms for `zone.execute`, and around 0.16m
|
|||
| 10000 | 810.687 | 1799.866 |
|
||||
| 50000 | 3387.361 | 8169.023 |
|
||||
|
||||
*10000 times of zone.executeSync on anonymouse function is 1780.241ms. The gap is within range of bench noise.
|
||||
|
||||
### Overhead during warm-up:
|
||||
|
||||
| Sequence of call | Time (ms) |
|
||||
|
|
|
@ -31,6 +31,13 @@ export function bench(zone: napa.zone.Zone): Promise<void> {
|
|||
}
|
||||
console.log(`Elapse of running empty function for ${REPEAT} times: ${formatTimeDiff(process.hrtime(start), true)}\n`);
|
||||
|
||||
console.log("## `zone.executeSync` overhead calling anonymous function\n");
|
||||
start = process.hrtime();
|
||||
for (let i = 0; i < REPEAT; ++i) {
|
||||
zone.executeSync(() => {}, ARGS);
|
||||
}
|
||||
console.log(`Elapse of running empty anonymous function for ${REPEAT} times: ${formatTimeDiff(process.hrtime(start), true)}\n`);
|
||||
|
||||
// execute after warm-up
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
let finished = 0;
|
||||
|
|
61
lib/store.ts
61
lib/store.ts
|
@ -1,59 +1,2 @@
|
|||
/// <summary> Store is a facility to share (built-in JavaScript types or Transportable subclasses) objects across isolates. </summary>
|
||||
export interface Store {
|
||||
/// <summary> Id of this store. </summary>
|
||||
readonly id: string;
|
||||
|
||||
/// <summary> Number of keys in this store. </summary>
|
||||
readonly size: number;
|
||||
|
||||
/// <summary> Check if this store has a key. </summary>
|
||||
/// <param name="key"> Case-sensitive string key. </summary>
|
||||
/// <returns> True if this store has the key. </returns>
|
||||
has(key: string): boolean;
|
||||
|
||||
/// <summary> Get JavaScript value by key. </summary>
|
||||
/// <param name="key"> Case-sensitive string key. </summary>
|
||||
/// <returns> Value for key, undefined if not found. </returns>
|
||||
get(key: string): any;
|
||||
|
||||
/// <summary> Insert or update a JavaScript value by key. </summary>
|
||||
/// <param name="key"> Case-sensitive string key. </summary>
|
||||
/// <param name="value"> Value. Any value of built-in JavaScript types or Transportable subclasses can be accepted. </summary>
|
||||
set(key: string, value: any): void;
|
||||
|
||||
/// <summary> Remove a key with its value from this store. </summary>
|
||||
/// <param name="key"> Case-sensitive string key. </summary>
|
||||
delete(key: string): void;
|
||||
}
|
||||
|
||||
let binding = require('./binding');
|
||||
|
||||
/// <summary> Create a store with an id. </summary>
|
||||
/// <param name="id"> String identifier which can be used to get the store from all isolates. </summary>
|
||||
/// <returns> A store object or throws Error if store with this id already exists. </returns>
|
||||
/// <remarks> Store object will be destroyed when reference from all isolates are unreferenced.
|
||||
/// It's usually a best practice to keep a long-living reference in user modules or global scope. </remarks>
|
||||
export function create(id: string): Store {
|
||||
return binding.createStore(id);
|
||||
}
|
||||
|
||||
/// <summary> Get a store with an id. </summary>
|
||||
/// <param name="id"> String identifier which is passed to create/getOrCreate earlier. </summary>
|
||||
/// <returns> A store object if exists, otherwise undefined. </returns>
|
||||
export function get(id: string): Store {
|
||||
return binding.getStore(id);
|
||||
}
|
||||
|
||||
/// <summary> Get a store with an id, or create it if not exist. </summary>
|
||||
/// <param name="id"> String identifier which can be used to get the store from all isolates. </summary>
|
||||
/// <returns> A store object associated with the id. </returns>
|
||||
/// <remarks> Store object will be destroyed when reference from all isolates are unreferenced.
|
||||
/// It's usually a best practice to keep a long-living reference in user modules or global scope. </remarks>
|
||||
export function getOrCreate(id: string): Store {
|
||||
return binding.getOrCreateStore(id);
|
||||
}
|
||||
|
||||
/// <summary> Returns number of stores that is alive. </summary>
|
||||
export function count(): number {
|
||||
return binding.getStoreCount();
|
||||
}
|
||||
export * from './store/store';
|
||||
export * from './store/store-api';
|
|
@ -0,0 +1,33 @@
|
|||
import { Store } from './store';
|
||||
|
||||
let binding = require('../binding');
|
||||
|
||||
/// <summary> Create a store with an id. </summary>
|
||||
/// <param name="id"> String identifier which can be used to get the store from all isolates. </summary>
|
||||
/// <returns> A store object or throws Error if store with this id already exists. </returns>
|
||||
/// <remarks> Store object will be destroyed when reference from all isolates are unreferenced.
|
||||
/// It's usually a best practice to keep a long-living reference in user modules or global scope. </remarks>
|
||||
export function create(id: string): Store {
|
||||
return binding.createStore(id);
|
||||
}
|
||||
|
||||
/// <summary> Get a store with an id. </summary>
|
||||
/// <param name="id"> String identifier which is passed to create/getOrCreate earlier. </summary>
|
||||
/// <returns> A store object if exists, otherwise undefined. </returns>
|
||||
export function get(id: string): Store {
|
||||
return binding.getStore(id);
|
||||
}
|
||||
|
||||
/// <summary> Get a store with an id, or create it if not exist. </summary>
|
||||
/// <param name="id"> String identifier which can be used to get the store from all isolates. </summary>
|
||||
/// <returns> A store object associated with the id. </returns>
|
||||
/// <remarks> Store object will be destroyed when reference from all isolates are unreferenced.
|
||||
/// It's usually a best practice to keep a long-living reference in user modules or global scope. </remarks>
|
||||
export function getOrCreate(id: string): Store {
|
||||
return binding.getOrCreateStore(id);
|
||||
}
|
||||
|
||||
/// <summary> Returns number of stores that is alive. </summary>
|
||||
export function count(): number {
|
||||
return binding.getStoreCount();
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/// <summary> Store is a facility to share (built-in JavaScript types or Transportable subclasses) objects across isolates. </summary>
|
||||
export interface Store {
|
||||
/// <summary> Id of this store. </summary>
|
||||
readonly id: string;
|
||||
|
||||
/// <summary> Number of keys in this store. </summary>
|
||||
readonly size: number;
|
||||
|
||||
/// <summary> Check if this store has a key. </summary>
|
||||
/// <param name="key"> Case-sensitive string key. </summary>
|
||||
/// <returns> True if this store has the key. </returns>
|
||||
has(key: string): boolean;
|
||||
|
||||
/// <summary> Get JavaScript value by key. </summary>
|
||||
/// <param name="key"> Case-sensitive string key. </summary>
|
||||
/// <returns> Value for key, undefined if not found. </returns>
|
||||
get(key: string): any;
|
||||
|
||||
/// <summary> Insert or update a JavaScript value by key. </summary>
|
||||
/// <param name="key"> Case-sensitive string key. </summary>
|
||||
/// <param name="value"> Value. Any value of built-in JavaScript types or Transportable subclasses can be accepted. </summary>
|
||||
set(key: string, value: any): void;
|
||||
|
||||
/// <summary> Remove a key with its value from this store. </summary>
|
||||
/// <param name="key"> Case-sensitive string key. </summary>
|
||||
delete(key: string): void;
|
||||
}
|
|
@ -10,10 +10,14 @@ export * from './transport/transport';
|
|||
|
||||
import { Handle } from './memory/handle';
|
||||
import { TransportContext } from './transport/transportable';
|
||||
import * as functionTransporter from './transport/function-transporter';
|
||||
|
||||
let binding = require('./binding');
|
||||
|
||||
/// <summary> Create a transport context. </summary>
|
||||
export function createTransportContext(handle? : Handle): TransportContext {
|
||||
return new binding.TransportContextWrap(handle);
|
||||
}
|
||||
}
|
||||
|
||||
export let saveFunction = functionTransporter.save;
|
||||
export let loadFunction = functionTransporter.load;
|
|
@ -0,0 +1,76 @@
|
|||
////////////////////////////////////////////////////////////////////////
|
||||
// Module to support function transport.
|
||||
|
||||
import { Store } from '../store/store';
|
||||
import * as assert from 'assert';
|
||||
|
||||
/// <summary> Function hash to function cache. </summary>
|
||||
let _hashToFunctionCache: {[hash: string]: (...args: any[]) => any} = {};
|
||||
|
||||
/// <summary> Function to hash cache. </summary>
|
||||
/// <remarks> Function cannot be used as a key in TypeScript. </remarks>
|
||||
let _functionToHashCache: any = {};
|
||||
|
||||
/// <summary> Marshalled function body cache. </summary>
|
||||
let _store: Store;
|
||||
|
||||
/// <summary> Get underlying store to save marshall function body across isolates. </summary>
|
||||
function getStore(): Store {
|
||||
if (_store == null) {
|
||||
// Lazy creation function store
|
||||
// 1) avoid circular runtime dependency between store and transport.
|
||||
// 2) avoid unnecessary cost when function transport is not used.
|
||||
_store = require('../store/store-api')
|
||||
.getOrCreate('__napajs_marshalled_functions');
|
||||
}
|
||||
return _store;
|
||||
}
|
||||
|
||||
/// <summary> Save function and get a hash string to use it later. </summary>
|
||||
export function save(func: (...args: any[]) => any): string {
|
||||
let hash = _functionToHashCache[(<any>(func))];
|
||||
if (hash == null) {
|
||||
// Should happen only on first marshall of input function in current isolate.
|
||||
let body = func.toString();
|
||||
hash = getFunctionHash(body);
|
||||
getStore().set(hash, body);
|
||||
cacheFunction(hash, func);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary> Load a function with a hash retrieved from `save`. </summary>
|
||||
export function load(hash: string): (...args: any[]) => any {
|
||||
let func = _hashToFunctionCache[hash];
|
||||
if (func == null) {
|
||||
// Should happen only on first unmarshall of given hash in current isolate..
|
||||
let body = getStore().get(hash);
|
||||
if (body == null) {
|
||||
throw new Error(`Function hash cannot be found: ${hash}`);
|
||||
}
|
||||
func = eval(`(${body})`);
|
||||
cacheFunction(hash, func);
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
/// <summary> Cache function with its hash in current isolate. </summary>
|
||||
function cacheFunction(hash: string, func: (...args: any[]) => any) {
|
||||
_functionToHashCache[<any>(func)] = hash;
|
||||
_hashToFunctionCache[hash] = func;
|
||||
}
|
||||
|
||||
/// <summary> Generate hash for function definition using DJB2 algorithm.
|
||||
/// See: https://en.wikipedia.org/wiki/DJB2
|
||||
/// </summary>
|
||||
function getFunctionHash(functionDef: string): string {
|
||||
let hash = 5381;
|
||||
for (let i = 0; i < functionDef.length; ++i) {
|
||||
hash = (hash * 33) ^ functionDef.charCodeAt(i);
|
||||
}
|
||||
|
||||
/* JavaScript does bitwise operations (like XOR, above) on 32-bit signed
|
||||
* integers. Since we want the results to be always positive, convert the
|
||||
* signed int to an unsigned by doing an unsigned bitshift. */
|
||||
return (hash >>> 0).toString(16);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import * as transportable from './transportable';
|
||||
|
||||
import * as functionTransporter from './function-transporter';
|
||||
import * as path from 'path';
|
||||
|
||||
/// <summary> Per-isolate cid => constructor registry. </summary>
|
||||
|
@ -25,7 +25,7 @@ export function register(subClass: new(...args: any[]) => any) {
|
|||
|
||||
/// <summary> Marshall transform a JS value to a plain JS value that will be stringified. </summary>
|
||||
export function marshallTransform(jsValue: any, context: transportable.TransportContext): any {
|
||||
if (jsValue != null && typeof jsValue === 'object' && !Array.isArray(jsValue)) {
|
||||
if (jsValue != null && typeof jsValue === 'object' && !Array.isArray(jsValue)) {
|
||||
let constructorName = Object.getPrototypeOf(jsValue).constructor.name;
|
||||
if (constructorName !== 'Object') {
|
||||
if (typeof jsValue['cid'] === 'function') {
|
||||
|
@ -45,6 +45,9 @@ export function marshallTransform(jsValue: any, context: transportable.Transport
|
|||
function unmarshallTransform(payload: any, context: transportable.TransportContext): any {
|
||||
if (payload != null && payload._cid !== undefined) {
|
||||
let cid = payload._cid;
|
||||
if (cid === 'function') {
|
||||
return functionTransporter.load(payload.hash);
|
||||
}
|
||||
let subClass = _registry.get(cid);
|
||||
if (subClass == null) {
|
||||
throw new Error(`Unrecognized Constructor ID (cid) "${cid}". Please ensure @cid is applied on the class or transport.register is called on the class.`);
|
||||
|
@ -58,41 +61,33 @@ function unmarshallTransform(payload: any, context: transportable.TransportConte
|
|||
|
||||
/// <summary> Unmarshall from JSON string to a JavaScript value, which could contain complex/native objects. </summary>
|
||||
/// <param name="json"> JSON string. </summary>
|
||||
/// <param name="reviver"> Reviver that transform parsed values into new values. </param>
|
||||
/// <param name="context"> Transport context to save shared pointers. </param>
|
||||
/// <returns> Parsed JavaScript value, which could be built-in JavaScript types or deserialized Transportable objects. </returns>
|
||||
export function unmarshall(
|
||||
json: string,
|
||||
context: transportable.TransportContext,
|
||||
reviver?: (key: any, value: any) => any): any {
|
||||
context: transportable.TransportContext): any {
|
||||
|
||||
return JSON.parse(json,
|
||||
(key: any, value: any): any => {
|
||||
value = unmarshallTransform(value, context);
|
||||
if (reviver != null) {
|
||||
value = reviver(key, value);
|
||||
}
|
||||
return value;
|
||||
return unmarshallTransform(value, context);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary> Marshall a JavaScript value to JSON. </summary>
|
||||
/// <param name="jsValue"> JavaScript value to stringify, which maybe built-in JavaScript types or transportable objects. </param>
|
||||
/// <param name="replacer"> Replace JS value with transformed value before writing to string. </param>
|
||||
/// <param name="space"> Space used to format JSON. </param>
|
||||
/// <param name="context"> Transport context to save shared pointers. </param>
|
||||
/// <returns> JSON string. </returns>
|
||||
export function marshall(
|
||||
jsValue: any,
|
||||
context: transportable.TransportContext,
|
||||
replacer?: (key: string, value: any) => any,
|
||||
space?: string | number): string {
|
||||
|
||||
context: transportable.TransportContext): string {
|
||||
|
||||
// Function is transportable only as root object.
|
||||
// This is to avoid unexpected marshalling on member functions.
|
||||
if (typeof jsValue === 'function') {
|
||||
return `{"_cid": "function", "hash": "${functionTransporter.save(jsValue)}"}`;
|
||||
}
|
||||
return JSON.stringify(jsValue,
|
||||
(key: string, value: any) => {
|
||||
value = marshallTransform(value, context);
|
||||
if (replacer) {
|
||||
value = replacer(key, value);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
space);
|
||||
}
|
||||
return marshallTransform(value, context);
|
||||
});
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import * as zone from "./zone";
|
||||
import * as transport from "../transport";
|
||||
import * as zone from './zone';
|
||||
import * as transport from '../transport';
|
||||
|
||||
declare var __in_napa: boolean;
|
||||
|
||||
|
@ -136,20 +136,32 @@ export class NapaZone implements zone.Zone {
|
|||
}
|
||||
|
||||
private createExecuteRequest(arg1: any, arg2: any, arg3?: any, arg4?: any) : ExecuteRequest {
|
||||
if (arg1 instanceof Function) {
|
||||
throw new Error("Execute with function is not implemented");
|
||||
|
||||
let moduleName: string = null;
|
||||
let functionName: string = null;
|
||||
let args: any[] = null;
|
||||
let timeout: number = 0;
|
||||
|
||||
if (typeof arg1 === 'function') {
|
||||
moduleName = "__function";
|
||||
functionName = transport.saveFunction(arg1);
|
||||
args = arg2;
|
||||
timeout = arg3;
|
||||
}
|
||||
else {
|
||||
moduleName = arg1;
|
||||
functionName = arg2;
|
||||
args = arg3;
|
||||
timeout = arg4;
|
||||
}
|
||||
|
||||
let transportContext: transport.TransportContext = transport.createTransportContext();
|
||||
let request : ExecuteRequest = {
|
||||
module: arg1,
|
||||
function: arg2,
|
||||
arguments: (<Array<any>>arg3).map(arg => { return transport.marshall(arg, transportContext); }),
|
||||
timeout: arg4,
|
||||
return {
|
||||
module: moduleName,
|
||||
function: functionName,
|
||||
arguments: (<Array<any>>args).map(arg => { return transport.marshall(arg, transportContext); }),
|
||||
timeout: timeout,
|
||||
transportContext: transportContext
|
||||
};
|
||||
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +1,31 @@
|
|||
var transport = require('napajs/lib/transport');
|
||||
|
||||
function __zone_execute__(moduleName, functionName, args, contextHandle) {
|
||||
var module = (moduleName == null || moduleName.length === 0)? this : require(moduleName);
|
||||
var func = module;
|
||||
if (functionName != null && functionName.length != 0) {
|
||||
var path = functionName.split('.');
|
||||
for (item of path) {
|
||||
func = func[item];
|
||||
if (func === undefined) {
|
||||
throw new Error("Cannot find function '" + functionName + "' in module '" + moduleName + "'");
|
||||
var module = null;
|
||||
if (moduleName == null || moduleName.length === 0) {
|
||||
module = this;
|
||||
} else if (moduleName !== '__function') {
|
||||
module = require(moduleName);
|
||||
}
|
||||
|
||||
var func = null;
|
||||
if (module != null) {
|
||||
func = module;
|
||||
if (functionName != null && functionName.length != 0) {
|
||||
var path = functionName.split('.');
|
||||
for (item of path) {
|
||||
func = func[item];
|
||||
if (func === undefined) {
|
||||
throw new Error("Cannot find function '" + functionName + "' in module '" + moduleName + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeof func !== 'function') {
|
||||
throw new Error("'" + functionName + "' in module '" + moduleName + "' is not a function");
|
||||
if (typeof func !== 'function') {
|
||||
throw new Error("'" + functionName + "' in module '" + moduleName + "' is not a function");
|
||||
}
|
||||
} else {
|
||||
// Anonymous function.
|
||||
func = transport.loadFunction(functionName);
|
||||
}
|
||||
|
||||
var transportContext = transport.createTransportContext(contextHandle);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
<QCustomInput Include="$(NapaVanillaRoot)\lib\core\core-modules.json" />
|
||||
<QCustomInput Include="$(NapaVanillaRoot)\package.json" />
|
||||
<QCustomInput Include="$(NapaVanillaRoot)\README.md" />
|
||||
<QCustomInput Include="$(NapaVanillaRoot)\docs\**\*" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
@ -49,7 +50,13 @@
|
|||
<DeclarationFiles Include="$(NapaVanillaRoot)\lib\$(IntermediateOutputPath)\**\*.d.ts"/>
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(DeclarationFiles)" DestinationFiles="@(DeclarationFiles->'$(PackageOutPath)\types\%(RecursiveDir)%(Filename)%(Extension)')" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
|
||||
|
||||
<!-- ./docs/*.* -->
|
||||
<ItemGroup>
|
||||
<DocFiles Include="$(NapaVanillaRoot)\docs\**\*.*"/>
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(DocFiles)" DestinationFiles="@(DocFiles->'$(PackageOutPath)\doc\%(RecursiveDir)%(Filename)%(Extension)')" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
|
||||
<!-- Setup ./inc directory -->
|
||||
<ItemGroup>
|
||||
<NapaIncFiles Include="$(NapaVanillaRoot)\inc\**\*"/>
|
||||
|
|
|
@ -56,7 +56,13 @@ export function executeTestFunction(id: string): any {
|
|||
// TODO: replace with execute when TODO:#3 is done.
|
||||
return zone.executeSync((input: string) => {
|
||||
return input;
|
||||
}, ['hello world']);
|
||||
}, ['hello world']).value;
|
||||
}
|
||||
|
||||
export function executeTestFunctionWithClosure(id: string): any {
|
||||
let zone = napa.zone.get(id);
|
||||
// TODO: replace with execute when TODO:#3 is done.
|
||||
return zone.executeSync(() => { return zone; }, []).value;
|
||||
}
|
||||
|
||||
/// <summary> Memory test helpers. </summary>
|
||||
|
@ -169,10 +175,14 @@ export function simpleTypeTransportTest() {
|
|||
});
|
||||
}
|
||||
|
||||
export function jsTransportableTest() {
|
||||
export function jsTransportTest() {
|
||||
testMarshallUnmarshall(new CanPass(napa.memory.crtAllocator));
|
||||
}
|
||||
|
||||
export function functionTransportTest() {
|
||||
testMarshallUnmarshall(() => { return 0; });
|
||||
}
|
||||
|
||||
export function addonTransportTest() {
|
||||
testMarshallUnmarshall(napa.memory.debugAllocator(napa.memory.crtAllocator));
|
||||
}
|
||||
|
|
|
@ -86,6 +86,26 @@ describe('napajs/store', () => {
|
|||
assert.deepEqual(store1.get('f'), debugAllocator);
|
||||
});
|
||||
|
||||
it('function type: set in node, get in node', () => {
|
||||
store1.set('g', () => { return 0; });
|
||||
assert.equal(store1.get('g').toString(), (() => { return 0; }).toString());
|
||||
});
|
||||
|
||||
it('function type: set in node, get in napa', () => {
|
||||
store1.set('h', () => { return 0; });
|
||||
napaZone.executeSync(NAPA_ZONE_TEST_MODULE, "storeVerifyGet", ['store1', 'h', () => { return 0; }]);
|
||||
});
|
||||
|
||||
it('function type: set in napa, get in napa', () => {
|
||||
napaZone.executeSync(NAPA_ZONE_TEST_MODULE, "storeSet", ['store1', 'i', () => { return 0; }]);
|
||||
napaZone.executeSync(NAPA_ZONE_TEST_MODULE, "storeVerifyGet", ['store1', 'i', () => { return 0; }]);
|
||||
});
|
||||
|
||||
it('function type: set in napa, get in node', () => {
|
||||
napaZone.executeSync(NAPA_ZONE_TEST_MODULE, "storeSet", ['store1', 'j', () => { return 0; }]);
|
||||
assert.deepEqual(store1.get('j').toString(), (() => { return 0; }).toString());
|
||||
});
|
||||
|
||||
it('delete in node, check in node', () => {
|
||||
assert(store1.has('a'));
|
||||
store1.delete('a');
|
||||
|
@ -112,9 +132,9 @@ describe('napajs/store', () => {
|
|||
});
|
||||
|
||||
it('size', () => {
|
||||
// set 'a', 'b', 'c', 'd', 'a', 'b', 'e', 'f'.
|
||||
// set 'a', 'b', 'c', 'd', 'a', 'b', 'e', 'f', 'g', 'h', 'i', 'j'.
|
||||
// delete 'a', 'b', 'c', 'd'
|
||||
assert.equal(store1.size, 2);
|
||||
assert.equal(store1.size, 6);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -34,6 +34,7 @@ describe('napajs/transport', () => {
|
|||
assert(napa.transport.isTransportable([1, 2, new t.CanPass(napa.memory.crtAllocator)]));
|
||||
assert(napa.transport.isTransportable({ a: 1}));
|
||||
assert(napa.transport.isTransportable({ a: 1, b: new t.CanPass(napa.memory.crtAllocator)}));
|
||||
assert(napa.transport.isTransportable(() => { return 0; }));
|
||||
assert(!napa.transport.isTransportable(new t.CannotPass()));
|
||||
assert(!napa.transport.isTransportable([1, new t.CannotPass()]));
|
||||
assert(!napa.transport.isTransportable({ a: 1, b: new t.CannotPass()}));
|
||||
|
@ -50,11 +51,11 @@ describe('napajs/transport', () => {
|
|||
}).timeout(3000);
|
||||
|
||||
it('@node: JS transportable', () => {
|
||||
t.jsTransportableTest();
|
||||
t.jsTransportTest();
|
||||
});
|
||||
|
||||
it('@napa: JS transportable', () => {
|
||||
napaZone.executeSync(NAPA_ZONE_TEST_MODULE, "jsTransportableTest", []);
|
||||
napaZone.executeSync(NAPA_ZONE_TEST_MODULE, "jsTransportTest", []);
|
||||
});
|
||||
|
||||
it('@node: addon transportable', () => {
|
||||
|
@ -65,6 +66,14 @@ describe('napajs/transport', () => {
|
|||
napaZone.executeSync(NAPA_ZONE_TEST_MODULE, "addonTransportTest", []);
|
||||
});
|
||||
|
||||
it('@node: function transportable', () => {
|
||||
t.functionTransportTest();
|
||||
});
|
||||
|
||||
it('@napa: function transportable', () => {
|
||||
napaZone.executeSync(NAPA_ZONE_TEST_MODULE, "functionTransportTest", []);
|
||||
});
|
||||
|
||||
it('@node: composite transportable', () => {
|
||||
t.compositeTransportTest();
|
||||
});
|
||||
|
|
|
@ -484,7 +484,7 @@ describe('napajs/zone', function () {
|
|||
});
|
||||
});
|
||||
|
||||
// Blocked by TODO #1 and TODO #5.
|
||||
// Blocked by TODO #1.
|
||||
it.skip('@node: -> node zone with anonymous function', () => {
|
||||
return napa.zone.current.execute((input: string) => {
|
||||
return input;
|
||||
|
@ -494,8 +494,7 @@ describe('napajs/zone', function () {
|
|||
});
|
||||
});
|
||||
|
||||
// TODO #5: implment anonymous function in execute/executeSync.
|
||||
it.skip('@node: -> napa zone with anonymous function', () => {
|
||||
it('@node: -> napa zone with anonymous function', () => {
|
||||
return napaZone1.execute((input: string) => {
|
||||
return input;
|
||||
}, ['hello world'])
|
||||
|
@ -504,8 +503,7 @@ describe('napajs/zone', function () {
|
|||
});
|
||||
});
|
||||
|
||||
// Blocked by TODO #5.
|
||||
it.skip('@napa: -> napa zone with anonymous function', () => {
|
||||
it('@napa: -> napa zone with anonymous function', () => {
|
||||
return napaZone1.execute(napaZoneTestModule, 'executeTestFunction', ["napa-zone2"])
|
||||
.then((result: napa.zone.ExecuteResult) => {
|
||||
assert.equal(result.value, 'hello world');
|
||||
|
@ -524,14 +522,23 @@ describe('napajs/zone', function () {
|
|||
it.skip('@node: -> node zone with anonymous function having closure (should fail)', () => {
|
||||
});
|
||||
|
||||
it.skip('@node: -> napa zone with anonymous function having closure (should fail)', () => {
|
||||
it('@node: -> napa zone with anonymous function having closure (should fail)', () => {
|
||||
return shouldFail(() => {
|
||||
return napaZone1.execute(() => { return napaZone1; }, ['hello world'])
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('@napa: -> napa zone with anonymous function having closure (should fail)', () => {
|
||||
it('@napa: -> napa zone with anonymous function having closure (should fail)', () => {
|
||||
return shouldFail(() => {
|
||||
return napaZone1.execute(napaZoneTestModule, 'executeTestFunctionWithClosure', ["napa-zone2"]);
|
||||
});
|
||||
});
|
||||
|
||||
// Blocked by TODO #1.
|
||||
it.skip('@napa: -> node zone with anonymous function having closure (should fail)', () => {
|
||||
return shouldFail(() => {
|
||||
return napaZone1.execute(napaZoneTestModule, 'executeTestFunctionWithClosure', ["node"]);
|
||||
});
|
||||
});
|
||||
|
||||
// Blocked by TODO #1.
|
||||
|
|
Загрузка…
Ссылка в новой задаче