Skip to content

Commit

Permalink
Merge pull request #614 from stephensli/refactor/wrapper
Browse files Browse the repository at this point in the history
Refactor wrapper.ts to improve maintainability
  • Loading branch information
cameel authored Sep 20, 2022
2 parents fe75220 + 864af41 commit 05a1955
Show file tree
Hide file tree
Showing 7 changed files with 589 additions and 330 deletions.
232 changes: 232 additions & 0 deletions bindings/compile.ts
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;
}
161 changes: 161 additions & 0 deletions bindings/core.ts
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);
}
Loading

0 comments on commit 05a1955

Please sign in to comment.