Skip to content

Commit

Permalink
Move wasm interface to function
Browse files Browse the repository at this point in the history
  • Loading branch information
jacogr committed Jan 10, 2018
1 parent be2305c commit f5f8edb
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 217 deletions.
33 changes: 11 additions & 22 deletions packages/client-wasm/src/create/imports.js
@@ -1,27 +1,16 @@
// ISC, Copyright 2017-2018 Jaco Greeff
// @flow

const runtime = require('@polkadot/client-wasm-runtime');
import type { RuntimeExports } from '@polkadot/client-wasm-runtime/types';

const createMemory = require('./memory');
const createTable = require('./table');

module.exports = function createImports (imports?: WebAssemblyImports = {}, initialMemory?: number, maximumMemory?: number): WebAssemblyImports {
imports.env = imports.env || {};
imports.env.memoryBase = imports.env.memoryBase || 0;
imports.env.tableBase = imports.env.tableBase || 0;

if (!imports.env.memory) {
// $FlowFixMe imports.env gets a value above
imports.env.memory = createMemory(initialMemory, maximumMemory);
}

if (!imports.env.table) {
// $FlowFixMe imports.env gets a value above
imports.env.table = createTable();
}

imports.env = Object.assign(imports.env, runtime(imports.env.memory));

return imports;
// flowlint-next-line unclear-type:off
module.exports = function createImports (memory: WebAssembly.Memory, table: WebAssembly.Table, runtime: RuntimeExports, imports?: Object = {}): WebAssemblyImports {
return Object.assign({}, imports, {
env: Object.assign({}, runtime, {
memory,
memoryBase: 0,
table,
tableBase: 0
})
});
};
116 changes: 21 additions & 95 deletions packages/client-wasm/src/create/imports.spec.js
@@ -1,116 +1,42 @@
// ISC, Copyright 2017-2018 Jaco Greeff

const runtime = require('@polkadot/client-wasm-runtime');
const isInstanceOf = require('@polkadot/util/is/instanceOf');

const { createImports } = require('./index');

describe('createImports', () => {
let imports;
let origWebAssembly;
let memOptions;

beforeEach(() => {
imports = {};
origWebAssembly = global.WebAssembly;

global.WebAssembly = class {
static Memory = class {
constructor (options) {
this.buffer = new Uint8Array(10);
memOptions = options;
}
};
static Table = class {};
};
});

afterEach(() => {
global.WebAssembly = origWebAssembly;
});

it('works when import has not been specified', () => {
it('sets the memoryBase & tableBase', () => {
expect(
createImports()
).toBeDefined();
});

it('exposes the default imports on env', () => {
expect(
createImports({}).env
).toMatchObject(
Object.keys(runtime).reduce((env, key) => {
env[key] = expect.anything();

return env;
}, {})
);
});

it('uses env.memoryBase when supplied', () => {
imports.env = { memoryBase: 'test' };
imports = createImports(imports);

expect(imports.env.memoryBase).toEqual('test');
});

it('uses env.tableBase when supplied', () => {
imports.env = { tableBase: 'test' };
imports = createImports(imports);

expect(imports.env.tableBase).toEqual('test');
});

it('uses env.memory when supplied', () => {
const memory = { buffer: new Uint8Array(10) };

imports.env = { memory };
imports = createImports(imports);

expect(imports.env.memory).toEqual(memory);
createImports({}, {}, {}).env
).toMatchObject({
memoryBase: 0,
tableBase: 0
});
});

it('creates env.memory when none supplied', () => {
imports = createImports(imports);
it('uses supplied memory', () => {
const memory = { 'some': { 'memory': { 'object': true } } };

expect(
isInstanceOf(
imports.env.memory, WebAssembly.Memory
)
).toEqual(true);
createImports(memory, {}, {}).env
).toMatchObject({
memory
});
});

it('creates env.memory using the supplied initial/maximum', () => {
createImports(imports, 4, 6);
it('uses supplied table', () => {
const table = { 'some': { 'table': { 'object': true } } };

expect(
memOptions
).toEqual({
initial: (4 * 1024) / 64,
maximum: (6 * 1024) / 64
createImports({}, table, {}).env
).toMatchObject({
table
});
});

it('uses env.table when supplied', () => {
imports.env = { table: 'test' };
imports = createImports(imports);

expect(imports.env.table).toEqual('test');
});

it('creates env.table when none supplied', () => {
imports = createImports(imports);
it('exposes the runtime imports on the env', () => {
const runtime = { 'ext_foo': 1, 'ext_bar': 2, 'ext_baz': 3 };

expect(
isInstanceOf(
imports.env.table, WebAssembly.Table
)
).toEqual(true);
});

it('sets default environment when none supplied', () => {
imports = createImports();

expect(imports.env).toBeDefined();
createImports({}, {}, runtime).env
).toMatchObject(runtime);
});
});
4 changes: 2 additions & 2 deletions packages/client-wasm/src/create/instance.js
@@ -1,6 +1,6 @@
// ISC, Copyright 2017-2018 Jaco Greeff
// @flow

module.exports = function createInstance (module: WebAssemblyModule, imports: WebAssemblyImports): WebAssemblyInstance {
return new WebAssembly.Instance(module, imports);
module.exports = function createInstance (wasmModule: WebAssemblyModule, imports: WebAssemblyImports): WebAssemblyInstance {
return new WebAssembly.Instance(wasmModule, imports);
};
4 changes: 2 additions & 2 deletions packages/client-wasm/src/index.js
@@ -1,6 +1,6 @@
// ISC, Copyright 2017-2018 Jaco Greeff
// @flow

const Wasm = require('./wasm');
const wasm = require('./wasm');

module.exports = Wasm;
module.exports = wasm;
36 changes: 13 additions & 23 deletions packages/client-wasm/src/wasm.js
@@ -1,31 +1,21 @@
// ISC, Copyright 2017-2018 Jaco Greeff
// @flow

const assert = require('@polkadot/util/assert');
const isObject = require('@polkadot/util/is/object');
import type { ConfigType } from '@polkadot/client/types';
import type { DbInterface } from '@polkadot/client-db/types';
import type { WasmExtraImports } from './types';

const { createImports, createInstance, createModule } = require('./create');
const createRuntime = require('@polkadot/client-wasm-runtime');

module.exports = class Wasm {
constructor (instance: WebAssemblyInstance) {
const exports = instance.exports;
const { createImports, createInstance, createMemory, createModule, createTable } = require('./create');

assert(isObject(exports), 'Expected function exports');
module.exports = function wasm ({ wasm: { memoryInitial, memoryMaximum } }: ConfigType, db: DbInterface, bytecode: Uint8Array, imports?: WasmExtraImports = {}): WebAssemblyInstance$Exports {
const memory = createMemory(memoryInitial, memoryMaximum);
const table = createTable();
const runtime = createRuntime(memory, db);

Object.keys(exports).forEach((key: string) => {
Object.defineProperty(this, key, {
configurable: false,
enumerable: true,
value: exports[key]
});
});
}

static fromCode (bytecode: Uint8Array, _imports?: WebAssemblyImports): Wasm {
const module = createModule(bytecode);
const imports = createImports(_imports);
const instance = createInstance(module, imports);

return new Wasm(instance);
}
return createInstance(
createModule(bytecode),
createImports(memory, table, runtime, imports)
).exports;
};
109 changes: 37 additions & 72 deletions packages/client-wasm/src/wasm.spec.js
@@ -1,95 +1,60 @@
// ISC, Copyright 2017-2018 Jaco Greeff

const isInstanceOf = require('@polkadot/util/is/instanceOf');

const { loadWasm } = require('../test/helpers');
const Wasm = require('./wasm');
const wasm = require('./wasm');

describe('Wasm', () => {
describe('checks', () => {
let origWebAssembly;
describe('wasm', () => {
let instance;

describe('valid modules', () => {
beforeEach(() => {
origWebAssembly = global.WebAssembly;

global.WebAssembly = class {
static Module = class {};
static Memory = class {};
static Table = class {};
static Instance = class {};
};
});

afterEach(() => {
global.WebAssembly = origWebAssembly;
instance = wasm(
{ wasm: {} }, {},
loadWasm('addTwo.wasm')
);
});

it('disallows empty exports', () => {
it('allows calls into the module', () => {
expect(
() => new Wasm(new WebAssembly.Instance())
).toThrow(/Expected function exports/);
instance.addTwo(22, 33)
).toEqual(55);
});
});

describe('instance', () => {
let wasm;
describe('imports', () => {
let callback;
let instance;

describe('valid modules', () => {
beforeEach(() => {
wasm = Wasm.fromCode(
loadWasm('addTwo.wasm')
);
});

it('creates a valid instance via fromCode', () => {
expect(
isInstanceOf(wasm, Wasm)
).toEqual(true);
});

it('allows calls into the module', () => {
expect(
wasm.addTwo(22, 33)
).toEqual(55);
});
beforeEach(() => {
callback = jest.fn();
instance = wasm(
{ wasm: {} }, {},
loadWasm('import.wasm'),
{ js: { callback } }
);
});

describe('imports', () => {
let callback;
let wasm;
it('allows imports to be called', () => {
instance.go(123);

beforeEach(() => {
callback = jest.fn();
wasm = Wasm.fromCode(
loadWasm('import.wasm'),
{ js: { callback } }
);
});

it('allows imports to be called', () => {
wasm.go(123);

expect(callback).toHaveBeenCalledWith(123);
});
expect(callback).toHaveBeenCalledWith(123);
});
});

describe('start', () => {
let callback;
// let wasm;

beforeEach(() => {
callback = jest.fn();
Wasm.fromCode(
loadWasm('start.wasm'),
{ js: { callback } }
);
});
describe('start', () => {
let callback;

it('allows imports to be called', () => {
// wasm.go(123);
beforeEach(() => {
callback = jest.fn();
instance = wasm(
{ wasm: {} }, {},
loadWasm('start.wasm'),
{ js: { callback } }
);
});

expect(callback).toHaveBeenCalledWith(1337);
});
it('allows imports to be called', () => {
expect(callback).toHaveBeenCalledWith(1337);
});
});
});
4 changes: 3 additions & 1 deletion packages/client/src/types.js
Expand Up @@ -5,14 +5,16 @@ import type { ChainNameType } from '@polkadot/client-chains/types';
import type { DbConfigType } from '@polkadot/client-db/types';
import type { P2pConfigType } from '@polkadot/client-p2p/types';
import type { RpcConfigType, HandlerType } from '@polkadot/client-rpc/types';
import type { WasmConfigType } from '@polkadot/client-wasm/types';
import type { RoleType } from '@polkadot/primitives/role';

export type ConfigType = {
chain: ChainNameType,
db: DbConfigType,
p2p: P2pConfigType,
rpc: RpcConfigType,
roles: Array<RoleType>
roles: Array<RoleType>,
wasm: WasmConfigType
};

export type EndpointType = {
Expand Down

0 comments on commit f5f8edb

Please sign in to comment.