Skip to content

Instantly share code, notes, and snippets.

@0mkara
Created July 22, 2019 14:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0mkara/fffc177ffa3f4380ccca65faee759cf0 to your computer and use it in GitHub Desktop.
Save 0mkara/fffc177ffa3f4380ccca65faee759cf0 to your computer and use it in GitHub Desktop.
// solc-js-webworker.js
const solcjs = require('solc-js')

var lid = 0
var compiler

onmessage = async event => {
  const [lid, from, path, ref, type, body] = event.data

  if (type === 'version') retrun version(from, lid, body)
  if (type === 'compile')  return compile(body)
  // ...
}

function version (from, path, ref, body) {
  compiler = await solcjs(version)
  self.postMessage([lid++, from,  path, ref,  compiler.version])
}

function compile (from, path, ref, body) {
  if (!compiler) compiler = solcjs()
  const output = await solcjs(body)
  self.postMessage([lid++, from,  path, ref,  output])
  // TODO: 1. handle errors
  // TODO: 2. enable multiple round trips to support resolving imports
}
// solc-js-worker-proxy.js
const worker_solcjs = new Worker('bundle_solc-js-webworker.js')
worker_solcjs.onmessage = handler

const from = '/', sent = [], received = {}
var lid = 1

module.exports = solcjs_worker_proxy

async function solcjs_worker_proxy (version) {
  return new Promise((resolve, reject) => {
    const message = [lid++, from, /*path*/'/', /*ref*/0/*(initialize new conversation)*/, /*type*/'init'/*(create new compiler)*/, /*body*/version || 'latest']
    sent.push([message, [resolve, reject])
    worker_solcjs.postMessage(message)
  })
}

function handler ({ data: message }) {
  const [lid_, from_, path_, ref_, type_, body_] = message
  try {
    const [[_lid, _from, _path, _ref, _type, _body], executor ] = msglog[ref_]
    if ((ref_ !== _lid) || (path_ !== _from)) throw new Error('@TODO: error in received `data`')
    ;(received[from_] || (received[from_] = [])).push(message)
    if (_type === 'init') return init([type_, body_], executor)
    if (_type === 'compile') return compile([type_, body_], executor)
    if (_type === 'help') return _help([type_, body_])
    // @TODO: handle more type's
  } catch (e) {
    console.error(e) // @TODO: handle communication initiated by worker or unexpected message from worker
  }
}

function init ([type, body], [resolve, reject]) {
  if (type === 'error') return reject(body)
  if (type === 'ok') {
    const path = `/compiler/${body}` // e.g. "/compiler/1"
    async function compile (sourcecode) {
      return new Promise((resolve, reject) => {
        const message = [lid++, from, path, 0, 'compile', sourcecode]
        sent.push([message, [resolve, reject])
        worker_solcjs.postMessage(message)
      })
    }
    return resolve(compile)
  }
  reject(`@TODO: handle unknown response type '${type}'`)
}
function compile (type, body, [resolve, reject]) {
  if (type === 'error') return reject(body)
  if (type === 'ok') return resolve(body)
  reject(`@TODO: handle unknown response type '${type}'`)
}
function _help ([type, body]) {
  if (type === 'ok') return console.log(body)
  // e.g. { '/': { types: ['help', 'init'] } }
  // or e.g. { '/': { types: ['help', 'init'] }, '/compiler/1': { types: ['compile', 'versions', 'version2url'] } }
  if (type === 'error') {}
  throw new Error(`@TODO: handle unknown response type '${type}'`)
}

Usage:

const solcjs = require('solc-js-worker-proxy')
const compiler = solcjs()
const output = solcjs`contract Foo {}`
// index.js
// package.json#scripts: { "build": "browserify index.js > bundle.js" }
// npm run build
const worker = require('./worker')
const renderer = require('./renderer')

if (location.pathname === '/' || location.pathname === '/index.html') renderer()
else if (location.pathname === '/bundle.js') worker() // that's actually how it works in web workers :-)
else console.error('unexpected pathname', location.pathname)
// renderer.js
module.exports = () => {
  const worker = new Worker(document.currentScript.src) // in practice a "bundle" to inline all required modules
  worker.onmessage = event => console.log('[from worker to renderer] ', event.data)
  worker.postMessage('hello worker')
  // ...
}
// worker.js
module.exports = () => {
  onmessage = async event => {
    console.log('[from renderer to worker] ', event.data)
  }
  postMessage('hello renderer')
  // ...
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment