-
Notifications
You must be signed in to change notification settings - Fork 479
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #614 from stephensli/refactor/wrapper
Refactor wrapper.ts to improve maintainability
- Loading branch information
Showing
7 changed files
with
589 additions
and
330 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
import assert from 'assert'; | ||
|
||
import { isNil } from '../common/helpers'; | ||
import { bindSolcMethod } from './helpers'; | ||
|
||
export function setupCompile (solJson, core) { | ||
return { | ||
compileJson: bindCompileJson(solJson), | ||
compileJsonCallback: bindCompileJsonCallback(solJson, core), | ||
compileJsonMulti: bindCompileJsonMulti(solJson), | ||
compileStandard: bindCompileStandard(solJson, core) | ||
}; | ||
} | ||
|
||
/********************** | ||
* COMPILE | ||
**********************/ | ||
|
||
/** | ||
* Returns a binding to the solidity compileJSON method. | ||
* input (text), optimize (bool) -> output (jsontext) | ||
* | ||
* @param solJson The Emscripten compiled Solidity object. | ||
*/ | ||
function bindCompileJson (solJson) { | ||
return bindSolcMethod( | ||
solJson, | ||
'compileJSON', | ||
'string', | ||
['string', 'number'], | ||
null | ||
); | ||
} | ||
|
||
/** | ||
* Returns a binding to the solidity compileJSONMulti method. | ||
* input (jsontext), optimize (bool) -> output (jsontext) | ||
* | ||
* @param solJson The Emscripten compiled Solidity object. | ||
*/ | ||
function bindCompileJsonMulti (solJson) { | ||
return bindSolcMethod( | ||
solJson, | ||
'compileJSONMulti', | ||
'string', | ||
['string', 'number'], | ||
null | ||
); | ||
} | ||
|
||
/** | ||
* Returns a binding to the solidity compileJSONCallback method. | ||
* input (jsontext), optimize (bool), callback (ptr) -> output (jsontext) | ||
* | ||
* @param solJson The Emscripten compiled Solidity object. | ||
* @param coreBindings The core bound Solidity methods. | ||
*/ | ||
function bindCompileJsonCallback (solJson, coreBindings) { | ||
const compileInternal = bindSolcMethod( | ||
solJson, | ||
'compileJSONCallback', | ||
'string', | ||
['string', 'number', 'number'], | ||
null | ||
); | ||
|
||
if (isNil(compileInternal)) return null; | ||
|
||
return function (input, optimize, readCallback) { | ||
return runWithCallbacks(solJson, coreBindings, readCallback, compileInternal, [input, optimize]); | ||
}; | ||
} | ||
|
||
/** | ||
* Returns a binding to the solidity solidity_compile method with a fallback to | ||
* compileStandard. | ||
* input (jsontext), callback (optional >= v6 only - ptr) -> output (jsontext) | ||
* | ||
* @param solJson The Emscripten compiled Solidity object. | ||
* @param coreBindings The core bound Solidity methods. | ||
*/ | ||
function bindCompileStandard (solJson, coreBindings) { | ||
let boundFunctionStandard: any = null; | ||
let boundFunctionSolidity: any = null; | ||
|
||
// input (jsontext), callback (ptr) -> output (jsontext) | ||
const compileInternal = bindSolcMethod( | ||
solJson, | ||
'compileStandard', | ||
'string', | ||
['string', 'number'], | ||
null | ||
); | ||
|
||
if (coreBindings.isVersion6OrNewer) { | ||
// input (jsontext), callback (ptr), callback_context (ptr) -> output (jsontext) | ||
boundFunctionSolidity = bindSolcMethod( | ||
solJson, | ||
'solidity_compile', | ||
'string', | ||
['string', 'number', 'number'], | ||
null | ||
); | ||
} else { | ||
// input (jsontext), callback (ptr) -> output (jsontext) | ||
boundFunctionSolidity = bindSolcMethod( | ||
solJson, | ||
'solidity_compile', | ||
'string', | ||
['string', 'number'], | ||
null | ||
); | ||
} | ||
|
||
if (!isNil(compileInternal)) { | ||
boundFunctionStandard = function (input, readCallback) { | ||
return runWithCallbacks(solJson, coreBindings, readCallback, compileInternal, [input]); | ||
}; | ||
} | ||
|
||
if (!isNil(boundFunctionSolidity)) { | ||
boundFunctionStandard = function (input, callbacks) { | ||
return runWithCallbacks(solJson, coreBindings, callbacks, boundFunctionSolidity, [input]); | ||
}; | ||
} | ||
|
||
return boundFunctionStandard; | ||
} | ||
|
||
/********************** | ||
* CALL BACKS | ||
**********************/ | ||
|
||
function wrapCallback (coreBindings, callback) { | ||
assert(typeof callback === 'function', 'Invalid callback specified.'); | ||
|
||
return function (data, contents, error) { | ||
const result = callback(coreBindings.copyFromCString(data)); | ||
if (typeof result.contents === 'string') { | ||
coreBindings.copyToCString(result.contents, contents); | ||
} | ||
if (typeof result.error === 'string') { | ||
coreBindings.copyToCString(result.error, error); | ||
} | ||
}; | ||
} | ||
|
||
function wrapCallbackWithKind (coreBindings, callback) { | ||
assert(typeof callback === 'function', 'Invalid callback specified.'); | ||
|
||
return function (context, kind, data, contents, error) { | ||
// Must be a null pointer. | ||
assert(context === 0, 'Callback context must be null.'); | ||
const result = callback(coreBindings.copyFromCString(kind), coreBindings.copyFromCString(data)); | ||
if (typeof result.contents === 'string') { | ||
coreBindings.copyToCString(result.contents, contents); | ||
} | ||
if (typeof result.error === 'string') { | ||
coreBindings.copyToCString(result.error, error); | ||
} | ||
}; | ||
} | ||
|
||
// calls compile() with args || cb | ||
function runWithCallbacks (solJson, coreBindings, callbacks, compile, args) { | ||
if (callbacks) { | ||
assert(typeof callbacks === 'object', 'Invalid callback object specified.'); | ||
} else { | ||
callbacks = {}; | ||
} | ||
|
||
let readCallback = callbacks.import; | ||
if (readCallback === undefined) { | ||
readCallback = function (data) { | ||
return { | ||
error: 'File import callback not supported' | ||
}; | ||
}; | ||
} | ||
|
||
let singleCallback; | ||
if (coreBindings.isVersion6OrNewer) { | ||
// After 0.6.x multiple kind of callbacks are supported. | ||
let smtSolverCallback = callbacks.smtSolver; | ||
if (smtSolverCallback === undefined) { | ||
smtSolverCallback = function (data) { | ||
return { | ||
error: 'SMT solver callback not supported' | ||
}; | ||
}; | ||
} | ||
|
||
singleCallback = function (kind, data) { | ||
if (kind === 'source') { | ||
return readCallback(data); | ||
} else if (kind === 'smt-query') { | ||
return smtSolverCallback(data); | ||
} else { | ||
assert(false, 'Invalid callback kind specified.'); | ||
} | ||
}; | ||
|
||
singleCallback = wrapCallbackWithKind(coreBindings, singleCallback); | ||
} else { | ||
// Old Solidity version only supported imports. | ||
singleCallback = wrapCallback(coreBindings, readCallback); | ||
} | ||
|
||
const cb = coreBindings.addFunction(singleCallback, 'viiiii'); | ||
let output; | ||
try { | ||
args.push(cb); | ||
if (coreBindings.isVersion6OrNewer) { | ||
// Callback context. | ||
args.push(null); | ||
} | ||
|
||
output = compile(...args); | ||
} finally { | ||
coreBindings.removeFunction(cb); | ||
} | ||
|
||
if (coreBindings.reset) { | ||
// Explicitly free memory. | ||
// | ||
// NOTE: cwrap() of "compile" will copy the returned pointer into a | ||
// Javascript string and it is not possible to call free() on it. | ||
// reset() however will clear up all allocations. | ||
coreBindings.reset(); | ||
} | ||
return output; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
import { bindSolcMethod, bindSolcMethodWithFallbackFunc } from './helpers'; | ||
import translate from '../translate'; | ||
import * as semver from 'semver'; | ||
import { isNil } from '../common/helpers'; | ||
|
||
export function setupCore (solJson) { | ||
const core = { | ||
alloc: bindAlloc(solJson), | ||
license: bindLicense(solJson), | ||
version: bindVersion(solJson), | ||
reset: bindReset(solJson) | ||
}; | ||
|
||
const helpers = { | ||
addFunction: unboundAddFunction.bind(this, solJson), | ||
removeFunction: unboundRemoveFunction.bind(this, solJson), | ||
|
||
copyFromCString: unboundCopyFromCString.bind(this, solJson), | ||
copyToCString: unboundCopyToCString.bind(this, solJson, core.alloc), | ||
|
||
// @ts-ignore | ||
versionToSemver: versionToSemver(core.version()) | ||
}; | ||
|
||
return { | ||
...core, | ||
...helpers, | ||
|
||
isVersion6OrNewer: semver.gt(helpers.versionToSemver(), '0.5.99') | ||
}; | ||
} | ||
|
||
/********************** | ||
* Core Functions | ||
**********************/ | ||
|
||
/** | ||
* Returns a binding to the solidity_alloc function. | ||
* | ||
* @param solJson The Emscripten compiled Solidity object. | ||
*/ | ||
function bindAlloc (solJson) { | ||
const allocBinding = bindSolcMethod( | ||
solJson, | ||
'solidity_alloc', | ||
'number', | ||
['number'], | ||
null | ||
); | ||
|
||
// the fallback malloc is not a cwrap function and should just be returned | ||
// directly in-case the alloc binding could not happen. | ||
if (isNil(allocBinding)) { | ||
return solJson._malloc; | ||
} | ||
|
||
return allocBinding; | ||
} | ||
|
||
/** | ||
* Returns a binding to the solidity_version method. | ||
* | ||
* @param solJson The Emscripten compiled Solidity object. | ||
*/ | ||
function bindVersion (solJson) { | ||
return bindSolcMethodWithFallbackFunc( | ||
solJson, | ||
'solidity_version', | ||
'string', | ||
[], | ||
'version' | ||
); | ||
} | ||
|
||
function versionToSemver (version) { | ||
return translate.versionToSemver.bind(this, version); | ||
} | ||
|
||
/** | ||
* Returns a binding to the solidity_license method. | ||
* | ||
* If the current solJson version < 0.4.14 then this will bind an empty function. | ||
* | ||
* @param solJson The Emscripten compiled Solidity object. | ||
*/ | ||
function bindLicense (solJson) { | ||
return bindSolcMethodWithFallbackFunc( | ||
solJson, | ||
'solidity_license', | ||
'string', | ||
[], | ||
'license', | ||
() => { | ||
} | ||
); | ||
} | ||
|
||
/** | ||
* Returns a binding to the solidity_reset method. | ||
* | ||
* @param solJson The Emscripten compiled Solidity object. | ||
*/ | ||
function bindReset (solJson) { | ||
return bindSolcMethod( | ||
solJson, | ||
'solidity_reset', | ||
null, | ||
[], | ||
null | ||
); | ||
} | ||
|
||
/********************** | ||
* Helpers Functions | ||
**********************/ | ||
|
||
/** | ||
* Copy to a C string. | ||
* | ||
* Allocates memory using solc's allocator. | ||
* | ||
* Before 0.6.0: | ||
* Assuming copyToCString is only used in the context of wrapCallback, solc will free these pointers. | ||
* See https://github.com/ethereum/solidity/blob/v0.5.13/libsolc/libsolc.h#L37-L40 | ||
* | ||
* After 0.6.0: | ||
* The duty is on solc-js to free these pointers. We accomplish that by calling `reset` at the end. | ||
* | ||
* @param solJson The Emscripten compiled Solidity object. | ||
* @param alloc The memory allocation function. | ||
* @param str The source string being copied to a C string. | ||
* @param ptr The pointer location where the C string will be set. | ||
*/ | ||
function unboundCopyToCString (solJson, alloc, str, ptr) { | ||
const length = solJson.lengthBytesUTF8(str); | ||
|
||
const buffer = alloc(length + 1); | ||
|
||
solJson.stringToUTF8(str, buffer, length + 1); | ||
solJson.setValue(ptr, buffer, '*'); | ||
} | ||
|
||
/** | ||
* Wrapper over Emscripten's C String copying function (which can be different | ||
* on different versions). | ||
* | ||
* @param solJson The Emscripten compiled Solidity object. | ||
* @param ptr The pointer location where the C string will be referenced. | ||
*/ | ||
function unboundCopyFromCString (solJson, ptr) { | ||
const copyFromCString = solJson.UTF8ToString || solJson.Pointer_stringify; | ||
return copyFromCString(ptr); | ||
} | ||
|
||
function unboundAddFunction (solJson, func, signature?) { | ||
return (solJson.addFunction || solJson.Runtime.addFunction)(func, signature); | ||
} | ||
|
||
function unboundRemoveFunction (solJson, ptr) { | ||
return (solJson.removeFunction || solJson.Runtime.removeFunction)(ptr); | ||
} |
Oops, something went wrong.