From b5907f541f25fe930fb4b4be2d8e8bd8b067cf83 Mon Sep 17 00:00:00 2001 From: Aliaksei Chapyzhenka Date: Mon, 16 Aug 2021 00:02:16 -0700 Subject: [PATCH] requireble brwoser friendly version of parser --- Makefile | 19 +++- lib/index.js | 12 ++- lib/web-vcd-parser.js | 242 ++++++++++++++++++++++++++++++++++++++++++ package.json | 19 ++-- 4 files changed, 275 insertions(+), 17 deletions(-) create mode 100644 lib/web-vcd-parser.js diff --git a/Makefile b/Makefile index 8a86e5e..82bdd02 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,9 @@ EXPORT_STRING = \ # warning and error flags CLANG_WARN_FLAGS = \ +-flto \ +-fno-exceptions \ +-Wl,--lto-O3 \ -Wall -Wextra \ -Wno-ignored-qualifiers \ -Wundef \ @@ -42,7 +45,7 @@ CLANG_OTHER_FLAGS = \ -CLANG_O_FLAG = '-O3' +CLANG_O_FLAG = '-Oz' ifdef NOOPT CLANG_O_FLAG = ' ' @@ -55,16 +58,23 @@ endif # works however slows down #-s DISABLE_EXCEPTION_CATCHING=0 \ + out/vcd.wasm: $(WASM_MAIN) $(CPP_FILES) $(HPP_FILES) Makefile mkdir -p out - emcc $(WASM_MAIN) $(CPP_FILES) -s WASM=1 -o out/vcd.html \ - -s DISABLE_EXCEPTION_CATCHING=0 \ + emcc \ + $(WASM_MAIN) \ + $(CPP_FILES) \ + -o out/vcd.html \ + -s DISABLE_EXCEPTION_CATCHING=1 \ -s ALLOW_MEMORY_GROWTH=1 \ -s ALLOW_TABLE_GROWTH=1 \ + -s MODULARIZE=1 \ + -s EXPORT_NAME=createVCD \ -s EXPORTED_FUNCTIONS='[$(EXPORT_STRING) "_main"]' \ - -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap", "addOnPostRun", "addFunction", "setValue", "getValue"]' \ + -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap", "addOnPostRun", "addFunction", "setValue", "getValue"]' \ $(CLANG_O_FLAG) $(CLANG_WARN_FLAGS) $(CLANG_OTHER_FLAGS) +# -s WASM=0 \ .PHONY: patchlib patchlib1 patchlib2 @@ -127,4 +137,3 @@ prepare: clean: rm -rf out/* - diff --git a/lib/index.js b/lib/index.js index bc48e45..1f2c52f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -6,12 +6,14 @@ const wasmparser = require('./wasmparser.js'); const and = require('./and.js'); const activity = require('./activity.js'); const wrapper = require('./wrapper.js'); +const webVcdParser = require('./web-vcd-parser.js'); module.exports = { version: pkg.version, - and: and, - activity: activity, - parser: parser, - wasmparser: wasmparser, - wrapper: wrapper + and, + activity, + parser, + wasmparser, + wrapper, + webVcdParser }; diff --git a/lib/web-vcd-parser.js b/lib/web-vcd-parser.js new file mode 100644 index 0000000..fc1ab42 --- /dev/null +++ b/lib/web-vcd-parser.js @@ -0,0 +1,242 @@ +'use strict'; + +const stream = require('stream'); +const EventEmitter = require('events').EventEmitter; + +const dotProp = require('dot-prop'); + +function _waitForStart(mod) { + return new Promise((resolve)=>{ + mod.addOnPostRun(resolve); + }); +} + +function u8ToBn(u8) { + var hex = []; + // let u8 = Uint8Array.from(buf); + + u8.forEach(function (i) { + var h = i.toString(16); + if (h.length % 2) { h = '0' + h; } + hex.push(h); + }); + + hex.reverse(); + + return BigInt('0x' + hex.join('')); +} + +let startCalled = 0; + +const getWrapper = wasm => { + + const c = {}; + + let bindCallback; + + const bindCWrap = () => { + const w = wasm.cwrap; + c.execute = w('execute', 'number', ['number', 'number', 'number', 'number', 'number', 'string']); + c.init = w('init', 'number', ['number', 'number', 'number', 'number']); + c.getTime = w('getTime', 'number', ['number']); + c.setTrigger = w('setTrigger', 'number', ['number', 'string']); + }; + + const start = async() => { + // if( !startCalled ) { + // await _waitForStart(wasm); + // startCalled++; + // } + // console.log('s1'); + bindCWrap(); + // console.log('s2'); + bindCallback(); + // console.log('s3'); + }; + + // gets a string from a c heap pointer and length + const getString = (name, len) => { + const view = wasm.HEAPU8.subarray(name, name+len); + + let string = ''; + for (let i = 0; i < len; i++) { + string += String.fromCharCode(view[i]); + } + return string; + }; + + let boundInfo; + + let boundSet; + let boundGet; + + let ee = []; + + let boundEE0; + let boundEE1; + + let context = -1; + + + // wasm.addFunction can't be called until after + // start finishes + bindCallback = () => { + boundSet = wasm.addFunction(function(name, len, type, v0, v1) { + + let prop = getString(name, len); + let tmp; + + switch(type) { + // set number + case 0: + boundInfo[prop] = v0; + // console.log(`setting ${prop} to ${boundInfo[prop]}`); + break; + // set string + case 1: + boundInfo[prop] = getString(v0, v1); + // console.log(`setting ${prop} to ${boundInfo[prop]}`); + break; + // set string to path + case 2: + dotProp.set(boundInfo, prop, getString(v0, v1)); + // console.log(`setting ${prop} to ${getString(v0, v1)}`); + break; + // path to path (any type) + case 3: + tmp = dotProp.get(boundInfo, getString(v0, v1)); + // console.log(`for ${getString(v0, v1)} got ${tmp}, set to ${prop}`); + dotProp.set(boundInfo, prop, tmp); + break; + // create empty object at path + case 4: + // console.log(`${prop} is new {}`); + dotProp.set(boundInfo, prop, {}); + break; + + default: throw new Error(); + } + + + // viiiii means returns void, accepts int int int int int + }, 'viiiii'); + + boundGet = wasm.addFunction(function(name, len) { + let prop = getString(name, len); + return prop; + }, 'iii'); + + + boundEE0 = wasm.addFunction(function(name, len) { + ee[0](getString(name, len)); + }, 'vii'); + + // const char* name, const size_t len, const uint64_t time, const uint8_t command, const int valueWords, const uint64_t* aValue, const uint64_t* aMask); + // boundEE1 = wasm.addFunction(function(eventName, l0, time, command, valueWords, value, mask) { + boundEE1 = wasm.addFunction(function(eventName, l0, time, command, valueWords, value, mask) { + const name = getString(eventName, l0); + // console.log(`event name`); + // console.log(`event ${name} time ${time} cmd ${command} wrds ${valueWords}`); + + + const view0 = wasm.HEAPU8.subarray(value, value+(valueWords*8)); + const view1 = wasm.HEAPU8.subarray(mask, mask+(valueWords*8)); + + let bigValue = u8ToBn(view0); + let bigMask = u8ToBn(view1); + + // console.log(bigValue.toString(16)); + + ee[1](name, time, command, bigValue, bigMask); + }, 'viiiiiii'); + + }; + + return { + start, + c, + init: (cb0, cb1, info) => { + boundInfo = info; + ee[0] = cb0; + ee[1] = cb1; + context = c.init(boundEE0, boundEE1, boundSet, boundGet); + return context; + }, + execute: (ctx, cb0, cb1, info, chunk) => { + boundInfo = info; + ee[0] = cb0; + ee[1] = cb1; + c.execute(ctx, boundEE0, boundEE1, boundSet, boundGet, chunk.toString()); + }, + setTrigger: (ctx, triggerString) => { + return c.setTrigger(ctx, triggerString); + }, + getTime: (ctx) => { + return BigInt(c.getTime(ctx)); + } + }; + +}; + +module.exports = async wasm => { + const lib = getWrapper(wasm); + // console.log('getWrapper', lib); + await lib.start(); + // console.log('vcd wasm srarted'); + + const wires = {}; + const info = {stack: [wires], wires: wires}; + + const s = new stream.Writable(); + + // gets called by c with 1 argument, a string + const lifemit = s.emit.bind(s); + + const triee = new EventEmitter(); + + // gets called by c with 5 arguments + // string eventName + // number state->time + // int command + // int state->value + // int state->mask + + const triemit = triee.emit.bind(triee); + let triemit2 = triemit; + + const cxt = lib.init(lifemit, triemit, info); + + s._write = function (chunk, encoding, callback) { + // console.log('about to write', chunk); + lib.execute(cxt, lifemit, triemit2, info, chunk); + // console.log(util.inspect(info, {showHidden: true, depth : null, colorize: true})); + // console.log(info.stack[0].top); + // console.log(info.stack[1]); + // console.log(info.stack[0].top == info.stack[1]); + callback(); + }; + + s.change = { + on: (id, fn) => { + triemit2 = triemit; + // console.log(id, fn); + triee.on(id, fn); + const triggerString = triee.eventNames().join(' ') + ' '; + lib.setTrigger(cxt, triggerString); + }, + any: fn => { + triemit2 = fn; + lib.setTrigger(cxt, '\0'); + } + }; + + s.info = info; + + s.getTime = () => lib.getTime(cxt); + + s.start = lib.start; + + return s; +}; + +/* global BigInt */ diff --git a/package.json b/package.json index ca22a2c..9489e88 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ "test": "eslint bin lib test && npm run test_napi && npm run test_wasm", "testonly": "nyc -r=text -r=lcov mocha", "watch": "mocha --watch", + "build.web": "browserify ./lib/vcd-web.js | terser --compress -o demo/vcd-web.min.js", + "watch.web": "watchify ./lib/vcd-web.js -o demo/vcd-web.min.js -v", "install": "node bin/build.js", "prepare": "node bin/build.js" }, @@ -35,19 +37,22 @@ }, "homepage": "https://github.com/wavedrom/vcd#readme", "dependencies": { - "async": "^3.1.0", "bindings": "^1.5.0", - "dot-prop": "^6.0.1", - "fs-extra": "^9.1.0", - "llparse": "^7.0.1" + "fs-extra": "^10.0.0", + "llparse": "^7.1.1" }, "devDependencies": { "@drom/eslint-config": "^0.10.0", + "browserify": "^17.0.0", "chai": "^4.3.4", - "eslint": "^7.24.0", + "dot-prop": "^6.0.1", + "eslint": "^7.31.0", + "http-server": "^0.12.3", "llparse-dot": "^1.0.1", - "mocha": "^8.3.2", - "nyc": "^15.1.0" + "mocha": "^9.0.3", + "nyc": "^15.1.0", + "terser": "^5.7.1", + "watchify": "^4.0.0" }, "eslintConfig": { "extends": "@drom/eslint-config/eslint4/node8",