Fallback in Contract Dev if no worker
This commit is contained in:
parent
e377ec3194
commit
f70e808056
@ -23,13 +23,8 @@ let workerRegistration;
|
||||
if ('serviceWorker' in navigator) {
|
||||
workerRegistration = runtime
|
||||
.register()
|
||||
.then(() => {
|
||||
console.log('registering service worker');
|
||||
return navigator.serviceWorker.ready;
|
||||
})
|
||||
.then(() => navigator.serviceWorker.ready)
|
||||
.then((registration) => {
|
||||
console.log('registered service worker');
|
||||
|
||||
const _worker = registration.active;
|
||||
_worker.controller = registration.active;
|
||||
const worker = new PromiseWorker(_worker);
|
||||
@ -68,7 +63,7 @@ export function setupWorker () {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('sw', error);
|
||||
dispatch(setError(error));
|
||||
dispatch(setWorker(null));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
import { handleActions } from 'redux-actions';
|
||||
|
||||
const initialState = {
|
||||
worker: null,
|
||||
worker: undefined,
|
||||
error: null
|
||||
};
|
||||
|
||||
|
@ -14,9 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import solc from 'solc/browser-wrapper';
|
||||
// import { isWebUri } from 'valid-url';
|
||||
import registerPromiseWorker from 'promise-worker/register';
|
||||
import SolidityUtils from '~/util/solidity';
|
||||
|
||||
const CACHE_NAME = 'parity-cache-v1';
|
||||
|
||||
@ -25,25 +24,71 @@ registerPromiseWorker((msg) => {
|
||||
});
|
||||
|
||||
self.addEventListener('install', (event) => {
|
||||
console.warn('installing sw');
|
||||
event.waitUntil(self.skipWaiting());
|
||||
});
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
console.warn('activating sw');
|
||||
event.waitUntil(self.clients.claim());
|
||||
});
|
||||
|
||||
self.solcVersions = {};
|
||||
self.addEventListener('fetch', (event) => {
|
||||
const { url } = event.request;
|
||||
|
||||
if (/raw.githubusercontent.com\/ethereum\/solc-bin(.+)list\.json$/.test(url)) {
|
||||
// Return the cached version, but still update it in background
|
||||
return event.respondWith(cachedFetcher(event.request, true));
|
||||
}
|
||||
|
||||
if (/raw.githubusercontent.com\/ethereum\/solc-bin(.+)soljson(.+)\.js$/.test(url)) {
|
||||
return event.respondWith(cachedFetcher(event.request));
|
||||
}
|
||||
});
|
||||
|
||||
self.solc = {};
|
||||
self.files = {};
|
||||
|
||||
function cachedFetcher (request, update = false) {
|
||||
return caches
|
||||
.match(request)
|
||||
.then((response) => {
|
||||
// Return cached response if exists and no
|
||||
// updates needed
|
||||
if (response && !update) {
|
||||
return response;
|
||||
}
|
||||
|
||||
const fetcher = fetch(request.clone())
|
||||
.then((response) => {
|
||||
// Check if we received a valid response
|
||||
if (!response || response.status !== 200) {
|
||||
return response;
|
||||
}
|
||||
|
||||
return caches
|
||||
.open(CACHE_NAME)
|
||||
.then((cache) => {
|
||||
cache.put(request, response.clone());
|
||||
return response;
|
||||
});
|
||||
});
|
||||
|
||||
// Cache hit - return response
|
||||
// Still want to perform the fetch (update)
|
||||
if (response) {
|
||||
return response;
|
||||
}
|
||||
|
||||
return fetcher;
|
||||
});
|
||||
}
|
||||
|
||||
function handleMessage (message) {
|
||||
switch (message.action) {
|
||||
case 'compile':
|
||||
return compile(message.data);
|
||||
|
||||
case 'load':
|
||||
return load(message.data);
|
||||
return getCompiler(message.data).then(() => 'ok');
|
||||
|
||||
case 'setFiles':
|
||||
return setFiles(message.data);
|
||||
@ -54,6 +99,15 @@ function handleMessage (message) {
|
||||
}
|
||||
}
|
||||
|
||||
function compile (data) {
|
||||
const { build } = data;
|
||||
|
||||
return getCompiler(build)
|
||||
.then((compiler) => {
|
||||
return SolidityUtils.compile(data, compiler);
|
||||
});
|
||||
}
|
||||
|
||||
function setFiles (files) {
|
||||
const prevFiles = self.files;
|
||||
const nextFiles = files.reduce((obj, file) => {
|
||||
@ -69,120 +123,22 @@ function setFiles (files) {
|
||||
return 'ok';
|
||||
}
|
||||
|
||||
// @todo re-implement find imports (with ASYNC fetch)
|
||||
// function findImports (path) {
|
||||
// if (self.files[path]) {
|
||||
// if (self.files[path].error) {
|
||||
// return Promise.reject(self.files[path].error);
|
||||
// }
|
||||
function getCompiler (build) {
|
||||
const { longVersion } = build;
|
||||
|
||||
// return Promise.resolve(self.files[path]);
|
||||
// }
|
||||
|
||||
// if (isWebUri(path)) {
|
||||
// console.log('[sw] fetching', path);
|
||||
|
||||
// return fetch(path)
|
||||
// .then((r) => r.text())
|
||||
// .then((c) => {
|
||||
// console.log('[sw]', 'got content at ' + path);
|
||||
// self.files[path] = c;
|
||||
// return c;
|
||||
// })
|
||||
// .catch((e) => {
|
||||
// console.error('[sw]', 'fetching', path, e);
|
||||
// self.files[path] = { error: e };
|
||||
// throw e;
|
||||
// });
|
||||
// }
|
||||
|
||||
// console.log(`[sw] path ${path} not found...`);
|
||||
// return Promise.reject('File not found');
|
||||
// }
|
||||
|
||||
function compile (data, optimized = 1) {
|
||||
const { sourcecode, build } = data;
|
||||
|
||||
return fetchSolidity(build)
|
||||
.then((compiler) => {
|
||||
const start = Date.now();
|
||||
console.log('[sw] compiling...');
|
||||
|
||||
const input = {
|
||||
'': sourcecode
|
||||
const fetcher = (url) => {
|
||||
const request = new Request(url);
|
||||
return cachedFetcher(request);
|
||||
};
|
||||
|
||||
const compiled = compiler.compile({ sources: input }, optimized);
|
||||
|
||||
const time = Math.round((Date.now() - start) / 100) / 10;
|
||||
console.log(`[sw] done compiling in ${time}s`);
|
||||
|
||||
compiled.version = build.longVersion;
|
||||
|
||||
return compiled;
|
||||
});
|
||||
}
|
||||
|
||||
function load (build) {
|
||||
return fetchSolidity(build)
|
||||
.then(() => 'ok');
|
||||
}
|
||||
|
||||
function fetchSolc (build) {
|
||||
const { path, longVersion } = build;
|
||||
const URL = `https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/${path}`;
|
||||
|
||||
return caches
|
||||
.match(URL)
|
||||
.then((response) => {
|
||||
if (response) {
|
||||
return response;
|
||||
}
|
||||
|
||||
console.log(`[sw] fetching solc-bin ${longVersion} at ${URL}`);
|
||||
|
||||
return fetch(URL)
|
||||
.then((response) => {
|
||||
if (!response || response.status !== 200) {
|
||||
return response;
|
||||
}
|
||||
|
||||
const responseToCache = response.clone();
|
||||
|
||||
return caches.open(CACHE_NAME)
|
||||
.then((cache) => {
|
||||
return cache.put(URL, responseToCache);
|
||||
})
|
||||
.then(() => {
|
||||
return response;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function fetchSolidity (build) {
|
||||
const { path, longVersion } = build;
|
||||
|
||||
if (self.solcVersions[path]) {
|
||||
return Promise.resolve(self.solcVersions[path]);
|
||||
}
|
||||
|
||||
return fetchSolc(build)
|
||||
.then((r) => r.text())
|
||||
.then((code) => {
|
||||
const solcCode = code.replace(/^var Module;/, 'var Module=self.__solcModule;');
|
||||
self.__solcModule = {};
|
||||
|
||||
console.log(`[sw] evaluating ${longVersion}`);
|
||||
|
||||
// eslint-disable-next-line no-eval
|
||||
eval(solcCode);
|
||||
|
||||
console.log(`[sw] done evaluating ${longVersion}`);
|
||||
|
||||
const compiler = solc(self.__solcModule);
|
||||
self.solcVersions[path] = compiler;
|
||||
|
||||
if (!self.solc[longVersion]) {
|
||||
self.solc[longVersion] = SolidityUtils
|
||||
.getCompiler(build, fetcher)
|
||||
.then((compiler) => {
|
||||
self.solc[longVersion] = compiler;
|
||||
return compiler;
|
||||
});
|
||||
}
|
||||
|
||||
return self.solc[longVersion];
|
||||
}
|
||||
|
79
js/src/util/solidity.js
Normal file
79
js/src/util/solidity.js
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import solc from 'solc/browser-wrapper';
|
||||
|
||||
export default class SolidityUtils {
|
||||
|
||||
static compile (data, compiler) {
|
||||
const { sourcecode, build, optimized = 1 } = data;
|
||||
|
||||
const start = Date.now();
|
||||
console.log('[solidity] compiling...');
|
||||
|
||||
const input = {
|
||||
'': sourcecode
|
||||
};
|
||||
|
||||
const compiled = compiler.compile({ sources: input }, optimized);
|
||||
|
||||
const time = Math.round((Date.now() - start) / 100) / 10;
|
||||
console.log(`[solidity] done compiling in ${time}s`);
|
||||
|
||||
compiled.version = build.longVersion;
|
||||
compiled.sourcecode = sourcecode;
|
||||
|
||||
return compiled;
|
||||
}
|
||||
|
||||
static getCompiler (build, _fetcher) {
|
||||
const { longVersion, path } = build;
|
||||
|
||||
const URL = `https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/${path}`;
|
||||
|
||||
const fetcher = typeof _fetcher === 'function'
|
||||
? _fetcher
|
||||
: (url) => fetch(url);
|
||||
|
||||
const isWorker = typeof window !== 'object';
|
||||
|
||||
return fetcher(URL)
|
||||
.then((r) => r.text())
|
||||
.then((code) => {
|
||||
// `window` for main thread, `self` for workers
|
||||
const _self = isWorker ? self : window;
|
||||
_self.Module = {};
|
||||
|
||||
const solcCode = code.replace('var Module;', `var Module=${isWorker ? 'self' : 'window'}.Module;`);
|
||||
|
||||
console.log(`[solidity] evaluating ${longVersion}`);
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
eval(solcCode);
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
|
||||
console.log(`[solidity] done evaluating ${longVersion}`);
|
||||
|
||||
const compiler = solc(_self.Module);
|
||||
delete _self.Module;
|
||||
|
||||
return compiler;
|
||||
});
|
||||
}
|
||||
}
|
@ -57,7 +57,7 @@ class WriteContract extends Component {
|
||||
const { setupWorker, worker } = this.props;
|
||||
setupWorker();
|
||||
|
||||
if (worker) {
|
||||
if (worker !== undefined) {
|
||||
this.store.setWorker(worker);
|
||||
}
|
||||
}
|
||||
@ -77,7 +77,7 @@ class WriteContract extends Component {
|
||||
|
||||
// Set the worker if not set before (eg. first page loading)
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (!this.props.worker && nextProps.worker) {
|
||||
if (this.props.worker === undefined && nextProps.worker !== undefined) {
|
||||
this.store.setWorker(nextProps.worker);
|
||||
}
|
||||
|
||||
|
@ -14,11 +14,12 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { action, observable } from 'mobx';
|
||||
import { action, observable, transaction } from 'mobx';
|
||||
import store from 'store';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import { sha3 } from '~/api/util/sha3';
|
||||
import SolidityUtils from '~/util/solidity';
|
||||
|
||||
const WRITE_CONTRACT_STORE_KEY = '_parity::writeContractStore';
|
||||
|
||||
@ -79,6 +80,9 @@ export default class WriteContractStore {
|
||||
snippets = SNIPPETS;
|
||||
worker = null;
|
||||
|
||||
useWorker = true;
|
||||
solc = {};
|
||||
|
||||
constructor () {
|
||||
this.debouncedCompile = debounce(this.handleCompile, 1000);
|
||||
}
|
||||
@ -131,6 +135,9 @@ export default class WriteContractStore {
|
||||
|
||||
this.selectedBuild = latestIndex;
|
||||
return promise;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.setWorkerError(error);
|
||||
});
|
||||
}
|
||||
|
||||
@ -143,15 +150,36 @@ export default class WriteContractStore {
|
||||
return this.loadSolidityVersion(this.builds[value]);
|
||||
}
|
||||
|
||||
getCompiler (build) {
|
||||
const { longVersion } = build;
|
||||
|
||||
if (!this.solc[longVersion]) {
|
||||
this.solc[longVersion] = SolidityUtils
|
||||
.getCompiler(build)
|
||||
.then((compiler) => {
|
||||
this.solc[longVersion] = compiler;
|
||||
return compiler;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.setWorkerError(error);
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(this.solc[longVersion]);
|
||||
}
|
||||
|
||||
@action loadSolidityVersion = (build) => {
|
||||
if (!this.worker) {
|
||||
if (this.worker === undefined) {
|
||||
return;
|
||||
} else if (this.worker === null) {
|
||||
this.useWorker = false;
|
||||
}
|
||||
|
||||
if (this.loadingSolidity) {
|
||||
return this.loadingSolidity;
|
||||
}
|
||||
|
||||
if (this.useWorker) {
|
||||
this.loadingSolidity = this.worker
|
||||
.postMessage({
|
||||
action: 'load',
|
||||
@ -159,16 +187,34 @@ export default class WriteContractStore {
|
||||
})
|
||||
.then((result) => {
|
||||
if (result !== 'ok') {
|
||||
this.setWorkerError(result);
|
||||
throw new Error('error while loading solidity: ' + result);
|
||||
}
|
||||
|
||||
this.loadingSolidity = false;
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.setWorkerError(error);
|
||||
})
|
||||
console.warn('error while loading solidity', error);
|
||||
this.useWorker = false;
|
||||
this.loadingSolidity = null;
|
||||
|
||||
return this.loadSolidityVersion(build);
|
||||
});
|
||||
} else {
|
||||
this.loadingSolidity = this
|
||||
.getCompiler(build)
|
||||
.then(() => {
|
||||
this.loadingSolidity = false;
|
||||
this.loading = false;
|
||||
|
||||
return 'ok';
|
||||
})
|
||||
.catch((error) => {
|
||||
this.setWorkerError(error);
|
||||
this.loadingSolidity = false;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
return this.loadingSolidity;
|
||||
}
|
||||
@ -202,9 +248,32 @@ export default class WriteContractStore {
|
||||
this.contract = this.contracts[Object.keys(this.contracts)[value]];
|
||||
}
|
||||
|
||||
compile = (data) => {
|
||||
if (this.useWorker) {
|
||||
return this.worker.postMessage({
|
||||
action: 'compile',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
window.setTimeout(() => {
|
||||
this
|
||||
.getCompiler(data.build)
|
||||
.then((compiler) => {
|
||||
return SolidityUtils.compile(data, compiler);
|
||||
})
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
@action handleCompile = (loadFiles = false) => {
|
||||
transaction(() => {
|
||||
this.compiled = false;
|
||||
this.compiling = true;
|
||||
});
|
||||
|
||||
const build = this.builds[this.selectedBuild];
|
||||
const version = build.longVersion;
|
||||
@ -219,19 +288,16 @@ export default class WriteContractStore {
|
||||
resolve(this.lastCompilation);
|
||||
}, 500);
|
||||
});
|
||||
} else if (this.worker) {
|
||||
promise = loadFiles
|
||||
} else {
|
||||
promise = loadFiles && this.useWorker
|
||||
? this.sendFilesToWorker()
|
||||
: Promise.resolve();
|
||||
|
||||
promise = promise
|
||||
.then(() => {
|
||||
return this.worker.postMessage({
|
||||
action: 'compile',
|
||||
data: {
|
||||
return this.compile({
|
||||
sourcecode: sourcecode,
|
||||
build: build
|
||||
}
|
||||
});
|
||||
})
|
||||
.then((data) => {
|
||||
@ -427,6 +493,10 @@ export default class WriteContractStore {
|
||||
}
|
||||
|
||||
sendFilesToWorker = () => {
|
||||
if (!this.useWorker) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const files = [].concat(
|
||||
Object.values(this.snippets),
|
||||
Object.values(this.savedContracts)
|
||||
|
Loading…
Reference in New Issue
Block a user