- Introduction
- API
isTransportable(jsValue: any): booleanregister(transportableClass: new(...args: any[]) => any): voidmarshall(jsValue: any, context: TransportContext): stringunmarshall(json: string, context: TransporteContext): any- class
TransportContext - interface
Transportable - abstract class
TransportableObjecttransportableObject.cid: stringtransportableObject.marshall(context: TransportContext): objecttransportableObject.unmarshall(payload: object, context: TransportContext): void- abstract
transportableObject.save(payload: object, context: TransportContext): void - abstract
transportableObject.load(payload: object, context: TransportContext): void
- decorator
cid
Existing JavaScript engines are not designed for running JavaScript across multiple VMs, which means every VM manages their own heap. Passing values from one VM to another has to be marshalled/unmarshalled. The size of payload and complexity of object will greatly impact communication efficiency. In Napa, we try to work out a design pattern for efficient object sharing, based on the fact that all JavaScript VMs (exposed as workers) reside in the same process, and native objects can be wrapped and exposed as JavaScripts objects.
Following concepts are introduced to implement this pattern:
Transportable types are JavaScript types that can be passed or shared transparently across Napa workers. They are used as value types for passing arguments in zone.broadcast / zone.execute, and sharing objects in key/value pairs via store.set / store.get.
Transportable types are:
- JavaScript primitive types: undefined, null, boolean, number, string
- Object (TypeScript class) that implement
Transportableinterface - Array or plain JavaScript object that is composite pattern of above.
- Function without referencing closures.
For user classes that implement 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.
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 to register a user Transportable class automatically, when using TypeScript with decorator feature enabled. Or call transport.register manually during module initialization.
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.
JavaScript function is a special transportable type, through marshalling its definition into a store, 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/__filenamecan be accessed in transported function, which is determined byoriginproperty of function. By defaultoriginproperty is set to current working directory.
It tells whether a JavaScript value is transportable or not.
// JS primitives
assert(transport.isTransportable(undefined));
assert(transport.isTransportable(null));
assert(transport.isTransportable(1));
assert(transport.isTransportable('string'));
assert(transport.isTransportable(true));
// Transportable addon
assert(transport.isTransportable(napa.memory.crtAllocator));
// Composite of transportable types.
assert(transport.isTransportable([
1,
"string",
{ a: napa.memory.crtAllocator }
]));
class B {
field1: number;
field2: string;
}
// Not transportable JS class. (not registered with @cid).
assert(!transport.isTransportable(new B()));Register a Transportable class before transport layer can marshall/unmarshall its instances.
User can also use class decorator @cid for class registration.
Example:
class A extends transport.AutoTransportable {
field1: string,
method1(): string {
return this.field1;
}
}
// Explicitly register class A in transport.
transport.register(A);Marshall a transportable JavaScript value into a JSON payload with a TransportContext. Error will be thrown if the value is not transportable.
Example:
var context = transport.createTransportContext();
var jsonPayload = transport.marshall(
[1, 'string', napa.memory.crtAllocator],
context);
console.log(jsonPayload);Unmarshall an transportable JavaScript value from a JSON payload with a TransportContext. Error will be thrown if cid property is found and not registered with transport layer.
Example:
var value = transport.unmarshall(jsonPayload, context);Class for Transport Context, that stores shared pointers and functions during marshall/unmarshall.
Save a shareable object in context.
Load a shareable object from handle.
Count of shareable objects saved in current context.
Interface for Transportable object.
Get accessor for Constructor ID. It is used to lookup constructor for payload of current class.
Marshall transform this object into a plain JavaScript object with the help of TransportContext.
Unmarshall transform marshalled payload into current object.
TBD
TBD