AVM client: move embedded wasm into a separate file (#200)

This commit is contained in:
Pavel 2021-12-28 17:43:54 +03:00 committed by GitHub
parent 63160dd0f0
commit 91021d8b40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 5876 additions and 52 deletions

View File

@ -6,3 +6,4 @@ wasm
# this file is auto-generated
src/wasm.js
src/importObject.ts
src/avm.wasm

View File

@ -2,11 +2,6 @@
New-Item -ItemType Directory -Force -Path ./wasm
wasm-pack build ../../air-interpreter --no-typescript --release -d ../avm/client/wasm
$base64string = [Convert]::ToBase64String([IO.File]::ReadAllBytes('./wasm/air_interpreter_client_bg.wasm'))
$data = "// auto-generated
module.exports = `"${base64string}`""
$data | Out-File "./src/wasm.js"
New-Item -ItemType Directory -Force -Path ./dist
cp wasm/air_interpreter_client_bg.wasm dist/avm.wasm
cp dist/avm.wasm src/__test__/

View File

@ -9,16 +9,6 @@
wasm-pack build ./air-interpreter --no-typescript --release -d ../avm/client/wasm
)
## base64 on MacOS doesn't have -w option
if echo | base64 -w0 > /dev/null 2>&1;
then
BASE64=$(base64 -w0 wasm/air_interpreter_client_bg.wasm)
else
BASE64=$(base64 wasm/air_interpreter_client_bg.wasm)
fi
cat << EOF > ./src/wasm.js
// auto-generated
module.exports = "$BASE64";
EOF
mkdir -p ./dist/
cp wasm/air_interpreter_client_bg.wasm dist/avm.wasm
cp dist/avm.wasm src/__test__/

View File

@ -1,4 +1,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testPathIgnorePatterns: ['dist'],
};

File diff suppressed because it is too large Load Diff

View File

@ -10,14 +10,15 @@
"files": [
"dist/*"
],
"bin": {
"copy-avm": "./dist/copyAvm.js"
},
"scripts": {
"build": "tsc",
"test": "jest"
},
"private": false,
"dependencies": {
"base64-js": "1.5.1"
},
"dependencies": {},
"devDependencies": {
"@types/jest": "^26.0.23",
"@types/node": "^14.0.0",

View File

@ -1,9 +1,13 @@
import { AirInterpreter } from '..';
import { readFileSync } from 'fs';
import path from 'path';
const vmPeerId = '12D3KooWNzutuy8WHXDKFqFsATvCR6j9cj2FijYbnd47geRKaQZS';
const createTestInterpreter = async () => {
return AirInterpreter.create('off', (level, message) => {
const file = readFileSync(path.resolve(__dirname, './avm.wasm'));
const module = await WebAssembly.compile(file);
return AirInterpreter.create(module, 'off', (level, message) => {
console.log(`level: ${level}, message=${message}`);
});
};

33
avm/client/src/copyAvm.ts Normal file
View File

@ -0,0 +1,33 @@
#! /usr/bin/env node
import fs from 'fs';
import path from 'path';
const firstArgument = process.argv[2];
if (!firstArgument) {
console.log(`Expected exactly 1 argument, got 0. Usage: ${path.basename(process.argv[1])} <destination directory>`);
process.exit(1);
}
let destPath = firstArgument;
if (!path.isAbsolute(destPath)) {
destPath = path.join(process.cwd(), destPath);
}
const wasmName = 'avm.wasm';
const packageName = '@fluencelabs/avm';
const modulePath = require.resolve(packageName);
const source = path.join(path.dirname(modulePath), wasmName);
const dest = path.join(destPath, wasmName);
console.log('ensure directory exists: ', destPath);
fs.mkdirSync(destPath, { recursive: true });
console.log('copying AVM wasm');
console.log('from: ', source);
console.log('to: ', dest);
fs.copyFileSync(source, dest);
console.log('done!');

View File

@ -14,9 +14,7 @@
* limitations under the License.
*/
import { toByteArray } from 'base64-js';
import { getStringFromWasm0, invoke } from './wrapper';
import wasmBs64 from './wasm';
export type LogLevel = 'info' | 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'off';
@ -82,28 +80,34 @@ class HostImportsConfig {
}
}
const interpreter_wasm = toByteArray(wasmBs64);
/// Instantiates WebAssembly runtime with AIR interpreter module
async function interpreterInstance(cfg: HostImportsConfig, logFunction: LogFunction): Promise<Instance> {
/// Create host imports that use module exports internally
/**
* Instantiates WebAssembly runtime with AIR interpreter module
*/
async function interpreterInstance(
module: WebAssembly.Module,
cfg: HostImportsConfig,
logFunction: LogFunction,
): Promise<Instance> {
// create host imports that use module exports internally
let imports = cfg.newImportObject();
/// Instantiate interpreter
let interpreter_module = await WebAssembly.compile(interpreter_wasm);
// instantiate interpreter
let interpreter_module = module;
let instance: Instance = await WebAssembly.instantiate(interpreter_module, imports);
/// Set exports, so host imports can use them
// set exports, so host imports can use them
cfg.setExports(instance.exports);
/// Trigger interpreter initialization (i.e., call main function)
// trigger interpreter initialization (i.e., call main function)
call_export(instance.exports.main, logFunction);
return instance;
}
/// If export is a function, call it. Otherwise log a warning.
/// NOTE: any here is unavoidable, see Function interface definition
/**
* If export is a function, call it. Otherwise log a warning.
* NOTE: any here is unavoidable, see Function interface definition
*/
function call_export(f: ExportValue, logFunction: LogFunction): any {
if (typeof f === 'function') {
return f();
@ -146,13 +150,18 @@ function log_import(cfg: HostImportsConfig, logFunction: LogFunction): LogImport
};
}
/// Returns import object that describes host functions called by AIR interpreter
/**
* Returns import object that describes host functions called by AIR interpreter
*/
function newImportObject(cfg: HostImportsConfig, logFunction: LogFunction): ImportObject {
return {
host: log_import(cfg, logFunction),
};
}
const decoder = new TextDecoder();
const encoder = new TextEncoder();
export class AirInterpreter {
private wasmWrapper;
private logLevel: LogLevel;
@ -161,12 +170,12 @@ export class AirInterpreter {
this.wasmWrapper = wasmWrapper;
}
static async create(logLevel: LogLevel, logFunction: LogFunction) {
static async create(module: WebAssembly.Module, logLevel: LogLevel, logFunction: LogFunction) {
const cfg = new HostImportsConfig((cfg) => {
return newImportObject(cfg, logFunction);
});
const instance = await interpreterInstance(cfg, logFunction);
const instance = await interpreterInstance(module, cfg, logFunction);
const res = new AirInterpreter(instance);
res.logLevel = logLevel;
return res;
@ -187,7 +196,7 @@ export class AirInterpreter {
};
}
const paramsToPass = Buffer.from(
const paramsToPass = encoder.encode(
JSON.stringify({
init_peer_id: params.initPeerId,
current_peer_id: params.currentPeerId,
@ -201,7 +210,7 @@ export class AirInterpreter {
prevData,
data,
paramsToPass,
Buffer.from(JSON.stringify(callResultsToPass)),
encoder.encode(JSON.stringify(callResultsToPass)),
this.logLevel,
);
@ -210,7 +219,7 @@ export class AirInterpreter {
result = JSON.parse(rawResult);
} catch (ex) {}
const callRequestsStr = new TextDecoder().decode(Buffer.from(result.call_requests));
const callRequestsStr = decoder.decode(new Uint8Array(result.call_requests));
let parsedCallRequests;
try {
if (callRequestsStr.length === 0) {