Skip to content

Commit f01190d

Browse files
committed
Fixed bad fift asm compilation, added SourceResolver callback
1 parent 6c3a943 commit f01190d

8 files changed

+124
-45
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ton-community/func-js",
3-
"version": "0.1.5",
3+
"version": "0.2.0",
44
"description": "The TON FunC smart-contract compiler",
55
"main": "dist/index.js",
66
"author": "TonTech",

src/index.ts

+74-24
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,19 @@ const {FuncFiftLibWasm} = require('./wasmlib/funcfiftlib.wasm.js')
66
// Prepare binary
77
const WasmBinary = base64Decode(FuncFiftLibWasm)
88

9+
export type SourcesMap = { [filename: string]: string }
10+
11+
export type SourceResolver = (path: string) => string;
12+
13+
export const mapSourceResolver = (map: SourcesMap): SourceResolver => {
14+
return (path: string) => {
15+
if (path in map) {
16+
return map[path];
17+
}
18+
throw new Error('Cannot find source file ' + path);
19+
};
20+
};
21+
922
/*
1023
* CompilerConfig example:
1124
* {
@@ -26,11 +39,9 @@ const WasmBinary = base64Decode(FuncFiftLibWasm)
2639
* }
2740
*
2841
*/
29-
export type SourcesMap = { [filename: string]: string }
30-
3142
export type CompilerConfig = {
3243
entryPoints: string[],
33-
sources: SourcesMap,
44+
sources: SourcesMap | SourceResolver,
3445
optLevel?: number
3546
};
3647

@@ -54,46 +65,85 @@ export type CompilerVersion = {
5465
funcFiftLibCommitDate: string
5566
}
5667

68+
const copyToCString = (mod: any, str: string) => {
69+
const len = mod.lengthBytesUTF8(str) + 1;
70+
const ptr = mod._malloc(len);
71+
mod.stringToUTF8(str, ptr, len);
72+
return ptr;
73+
}
74+
75+
const copyToCStringPtr = (mod: any, str: string, ptr: any) => {
76+
const allocated = copyToCString(mod, str);
77+
mod.setValue(ptr, allocated, '*');
78+
return allocated;
79+
};
80+
81+
const copyFromCString = (mod: any, ptr: any) => {
82+
return mod.UTF8ToString(ptr);
83+
};
84+
5785
export async function compilerVersion(): Promise<CompilerVersion> {
58-
let mod = await CompilerModule({ wasmBinary: WasmBinary });
86+
const mod = await CompilerModule({ wasmBinary: WasmBinary });
5987

60-
let versionJsonPointer = mod._version();
61-
let versionJson = mod.UTF8ToString(versionJsonPointer);
88+
const versionJsonPointer = mod._version();
89+
const versionJson = copyFromCString(mod, versionJsonPointer);
6290
mod._free(versionJsonPointer);
6391

6492
return JSON.parse(versionJson);
6593
}
6694

6795
export async function compileFunc(compileConfig: CompilerConfig): Promise<CompileResult> {
68-
69-
let entryWithNoSource = compileConfig.entryPoints.find(filename => typeof compileConfig.sources[filename] !== 'string')
96+
const resolver = typeof compileConfig.sources === 'function' ? compileConfig.sources : mapSourceResolver(compileConfig.sources);
97+
98+
const entryWithNoSource = compileConfig.entryPoints.find(filename => {
99+
try {
100+
resolver(filename);
101+
return false;
102+
} catch (e) {
103+
return true;
104+
}
105+
});
70106
if (entryWithNoSource) {
71107
throw new Error(`The entry point ${entryWithNoSource} has not provided in sources.`)
72108
}
73109

74-
let mod = await CompilerModule({ wasmBinary: WasmBinary });
75-
76-
// Write sources to virtual FS
77-
for (let fileName in compileConfig.sources) {
78-
let source = compileConfig.sources[fileName];
79-
mod.FS.writeFile(fileName, source);
80-
}
81-
82-
let configStr = JSON.stringify({
110+
const mod = await CompilerModule({ wasmBinary: WasmBinary });
111+
112+
const allocatedPointers = [];
113+
114+
const callbackPtr = mod.addFunction((_kind: any, _data: any, contents: any, error: any) => {
115+
const kind: string = copyFromCString(mod, _kind);
116+
const data: string = copyFromCString(mod, _data);
117+
if (kind === 'realpath') {
118+
allocatedPointers.push(copyToCStringPtr(mod, data, contents));
119+
} else if (kind === 'source') {
120+
try {
121+
const source = resolver(data);
122+
allocatedPointers.push(copyToCStringPtr(mod, source, contents));
123+
} catch (err) {
124+
const e = err as any;
125+
allocatedPointers.push(copyToCStringPtr(mod, 'message' in e ? e.message : e.toString(), error));
126+
}
127+
} else {
128+
allocatedPointers.push(copyToCStringPtr(mod, 'Unknown callback kind ' + kind, error));
129+
}
130+
}, 'viiii');
131+
132+
const configStr = JSON.stringify({
83133
sources: compileConfig.entryPoints,
84134
optLevel: compileConfig.optLevel || 2
85135
});
86136

87-
let configStrPointer = mod._malloc(configStr.length + 1);
88-
mod.stringToUTF8(configStr, configStrPointer, configStr.length + 1);
137+
const configStrPointer = copyToCString(mod, configStr);
138+
allocatedPointers.push(configStrPointer);
89139

90-
let resultPointer = mod._func_compile(configStrPointer);
91-
let retJson = mod.UTF8ToString(resultPointer);
140+
const resultPointer = mod._func_compile(configStrPointer, callbackPtr);
141+
allocatedPointers.push(resultPointer);
142+
const retJson = copyFromCString(mod, resultPointer);
92143

93144
// Cleanup
94-
mod._free(resultPointer);
95-
mod._free(configStrPointer);
96-
mod = null
145+
allocatedPointers.forEach(ptr => mod._free(ptr));
146+
mod.removeFunction(callbackPtr);
97147

98148
return JSON.parse(retJson);
99149
}

src/wasmlib/funcfiftlib.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/wasmlib/funcfiftlib.wasm

-202 KB
Binary file not shown.

src/wasmlib/funcfiftlib.wasm.js

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/compiler.spec.ts

+42-14
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import {compileFunc, compilerVersion, SuccessResult, ErrorResult} from '../src/index';
1+
import {compileFunc, compilerVersion, SuccessResult, ErrorResult, mapSourceResolver} from '../src/index';
22
import fs from 'fs';
33
import {Cell} from 'ton';
44

55
describe('ton-compiler', () => {
66
const walletCodeCellHash = Buffer.from("hA3nAz+xEJePYGrDyjJ+BXBcxSp9Y2xaAFLRgGntfDs=", 'base64');
77

88
const compilerVersionExpected = {
9-
funcVersion: "0.2.0",
10-
funcFiftLibCommitHash: "a18a5ed15c8c5a149d48a7d8b0523f13b76f5123",
11-
funcFiftLibCommitDate: "2022-09-21 14:11:40 +0700"
12-
}
9+
funcVersion: "0.3.0",
10+
funcFiftLibCommitHash: "3dd87ae7a703d2771c4e299a4490eb66787eb270",
11+
funcFiftLibCommitDate: "2022-11-15 17:17:07 +0300"
12+
};
1313

1414
it('should return compiler version', async () => {
1515
let version = await compilerVersion();
@@ -26,13 +26,28 @@ describe('ton-compiler', () => {
2626
}
2727
});
2828

29+
expect(result.status).toEqual('ok');
30+
result = result as SuccessResult;
31+
32+
let codeCell = Cell.fromBoc(Buffer.from(result.codeBoc, "base64"))[0];
33+
expect(codeCell.hash().equals(walletCodeCellHash)).toBe(true);
34+
});
2935

36+
it('should compile using map source resolver', async () => {
37+
let result = await compileFunc({
38+
optLevel: 2,
39+
entryPoints: ["stdlib.fc", "wallet-code.fc"],
40+
sources: mapSourceResolver({
41+
"stdlib.fc": fs.readFileSync('./test/contracts/stdlib.fc', { encoding: 'utf-8' }),
42+
"wallet-code.fc": fs.readFileSync('./test/contracts/wallet-code.fc', { encoding: 'utf-8' })
43+
})
44+
});
3045

3146
expect(result.status).toEqual('ok');
3247
result = result as SuccessResult;
3348

3449
let codeCell = Cell.fromBoc(Buffer.from(result.codeBoc, "base64"))[0];
35-
expect(codeCell.hash().equals(walletCodeCellHash)).toBe(true)
50+
expect(codeCell.hash().equals(walletCodeCellHash)).toBe(true);
3651
});
3752

3853
it('should handle includes', async () => {
@@ -50,15 +65,14 @@ describe('ton-compiler', () => {
5065
result = result as SuccessResult;
5166

5267
let codeCell = Cell.fromBoc(Buffer.from(result.codeBoc, "base64"))[0];
53-
expect(codeCell.hash().equals(walletCodeCellHash)).toBe(true)
68+
expect(codeCell.hash().equals(walletCodeCellHash)).toBe(true);
5469
});
5570

5671
it('should fail if entry point source is not provided', async () => {
5772
expect(compileFunc({
5873
optLevel: 2,
5974
entryPoints: ["main.fc"],
60-
sources: {
61-
}
75+
sources: {}
6276
})).rejects.toThrowError('The entry point main.fc has not provided in sources.');
6377
});
6478

@@ -67,7 +81,7 @@ describe('ton-compiler', () => {
6781
#pragma version ^${compilerVersionExpected.funcVersion};
6882
6983
() main() { return(); }
70-
`
84+
`;
7185
let result = await compileFunc({
7286
optLevel: 1,
7387
entryPoints: ["main.fc"],
@@ -82,7 +96,7 @@ describe('ton-compiler', () => {
8296
#pragma version <${compilerVersionExpected.funcVersion};
8397
8498
() main() { return(); }
85-
`
99+
`;
86100
result = await compileFunc({
87101
optLevel: 1,
88102
entryPoints: ["main.fc"],
@@ -91,8 +105,22 @@ describe('ton-compiler', () => {
91105
}
92106
});
93107

94-
expect(result.status).toEqual('error')
108+
expect(result.status).toEqual('error');
109+
result = result as ErrorResult;
110+
expect(result.message.indexOf(`FunC version ${compilerVersionExpected.funcVersion} does not satisfy condition <${compilerVersionExpected.funcVersion}`) >= 0).toBeTruthy();
111+
});
112+
113+
it('should fail if a non-existing source is included', async () => {
114+
let result = await compileFunc({
115+
optLevel: 2,
116+
entryPoints: ["main.fc"],
117+
sources: {
118+
"main.fc": '#include "non-existing.fc";'
119+
}
120+
});
121+
122+
expect(result.status).toEqual('error');
95123
result = result as ErrorResult;
96-
expect(result.message.indexOf(`FunC version ${compilerVersionExpected.funcVersion} does not satisfy condition <0.2.0`) != undefined);
97-
})
124+
expect(result.message.indexOf('Cannot find source file non-existing.fc') >= 0).toBeTruthy();
125+
});
98126
});

tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"compilerOptions": {
3-
"target": "ESNext",
3+
"target": "ES2019",
44
"outDir": "dist",
55
"module": "commonjs",
66
"declaration": true,

version.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"funcVersion": "0.2.0",
3-
"funcFiftLibCommitHash": "4dc54cefc49250fd39dcbe6d764b4493870354dc",
4-
"funcFiftLibCommitDate": "2022-09-15 00:53:02 +0700"
2+
"funcVersion": "0.3.0",
3+
"funcFiftLibCommitHash": "3dd87ae7a703d2771c4e299a4490eb66787eb270",
4+
"funcFiftLibCommitDate": "2022-11-15 17:17:07 +0300"
55
}

0 commit comments

Comments
 (0)