Skip to content

Commit 05a1955

Browse files
authored
Merge pull request #614 from stephensli/refactor/wrapper
Refactor wrapper.ts to improve maintainability
2 parents fe75220 + 864af41 commit 05a1955

File tree

7 files changed

+589
-330
lines changed

7 files changed

+589
-330
lines changed

bindings/compile.ts

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
import assert from 'assert';
2+
3+
import { isNil } from '../common/helpers';
4+
import { bindSolcMethod } from './helpers';
5+
6+
export function setupCompile (solJson, core) {
7+
return {
8+
compileJson: bindCompileJson(solJson),
9+
compileJsonCallback: bindCompileJsonCallback(solJson, core),
10+
compileJsonMulti: bindCompileJsonMulti(solJson),
11+
compileStandard: bindCompileStandard(solJson, core)
12+
};
13+
}
14+
15+
/**********************
16+
* COMPILE
17+
**********************/
18+
19+
/**
20+
* Returns a binding to the solidity compileJSON method.
21+
* input (text), optimize (bool) -> output (jsontext)
22+
*
23+
* @param solJson The Emscripten compiled Solidity object.
24+
*/
25+
function bindCompileJson (solJson) {
26+
return bindSolcMethod(
27+
solJson,
28+
'compileJSON',
29+
'string',
30+
['string', 'number'],
31+
null
32+
);
33+
}
34+
35+
/**
36+
* Returns a binding to the solidity compileJSONMulti method.
37+
* input (jsontext), optimize (bool) -> output (jsontext)
38+
*
39+
* @param solJson The Emscripten compiled Solidity object.
40+
*/
41+
function bindCompileJsonMulti (solJson) {
42+
return bindSolcMethod(
43+
solJson,
44+
'compileJSONMulti',
45+
'string',
46+
['string', 'number'],
47+
null
48+
);
49+
}
50+
51+
/**
52+
* Returns a binding to the solidity compileJSONCallback method.
53+
* input (jsontext), optimize (bool), callback (ptr) -> output (jsontext)
54+
*
55+
* @param solJson The Emscripten compiled Solidity object.
56+
* @param coreBindings The core bound Solidity methods.
57+
*/
58+
function bindCompileJsonCallback (solJson, coreBindings) {
59+
const compileInternal = bindSolcMethod(
60+
solJson,
61+
'compileJSONCallback',
62+
'string',
63+
['string', 'number', 'number'],
64+
null
65+
);
66+
67+
if (isNil(compileInternal)) return null;
68+
69+
return function (input, optimize, readCallback) {
70+
return runWithCallbacks(solJson, coreBindings, readCallback, compileInternal, [input, optimize]);
71+
};
72+
}
73+
74+
/**
75+
* Returns a binding to the solidity solidity_compile method with a fallback to
76+
* compileStandard.
77+
* input (jsontext), callback (optional >= v6 only - ptr) -> output (jsontext)
78+
*
79+
* @param solJson The Emscripten compiled Solidity object.
80+
* @param coreBindings The core bound Solidity methods.
81+
*/
82+
function bindCompileStandard (solJson, coreBindings) {
83+
let boundFunctionStandard: any = null;
84+
let boundFunctionSolidity: any = null;
85+
86+
// input (jsontext), callback (ptr) -> output (jsontext)
87+
const compileInternal = bindSolcMethod(
88+
solJson,
89+
'compileStandard',
90+
'string',
91+
['string', 'number'],
92+
null
93+
);
94+
95+
if (coreBindings.isVersion6OrNewer) {
96+
// input (jsontext), callback (ptr), callback_context (ptr) -> output (jsontext)
97+
boundFunctionSolidity = bindSolcMethod(
98+
solJson,
99+
'solidity_compile',
100+
'string',
101+
['string', 'number', 'number'],
102+
null
103+
);
104+
} else {
105+
// input (jsontext), callback (ptr) -> output (jsontext)
106+
boundFunctionSolidity = bindSolcMethod(
107+
solJson,
108+
'solidity_compile',
109+
'string',
110+
['string', 'number'],
111+
null
112+
);
113+
}
114+
115+
if (!isNil(compileInternal)) {
116+
boundFunctionStandard = function (input, readCallback) {
117+
return runWithCallbacks(solJson, coreBindings, readCallback, compileInternal, [input]);
118+
};
119+
}
120+
121+
if (!isNil(boundFunctionSolidity)) {
122+
boundFunctionStandard = function (input, callbacks) {
123+
return runWithCallbacks(solJson, coreBindings, callbacks, boundFunctionSolidity, [input]);
124+
};
125+
}
126+
127+
return boundFunctionStandard;
128+
}
129+
130+
/**********************
131+
* CALL BACKS
132+
**********************/
133+
134+
function wrapCallback (coreBindings, callback) {
135+
assert(typeof callback === 'function', 'Invalid callback specified.');
136+
137+
return function (data, contents, error) {
138+
const result = callback(coreBindings.copyFromCString(data));
139+
if (typeof result.contents === 'string') {
140+
coreBindings.copyToCString(result.contents, contents);
141+
}
142+
if (typeof result.error === 'string') {
143+
coreBindings.copyToCString(result.error, error);
144+
}
145+
};
146+
}
147+
148+
function wrapCallbackWithKind (coreBindings, callback) {
149+
assert(typeof callback === 'function', 'Invalid callback specified.');
150+
151+
return function (context, kind, data, contents, error) {
152+
// Must be a null pointer.
153+
assert(context === 0, 'Callback context must be null.');
154+
const result = callback(coreBindings.copyFromCString(kind), coreBindings.copyFromCString(data));
155+
if (typeof result.contents === 'string') {
156+
coreBindings.copyToCString(result.contents, contents);
157+
}
158+
if (typeof result.error === 'string') {
159+
coreBindings.copyToCString(result.error, error);
160+
}
161+
};
162+
}
163+
164+
// calls compile() with args || cb
165+
function runWithCallbacks (solJson, coreBindings, callbacks, compile, args) {
166+
if (callbacks) {
167+
assert(typeof callbacks === 'object', 'Invalid callback object specified.');
168+
} else {
169+
callbacks = {};
170+
}
171+
172+
let readCallback = callbacks.import;
173+
if (readCallback === undefined) {
174+
readCallback = function (data) {
175+
return {
176+
error: 'File import callback not supported'
177+
};
178+
};
179+
}
180+
181+
let singleCallback;
182+
if (coreBindings.isVersion6OrNewer) {
183+
// After 0.6.x multiple kind of callbacks are supported.
184+
let smtSolverCallback = callbacks.smtSolver;
185+
if (smtSolverCallback === undefined) {
186+
smtSolverCallback = function (data) {
187+
return {
188+
error: 'SMT solver callback not supported'
189+
};
190+
};
191+
}
192+
193+
singleCallback = function (kind, data) {
194+
if (kind === 'source') {
195+
return readCallback(data);
196+
} else if (kind === 'smt-query') {
197+
return smtSolverCallback(data);
198+
} else {
199+
assert(false, 'Invalid callback kind specified.');
200+
}
201+
};
202+
203+
singleCallback = wrapCallbackWithKind(coreBindings, singleCallback);
204+
} else {
205+
// Old Solidity version only supported imports.
206+
singleCallback = wrapCallback(coreBindings, readCallback);
207+
}
208+
209+
const cb = coreBindings.addFunction(singleCallback, 'viiiii');
210+
let output;
211+
try {
212+
args.push(cb);
213+
if (coreBindings.isVersion6OrNewer) {
214+
// Callback context.
215+
args.push(null);
216+
}
217+
218+
output = compile(...args);
219+
} finally {
220+
coreBindings.removeFunction(cb);
221+
}
222+
223+
if (coreBindings.reset) {
224+
// Explicitly free memory.
225+
//
226+
// NOTE: cwrap() of "compile" will copy the returned pointer into a
227+
// Javascript string and it is not possible to call free() on it.
228+
// reset() however will clear up all allocations.
229+
coreBindings.reset();
230+
}
231+
return output;
232+
}

bindings/core.ts

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import { bindSolcMethod, bindSolcMethodWithFallbackFunc } from './helpers';
2+
import translate from '../translate';
3+
import * as semver from 'semver';
4+
import { isNil } from '../common/helpers';
5+
6+
export function setupCore (solJson) {
7+
const core = {
8+
alloc: bindAlloc(solJson),
9+
license: bindLicense(solJson),
10+
version: bindVersion(solJson),
11+
reset: bindReset(solJson)
12+
};
13+
14+
const helpers = {
15+
addFunction: unboundAddFunction.bind(this, solJson),
16+
removeFunction: unboundRemoveFunction.bind(this, solJson),
17+
18+
copyFromCString: unboundCopyFromCString.bind(this, solJson),
19+
copyToCString: unboundCopyToCString.bind(this, solJson, core.alloc),
20+
21+
// @ts-ignore
22+
versionToSemver: versionToSemver(core.version())
23+
};
24+
25+
return {
26+
...core,
27+
...helpers,
28+
29+
isVersion6OrNewer: semver.gt(helpers.versionToSemver(), '0.5.99')
30+
};
31+
}
32+
33+
/**********************
34+
* Core Functions
35+
**********************/
36+
37+
/**
38+
* Returns a binding to the solidity_alloc function.
39+
*
40+
* @param solJson The Emscripten compiled Solidity object.
41+
*/
42+
function bindAlloc (solJson) {
43+
const allocBinding = bindSolcMethod(
44+
solJson,
45+
'solidity_alloc',
46+
'number',
47+
['number'],
48+
null
49+
);
50+
51+
// the fallback malloc is not a cwrap function and should just be returned
52+
// directly in-case the alloc binding could not happen.
53+
if (isNil(allocBinding)) {
54+
return solJson._malloc;
55+
}
56+
57+
return allocBinding;
58+
}
59+
60+
/**
61+
* Returns a binding to the solidity_version method.
62+
*
63+
* @param solJson The Emscripten compiled Solidity object.
64+
*/
65+
function bindVersion (solJson) {
66+
return bindSolcMethodWithFallbackFunc(
67+
solJson,
68+
'solidity_version',
69+
'string',
70+
[],
71+
'version'
72+
);
73+
}
74+
75+
function versionToSemver (version) {
76+
return translate.versionToSemver.bind(this, version);
77+
}
78+
79+
/**
80+
* Returns a binding to the solidity_license method.
81+
*
82+
* If the current solJson version < 0.4.14 then this will bind an empty function.
83+
*
84+
* @param solJson The Emscripten compiled Solidity object.
85+
*/
86+
function bindLicense (solJson) {
87+
return bindSolcMethodWithFallbackFunc(
88+
solJson,
89+
'solidity_license',
90+
'string',
91+
[],
92+
'license',
93+
() => {
94+
}
95+
);
96+
}
97+
98+
/**
99+
* Returns a binding to the solidity_reset method.
100+
*
101+
* @param solJson The Emscripten compiled Solidity object.
102+
*/
103+
function bindReset (solJson) {
104+
return bindSolcMethod(
105+
solJson,
106+
'solidity_reset',
107+
null,
108+
[],
109+
null
110+
);
111+
}
112+
113+
/**********************
114+
* Helpers Functions
115+
**********************/
116+
117+
/**
118+
* Copy to a C string.
119+
*
120+
* Allocates memory using solc's allocator.
121+
*
122+
* Before 0.6.0:
123+
* Assuming copyToCString is only used in the context of wrapCallback, solc will free these pointers.
124+
* See https://github.com/ethereum/solidity/blob/v0.5.13/libsolc/libsolc.h#L37-L40
125+
*
126+
* After 0.6.0:
127+
* The duty is on solc-js to free these pointers. We accomplish that by calling `reset` at the end.
128+
*
129+
* @param solJson The Emscripten compiled Solidity object.
130+
* @param alloc The memory allocation function.
131+
* @param str The source string being copied to a C string.
132+
* @param ptr The pointer location where the C string will be set.
133+
*/
134+
function unboundCopyToCString (solJson, alloc, str, ptr) {
135+
const length = solJson.lengthBytesUTF8(str);
136+
137+
const buffer = alloc(length + 1);
138+
139+
solJson.stringToUTF8(str, buffer, length + 1);
140+
solJson.setValue(ptr, buffer, '*');
141+
}
142+
143+
/**
144+
* Wrapper over Emscripten's C String copying function (which can be different
145+
* on different versions).
146+
*
147+
* @param solJson The Emscripten compiled Solidity object.
148+
* @param ptr The pointer location where the C string will be referenced.
149+
*/
150+
function unboundCopyFromCString (solJson, ptr) {
151+
const copyFromCString = solJson.UTF8ToString || solJson.Pointer_stringify;
152+
return copyFromCString(ptr);
153+
}
154+
155+
function unboundAddFunction (solJson, func, signature?) {
156+
return (solJson.addFunction || solJson.Runtime.addFunction)(func, signature);
157+
}
158+
159+
function unboundRemoveFunction (solJson, ptr) {
160+
return (solJson.removeFunction || solJson.Runtime.removeFunction)(ptr);
161+
}

0 commit comments

Comments
 (0)