@opcat-labs/scrypt-ts-transpiler-opcat 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compile/compilerWrapper.d.ts +203 -0
- package/dist/compile/compilerWrapper.d.ts.map +1 -0
- package/dist/compile/compilerWrapper.js +1019 -0
- package/dist/compile/compilerWrapper.js.map +1 -0
- package/dist/compile/findCompiler.d.ts +3 -0
- package/dist/compile/findCompiler.d.ts.map +1 -0
- package/dist/compile/findCompiler.js +102 -0
- package/dist/compile/findCompiler.js.map +1 -0
- package/dist/compile/getBinary.d.ts +3 -0
- package/dist/compile/getBinary.d.ts.map +1 -0
- package/dist/compile/getBinary.js +94 -0
- package/dist/compile/getBinary.js.map +1 -0
- package/dist/debug.d.ts +25 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +110 -0
- package/dist/debug.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +110 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer.d.ts +52 -0
- package/dist/indexer.d.ts.map +1 -0
- package/dist/indexer.js +189 -0
- package/dist/indexer.js.map +1 -0
- package/dist/relinker.d.ts +44 -0
- package/dist/relinker.d.ts.map +1 -0
- package/dist/relinker.js +321 -0
- package/dist/relinker.js.map +1 -0
- package/dist/resolver.d.ts +3 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +280 -0
- package/dist/resolver.js.map +1 -0
- package/dist/scryptParser.d.ts +31 -0
- package/dist/scryptParser.d.ts.map +1 -0
- package/dist/scryptParser.js +372 -0
- package/dist/scryptParser.js.map +1 -0
- package/dist/snippets.d.ts +39 -0
- package/dist/snippets.d.ts.map +1 -0
- package/dist/snippets.js +54 -0
- package/dist/snippets.js.map +1 -0
- package/dist/transpiler.d.ts +314 -0
- package/dist/transpiler.d.ts.map +1 -0
- package/dist/transpiler.js +4239 -0
- package/dist/transpiler.js.map +1 -0
- package/dist/types.d.ts +33 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +31 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +23 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +352 -0
- package/dist/utils.js.map +1 -0
- package/package.json +43 -0
|
@@ -0,0 +1,4239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.Transpiler = void 0;
|
|
30
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
31
|
+
const tsquery_1 = require("@phenomnomnominal/tsquery");
|
|
32
|
+
const indexer_1 = require("./indexer");
|
|
33
|
+
const path = __importStar(require("path"));
|
|
34
|
+
const utils_1 = require("./utils");
|
|
35
|
+
const compilerWrapper_1 = require("./compile/compilerWrapper");
|
|
36
|
+
const findCompiler_1 = require("./compile/findCompiler");
|
|
37
|
+
const os_1 = require("os");
|
|
38
|
+
const fs_1 = require("fs");
|
|
39
|
+
const types_1 = require("./types");
|
|
40
|
+
const snippets_1 = require("./snippets");
|
|
41
|
+
const relinker_1 = require("./relinker");
|
|
42
|
+
const BUILDIN_PACKAGE_NAME = '@opcat-labs/scrypt-ts-opcat';
|
|
43
|
+
/**
|
|
44
|
+
* @ignore
|
|
45
|
+
*/
|
|
46
|
+
const DEFAULT_AST_COMPILE_OPTS = Object.freeze({
|
|
47
|
+
ast: true,
|
|
48
|
+
asm: false,
|
|
49
|
+
hex: false,
|
|
50
|
+
debug: false,
|
|
51
|
+
artifact: false,
|
|
52
|
+
sourceMap: false,
|
|
53
|
+
outputToFiles: true,
|
|
54
|
+
cmdArgs: '--std',
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* @ignore
|
|
58
|
+
*/
|
|
59
|
+
var DecoratorName;
|
|
60
|
+
(function (DecoratorName) {
|
|
61
|
+
DecoratorName["Prop"] = "prop";
|
|
62
|
+
DecoratorName["Method"] = "method";
|
|
63
|
+
})(DecoratorName || (DecoratorName = {}));
|
|
64
|
+
/**
|
|
65
|
+
* @ignore
|
|
66
|
+
*/
|
|
67
|
+
class EmittedLine {
|
|
68
|
+
constructor(prefixTabs = 0, currentCol = 0, codeLines = '', sourceMap = []) {
|
|
69
|
+
this.prefixTabs = prefixTabs;
|
|
70
|
+
this.currentCol = currentCol;
|
|
71
|
+
this.code = codeLines;
|
|
72
|
+
this.sourceMap = sourceMap;
|
|
73
|
+
}
|
|
74
|
+
copy() {
|
|
75
|
+
return new EmittedLine(this.prefixTabs, this.currentCol, this.code, JSON.parse(JSON.stringify(this.sourceMap)));
|
|
76
|
+
}
|
|
77
|
+
findByCol(col) {
|
|
78
|
+
for (let i = 0; i < this.sourceMap.length; i++) {
|
|
79
|
+
const sourceMap = this.sourceMap[i];
|
|
80
|
+
if (col >= sourceMap[0]) {
|
|
81
|
+
const nextSourceMap = this.sourceMap[i + 1];
|
|
82
|
+
if (nextSourceMap && col <= nextSourceMap[0]) {
|
|
83
|
+
return sourceMap;
|
|
84
|
+
}
|
|
85
|
+
else if (!nextSourceMap) {
|
|
86
|
+
return sourceMap;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return this.sourceMap[0];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* @ignore
|
|
95
|
+
*/
|
|
96
|
+
class EmittedSection {
|
|
97
|
+
constructor(initialLine) {
|
|
98
|
+
this.lines = [];
|
|
99
|
+
this.errors = [];
|
|
100
|
+
if (initialLine)
|
|
101
|
+
this.lines.push(initialLine);
|
|
102
|
+
this.skipNextAppend = false;
|
|
103
|
+
}
|
|
104
|
+
static join(...sections) {
|
|
105
|
+
const completed = new EmittedSection();
|
|
106
|
+
sections.forEach((sec) => completed.concat(sec));
|
|
107
|
+
return completed;
|
|
108
|
+
}
|
|
109
|
+
getLastLine() {
|
|
110
|
+
return this.lines[this.lines.length - 1];
|
|
111
|
+
}
|
|
112
|
+
getCode() {
|
|
113
|
+
return this.lines.map((l) => l.code).join('\n');
|
|
114
|
+
}
|
|
115
|
+
getSourceMap() {
|
|
116
|
+
return this.lines.map((l) => l.sourceMap);
|
|
117
|
+
}
|
|
118
|
+
append(code, srcLocation) {
|
|
119
|
+
if (this.skipNextAppend) {
|
|
120
|
+
this.skipNextAppend = false;
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
let lastLine = this.getLastLine();
|
|
124
|
+
const inlineCode = code.replaceAll(/\n/g, '');
|
|
125
|
+
const startNewLine = code.startsWith('\n');
|
|
126
|
+
if (startNewLine || !lastLine) {
|
|
127
|
+
const prefixTab = lastLine?.prefixTabs ?? 0;
|
|
128
|
+
lastLine = new EmittedLine(prefixTab, prefixTab * 2, Array(prefixTab * 2)
|
|
129
|
+
.fill(' ')
|
|
130
|
+
.join('') + inlineCode);
|
|
131
|
+
this.lines.push(lastLine);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
lastLine.code += inlineCode;
|
|
135
|
+
}
|
|
136
|
+
if (srcLocation) {
|
|
137
|
+
// adjust col to skip prefix spaces
|
|
138
|
+
const skipSpaceCol = inlineCode.search(/[^ ]/);
|
|
139
|
+
const adjustCol = lastLine.currentCol + (skipSpaceCol > 0 ? skipSpaceCol : 0);
|
|
140
|
+
// sourcemap format: [ generatedCodeColumn, sourceIndex, sourceCodeLine, sourceCodeColumn, nameIndex? ]
|
|
141
|
+
lastLine.sourceMap.push([adjustCol, 0, srcLocation.line, srcLocation.character]);
|
|
142
|
+
}
|
|
143
|
+
lastLine.currentCol += inlineCode.length;
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
appendWith(ctx, updater, increaseTab = false) {
|
|
147
|
+
if (this.skipNextAppend) {
|
|
148
|
+
this.skipNextAppend = false;
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
// use the last line of current section as the start line of upcoming section
|
|
153
|
+
const initialLine = (this.getLastLine() || new EmittedLine()).copy();
|
|
154
|
+
if (increaseTab) {
|
|
155
|
+
initialLine.prefixTabs += 1;
|
|
156
|
+
}
|
|
157
|
+
this.concat(updater.call(ctx, new EmittedSection(initialLine)), true);
|
|
158
|
+
if (increaseTab) {
|
|
159
|
+
this.getLastLine().prefixTabs -= 1;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
if (err instanceof types_1.TranspileError) {
|
|
164
|
+
this.errors.push(err);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
throw err;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
concat(postSection, lastLineOverlays = false) {
|
|
173
|
+
if (lastLineOverlays)
|
|
174
|
+
this.lines.pop();
|
|
175
|
+
this.lines = this.lines.concat(postSection.lines);
|
|
176
|
+
this.errors = this.errors.concat(postSection.errors);
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const SmartContractBuiltinMethods = [
|
|
181
|
+
'buildChangeOutput',
|
|
182
|
+
'appendStateOutput',
|
|
183
|
+
'buildStateOutputs',
|
|
184
|
+
'relTimeLock',
|
|
185
|
+
'absTimeLock',
|
|
186
|
+
'checkSig',
|
|
187
|
+
'checkMultiSig',
|
|
188
|
+
'checkPreimageAdvanced',
|
|
189
|
+
'checkPreimageSigHashType',
|
|
190
|
+
'checkPreimage',
|
|
191
|
+
'insertCodeSeparator',
|
|
192
|
+
'checkInputState',
|
|
193
|
+
'backtraceToOutpoint',
|
|
194
|
+
'backtraceToScript',
|
|
195
|
+
'checkOutputs',
|
|
196
|
+
];
|
|
197
|
+
/**
|
|
198
|
+
* @ignore
|
|
199
|
+
*/
|
|
200
|
+
class Transpiler {
|
|
201
|
+
constructor(sourceFile, host, checker, tsRootDir, scryptOutDir, indexer, compilerOptions) {
|
|
202
|
+
this.scComponents = [];
|
|
203
|
+
// public publicMethods: ts.MethodDeclaration[] = [];
|
|
204
|
+
this.methodInfos = new Map();
|
|
205
|
+
this.propInfos = new Map();
|
|
206
|
+
this._localTypeSymbols = new Map();
|
|
207
|
+
this._accessBuiltinsSymbols = new Set();
|
|
208
|
+
this._importedTypeSymbols = new Map();
|
|
209
|
+
this._stateTypeSymbols = new Map();
|
|
210
|
+
this._watch = false;
|
|
211
|
+
this._constructorParametersMap = new Map();
|
|
212
|
+
this._srcFile = sourceFile;
|
|
213
|
+
this._host = host;
|
|
214
|
+
this._checker = checker;
|
|
215
|
+
this._tsRootDir = tsRootDir;
|
|
216
|
+
this._scryptOutDir = scryptOutDir;
|
|
217
|
+
this._indexer = indexer;
|
|
218
|
+
this._compilerOptions = compilerOptions;
|
|
219
|
+
this._watch = compilerOptions.watch || false;
|
|
220
|
+
this.loadBuiltinIndexer();
|
|
221
|
+
this.searchSmartContractComponents();
|
|
222
|
+
this.searchTopCtcs();
|
|
223
|
+
}
|
|
224
|
+
get ctxMethods() {
|
|
225
|
+
return this.getCtxMethodInfos().map((info) => info.name);
|
|
226
|
+
}
|
|
227
|
+
get _scryptRelativePath() {
|
|
228
|
+
return this.getRelativePathFromTsRoot(this._srcFile.fileName);
|
|
229
|
+
}
|
|
230
|
+
get _scryptFullPath() {
|
|
231
|
+
return path.join(this._scryptOutDir, this._scryptRelativePath);
|
|
232
|
+
}
|
|
233
|
+
get currentContractName() {
|
|
234
|
+
return this._currentContract.name.getText();
|
|
235
|
+
}
|
|
236
|
+
get currentbaseContractName() {
|
|
237
|
+
return Transpiler.getBaseContractName(this._currentContract).baseContractName;
|
|
238
|
+
}
|
|
239
|
+
get currentbaseContract() {
|
|
240
|
+
return Transpiler.contractAst.get(this.currentbaseContractName);
|
|
241
|
+
}
|
|
242
|
+
get _transformationResultRelativePath() {
|
|
243
|
+
return (0, utils_1.alterFileExt)(this._scryptRelativePath, 'transformer.json', Transpiler.scryptFileExt);
|
|
244
|
+
}
|
|
245
|
+
get _transformationResultFullPath() {
|
|
246
|
+
return (0, utils_1.alterFileExt)(this._scryptFullPath, 'transformer.json', Transpiler.scryptFileExt);
|
|
247
|
+
}
|
|
248
|
+
get _sourceMapRelativePath() {
|
|
249
|
+
return (0, utils_1.alterFileExt)(this._scryptRelativePath, 'scrypt.map', Transpiler.scryptFileExt);
|
|
250
|
+
}
|
|
251
|
+
get _sourceMapFullPath() {
|
|
252
|
+
return (0, utils_1.alterFileExt)(this._scryptFullPath, 'scrypt.map', Transpiler.scryptFileExt);
|
|
253
|
+
}
|
|
254
|
+
setLocalSymbols(localTypeSymbols) {
|
|
255
|
+
this._localTypeSymbols = localTypeSymbols;
|
|
256
|
+
}
|
|
257
|
+
transform(allmissSym, relinker) {
|
|
258
|
+
console.log(`transform ${this._srcFile.fileName} to ${this._scryptFullPath}`);
|
|
259
|
+
// transform the contract, library, structs and find its imported symbols
|
|
260
|
+
const contractAndLibs = this.scComponents.map((cDef) => this.transformClassDeclaration(cDef));
|
|
261
|
+
const structs = this.transformTypeLiteralAndInterfaces();
|
|
262
|
+
// transform the import expressions
|
|
263
|
+
const imports = this.transformImports(allmissSym, relinker);
|
|
264
|
+
const result = EmittedSection.join(imports, structs, ...contractAndLibs);
|
|
265
|
+
if (this._watch) {
|
|
266
|
+
setTimeout(() => {
|
|
267
|
+
this.diagnose(result.errors);
|
|
268
|
+
}, 500);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
this.outputScrypt(result);
|
|
272
|
+
if (result.errors.length == 0) {
|
|
273
|
+
try {
|
|
274
|
+
relinker.relink(this._scryptFullPath);
|
|
275
|
+
}
|
|
276
|
+
catch (_err) {
|
|
277
|
+
console.log('relink failed', this._scryptFullPath);
|
|
278
|
+
console.log(_err);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
this.updateIndex();
|
|
282
|
+
this.diagnose(result.errors);
|
|
283
|
+
this.outputSourceMapFile(result);
|
|
284
|
+
this.outputTransformationResult(result);
|
|
285
|
+
}
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
288
|
+
isTransformable() {
|
|
289
|
+
return this.scComponents.length > 0;
|
|
290
|
+
}
|
|
291
|
+
getSCComponents() {
|
|
292
|
+
return this.scComponents;
|
|
293
|
+
}
|
|
294
|
+
isFromThirdParty(filepath) {
|
|
295
|
+
return filepath.endsWith('.d.ts');
|
|
296
|
+
}
|
|
297
|
+
outputScrypt(result) {
|
|
298
|
+
typescript_1.default.sys.writeFile(this._scryptFullPath, result.getCode());
|
|
299
|
+
}
|
|
300
|
+
outputTransformationResult(result) {
|
|
301
|
+
const transResult = {
|
|
302
|
+
success: result.errors.length === 0,
|
|
303
|
+
errors: result.errors,
|
|
304
|
+
scryptfile: this._scryptRelativePath,
|
|
305
|
+
sourceMapFile: this._sourceMapRelativePath,
|
|
306
|
+
ctxMethods: this.ctxMethods,
|
|
307
|
+
};
|
|
308
|
+
typescript_1.default.sys.writeFile(this._transformationResultFullPath, JSON.stringify(transResult, null, 2));
|
|
309
|
+
}
|
|
310
|
+
diagnose(errors) {
|
|
311
|
+
errors.forEach((error) => this.outputDiagnostic(error));
|
|
312
|
+
}
|
|
313
|
+
outputDiagnostic(diag) {
|
|
314
|
+
console.log(`${BUILDIN_PACKAGE_NAME} ERROR - ${diag.srcRange.fileName}:${diag.srcRange.start.line}:${diag.srcRange.start.character}:${diag.srcRange.end.line}:${diag.srcRange.end.character} - ${diag.message}\n`);
|
|
315
|
+
}
|
|
316
|
+
// check transformed scrypt code to collect compile time errors
|
|
317
|
+
checkTransformedScrypt(result) {
|
|
318
|
+
if (result.errors.length === 0 && (0, fs_1.existsSync)(this._scryptFullPath)) {
|
|
319
|
+
try {
|
|
320
|
+
const tmpDir = (0, fs_1.mkdtempSync)(path.join((0, os_1.tmpdir)(), 'scrypt-ts-btc'));
|
|
321
|
+
const input = {
|
|
322
|
+
path: this._scryptFullPath,
|
|
323
|
+
content: result.getCode(),
|
|
324
|
+
};
|
|
325
|
+
const settings = Object.assign({}, DEFAULT_AST_COMPILE_OPTS, {
|
|
326
|
+
cmdPrefix: (0, findCompiler_1.findCompiler)(),
|
|
327
|
+
outputDir: tmpDir,
|
|
328
|
+
});
|
|
329
|
+
const astResult = (0, compilerWrapper_1.compile)(input, settings);
|
|
330
|
+
const errors = this.compileError2transpileError(astResult.errors, result.lines);
|
|
331
|
+
return errors;
|
|
332
|
+
}
|
|
333
|
+
catch (err) {
|
|
334
|
+
// for the case when scrypt compiler crushed.
|
|
335
|
+
return [
|
|
336
|
+
new types_1.TranspileError(`Internal compilation FATAL error raised for auto-generated file: ${this._scryptFullPath} , please report it as a bug to ${BUILDIN_PACKAGE_NAME}\n${err}`, {
|
|
337
|
+
fileName: this._scryptFullPath,
|
|
338
|
+
start: { line: -1, character: -1 },
|
|
339
|
+
end: { line: -1, character: -1 },
|
|
340
|
+
}),
|
|
341
|
+
];
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return [];
|
|
345
|
+
}
|
|
346
|
+
compileError2transpileError(errors, scryptLines) {
|
|
347
|
+
const tsSrcLines = this._srcFile.getFullText().split('\n');
|
|
348
|
+
return errors.map((error) => {
|
|
349
|
+
const scryptLine = scryptLines[(error.position[0]?.line || 0) - 1];
|
|
350
|
+
const sourcemap = scryptLine
|
|
351
|
+
? scryptLine.findByCol(error.position[0]?.column || -1)
|
|
352
|
+
: undefined;
|
|
353
|
+
if (sourcemap) {
|
|
354
|
+
const tsLine = sourcemap[2];
|
|
355
|
+
const tsColumn = sourcemap[3];
|
|
356
|
+
// code from current column to end of line
|
|
357
|
+
const tsCode = tsSrcLines[tsLine]
|
|
358
|
+
.slice(tsColumn)
|
|
359
|
+
.replace(/\/\/.*/, '')
|
|
360
|
+
.trim();
|
|
361
|
+
return new types_1.TranspileError(`The code '${tsCode}' was successfully transformed but compiled with error: ${error.message}`, {
|
|
362
|
+
fileName: this._srcFile.fileName,
|
|
363
|
+
start: { line: tsLine, character: tsColumn },
|
|
364
|
+
end: { line: tsLine, character: tsColumn + tsCode.length },
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
// when can not find sourcemap for the certain `scryptLine`, use the first sourcemap
|
|
369
|
+
const sourcemap = scryptLines[1]?.sourceMap[0];
|
|
370
|
+
return new types_1.TranspileError(`Internal compilation error raised for auto-generated file: ${this._scryptFullPath}:${error.position[0]?.line || -1}:${error.position[0]?.column || -1} , please report it as a bug to ${BUILDIN_PACKAGE_NAME}`, {
|
|
371
|
+
fileName: this._srcFile.fileName,
|
|
372
|
+
start: {
|
|
373
|
+
line: sourcemap ? sourcemap[2] : -1,
|
|
374
|
+
character: sourcemap ? sourcemap[3] : -1,
|
|
375
|
+
},
|
|
376
|
+
end: { line: -1, character: -1 },
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
outputSourceMapFile(result) {
|
|
382
|
+
typescript_1.default.sys.writeFile(this._sourceMapFullPath, JSON.stringify(result.getSourceMap()));
|
|
383
|
+
}
|
|
384
|
+
updateIndex() {
|
|
385
|
+
const globalSymbols = Array.from(this._localTypeSymbols.entries()).map(([symbolName, symbol]) => {
|
|
386
|
+
const symbolFile = this.findDeclarationFile(symbol);
|
|
387
|
+
let symbolDec = symbol.declarations[0];
|
|
388
|
+
symbolDec = symbolDec['name'] || symbolDec;
|
|
389
|
+
const stateTypeSymbol = this._stateTypeSymbols.get(symbolName);
|
|
390
|
+
return {
|
|
391
|
+
name: symbolName,
|
|
392
|
+
srcRange: {
|
|
393
|
+
fileName: symbolFile.fileName,
|
|
394
|
+
start: symbolFile.getLineAndCharacterOfPosition(symbolDec.getStart()),
|
|
395
|
+
end: symbolFile.getLineAndCharacterOfPosition(symbolDec.getEnd()),
|
|
396
|
+
},
|
|
397
|
+
stateType: stateTypeSymbol?.name,
|
|
398
|
+
};
|
|
399
|
+
});
|
|
400
|
+
this._indexer.addSymbols(globalSymbols, this._scryptRelativePath);
|
|
401
|
+
}
|
|
402
|
+
isExtendsSCComponent(node) {
|
|
403
|
+
let isExtends = false;
|
|
404
|
+
if (this.isContract(node)) {
|
|
405
|
+
Transpiler.contractAst.set(node.name.getText(), node);
|
|
406
|
+
if ((0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.AbstractKeyword)) {
|
|
407
|
+
// ignore abstract class, then are base contract class
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
isExtends = true;
|
|
411
|
+
}
|
|
412
|
+
if (this.isLibrary(node)) {
|
|
413
|
+
isExtends = true;
|
|
414
|
+
}
|
|
415
|
+
if (!isExtends) {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
const typeParameterNames = (node.typeParameters || []).map((v) => v.name.getText());
|
|
419
|
+
const { typeArgName: stateTypeName, stateTypeNode } = Transpiler.getBaseContractName(node);
|
|
420
|
+
if (stateTypeName) {
|
|
421
|
+
// exclude the case like: `class ContractA<T> extends Base<T>`
|
|
422
|
+
if (typeParameterNames.includes(stateTypeName)) {
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
if (typescript_1.default.isTypeReferenceNode(stateTypeNode)) {
|
|
426
|
+
const typeRef = this._checker.getSymbolAtLocation(stateTypeNode.typeName);
|
|
427
|
+
if (typeRef) {
|
|
428
|
+
this._stateTypeSymbols.set(node.name.getText(), typeRef.flags & typescript_1.default.SymbolFlags.Alias
|
|
429
|
+
? this._checker.getAliasedSymbol(typeRef)
|
|
430
|
+
: typeRef);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
// TODO: add support for type literal like: `SmartContract<{ state: number }>`
|
|
435
|
+
// currently, do not support type literal state type, and throw error in `checkLiteralStateType`
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
isContract(node) {
|
|
441
|
+
if (Array.isArray(node.heritageClauses) && node.heritageClauses.length === 1) {
|
|
442
|
+
const clause = node.heritageClauses[0];
|
|
443
|
+
if (clause.token == typescript_1.default.SyntaxKind.ExtendsKeyword && clause.types.length === 1) {
|
|
444
|
+
const { baseContractName } = Transpiler.getBaseContractName(node);
|
|
445
|
+
return baseContractName === 'SmartContract' || Transpiler.contractAst.has(baseContractName);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
checkLiteralStateType() {
|
|
451
|
+
const node = this._currentContract;
|
|
452
|
+
if (Array.isArray(node.heritageClauses) && node.heritageClauses.length === 1) {
|
|
453
|
+
const clause = node.heritageClauses[0];
|
|
454
|
+
if (clause.token === typescript_1.default.SyntaxKind.ExtendsKeyword && clause.types.length === 1) {
|
|
455
|
+
const type = clause.types[0];
|
|
456
|
+
if (type.kind === typescript_1.default.SyntaxKind.ExpressionWithTypeArguments) {
|
|
457
|
+
const stateTypeNode = type.typeArguments?.length > 0 ? type.typeArguments[0] : undefined;
|
|
458
|
+
if (stateTypeNode && typescript_1.default.isTypeLiteralNode(stateTypeNode)) {
|
|
459
|
+
throw new types_1.TranspileError(`State type of the contract \`${node.name.getText()}\` should be a TypeReference instead of a ${typescript_1.default.SyntaxKind[stateTypeNode.kind]}`, this.getRange(stateTypeNode));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
static getBaseContractName(node) {
|
|
466
|
+
if (Array.isArray(node.heritageClauses) && node.heritageClauses.length === 1) {
|
|
467
|
+
const clause = node.heritageClauses[0];
|
|
468
|
+
if (clause.token == typescript_1.default.SyntaxKind.ExtendsKeyword && clause.types.length === 1) {
|
|
469
|
+
const type = clause.types[0];
|
|
470
|
+
if (type.kind === typescript_1.default.SyntaxKind.ExpressionWithTypeArguments) {
|
|
471
|
+
// get base contract node, and parse the chains
|
|
472
|
+
const baseContractName = type.expression.getText();
|
|
473
|
+
// class Child extends Base<State>
|
|
474
|
+
let typeArgName = type.typeArguments?.length > 0 ? type.typeArguments[0].getText() : undefined;
|
|
475
|
+
let stateTypeNode = undefined;
|
|
476
|
+
if (typeArgName) {
|
|
477
|
+
stateTypeNode = node.heritageClauses[0].types[0].typeArguments[0];
|
|
478
|
+
}
|
|
479
|
+
// class Child extends Base
|
|
480
|
+
// class Base extends SmartContract<State>
|
|
481
|
+
if (!typeArgName &&
|
|
482
|
+
baseContractName !== 'SmartContract' &&
|
|
483
|
+
baseContractName !== 'SmartContractLib') {
|
|
484
|
+
const baseContractAst = Transpiler.contractAst.get(baseContractName);
|
|
485
|
+
if (!baseContractAst) {
|
|
486
|
+
return { baseContractName };
|
|
487
|
+
}
|
|
488
|
+
const baseRes = this.getBaseContractName(baseContractAst);
|
|
489
|
+
typeArgName = baseRes.typeArgName;
|
|
490
|
+
stateTypeNode = baseRes.stateTypeNode;
|
|
491
|
+
}
|
|
492
|
+
return {
|
|
493
|
+
baseContractName,
|
|
494
|
+
typeArgName,
|
|
495
|
+
stateTypeNode,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
return {
|
|
499
|
+
baseContractName: type.getText(),
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
throw new Error(`Can't get base contract for class ${node.name.getText()}`);
|
|
504
|
+
}
|
|
505
|
+
isInherited(node) {
|
|
506
|
+
if (node.heritageClauses &&
|
|
507
|
+
node.heritageClauses.length === 1 &&
|
|
508
|
+
node.heritageClauses[0].token === typescript_1.default.SyntaxKind.ExtendsKeyword) {
|
|
509
|
+
const { baseContractName } = Transpiler.getBaseContractName(node);
|
|
510
|
+
return Transpiler.contractAst.has(baseContractName);
|
|
511
|
+
}
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
isTranspilingConstructor(node) {
|
|
515
|
+
if (!node.parent) {
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
if (typescript_1.default.isConstructorDeclaration(node.parent)) {
|
|
519
|
+
return true;
|
|
520
|
+
}
|
|
521
|
+
return this.isTranspilingConstructor(node.parent);
|
|
522
|
+
}
|
|
523
|
+
isTranspilingBaseContract(node) {
|
|
524
|
+
const cls = Transpiler.getClassDeclaration(node);
|
|
525
|
+
if (cls && cls.name.getText() !== this.currentContractName) {
|
|
526
|
+
return true;
|
|
527
|
+
}
|
|
528
|
+
return false;
|
|
529
|
+
}
|
|
530
|
+
isLibrary(node) {
|
|
531
|
+
if (node.heritageClauses &&
|
|
532
|
+
node.heritageClauses.length === 1 &&
|
|
533
|
+
node?.heritageClauses[0].token === typescript_1.default.SyntaxKind.ExtendsKeyword) {
|
|
534
|
+
const parentClassName = node?.heritageClauses[0].types[0]?.getText();
|
|
535
|
+
return parentClassName === 'SmartContractLib' || /^StateLib<.+>$/.test(parentClassName);
|
|
536
|
+
}
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* get relative path starting from `tsRootDir`
|
|
541
|
+
* @param fullFilePath
|
|
542
|
+
* @param ext extension of the path
|
|
543
|
+
* @returns
|
|
544
|
+
*/
|
|
545
|
+
getRelativePathFromTsRoot(fullFilePath, ext = Transpiler.scryptFileExt) {
|
|
546
|
+
// ts source file to root dir relative path which will be kept in scrypt output structure.
|
|
547
|
+
let root2srcRelativePath = path.relative(this._tsRootDir, fullFilePath).replaceAll('\\', '/');
|
|
548
|
+
if (root2srcRelativePath.startsWith('src/contracts/')) {
|
|
549
|
+
root2srcRelativePath = root2srcRelativePath.replace('src/contracts/', '');
|
|
550
|
+
}
|
|
551
|
+
if (root2srcRelativePath.startsWith('../')) {
|
|
552
|
+
root2srcRelativePath = root2srcRelativePath.replace('../', '');
|
|
553
|
+
}
|
|
554
|
+
const basename = path.basename(root2srcRelativePath, path.extname(root2srcRelativePath));
|
|
555
|
+
return path
|
|
556
|
+
.join(path.dirname(root2srcRelativePath), `${basename}.${ext}`)
|
|
557
|
+
.replaceAll('\\', '/');
|
|
558
|
+
}
|
|
559
|
+
getRelativePathFromArtifacts(fullFilePath, ext = Transpiler.scryptFileExt) {
|
|
560
|
+
// ts source file to root dir relative path which will be kept in scrypt output structure.
|
|
561
|
+
const relativePath = path.relative(this._scryptOutDir, fullFilePath).replaceAll('\\', '/');
|
|
562
|
+
const basename = path.basename(relativePath, path.extname(relativePath));
|
|
563
|
+
return path.join(path.dirname(relativePath), `${basename}.${ext}`).replaceAll('\\', '/');
|
|
564
|
+
}
|
|
565
|
+
searchSmartContractComponents() {
|
|
566
|
+
const scComponentDefs = (0, tsquery_1.tsquery)(this._srcFile, 'ClassDeclaration:has(HeritageClause)');
|
|
567
|
+
const scComponents = scComponentDefs.filter((cDef) => typescript_1.default.isClassDeclaration(cDef) && this.isExtendsSCComponent(cDef));
|
|
568
|
+
this.scComponents = scComponents;
|
|
569
|
+
}
|
|
570
|
+
searchTopCtcs() {
|
|
571
|
+
const declarations = (0, tsquery_1.tsquery)(this._srcFile, 'SourceFile > VariableStatement > VariableDeclarationList');
|
|
572
|
+
const ctcDeclarations = declarations.filter((d) => {
|
|
573
|
+
if (typescript_1.default.isVariableDeclarationList(d)) {
|
|
574
|
+
return this.isCtcDeclaration(d.declarations[0]);
|
|
575
|
+
}
|
|
576
|
+
return false;
|
|
577
|
+
});
|
|
578
|
+
ctcDeclarations.forEach((d) => {
|
|
579
|
+
Transpiler.topCtcs.set(`${(0, utils_1.sha1)(this._srcFile.fileName)}:${d.declarations[0].name.getText()}`, this.evalCtcExpression(d.declarations[0].initializer));
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
getCoordinates(pos) {
|
|
583
|
+
return pos ? this._srcFile.getLineAndCharacterOfPosition(pos) : undefined;
|
|
584
|
+
}
|
|
585
|
+
getRange(node) {
|
|
586
|
+
return {
|
|
587
|
+
fileName: this._srcFile.fileName,
|
|
588
|
+
start: this.getCoordinates(node.getStart()),
|
|
589
|
+
end: this.getCoordinates(node.getEnd()),
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
getResolvedType(node) {
|
|
593
|
+
const symbol = this._checker.getSymbolAtLocation(node);
|
|
594
|
+
return symbol
|
|
595
|
+
? this._checker.getTypeOfSymbolAtLocation(symbol, symbol.declarations[0])
|
|
596
|
+
: undefined;
|
|
597
|
+
}
|
|
598
|
+
findDeclarationFile(symbol) {
|
|
599
|
+
let node = symbol.declarations[0];
|
|
600
|
+
while (node?.parent) {
|
|
601
|
+
node = node.parent;
|
|
602
|
+
}
|
|
603
|
+
return typescript_1.default.isSourceFile(node) ? node : undefined;
|
|
604
|
+
}
|
|
605
|
+
transformProps(section, node) {
|
|
606
|
+
const category = this.isContract(node) ? 'contract' : 'library';
|
|
607
|
+
node.members.forEach((m) => {
|
|
608
|
+
if (m.kind === typescript_1.default.SyntaxKind.PropertyDeclaration) {
|
|
609
|
+
section.appendWith(this, (memSec) => {
|
|
610
|
+
return this.transformPropertyDeclaration(m, memSec, category);
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
checkPropsOverride(node, baseContract) {
|
|
616
|
+
const baseContractProps = baseContract.members
|
|
617
|
+
.filter((m) => Transpiler.isProperty(m))
|
|
618
|
+
.map((m) => m.name.getText());
|
|
619
|
+
node.members.forEach((m) => {
|
|
620
|
+
if (Transpiler.isProperty(m)) {
|
|
621
|
+
if (baseContractProps.includes(m.name.getText())) {
|
|
622
|
+
throw new types_1.TranspileError(`Untransformable property: '${m.name.getText()}', already defined in base contract '${baseContract.name.getText()}'`, this.getRange(node));
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
checkMethodsOverride(node, baseContract) {
|
|
628
|
+
const baseContractMethods = baseContract.members
|
|
629
|
+
.filter((m) => Transpiler.isMethod(m))
|
|
630
|
+
.map((m) => m.name.getText());
|
|
631
|
+
node.members.forEach((m) => {
|
|
632
|
+
if (Transpiler.isMethod(m)) {
|
|
633
|
+
if (baseContractMethods.includes(m.name.getText())) {
|
|
634
|
+
throw new types_1.TranspileError(`Untransformable method: '${m.name.getText()}', already defined in base contract '${baseContract.name.getText()}'`, this.getRange(node));
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
transformMethods(section, node) {
|
|
640
|
+
const category = this.isContract(node) ? 'contract' : 'library';
|
|
641
|
+
node.members.forEach((m) => {
|
|
642
|
+
if (m.kind === typescript_1.default.SyntaxKind.MethodDeclaration) {
|
|
643
|
+
section.appendWith(this, (memSec) => {
|
|
644
|
+
return this.transformMethodDeclaration(m, memSec, category);
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
transformClassDeclaration(node) {
|
|
650
|
+
const className = node.name.getText();
|
|
651
|
+
this._currentContract = node;
|
|
652
|
+
// also add it to local type symbols
|
|
653
|
+
this._localTypeSymbols.set(className, this._checker.getSymbolAtLocation(node.name));
|
|
654
|
+
const stateTypeSymbol = this._stateTypeSymbols.get(className);
|
|
655
|
+
if (stateTypeSymbol) {
|
|
656
|
+
if (stateTypeSymbol.declarations[0].getSourceFile() === node.getSourceFile()) {
|
|
657
|
+
this._localTypeSymbols.set(stateTypeSymbol.name, stateTypeSymbol);
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
this._importedTypeSymbols.set(stateTypeSymbol.name, stateTypeSymbol);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
const category = this.isContract(node) ? 'contract' : 'library';
|
|
664
|
+
const baseContract = this.currentbaseContract;
|
|
665
|
+
// Preprocess all methods
|
|
666
|
+
const section = new EmittedSection();
|
|
667
|
+
try {
|
|
668
|
+
this.checkLiteralStateType();
|
|
669
|
+
this.initAllPropInfos();
|
|
670
|
+
this.initAllMethodInfos();
|
|
671
|
+
this.injectScryptStructs(section);
|
|
672
|
+
section
|
|
673
|
+
.append('\n')
|
|
674
|
+
.append(`\n${category} `)
|
|
675
|
+
.append(`${className} {`, this.getCoordinates(node.name.getStart()))
|
|
676
|
+
.appendWith(this, (membersSection) => {
|
|
677
|
+
if (baseContract) {
|
|
678
|
+
this.checkPropsOverride(node, baseContract);
|
|
679
|
+
this.transformProps(membersSection, baseContract);
|
|
680
|
+
}
|
|
681
|
+
this.transformProps(membersSection, node);
|
|
682
|
+
this.injectScryptProps(membersSection);
|
|
683
|
+
this.transformConstructor(membersSection, node, baseContract);
|
|
684
|
+
if (baseContract) {
|
|
685
|
+
this.checkMethodsOverride(node, baseContract);
|
|
686
|
+
this.transformMethods(membersSection, baseContract);
|
|
687
|
+
}
|
|
688
|
+
this.transformMethods(membersSection, node);
|
|
689
|
+
if (this.isStateful()) {
|
|
690
|
+
this.injectSerializeStateFunc(membersSection);
|
|
691
|
+
}
|
|
692
|
+
// if (this.accessChange()) {
|
|
693
|
+
// membersSection.append(`\n${BUILD_CHANGE_OUTPUT_FUNCTION}`);
|
|
694
|
+
// }
|
|
695
|
+
if (this.getPublicMethodCount() === 0 && category === 'contract') {
|
|
696
|
+
throw new types_1.TranspileError('A `SmartContract` should have at least one public `@method`', this.getRange(node.name));
|
|
697
|
+
}
|
|
698
|
+
return membersSection;
|
|
699
|
+
}, true)
|
|
700
|
+
.append('\n}');
|
|
701
|
+
const methodInfos = Array.from(this.methodInfos, ([key, methodInfo]) => ({ key, methodInfo }))
|
|
702
|
+
.filter((i) => i.key.startsWith(`${className}.`))
|
|
703
|
+
.filter((i) => i.methodInfo.codeSeparatorCount > 0);
|
|
704
|
+
if (methodInfos.length > 1) {
|
|
705
|
+
throw new types_1.TranspileError(`insertCodeSeparator() can only be called by one pulic method`, this.getRange(node));
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
catch (error) {
|
|
709
|
+
if (error instanceof types_1.TranspileError) {
|
|
710
|
+
section.errors.push(error);
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
throw error;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return section;
|
|
717
|
+
}
|
|
718
|
+
injectScryptProps(section) {
|
|
719
|
+
if (this.shouldInjectSHPreimageProp()) {
|
|
720
|
+
section.append(`\nSHPreimage ${snippets_1.InjectedProp_SHPreimage};`);
|
|
721
|
+
}
|
|
722
|
+
if (this.shouldInjectChangeProp()) {
|
|
723
|
+
section.append(`\nTxOut ${snippets_1.InjectedProp_ChangeInfo};`);
|
|
724
|
+
}
|
|
725
|
+
if (this.shouldInjectPrevoutsProp()) {
|
|
726
|
+
section.append(`\nbytes ${snippets_1.InjectedProp_PrevoutsCtx};`);
|
|
727
|
+
}
|
|
728
|
+
if (this.shouldInjectPrevoutProp()) {
|
|
729
|
+
section.append(`\nOutpoint ${snippets_1.InjectedProp_Prevout};`);
|
|
730
|
+
}
|
|
731
|
+
if (this.shouldInjectSpentScriptsProp()) {
|
|
732
|
+
section.append(`\nbytes ${snippets_1.InjectedProp_SpentScriptHashes};`);
|
|
733
|
+
}
|
|
734
|
+
if (this.shouldInjectSpentAmountsProp()) {
|
|
735
|
+
section.append(`\nbytes ${snippets_1.InjectedProp_SpentAmounts};`);
|
|
736
|
+
}
|
|
737
|
+
if (this.shouldInjectSpentDataHashesProp()) {
|
|
738
|
+
section.append(`\nbytes ${snippets_1.InjectedProp_SpentDataHashes};`);
|
|
739
|
+
}
|
|
740
|
+
if (this.shouldInjectPrevTxHashPreimageProp()) {
|
|
741
|
+
section.append(`\nTxHashPreimage ${snippets_1.InjectedProp_PrevTxHashPreimage};`);
|
|
742
|
+
}
|
|
743
|
+
if (this.shouldInjectCurStateProp()) {
|
|
744
|
+
const stateTypeSymbol = this._stateTypeSymbols.get(this.currentContractName);
|
|
745
|
+
if (!stateTypeSymbol) {
|
|
746
|
+
throw new Error('State type symbol is not defined');
|
|
747
|
+
}
|
|
748
|
+
section.append(`\n${stateTypeSymbol.name} ${snippets_1.InjectedProp_NextState};`);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
injectScryptStructs(_section) { }
|
|
752
|
+
injectSerializeStateFunc(section) {
|
|
753
|
+
const stateTypeSymbol = this._stateTypeSymbols.get(this.currentContractName);
|
|
754
|
+
if (!stateTypeSymbol) {
|
|
755
|
+
throw new Error('State type symbol is not defined');
|
|
756
|
+
}
|
|
757
|
+
const flattenProps = (prop, prefix) => {
|
|
758
|
+
const dec = prop.flags & typescript_1.default.SymbolFlags.Alias
|
|
759
|
+
? this._checker.getAliasedSymbol(prop).declarations[0]
|
|
760
|
+
: prop.declarations[0];
|
|
761
|
+
let members;
|
|
762
|
+
// interface StateA {count: Int32}; SmartContract<StateA>
|
|
763
|
+
if (typescript_1.default.isInterfaceDeclaration(dec)) {
|
|
764
|
+
members = dec.members;
|
|
765
|
+
}
|
|
766
|
+
// SmartContract<{count: Int32}>
|
|
767
|
+
if (typescript_1.default.isTypeLiteralNode(dec)) {
|
|
768
|
+
members = dec.members;
|
|
769
|
+
}
|
|
770
|
+
// type StateA = {count: Int32}; SmartContract<StateA>
|
|
771
|
+
if (typescript_1.default.isTypeAliasDeclaration(dec) && typescript_1.default.isTypeLiteralNode(dec.type)) {
|
|
772
|
+
members = dec.type.members;
|
|
773
|
+
}
|
|
774
|
+
// state type must be a struct type
|
|
775
|
+
if (!members && prefix === snippets_1.InjectedParam_CurState) {
|
|
776
|
+
throw new Error(`State type must extends StructObject`);
|
|
777
|
+
}
|
|
778
|
+
// is a primitive type, just return
|
|
779
|
+
const type = this._checker.getTypeAtLocation(dec);
|
|
780
|
+
const typeStr = this.type2ResolvedName(type);
|
|
781
|
+
if (allowedTypeStr(typeStr)) {
|
|
782
|
+
return [
|
|
783
|
+
{
|
|
784
|
+
name: prefix,
|
|
785
|
+
type,
|
|
786
|
+
},
|
|
787
|
+
];
|
|
788
|
+
}
|
|
789
|
+
const flattenFixedArray = (type, prefix) => {
|
|
790
|
+
const len = type.aliasTypeArguments[1];
|
|
791
|
+
if (len.isNumberLiteral()) {
|
|
792
|
+
return Array.from({ length: len.value }).flatMap((_, i) => {
|
|
793
|
+
const elemType = type.aliasTypeArguments[0];
|
|
794
|
+
const typeStr = this.type2ResolvedName(elemType);
|
|
795
|
+
if (elemType.aliasSymbol?.name === 'FixedArray') {
|
|
796
|
+
// fixed array type under fixed array type
|
|
797
|
+
return flattenFixedArray(elemType, `${prefix}[${i}]`);
|
|
798
|
+
}
|
|
799
|
+
else {
|
|
800
|
+
// non-fixed array types under fixed array type
|
|
801
|
+
const symbol = elemType.aliasSymbol || elemType.symbol;
|
|
802
|
+
if (!symbol) {
|
|
803
|
+
if (allowedTypeStr(typeStr)) {
|
|
804
|
+
// compatible with bigint, boolean, string
|
|
805
|
+
return [
|
|
806
|
+
{
|
|
807
|
+
name: `${prefix}[${i}]`,
|
|
808
|
+
type: elemType,
|
|
809
|
+
},
|
|
810
|
+
];
|
|
811
|
+
}
|
|
812
|
+
else {
|
|
813
|
+
throw new Error(`Unsupported type for prop hash: ${prefix}[${i}]: ${typeStr}`);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
else {
|
|
817
|
+
return flattenProps(symbol, `${prefix}[${i}]`);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
else {
|
|
823
|
+
throw new Error(`Unsupported type for prop hash: ${prefix}[index]: ${typeStr}`);
|
|
824
|
+
}
|
|
825
|
+
};
|
|
826
|
+
if (!members) {
|
|
827
|
+
// not a struct type, throw error
|
|
828
|
+
throw new Error(`Unsupported type for prop hash: ${prop.name}: ${typeStr}`);
|
|
829
|
+
}
|
|
830
|
+
else {
|
|
831
|
+
// is a struct type, do recursive flatten for each member
|
|
832
|
+
return members.flatMap((m) => {
|
|
833
|
+
const k = m.name.getText();
|
|
834
|
+
const type = this._checker.getTypeAtLocation(m);
|
|
835
|
+
const typeStr = this.type2ResolvedName(type);
|
|
836
|
+
if (type.aliasSymbol?.name === 'FixedArray') {
|
|
837
|
+
// fixed array type under struct type
|
|
838
|
+
return flattenFixedArray(type, `${prefix}.${k}`);
|
|
839
|
+
}
|
|
840
|
+
else {
|
|
841
|
+
// non-fixed array types under struct type
|
|
842
|
+
const symbol = type.aliasSymbol || type.symbol;
|
|
843
|
+
if (!symbol) {
|
|
844
|
+
if (allowedTypeStr(typeStr)) {
|
|
845
|
+
// compatible with bigint, boolean, string
|
|
846
|
+
return [
|
|
847
|
+
{
|
|
848
|
+
name: `${prefix}.${k}`,
|
|
849
|
+
type,
|
|
850
|
+
},
|
|
851
|
+
];
|
|
852
|
+
}
|
|
853
|
+
else {
|
|
854
|
+
throw new Error(`Unsupported type for prop hash: ${prefix}.${k}: ${typeStr}`);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
return flattenProps(symbol, `${prefix}.${k}`);
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
function allowedTypeStr(typeStr) {
|
|
863
|
+
try {
|
|
864
|
+
propHash('dummyName', typeStr);
|
|
865
|
+
return true;
|
|
866
|
+
}
|
|
867
|
+
catch (_e) {
|
|
868
|
+
return false;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
function byteStringify(name, typeStr) {
|
|
872
|
+
switch (typeStr) {
|
|
873
|
+
case 'Int32':
|
|
874
|
+
case 'SigHashType':
|
|
875
|
+
case 'bigint':
|
|
876
|
+
return `pack(${name})`;
|
|
877
|
+
case 'Bool':
|
|
878
|
+
case 'boolean':
|
|
879
|
+
return `(${name} ? b'01' : b'')`;
|
|
880
|
+
case 'ByteString':
|
|
881
|
+
case 'Sha256':
|
|
882
|
+
case 'Sha1':
|
|
883
|
+
case 'Sig':
|
|
884
|
+
case 'PubKey':
|
|
885
|
+
case 'Ripemd160':
|
|
886
|
+
case 'OpCodeType':
|
|
887
|
+
case 'string':
|
|
888
|
+
return `${name}`;
|
|
889
|
+
default: {
|
|
890
|
+
throw new Error(`Unsupported type for byteStringify: ${typeStr}`);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
function propHash(name, typeStr) {
|
|
895
|
+
switch (typeStr) {
|
|
896
|
+
case 'Int32':
|
|
897
|
+
case 'SigHashType':
|
|
898
|
+
case 'bigint':
|
|
899
|
+
return `hash160(pack(${name}))`;
|
|
900
|
+
case 'Bool':
|
|
901
|
+
case 'boolean':
|
|
902
|
+
return `hash160(${name} ? b'01' : b'')`;
|
|
903
|
+
case 'ByteString':
|
|
904
|
+
case 'Sha256':
|
|
905
|
+
case 'Sha1':
|
|
906
|
+
case 'Sig':
|
|
907
|
+
case 'PubKey':
|
|
908
|
+
case 'Ripemd160':
|
|
909
|
+
case 'OpCodeType':
|
|
910
|
+
case 'string':
|
|
911
|
+
return `hash160(${name})`;
|
|
912
|
+
default: {
|
|
913
|
+
throw new Error(`Unsupported type for prop hash: ${typeStr}`);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
const hashes = flattenProps(stateTypeSymbol, snippets_1.InjectedParam_CurState).map(({ name, type }) => propHash(name, this.type2ResolvedName(type)));
|
|
918
|
+
const dataPart = flattenProps(stateTypeSymbol, snippets_1.InjectedParam_CurState).map(({ name, type }) => {
|
|
919
|
+
const bytes = byteStringify(name, this.type2ResolvedName(type));
|
|
920
|
+
return `num2bin(len(${bytes}), 2) + ${bytes}`;
|
|
921
|
+
// return `StdUtils.writeVarint(${bytes})`;
|
|
922
|
+
});
|
|
923
|
+
section
|
|
924
|
+
.append('\n')
|
|
925
|
+
.append(`static function serializeState(${stateTypeSymbol.name} ${snippets_1.InjectedParam_CurState}): bytes {`)
|
|
926
|
+
.append('\n')
|
|
927
|
+
.append(` return ${dataPart.join(' + ')} + hash160(${hashes.join(' + ')});`)
|
|
928
|
+
.append('\n')
|
|
929
|
+
.append('}')
|
|
930
|
+
.append('\n')
|
|
931
|
+
.append(`static function stateHash(${stateTypeSymbol.name} ${snippets_1.InjectedParam_CurState}): bytes {`)
|
|
932
|
+
.append('\n')
|
|
933
|
+
.append(` return sha256(${dataPart.join(' + ')} + hash160(${hashes.join(' + ')}));`)
|
|
934
|
+
.append('\n')
|
|
935
|
+
.append('}');
|
|
936
|
+
}
|
|
937
|
+
transformPropertySignature(node, toSection) {
|
|
938
|
+
return this.transformEnclosingTypeNode(node.type || node, toSection)
|
|
939
|
+
.append(` ${node.name.getText()}`, this.getCoordinates(node.name.getStart()))
|
|
940
|
+
.append(';');
|
|
941
|
+
}
|
|
942
|
+
transformPropertyDeclaration(node, toSection, category) {
|
|
943
|
+
if (!Transpiler.isProperty(node))
|
|
944
|
+
return toSection;
|
|
945
|
+
toSection.append('\n');
|
|
946
|
+
if (Transpiler.isStateProperty(node)) {
|
|
947
|
+
if (category === 'library') {
|
|
948
|
+
const decorator = Transpiler.findDecorator(node, DecoratorName.Prop);
|
|
949
|
+
throw new types_1.TranspileError(`Untransformable property: '${node.name.getText()}', \`@prop(true)\` is only allowed to be used in \`SmartContract\``, this.getRange(decorator.expression));
|
|
950
|
+
}
|
|
951
|
+
if (Transpiler.isStaticProperty(node)) {
|
|
952
|
+
throw new types_1.TranspileError(`Untransformable property: '${node.name.getText()}', \`@prop(true)\` cannot be static`, this.getRange(node));
|
|
953
|
+
}
|
|
954
|
+
if ((0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.ReadonlyKeyword)) {
|
|
955
|
+
throw new types_1.TranspileError(`Untransformable property: '${node.name.getText()}', \`@prop(true)\` cannot be readonly`, this.getRange(node));
|
|
956
|
+
}
|
|
957
|
+
toSection.append('@state ');
|
|
958
|
+
}
|
|
959
|
+
else if (Transpiler.isStaticProperty(node)) {
|
|
960
|
+
if (!node.initializer) {
|
|
961
|
+
throw new types_1.TranspileError(`Untransformable property: '${node.name.getText()}', static property shall be initialized when declared`, this.getRange(node));
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
toSection
|
|
965
|
+
.appendWith(this, (toSec) => {
|
|
966
|
+
return this.transformModifiers(node, toSec);
|
|
967
|
+
})
|
|
968
|
+
.appendWith(this, (toSec) => {
|
|
969
|
+
if (!node.type) {
|
|
970
|
+
throw new types_1.TranspileError(`Untransformable property: '${node.name.getText()}', all \`prop()\` should be typed explicitly`, this.getRange(node));
|
|
971
|
+
}
|
|
972
|
+
return this.transformEnclosingTypeNode(node.type, toSec);
|
|
973
|
+
})
|
|
974
|
+
.append(` ${node.name.getText()}`, this.getCoordinates(node.name.getStart()));
|
|
975
|
+
if (node.initializer) {
|
|
976
|
+
if (Transpiler.isStaticProperty(node)) {
|
|
977
|
+
toSection
|
|
978
|
+
.append(' = ')
|
|
979
|
+
.appendWith(this, (toSec) => {
|
|
980
|
+
return this.transformExpression(node.initializer, toSec);
|
|
981
|
+
})
|
|
982
|
+
.append(';');
|
|
983
|
+
return toSection;
|
|
984
|
+
}
|
|
985
|
+
else {
|
|
986
|
+
throw new types_1.TranspileError(`Untransformable property: '${node.name.getText()}', Non-static properties shall only be initialized in the constructor`, this.getRange(node.initializer));
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
toSection.append(';');
|
|
990
|
+
return toSection;
|
|
991
|
+
}
|
|
992
|
+
buildConstructorParametersMap(node, baseContract) {
|
|
993
|
+
function getSuperParameters() {
|
|
994
|
+
const superStmt = node.body.statements[0];
|
|
995
|
+
const callexpr = superStmt.expression;
|
|
996
|
+
if (callexpr.arguments[0] &&
|
|
997
|
+
typescript_1.default.isSpreadElement(callexpr.arguments[0]) &&
|
|
998
|
+
callexpr.arguments[0].getText().endsWith('arguments')) {
|
|
999
|
+
return node.parameters;
|
|
1000
|
+
}
|
|
1001
|
+
else {
|
|
1002
|
+
return callexpr.arguments;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
const basector = this.getConstructor(baseContract);
|
|
1006
|
+
if (!basector) {
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
const superParameters = getSuperParameters();
|
|
1010
|
+
basector.parameters.forEach((parameter, index) => {
|
|
1011
|
+
const superParameter = superParameters[index];
|
|
1012
|
+
this._constructorParametersMap.set(parameter.name.getText(), superParameter);
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
static accessSetConstructor(statements) {
|
|
1016
|
+
return (statements.findIndex((statement) => {
|
|
1017
|
+
return (typescript_1.default.isExpressionStatement(statement) &&
|
|
1018
|
+
statement.expression.kind === typescript_1.default.SyntaxKind.CallExpression &&
|
|
1019
|
+
/^this\.init\(/.test(statement.expression.getText()));
|
|
1020
|
+
}) > -1);
|
|
1021
|
+
}
|
|
1022
|
+
checkSuperStmt(node) {
|
|
1023
|
+
const superStmt = node.body.statements[0];
|
|
1024
|
+
const callexpr = superStmt.expression;
|
|
1025
|
+
if (!typescript_1.default.isCallExpression(callexpr) || callexpr.expression.getText() !== 'super') {
|
|
1026
|
+
throw new types_1.TranspileError(`Constructors for derived classes must contain a \`super()\` call`, this.getRange(node));
|
|
1027
|
+
}
|
|
1028
|
+
if (Transpiler.accessSetConstructor(node.body.statements)) {
|
|
1029
|
+
throw new types_1.TranspileError(`Direct subclasses of \`SmartContract\` do not need to call \`this.init()\`.`, this.getRange(node));
|
|
1030
|
+
}
|
|
1031
|
+
if (callexpr.arguments[0] &&
|
|
1032
|
+
typescript_1.default.isSpreadElement(callexpr.arguments[0]) &&
|
|
1033
|
+
callexpr.arguments[0].getText().endsWith('arguments')) {
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
if (node.parameters.length !== callexpr.arguments.length) {
|
|
1037
|
+
throw new types_1.TranspileError(`All parameters in the constructor must be passed to the \`super()\` call`, this.getRange(node));
|
|
1038
|
+
}
|
|
1039
|
+
node.parameters.forEach((p, pIdx) => {
|
|
1040
|
+
const arg = callexpr.arguments[pIdx];
|
|
1041
|
+
if (arg.getText() !== p.name.getText()) {
|
|
1042
|
+
throw new types_1.TranspileError(`All parameters in the constructor must be passed to the \`super()\` call following their declaration order`, this.getRange(node));
|
|
1043
|
+
}
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
checkSetConstructorStmt(node) {
|
|
1047
|
+
const superStmt = node.body.statements[0];
|
|
1048
|
+
const callexpr = superStmt.expression;
|
|
1049
|
+
if (!typescript_1.default.isCallExpression(callexpr) || callexpr.expression.getText() !== 'super') {
|
|
1050
|
+
throw new types_1.TranspileError(`Constructors for derived classes must contain a \`super()\` call`, this.getRange(node));
|
|
1051
|
+
}
|
|
1052
|
+
if (callexpr.arguments[0] &&
|
|
1053
|
+
typescript_1.default.isSpreadElement(callexpr.arguments[0]) &&
|
|
1054
|
+
callexpr.arguments[0].getText().endsWith('arguments')) {
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
if (node.parameters.length === callexpr.arguments.length) {
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
canBeImplicitConstructor(node) {
|
|
1062
|
+
// Test if it can be an implicit constructor.
|
|
1063
|
+
let res = true;
|
|
1064
|
+
const ctorParamNames = [];
|
|
1065
|
+
node.parameters.forEach((p) => {
|
|
1066
|
+
const pText = p.name.getFullText().trim();
|
|
1067
|
+
ctorParamNames.push(pText);
|
|
1068
|
+
});
|
|
1069
|
+
node.body.statements.slice(1).forEach((stmt) => {
|
|
1070
|
+
// For each statement in the constr check if its a property init stmt.
|
|
1071
|
+
// If not, the constructor cannot be implicit.
|
|
1072
|
+
const isPropSet = this.queryPropertyInitializedInStmt(stmt);
|
|
1073
|
+
if (!isPropSet) {
|
|
1074
|
+
res = false;
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
// If its a prop init stmt, then also check if the value being set is a
|
|
1078
|
+
// constructor parameter.
|
|
1079
|
+
if (!typescript_1.default.isExpressionStatement(stmt) || !typescript_1.default.isBinaryExpression(stmt.expression)) {
|
|
1080
|
+
res = false;
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
const exprName = stmt.expression.right.getFullText().trim();
|
|
1084
|
+
let found = false;
|
|
1085
|
+
for (const ctorParamName of ctorParamNames) {
|
|
1086
|
+
if (ctorParamName == exprName) {
|
|
1087
|
+
found = true;
|
|
1088
|
+
break;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
if (!found) {
|
|
1092
|
+
res = false;
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
1096
|
+
// Check the order of the ctor params compared to the order of prop declarations.
|
|
1097
|
+
const props = node.parent.members.filter((member) => Transpiler.isProperty(member));
|
|
1098
|
+
props.forEach((p) => {
|
|
1099
|
+
const pName = p.name.getFullText().trim();
|
|
1100
|
+
const ctorParamName = ctorParamNames.shift();
|
|
1101
|
+
if (pName != ctorParamName) {
|
|
1102
|
+
res = false;
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
});
|
|
1106
|
+
// Check if the contract contains any non-props. It can still contain static (readonly) non-props.
|
|
1107
|
+
const nonProps = node.parent.members.filter((member) => this.isNonProp(member) && !this.isStaticReadOnlyNonProp(member));
|
|
1108
|
+
if (nonProps.length > 0) {
|
|
1109
|
+
res = false;
|
|
1110
|
+
}
|
|
1111
|
+
// Also check for VarIntReader properties.
|
|
1112
|
+
if (this.accessSHPreimage() || this.accessChange() || this.accessPrevouts()) {
|
|
1113
|
+
res = false;
|
|
1114
|
+
}
|
|
1115
|
+
return res;
|
|
1116
|
+
}
|
|
1117
|
+
transformConstructorBody(section, node) {
|
|
1118
|
+
const allProps = this.allPropertyDeclaration(node.parent);
|
|
1119
|
+
const initializedProps = [];
|
|
1120
|
+
node.body.statements.slice(1).forEach((stmt) => {
|
|
1121
|
+
section.append('\n').appendWith(this, (stmtsSec) => {
|
|
1122
|
+
const prop = this.queryPropertyInitializedInStmt(stmt);
|
|
1123
|
+
if (prop) {
|
|
1124
|
+
initializedProps.push(prop);
|
|
1125
|
+
if (!Transpiler.isProperty(prop)) {
|
|
1126
|
+
return stmtsSec; //allow but ignore Non-Prop Property
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
return this.transformStatement(stmt, stmtsSec);
|
|
1130
|
+
}, true);
|
|
1131
|
+
});
|
|
1132
|
+
allProps.forEach((prop) => {
|
|
1133
|
+
if (!initializedProps.map((p) => p.name.getText()).includes(prop.name.getText())) {
|
|
1134
|
+
// allowed static const prop initialized
|
|
1135
|
+
if (!Transpiler.isStaticProperty(prop)) {
|
|
1136
|
+
throw new types_1.TranspileError(`property \`${prop.name.getText()}\` must be initialized in the constructor`, this.getRange(prop));
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
});
|
|
1140
|
+
return section;
|
|
1141
|
+
}
|
|
1142
|
+
transformConstructor(section, cls, baseContract) {
|
|
1143
|
+
this.checkConstructor(cls);
|
|
1144
|
+
const node = cls.members.find((m) => m.kind === typescript_1.default.SyntaxKind.Constructor);
|
|
1145
|
+
if (node) {
|
|
1146
|
+
if (baseContract) {
|
|
1147
|
+
this.buildConstructorParametersMap(node, baseContract);
|
|
1148
|
+
}
|
|
1149
|
+
section.appendWith(this, (section) => {
|
|
1150
|
+
if (!node.body) {
|
|
1151
|
+
throw new types_1.TranspileError(`Missing function body`, this.getRange(node));
|
|
1152
|
+
}
|
|
1153
|
+
const tmpSection = new EmittedSection();
|
|
1154
|
+
tmpSection.lines = section.lines;
|
|
1155
|
+
tmpSection.errors = section.errors;
|
|
1156
|
+
tmpSection
|
|
1157
|
+
.append('\n')
|
|
1158
|
+
.append('constructor', this.getCoordinates(node.getStart()))
|
|
1159
|
+
.append('(')
|
|
1160
|
+
.appendWith(this, (psSec) => {
|
|
1161
|
+
node.parameters.forEach((p, pIdx) => {
|
|
1162
|
+
psSec.appendWith(this, (pSec) => {
|
|
1163
|
+
const sec = this.transformParameter(p, pSec);
|
|
1164
|
+
if (pIdx !== node.parameters.length - 1) {
|
|
1165
|
+
sec.append(', ');
|
|
1166
|
+
}
|
|
1167
|
+
return sec;
|
|
1168
|
+
});
|
|
1169
|
+
});
|
|
1170
|
+
return psSec;
|
|
1171
|
+
})
|
|
1172
|
+
.append(') ');
|
|
1173
|
+
tmpSection
|
|
1174
|
+
.append('{')
|
|
1175
|
+
.appendWith(this, (sec) => {
|
|
1176
|
+
if (baseContract) {
|
|
1177
|
+
baseContract.members.forEach((member) => {
|
|
1178
|
+
if (member.kind === typescript_1.default.SyntaxKind.Constructor) {
|
|
1179
|
+
this.transformConstructorBody(sec, member);
|
|
1180
|
+
}
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
return this.transformConstructorBody(sec, node);
|
|
1184
|
+
}, true)
|
|
1185
|
+
.append('\n}');
|
|
1186
|
+
// If the constructor can be implicit, we don't even have to transform it.
|
|
1187
|
+
if (this.canBeImplicitConstructor(node)) {
|
|
1188
|
+
section.errors = tmpSection.errors;
|
|
1189
|
+
return section;
|
|
1190
|
+
}
|
|
1191
|
+
return tmpSection;
|
|
1192
|
+
});
|
|
1193
|
+
}
|
|
1194
|
+
else {
|
|
1195
|
+
if (this.accessSHPreimage() || this.accessChange() || this.accessPrevouts()) {
|
|
1196
|
+
section.append(`\n${snippets_1.EMPTY_CONSTRUCTOR}`);
|
|
1197
|
+
return section;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
return section;
|
|
1201
|
+
}
|
|
1202
|
+
setMethodDecOptions(methodDec) {
|
|
1203
|
+
const expression = methodDec.expression;
|
|
1204
|
+
// set default value for options
|
|
1205
|
+
this._currentMethodDecOptions = {
|
|
1206
|
+
autoCheckInputState: true,
|
|
1207
|
+
};
|
|
1208
|
+
if (typescript_1.default.isCallExpression(expression) && expression.arguments[0]) {
|
|
1209
|
+
const argTxt = expression.arguments[0].getText();
|
|
1210
|
+
this._currentMethodDecOptions.autoCheckInputState =
|
|
1211
|
+
!/autoCheckInputState: false/.test(argTxt);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
transformMethodDeclaration(node, toSection, category) {
|
|
1215
|
+
const methodDec = Transpiler.findDecorator(node, DecoratorName.Method);
|
|
1216
|
+
if (!methodDec)
|
|
1217
|
+
return toSection;
|
|
1218
|
+
const match = /^method\((.*)?\)$/.exec(methodDec.expression.getText());
|
|
1219
|
+
if (!match) {
|
|
1220
|
+
return toSection;
|
|
1221
|
+
}
|
|
1222
|
+
this.setMethodDecOptions(methodDec);
|
|
1223
|
+
this._currentMethodName = node.name.getText();
|
|
1224
|
+
let sigHashType = '41'; //SigHash.ALL;
|
|
1225
|
+
switch (match[1]) {
|
|
1226
|
+
case 'SigHash.ANYONECANPAY_SINGLE':
|
|
1227
|
+
sigHashType = 'c3'; // SigHash.ANYONECANPAY_SINGLE;
|
|
1228
|
+
break;
|
|
1229
|
+
case 'SigHash.ANYONECANPAY_ALL':
|
|
1230
|
+
sigHashType = 'c1'; //SigHash.ANYONECANPAY_ALL;
|
|
1231
|
+
break;
|
|
1232
|
+
case 'SigHash.ANYONECANPAY_NONE':
|
|
1233
|
+
sigHashType = 'c2'; //SigHash.ANYONECANPAY_NONE;
|
|
1234
|
+
break;
|
|
1235
|
+
case 'SigHash.ALL':
|
|
1236
|
+
sigHashType = '41'; //SigHash.ALL;
|
|
1237
|
+
break;
|
|
1238
|
+
case 'SigHash.SINGLE':
|
|
1239
|
+
sigHashType = '43'; //SigHash.SINGLE;
|
|
1240
|
+
break;
|
|
1241
|
+
case 'SigHash.NONE':
|
|
1242
|
+
sigHashType = '42'; //SigHash.NONE;
|
|
1243
|
+
break;
|
|
1244
|
+
default:
|
|
1245
|
+
break;
|
|
1246
|
+
}
|
|
1247
|
+
const isPublicMethod = Transpiler.isPublicMethod(node);
|
|
1248
|
+
if (isPublicMethod) {
|
|
1249
|
+
if (category === 'library') {
|
|
1250
|
+
const publicModifier = typescript_1.default
|
|
1251
|
+
.getModifiers(node)
|
|
1252
|
+
.find((m) => m.kind === typescript_1.default.SyntaxKind.PublicKeyword);
|
|
1253
|
+
throw new types_1.TranspileError(`\`@method\` in \`SmartContractLib\` should not be declared as \`public\``, this.getRange(publicModifier ? publicModifier : node));
|
|
1254
|
+
}
|
|
1255
|
+
const retStmt = (0, utils_1.findReturnStatement)(node);
|
|
1256
|
+
if (retStmt) {
|
|
1257
|
+
throw new types_1.TranspileError(`public methods cannot contain an explicit return statement`, this.getRange(retStmt));
|
|
1258
|
+
}
|
|
1259
|
+
if (node.type) {
|
|
1260
|
+
// has return type
|
|
1261
|
+
throw new types_1.TranspileError(`public methods cannot have a return type`, this.getRange(node.type));
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
else {
|
|
1265
|
+
if (!node.type) {
|
|
1266
|
+
throw new types_1.TranspileError(`non-public methods must declare the return type explicitly`, this.getRange(node));
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
if (!node.body) {
|
|
1270
|
+
throw new types_1.TranspileError(`Missing function body`, this.getRange(node));
|
|
1271
|
+
}
|
|
1272
|
+
const shouldAutoAppendSighashPreimage = this.shouldAutoAppendSighashPreimage(node);
|
|
1273
|
+
const shouldAutoAppendChangeAmount = this.shouldAutoAppendChangeAmount(node);
|
|
1274
|
+
const shouldAutoAppendPrevouts = this.shouldAutoAppendPrevouts(node);
|
|
1275
|
+
const shouldAutoAppendPrevout = this.shouldAutoAppendPrevout(node);
|
|
1276
|
+
const shouldAutoAppendSpentScripts = this.shouldAutoAppendSpentScripts(node);
|
|
1277
|
+
const shouldAutoAppendSpentAmounts = this.shouldAutoAppendSpentAmounts(node);
|
|
1278
|
+
const shouldAutoAppendSpentDataHashes = this.shouldAutoAppendSpentDataHashes(node);
|
|
1279
|
+
const shouldAutoAppendStateArgs = this.shouldAutoAppendStateArgs(node);
|
|
1280
|
+
const shouldAutoAppendPrevTxHashPreimage = this.shouldAutoAppendPrevTxHashPreimageArgs(node);
|
|
1281
|
+
const buildChangeOutputExpression = (0, utils_1.findBuildChangeOutputExpression)(node);
|
|
1282
|
+
if (shouldAutoAppendSighashPreimage.shouldAppendArguments &&
|
|
1283
|
+
buildChangeOutputExpression !== undefined) {
|
|
1284
|
+
const allowedSighashType = ['c1', '41']; // Only sighash ALL allowed.
|
|
1285
|
+
if (!allowedSighashType.includes(sigHashType)) {
|
|
1286
|
+
throw new types_1.TranspileError(`Can only use sighash ALL or ANYONECANPAY_ALL if using \`this.buildChangeOutput()\``, this.getRange(node));
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
toSection
|
|
1290
|
+
.append('\n')
|
|
1291
|
+
.appendWith(this, (toSec) => {
|
|
1292
|
+
return this.transformModifiers(node, toSec);
|
|
1293
|
+
})
|
|
1294
|
+
.append('function ')
|
|
1295
|
+
.append(node.name.getText(), this.getCoordinates(node.name.getStart()))
|
|
1296
|
+
.append('(')
|
|
1297
|
+
.appendWith(this, (psSec) => {
|
|
1298
|
+
// not allow SmartContract as parameter
|
|
1299
|
+
const inValidParams = node.parameters.find((p) => p.type?.getText() === 'SmartContract');
|
|
1300
|
+
if (inValidParams) {
|
|
1301
|
+
throw new types_1.TranspileError(`Untransformable parameter: '${node.getText()}'`, this.getRange(node));
|
|
1302
|
+
}
|
|
1303
|
+
node.parameters.forEach((p, pIdx) => {
|
|
1304
|
+
psSec.appendWith(this, (pSec) => {
|
|
1305
|
+
const sec = this.transformParameter(p, pSec);
|
|
1306
|
+
if (pIdx !== node.parameters.length - 1) {
|
|
1307
|
+
sec.append(', ');
|
|
1308
|
+
}
|
|
1309
|
+
return sec;
|
|
1310
|
+
});
|
|
1311
|
+
});
|
|
1312
|
+
let paramLen = node.parameters.length;
|
|
1313
|
+
if (shouldAutoAppendSighashPreimage.shouldAppendArguments) {
|
|
1314
|
+
this._accessBuiltinsSymbols.add('SHPreimage');
|
|
1315
|
+
if (paramLen > 0) {
|
|
1316
|
+
psSec.append(', ');
|
|
1317
|
+
}
|
|
1318
|
+
psSec.append(`SHPreimage ${snippets_1.InjectedParam_SHPreimage}`);
|
|
1319
|
+
paramLen += 2;
|
|
1320
|
+
}
|
|
1321
|
+
if (shouldAutoAppendChangeAmount.shouldAppendArguments) {
|
|
1322
|
+
this._accessBuiltinsSymbols.add('TxOut');
|
|
1323
|
+
if (paramLen > 0) {
|
|
1324
|
+
psSec.append(', ');
|
|
1325
|
+
}
|
|
1326
|
+
psSec.append(`TxOut ${snippets_1.InjectedParam_ChangeInfo}`);
|
|
1327
|
+
paramLen += 2;
|
|
1328
|
+
}
|
|
1329
|
+
if (shouldAutoAppendStateArgs.shouldAppendArguments) {
|
|
1330
|
+
if (paramLen > 0) {
|
|
1331
|
+
psSec.append(', ');
|
|
1332
|
+
}
|
|
1333
|
+
const stateTypeSymbol = this._stateTypeSymbols.get(this.currentContractName);
|
|
1334
|
+
this._accessBuiltinsSymbols.add('StdUtils');
|
|
1335
|
+
if (!stateTypeSymbol) {
|
|
1336
|
+
throw new Error('State type symbol is not defined');
|
|
1337
|
+
}
|
|
1338
|
+
psSec.append(`${stateTypeSymbol.name} ${snippets_1.InjectedParam_CurState}`);
|
|
1339
|
+
paramLen += 1;
|
|
1340
|
+
}
|
|
1341
|
+
// note this should be the third from bottom parameter if exists
|
|
1342
|
+
if (shouldAutoAppendPrevouts.shouldAppendArguments) {
|
|
1343
|
+
this._accessBuiltinsSymbols.add('Outpoint');
|
|
1344
|
+
if (paramLen > 0) {
|
|
1345
|
+
psSec.append(', ');
|
|
1346
|
+
}
|
|
1347
|
+
psSec.append(`bytes ${snippets_1.InjectedParam_Prevouts}`);
|
|
1348
|
+
paramLen += 1;
|
|
1349
|
+
}
|
|
1350
|
+
// note this should be the second from bottom parameter if exists
|
|
1351
|
+
if (shouldAutoAppendSpentAmounts.shouldAppendArguments) {
|
|
1352
|
+
if (paramLen > 0) {
|
|
1353
|
+
psSec.append(', ');
|
|
1354
|
+
}
|
|
1355
|
+
psSec.append(`bytes ${snippets_1.InjectedParam_SpentAmounts}`);
|
|
1356
|
+
paramLen += 1;
|
|
1357
|
+
}
|
|
1358
|
+
if (shouldAutoAppendSpentDataHashes.shouldAppendArguments) {
|
|
1359
|
+
if (paramLen > 0) {
|
|
1360
|
+
psSec.append(', ');
|
|
1361
|
+
}
|
|
1362
|
+
psSec.append(`bytes ${snippets_1.InjectedParam_SpentDataHashes}`);
|
|
1363
|
+
paramLen += 1;
|
|
1364
|
+
}
|
|
1365
|
+
// note this should be the last parameter if exists
|
|
1366
|
+
if (shouldAutoAppendSpentScripts.shouldAppendArguments) {
|
|
1367
|
+
if (paramLen > 0) {
|
|
1368
|
+
psSec.append(', ');
|
|
1369
|
+
}
|
|
1370
|
+
psSec.append(`bytes ${snippets_1.InjectedParam_SpentScriptHashes}`);
|
|
1371
|
+
paramLen += 1;
|
|
1372
|
+
}
|
|
1373
|
+
if (shouldAutoAppendPrevTxHashPreimage.shouldAppendArguments) {
|
|
1374
|
+
if (paramLen > 0) {
|
|
1375
|
+
psSec.append(', ');
|
|
1376
|
+
}
|
|
1377
|
+
psSec.append(`TxHashPreimage ${snippets_1.InjectedParam_PrevTxHashPreimage}`);
|
|
1378
|
+
paramLen += 1;
|
|
1379
|
+
}
|
|
1380
|
+
return psSec;
|
|
1381
|
+
})
|
|
1382
|
+
.append(') ');
|
|
1383
|
+
// non-public method return type
|
|
1384
|
+
let autoReturnStatement = '';
|
|
1385
|
+
if (!isPublicMethod) {
|
|
1386
|
+
// for non-public method, `node.type` is not `undefined` definitely
|
|
1387
|
+
if (node.type.kind !== typescript_1.default.SyntaxKind.VoidKeyword) {
|
|
1388
|
+
// return not void
|
|
1389
|
+
toSection
|
|
1390
|
+
.append(': ')
|
|
1391
|
+
.appendWith(this, (toSec) => this.transformEnclosingTypeNode(node.type, toSec))
|
|
1392
|
+
.append(' ');
|
|
1393
|
+
}
|
|
1394
|
+
else {
|
|
1395
|
+
// return void
|
|
1396
|
+
toSection.append(': bool ');
|
|
1397
|
+
autoReturnStatement = '\n return true;';
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
this._accessBuiltinsSymbols.add('TxUtils');
|
|
1401
|
+
this._accessBuiltinsSymbols.add('ContextUtils');
|
|
1402
|
+
return toSection
|
|
1403
|
+
.append('{')
|
|
1404
|
+
.appendWith(this, (sec) => {
|
|
1405
|
+
if (shouldAutoAppendSighashPreimage.shouldAppendArguments) {
|
|
1406
|
+
sec
|
|
1407
|
+
.append('\n')
|
|
1408
|
+
.append(`${snippets_1.CALL_CHECK_SHPREIMAGE};`)
|
|
1409
|
+
.append(`\n`);
|
|
1410
|
+
}
|
|
1411
|
+
if (shouldAutoAppendSighashPreimage.shouldAppendThisAssignment) {
|
|
1412
|
+
sec.append((0, snippets_1.thisAssignment)(snippets_1.InjectedProp_SHPreimage)).append('\n');
|
|
1413
|
+
}
|
|
1414
|
+
if (shouldAutoAppendChangeAmount.shouldAppendThisAssignment) {
|
|
1415
|
+
sec.append(`\n`).append((0, snippets_1.thisAssignment)(snippets_1.InjectedProp_ChangeInfo)).append(`\n`);
|
|
1416
|
+
}
|
|
1417
|
+
if (shouldAutoAppendSpentAmounts.shouldAppendArguments) {
|
|
1418
|
+
sec
|
|
1419
|
+
.append('\n')
|
|
1420
|
+
.append(`int ${snippets_1.InjectedVar_InputCount} = ContextUtils.checkSpentAmounts(${snippets_1.InjectedParam_SpentAmounts}, ${snippets_1.InjectedParam_SHPreimage}.hashSpentAmounts);`)
|
|
1421
|
+
.append('\n');
|
|
1422
|
+
}
|
|
1423
|
+
if (shouldAutoAppendPrevouts.shouldAppendArguments) {
|
|
1424
|
+
sec
|
|
1425
|
+
.append('\n')
|
|
1426
|
+
.append(`Outpoint ${snippets_1.InjectedProp_Prevout} = ContextUtils.checkPrevouts(${snippets_1.InjectedParam_Prevouts}, ${snippets_1.InjectedParam_SHPreimage}.hashPrevouts, ${snippets_1.InjectedParam_SHPreimage}.inputIndex, ${snippets_1.InjectedVar_InputCount});`)
|
|
1427
|
+
.append('\n');
|
|
1428
|
+
}
|
|
1429
|
+
if (shouldAutoAppendPrevouts.shouldAppendThisAssignment) {
|
|
1430
|
+
sec.append((0, snippets_1.thisAssignment)(snippets_1.InjectedProp_PrevoutsCtx)).append('\n');
|
|
1431
|
+
}
|
|
1432
|
+
if (shouldAutoAppendPrevout.shouldAppendThisAssignment) {
|
|
1433
|
+
sec.append((0, snippets_1.thisAssignment)(snippets_1.InjectedProp_Prevout)).append('\n');
|
|
1434
|
+
}
|
|
1435
|
+
if (shouldAutoAppendSpentScripts.shouldAppendArguments) {
|
|
1436
|
+
sec
|
|
1437
|
+
.append('\n')
|
|
1438
|
+
.append(`ContextUtils.checkSpentScripts(${snippets_1.InjectedParam_SpentScriptHashes}, ${snippets_1.InjectedParam_SHPreimage}.hashSpentScriptHashes, ${snippets_1.InjectedVar_InputCount});`)
|
|
1439
|
+
.append('\n');
|
|
1440
|
+
}
|
|
1441
|
+
if (shouldAutoAppendSpentScripts.shouldAppendThisAssignment) {
|
|
1442
|
+
sec.append((0, snippets_1.thisAssignment)(snippets_1.InjectedProp_SpentScriptHashes)).append('\n');
|
|
1443
|
+
}
|
|
1444
|
+
if (shouldAutoAppendSpentDataHashes.shouldAppendArguments) {
|
|
1445
|
+
sec
|
|
1446
|
+
.append('\n')
|
|
1447
|
+
.append(`ContextUtils.checkSpentDataHashes(${snippets_1.InjectedParam_SpentDataHashes}, ${snippets_1.InjectedParam_SHPreimage}.hashSpentDataHashes, ${snippets_1.InjectedVar_InputCount});`)
|
|
1448
|
+
.append('\n');
|
|
1449
|
+
}
|
|
1450
|
+
if (shouldAutoAppendStateArgs.shouldAppendArguments) {
|
|
1451
|
+
// append initialize next state
|
|
1452
|
+
const stateTypeSymbol = this._stateTypeSymbols.get(this.currentContractName);
|
|
1453
|
+
sec
|
|
1454
|
+
.append('\n')
|
|
1455
|
+
.append(`${stateTypeSymbol.name} ${snippets_1.InjectedVar_NextState} = ${snippets_1.InjectedParam_CurState};`)
|
|
1456
|
+
.append('\n');
|
|
1457
|
+
if (shouldAutoAppendStateArgs.shouldAppendThisAssignment) {
|
|
1458
|
+
sec
|
|
1459
|
+
.append('\n')
|
|
1460
|
+
.append(`${(0, snippets_1.thisAssignment)(snippets_1.InjectedVar_NextState)}`)
|
|
1461
|
+
.append('\n');
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
if (shouldAutoAppendStateArgs.shouldAppendArguments &&
|
|
1465
|
+
this._currentMethodDecOptions.autoCheckInputState) {
|
|
1466
|
+
// append checkInputState for current input
|
|
1467
|
+
const rawState = this._stateTypeSymbols.has(this.currentContractName)
|
|
1468
|
+
? `${this._currentContract.name.getText()}.serializeState(${snippets_1.InjectedParam_CurState})`
|
|
1469
|
+
: "b''";
|
|
1470
|
+
// here no need to access this, because it's in public function, the variable we access is in the arguments
|
|
1471
|
+
this._accessBuiltinsSymbols.add('StateUtils');
|
|
1472
|
+
sec
|
|
1473
|
+
.append('\n')
|
|
1474
|
+
.append(`StateUtils.checkInputState(${snippets_1.InjectedParam_SHPreimage}.inputIndex, ${rawState}, ${snippets_1.InjectedParam_SpentDataHashes});`)
|
|
1475
|
+
.append('\n');
|
|
1476
|
+
}
|
|
1477
|
+
if (shouldAutoAppendPrevTxHashPreimage.shouldAppendArguments) {
|
|
1478
|
+
sec
|
|
1479
|
+
.append('\n')
|
|
1480
|
+
.append(`Backtrace.checkPrevTxHashPreimage(${snippets_1.InjectedParam_PrevTxHashPreimage}, ${snippets_1.InjectedParam_Prevouts}, ${snippets_1.InjectedParam_SHPreimage}.inputIndex);`)
|
|
1481
|
+
.append('\n');
|
|
1482
|
+
}
|
|
1483
|
+
if (shouldAutoAppendPrevTxHashPreimage.shouldAppendThisAssignment) {
|
|
1484
|
+
sec.append((0, snippets_1.thisAssignment)(snippets_1.InjectedProp_PrevTxHashPreimage)).append('\n');
|
|
1485
|
+
}
|
|
1486
|
+
node.body.statements.forEach((stmt) => {
|
|
1487
|
+
sec.append('\n').appendWith(this, (stmtsSec) => {
|
|
1488
|
+
return this.transformStatement(stmt, stmtsSec);
|
|
1489
|
+
}, true);
|
|
1490
|
+
});
|
|
1491
|
+
if (isPublicMethod) {
|
|
1492
|
+
if (!this.verifyLastAssertStatement(node.body.statements)) {
|
|
1493
|
+
throw new types_1.TranspileError(`Untransformable public method: Public method \`${node.name.getText()}\` not ended with \`assert()\``, this.getRange(node));
|
|
1494
|
+
}
|
|
1495
|
+
if (this.needAppendLastStatement(node.body.statements)) {
|
|
1496
|
+
sec.append('\n').append('require(true);\n');
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
return sec;
|
|
1500
|
+
}, true)
|
|
1501
|
+
.append(autoReturnStatement)
|
|
1502
|
+
.append('\n}');
|
|
1503
|
+
}
|
|
1504
|
+
isAssertStatement(node) {
|
|
1505
|
+
if (typescript_1.default.isExpressionStatement(node)) {
|
|
1506
|
+
const s = node;
|
|
1507
|
+
if (typescript_1.default.isCallExpression(s.expression)) {
|
|
1508
|
+
const e = s.expression;
|
|
1509
|
+
return e.expression.getText() === 'assert';
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
else if (typescript_1.default.isForStatement(node)) {
|
|
1513
|
+
return this.isForStatementAssert(node);
|
|
1514
|
+
}
|
|
1515
|
+
else if (typescript_1.default.isIfStatement(node)) {
|
|
1516
|
+
return this.isIfStatementAssert(node);
|
|
1517
|
+
}
|
|
1518
|
+
else if (typescript_1.default.isBlock(node)) {
|
|
1519
|
+
return this.isBlockStatementAssert(node);
|
|
1520
|
+
}
|
|
1521
|
+
return false;
|
|
1522
|
+
}
|
|
1523
|
+
isBlockStatementAssert(node) {
|
|
1524
|
+
if (typescript_1.default.isBlock(node)) {
|
|
1525
|
+
const block = node;
|
|
1526
|
+
if (block.statements.length === 0) {
|
|
1527
|
+
return false;
|
|
1528
|
+
}
|
|
1529
|
+
let i = block.statements.length - 1;
|
|
1530
|
+
while (i >= 0 && this.isConsoleLogStatement(block.statements[i])) {
|
|
1531
|
+
--i;
|
|
1532
|
+
}
|
|
1533
|
+
return i >= 0 && this.isAssertStatement(block.statements[i]);
|
|
1534
|
+
}
|
|
1535
|
+
return false;
|
|
1536
|
+
}
|
|
1537
|
+
isForStatementAssert(node) {
|
|
1538
|
+
if (typescript_1.default.isForStatement(node)) {
|
|
1539
|
+
const s = node;
|
|
1540
|
+
return this.isAssertStatement(s.statement);
|
|
1541
|
+
}
|
|
1542
|
+
return false;
|
|
1543
|
+
}
|
|
1544
|
+
isIfStatementAssert(node) {
|
|
1545
|
+
if (typescript_1.default.isIfStatement(node)) {
|
|
1546
|
+
const s = node;
|
|
1547
|
+
if (s.elseStatement) {
|
|
1548
|
+
return this.isAssertStatement(s.thenStatement) && this.isAssertStatement(s.elseStatement);
|
|
1549
|
+
}
|
|
1550
|
+
return false;
|
|
1551
|
+
}
|
|
1552
|
+
return false;
|
|
1553
|
+
}
|
|
1554
|
+
isConsoleLogStatement(node) {
|
|
1555
|
+
if (typescript_1.default.isExpressionStatement(node)) {
|
|
1556
|
+
const s = node;
|
|
1557
|
+
return (s.expression.kind === typescript_1.default.SyntaxKind.CallExpression &&
|
|
1558
|
+
/^console\.log/.test(s.expression.getText()));
|
|
1559
|
+
}
|
|
1560
|
+
return false;
|
|
1561
|
+
}
|
|
1562
|
+
verifyLastAssertStatement(statements) {
|
|
1563
|
+
if (statements.length === 0) {
|
|
1564
|
+
return false;
|
|
1565
|
+
}
|
|
1566
|
+
let i = statements.length - 1;
|
|
1567
|
+
while (i >= 0 && this.isConsoleLogStatement(statements[i])) {
|
|
1568
|
+
--i;
|
|
1569
|
+
}
|
|
1570
|
+
return i >= 0 && this.isAssertStatement(statements[i]);
|
|
1571
|
+
}
|
|
1572
|
+
needAppendLastStatement(statements) {
|
|
1573
|
+
if (statements.length === 0) {
|
|
1574
|
+
return false;
|
|
1575
|
+
}
|
|
1576
|
+
let i = statements.length - 1;
|
|
1577
|
+
while (i >= 0 && this.isConsoleLogStatement(statements[i])) {
|
|
1578
|
+
--i;
|
|
1579
|
+
}
|
|
1580
|
+
return i >= 0 && !typescript_1.default.isExpressionStatement(statements[i]);
|
|
1581
|
+
}
|
|
1582
|
+
hasPropertyAccessExpression(node, propName) {
|
|
1583
|
+
if (typescript_1.default.isPropertyAccessExpression(node)) {
|
|
1584
|
+
const ae = node;
|
|
1585
|
+
if (ae.expression.getText() === 'this' && ae.name.getText() === propName) {
|
|
1586
|
+
return true;
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
return typescript_1.default.forEachChild(node, (node) => this.hasPropertyAccessExpression(node, propName));
|
|
1590
|
+
}
|
|
1591
|
+
hasFunctionCallExpression(node, funcName) {
|
|
1592
|
+
if (typescript_1.default.isCallExpression(node)) {
|
|
1593
|
+
const callee = node.expression;
|
|
1594
|
+
if (typescript_1.default.isPropertyAccessExpression(callee) &&
|
|
1595
|
+
callee.expression.getText() === 'this' &&
|
|
1596
|
+
callee.name.getText() === funcName) {
|
|
1597
|
+
return true;
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
return typescript_1.default.forEachChild(node, (node) => this.hasFunctionCallExpression(node, funcName));
|
|
1601
|
+
}
|
|
1602
|
+
createMethodAccessInfo(method, currentMethodName, isBase) {
|
|
1603
|
+
const accessInfo = {
|
|
1604
|
+
accessSHPreimage: false,
|
|
1605
|
+
accessSHPreimageInSubCall: false,
|
|
1606
|
+
accessChange: false,
|
|
1607
|
+
accessChangeInSubCall: false,
|
|
1608
|
+
accessState: false,
|
|
1609
|
+
accessStateInSubCall: false,
|
|
1610
|
+
accessPrevouts: false,
|
|
1611
|
+
accessPrevoutsInSubCall: false,
|
|
1612
|
+
accessPrevout: false,
|
|
1613
|
+
accessPrevoutInSubCall: false,
|
|
1614
|
+
accessSpentScripts: false,
|
|
1615
|
+
accessSpentScriptsInSubCall: false,
|
|
1616
|
+
accessSpentAmounts: false,
|
|
1617
|
+
accessSpentAmountsInSubCall: false,
|
|
1618
|
+
accessSpentDataHashes: false,
|
|
1619
|
+
accessSpentDataHashesInSubCall: false,
|
|
1620
|
+
accessBacktrace: false,
|
|
1621
|
+
accessBacktraceInSubCall: false,
|
|
1622
|
+
accessCSV: false,
|
|
1623
|
+
accessCLTV: false,
|
|
1624
|
+
};
|
|
1625
|
+
function vistMethodChild(self, node) {
|
|
1626
|
+
if (typescript_1.default.isPropertyAccessExpression(node)) {
|
|
1627
|
+
// access properties under `this.ctx`
|
|
1628
|
+
if (node.expression.getText() === 'this.ctx') {
|
|
1629
|
+
Object.assign(accessInfo, {
|
|
1630
|
+
accessSHPreimage: true,
|
|
1631
|
+
});
|
|
1632
|
+
switch (node.name.getText()) {
|
|
1633
|
+
case 'prevouts':
|
|
1634
|
+
Object.assign(accessInfo, {
|
|
1635
|
+
accessPrevouts: true,
|
|
1636
|
+
accessSpentAmounts: true,
|
|
1637
|
+
});
|
|
1638
|
+
break;
|
|
1639
|
+
case 'prevout':
|
|
1640
|
+
Object.assign(accessInfo, {
|
|
1641
|
+
accessPrevout: true,
|
|
1642
|
+
accessSpentAmounts: true,
|
|
1643
|
+
});
|
|
1644
|
+
break;
|
|
1645
|
+
case 'spentAmounts':
|
|
1646
|
+
Object.assign(accessInfo, {
|
|
1647
|
+
accessSpentAmounts: true,
|
|
1648
|
+
});
|
|
1649
|
+
break;
|
|
1650
|
+
case 'spentScriptHashes':
|
|
1651
|
+
Object.assign(accessInfo, {
|
|
1652
|
+
accessSpentAmounts: true,
|
|
1653
|
+
accessSpentScripts: true,
|
|
1654
|
+
});
|
|
1655
|
+
break;
|
|
1656
|
+
case 'spentDataHashes':
|
|
1657
|
+
Object.assign(accessInfo, {
|
|
1658
|
+
accessSpentAmounts: true,
|
|
1659
|
+
accessSpentDataHashes: true,
|
|
1660
|
+
});
|
|
1661
|
+
break;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
// access properties under `this`
|
|
1665
|
+
if (node.expression.getText() === 'this') {
|
|
1666
|
+
if (node.name.getText() === 'changeInfo') {
|
|
1667
|
+
Object.assign(accessInfo, {
|
|
1668
|
+
accessChange: true,
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
else if (node.name.getText() === 'state') {
|
|
1672
|
+
Object.assign(accessInfo, {
|
|
1673
|
+
accessSHPreimage: true, // accessSpentDataHashes depends on shPreimage
|
|
1674
|
+
accessState: true,
|
|
1675
|
+
accessSpentAmounts: true, // spentDataHashes depends on spentAmounts
|
|
1676
|
+
accessSpentDataHashes: true, // state depends on spentDataHashes
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
else if (typescript_1.default.isCallExpression(node)) {
|
|
1682
|
+
const callee = node.expression;
|
|
1683
|
+
if (typescript_1.default.isPropertyAccessExpression(callee) && callee.expression.getText() === 'this') {
|
|
1684
|
+
const methodName = callee.name.getText();
|
|
1685
|
+
if (methodName === currentMethodName) {
|
|
1686
|
+
throw new types_1.TranspileError(`Cycle detected in function call: this.${methodName}()!`, self.getRange(node));
|
|
1687
|
+
}
|
|
1688
|
+
else if (SmartContractBuiltinMethods.includes(methodName)) {
|
|
1689
|
+
if (methodName === 'buildChangeOutput') {
|
|
1690
|
+
Object.assign(accessInfo, {
|
|
1691
|
+
accessChange: true,
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
else if (methodName === 'checkInputState') {
|
|
1695
|
+
Object.assign(accessInfo, {
|
|
1696
|
+
accessSHPreimage: true, // accessSpentDataHashes depends on shPreimage
|
|
1697
|
+
accessState: true,
|
|
1698
|
+
accessSpentDataHashes: true,
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
else if (['relTimeLock'].includes(methodName)) {
|
|
1702
|
+
Object.assign(accessInfo, {
|
|
1703
|
+
accessCSV: true,
|
|
1704
|
+
});
|
|
1705
|
+
}
|
|
1706
|
+
else if (['absTimeLock'].includes(methodName)) {
|
|
1707
|
+
Object.assign(accessInfo, {
|
|
1708
|
+
accessCLTV: true,
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
else if (['backtraceToOutpoint', 'backtraceToScript'].includes(methodName)) {
|
|
1712
|
+
Object.assign(accessInfo, {
|
|
1713
|
+
accessSHPreimage: true,
|
|
1714
|
+
accessPrevouts: true,
|
|
1715
|
+
accessSpentScripts: true,
|
|
1716
|
+
accessSpentAmounts: true,
|
|
1717
|
+
accessBacktrace: true,
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
else {
|
|
1722
|
+
// access in subCall method
|
|
1723
|
+
const clsName = Transpiler.getClassDeclaration(node).name.getText();
|
|
1724
|
+
let methodInfo = self.methodInfos.get(`${clsName}.${methodName}`);
|
|
1725
|
+
if (!methodInfo) {
|
|
1726
|
+
const m = self.findMethodDeclaration(methodName);
|
|
1727
|
+
if (!m) {
|
|
1728
|
+
const msg = `\`${methodName}\` is not \`@method\` decorated so cannot be called in \`${currentMethodName}\`.`;
|
|
1729
|
+
throw new types_1.TranspileError(msg, self.getRange(callee.name));
|
|
1730
|
+
}
|
|
1731
|
+
methodInfo = self.createMethodInfo(m, currentMethodName, isBase);
|
|
1732
|
+
if (!methodInfo) {
|
|
1733
|
+
throw new types_1.TranspileError('createMethodInfo failed', self.getRange(callee.name));
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
Transpiler.checkAccessInSubCall(currentMethodName, self.getRange(callee.name), methodInfo);
|
|
1737
|
+
Transpiler.pickAccessInfo(accessInfo, methodInfo.accessInfo);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
typescript_1.default.forEachChild(node, (node) => vistMethodChild(self, node));
|
|
1742
|
+
}
|
|
1743
|
+
vistMethodChild(this, method);
|
|
1744
|
+
return accessInfo;
|
|
1745
|
+
}
|
|
1746
|
+
static checkAccessInSubCall(calleeMethod, calleeRange, subCallMethodInfo) {
|
|
1747
|
+
if (subCallMethodInfo.isPublic) {
|
|
1748
|
+
// public function with access to injected arguments cannot be called by another function
|
|
1749
|
+
const accessInfo = subCallMethodInfo.accessInfo;
|
|
1750
|
+
const getErrorMsg = () => `\`${calleeMethod}\` cannot call public function \`${subCallMethodInfo.name}\`, because public function \`${subCallMethodInfo.name}\` has access to \`this.ctx\`, \`this.state\`, \`this.changeInfo\`, or \`backtrace\``;
|
|
1751
|
+
if (accessInfo.accessSHPreimage) {
|
|
1752
|
+
throw new types_1.TranspileError(getErrorMsg(), calleeRange);
|
|
1753
|
+
}
|
|
1754
|
+
if (accessInfo.accessChange) {
|
|
1755
|
+
throw new types_1.TranspileError(getErrorMsg(), calleeRange);
|
|
1756
|
+
}
|
|
1757
|
+
if (accessInfo.accessState) {
|
|
1758
|
+
throw new types_1.TranspileError(getErrorMsg(), calleeRange);
|
|
1759
|
+
}
|
|
1760
|
+
if (accessInfo.accessPrevouts) {
|
|
1761
|
+
throw new types_1.TranspileError(getErrorMsg(), calleeRange);
|
|
1762
|
+
}
|
|
1763
|
+
if (accessInfo.accessSpentAmounts) {
|
|
1764
|
+
throw new types_1.TranspileError(getErrorMsg(), calleeRange);
|
|
1765
|
+
}
|
|
1766
|
+
if (accessInfo.accessSpentScripts) {
|
|
1767
|
+
throw new types_1.TranspileError(getErrorMsg(), calleeRange);
|
|
1768
|
+
}
|
|
1769
|
+
if (accessInfo.accessSpentDataHashes) {
|
|
1770
|
+
throw new types_1.TranspileError(getErrorMsg(), calleeRange);
|
|
1771
|
+
}
|
|
1772
|
+
if (accessInfo.accessBacktrace) {
|
|
1773
|
+
throw new types_1.TranspileError(getErrorMsg(), calleeRange);
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
static pickAccessInfo(target, info) {
|
|
1778
|
+
return Object.keys(info).reduce((acc, current) => {
|
|
1779
|
+
if (info[current] === true) {
|
|
1780
|
+
Object.assign(acc, {
|
|
1781
|
+
[current]: info[current],
|
|
1782
|
+
});
|
|
1783
|
+
Object.assign(acc, {
|
|
1784
|
+
[current + 'InSubCall']: info[current],
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1787
|
+
return acc;
|
|
1788
|
+
}, target);
|
|
1789
|
+
}
|
|
1790
|
+
initAllMethodInfos() {
|
|
1791
|
+
if (this.currentbaseContract) {
|
|
1792
|
+
this.currentbaseContract.members.forEach((m) => {
|
|
1793
|
+
if (Transpiler.isMethod(m)) {
|
|
1794
|
+
this.createMethodInfo(m, m.name.getText(), true);
|
|
1795
|
+
}
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1798
|
+
this._currentContract.members.forEach((m) => {
|
|
1799
|
+
if (Transpiler.isMethod(m)) {
|
|
1800
|
+
this.createMethodInfo(m, m.name.getText(), false);
|
|
1801
|
+
}
|
|
1802
|
+
});
|
|
1803
|
+
}
|
|
1804
|
+
initAllPropInfos() {
|
|
1805
|
+
if (this.currentbaseContract) {
|
|
1806
|
+
this.currentbaseContract.members.forEach((m) => {
|
|
1807
|
+
if (Transpiler.isProperty(m)) {
|
|
1808
|
+
this.createPropInfo(m, true);
|
|
1809
|
+
}
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
this._currentContract.members.forEach((m) => {
|
|
1813
|
+
if (Transpiler.isProperty(m)) {
|
|
1814
|
+
this.createPropInfo(m, false);
|
|
1815
|
+
}
|
|
1816
|
+
});
|
|
1817
|
+
}
|
|
1818
|
+
createMethodInfo(m, currentMethodName, isBase) {
|
|
1819
|
+
//save methods info
|
|
1820
|
+
const cls = Transpiler.getClassDeclaration(m);
|
|
1821
|
+
if (!cls) {
|
|
1822
|
+
throw new types_1.TranspileError(`getClassDeclaration undefined`, this.getRange(m.name));
|
|
1823
|
+
}
|
|
1824
|
+
const key = `${cls.name.getText()}.${m.name.getText()}`;
|
|
1825
|
+
if (!this.methodInfos.has(key)) {
|
|
1826
|
+
const methodInfo = {
|
|
1827
|
+
isPublic: Transpiler.isPublicMethod(m),
|
|
1828
|
+
isBase: isBase,
|
|
1829
|
+
accessInfo: this.createMethodAccessInfo(m, currentMethodName, isBase),
|
|
1830
|
+
name: m.name.getText(),
|
|
1831
|
+
codeSeparatorCount: 0,
|
|
1832
|
+
};
|
|
1833
|
+
this.methodInfos.set(key, methodInfo);
|
|
1834
|
+
}
|
|
1835
|
+
return this.methodInfos.get(key);
|
|
1836
|
+
}
|
|
1837
|
+
createPropInfo(m, isBase) {
|
|
1838
|
+
const cls = Transpiler.getClassDeclaration(m);
|
|
1839
|
+
if (!cls) {
|
|
1840
|
+
throw new Error('No ClassDeclaration found!');
|
|
1841
|
+
}
|
|
1842
|
+
//save prop info
|
|
1843
|
+
const key = `${cls.name.getText()}.${m.name.getText()}`;
|
|
1844
|
+
if (!this.propInfos.has(key)) {
|
|
1845
|
+
const propInfo = {
|
|
1846
|
+
name: m.name.getText(),
|
|
1847
|
+
isState: Transpiler.isStateProperty(m),
|
|
1848
|
+
isStatic: Transpiler.isStaticProperty(m),
|
|
1849
|
+
isReadonly: Transpiler.isReadonlyProperty(m),
|
|
1850
|
+
isBase: isBase,
|
|
1851
|
+
isCTC: this.isCtcDeclaration(m),
|
|
1852
|
+
};
|
|
1853
|
+
this.propInfos.set(key, propInfo);
|
|
1854
|
+
}
|
|
1855
|
+
return this.propInfos.get(key);
|
|
1856
|
+
}
|
|
1857
|
+
// TODO: don't support override now
|
|
1858
|
+
findMethodInfo(name) {
|
|
1859
|
+
const methodInfo = this.methodInfos.get(`${this.currentContractName}.${name}`);
|
|
1860
|
+
if (methodInfo) {
|
|
1861
|
+
return methodInfo;
|
|
1862
|
+
}
|
|
1863
|
+
if (this.currentbaseContract) {
|
|
1864
|
+
const methodInfo = this.methodInfos.get(`${this.currentbaseContractName}.${name}`);
|
|
1865
|
+
if (methodInfo) {
|
|
1866
|
+
return methodInfo;
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
return undefined;
|
|
1870
|
+
}
|
|
1871
|
+
getMethodContainsTheNode(node) {
|
|
1872
|
+
let methodNode;
|
|
1873
|
+
let _node = node;
|
|
1874
|
+
const baseContractNode = this.currentbaseContract;
|
|
1875
|
+
while (_node.parent.parent) {
|
|
1876
|
+
// current contract
|
|
1877
|
+
if (this.scComponents.includes(_node.parent.parent)) {
|
|
1878
|
+
methodNode = _node.parent;
|
|
1879
|
+
break;
|
|
1880
|
+
}
|
|
1881
|
+
// base contract
|
|
1882
|
+
if (_node.parent.parent === baseContractNode) {
|
|
1883
|
+
methodNode = _node.parent;
|
|
1884
|
+
break;
|
|
1885
|
+
}
|
|
1886
|
+
_node = _node.parent;
|
|
1887
|
+
}
|
|
1888
|
+
if (!methodNode) {
|
|
1889
|
+
throw new types_1.TranspileError(`Cannot find the method that contains the \`${node.getText()}\``, this.getRange(node));
|
|
1890
|
+
}
|
|
1891
|
+
return methodNode;
|
|
1892
|
+
}
|
|
1893
|
+
getCtxMethodInfos(isPublic = true) {
|
|
1894
|
+
return Array.from(this.methodInfos.values()).filter((info) => info.accessInfo.accessSHPreimage && info.isPublic === isPublic);
|
|
1895
|
+
}
|
|
1896
|
+
findPropInfo(name) {
|
|
1897
|
+
const propInfo = this.propInfos.get(`${this.currentContractName}.${name}`);
|
|
1898
|
+
if (propInfo) {
|
|
1899
|
+
return propInfo;
|
|
1900
|
+
}
|
|
1901
|
+
if (this.currentbaseContract) {
|
|
1902
|
+
const propInfo = this.propInfos.get(`${this.currentbaseContractName}.${name}`);
|
|
1903
|
+
if (propInfo) {
|
|
1904
|
+
return propInfo;
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
return undefined;
|
|
1908
|
+
}
|
|
1909
|
+
getStatePropInfos() {
|
|
1910
|
+
return Array.from(this.propInfos.values()).filter((info) => info.isState);
|
|
1911
|
+
}
|
|
1912
|
+
getPublicMethodCount() {
|
|
1913
|
+
let count = 0;
|
|
1914
|
+
this.methodInfos.forEach((info) => {
|
|
1915
|
+
if (info.isPublic) {
|
|
1916
|
+
count++;
|
|
1917
|
+
}
|
|
1918
|
+
});
|
|
1919
|
+
return count;
|
|
1920
|
+
}
|
|
1921
|
+
/**
|
|
1922
|
+
* does the current contract have a method to access CTX
|
|
1923
|
+
* @returns
|
|
1924
|
+
*/
|
|
1925
|
+
accessSHPreimage() {
|
|
1926
|
+
let ret = false;
|
|
1927
|
+
this.methodInfos.forEach((info) => {
|
|
1928
|
+
if (info.accessInfo.accessSHPreimage) {
|
|
1929
|
+
ret = true;
|
|
1930
|
+
}
|
|
1931
|
+
});
|
|
1932
|
+
return ret;
|
|
1933
|
+
}
|
|
1934
|
+
checkShouldInject(methodCheckFn) {
|
|
1935
|
+
// do not inject if the current contract is a library
|
|
1936
|
+
if (this.isLibrary(this._currentContract))
|
|
1937
|
+
return false;
|
|
1938
|
+
let ret = false;
|
|
1939
|
+
this._currentContract.members.forEach((member) => {
|
|
1940
|
+
if (member.kind === typescript_1.default.SyntaxKind.MethodDeclaration) {
|
|
1941
|
+
const method = member;
|
|
1942
|
+
// do not inject if the method is not decorated with @method
|
|
1943
|
+
const methodDec = Transpiler.findDecorator(method, DecoratorName.Method);
|
|
1944
|
+
if (!methodDec)
|
|
1945
|
+
return;
|
|
1946
|
+
const match = /^method\((.*)?\)$/.exec(methodDec.expression.getText());
|
|
1947
|
+
if (!match) {
|
|
1948
|
+
return;
|
|
1949
|
+
}
|
|
1950
|
+
// do not inject if the method is not public
|
|
1951
|
+
if (!Transpiler.isPublicMethod(method))
|
|
1952
|
+
return;
|
|
1953
|
+
// do not inject if the method is static
|
|
1954
|
+
if (Transpiler.isStaticMethod(method))
|
|
1955
|
+
return;
|
|
1956
|
+
try {
|
|
1957
|
+
if (methodCheckFn(method)) {
|
|
1958
|
+
ret = true;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
catch (_e) {
|
|
1962
|
+
// ignore error
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
});
|
|
1966
|
+
return ret;
|
|
1967
|
+
}
|
|
1968
|
+
shouldInjectSHPreimageProp() {
|
|
1969
|
+
return this.checkShouldInject((method) => this.shouldAutoAppendSighashPreimage(method).shouldAccessThis);
|
|
1970
|
+
}
|
|
1971
|
+
shouldInjectChangeProp() {
|
|
1972
|
+
return this.checkShouldInject((method) => this.shouldAutoAppendChangeAmount(method).shouldAccessThis);
|
|
1973
|
+
}
|
|
1974
|
+
shouldInjectPrevoutsProp() {
|
|
1975
|
+
return this.checkShouldInject((method) => this.shouldAutoAppendPrevouts(method).shouldAccessThis);
|
|
1976
|
+
}
|
|
1977
|
+
shouldInjectPrevoutProp() {
|
|
1978
|
+
return this.checkShouldInject((method) => this.shouldAutoAppendPrevout(method).shouldAccessThis);
|
|
1979
|
+
}
|
|
1980
|
+
shouldInjectSpentScriptsProp() {
|
|
1981
|
+
return this.checkShouldInject((method) => this.shouldAutoAppendSpentScripts(method).shouldAccessThis);
|
|
1982
|
+
}
|
|
1983
|
+
shouldInjectSpentAmountsProp() {
|
|
1984
|
+
return this.checkShouldInject((method) => this.shouldAutoAppendSpentAmounts(method).shouldAccessThis);
|
|
1985
|
+
}
|
|
1986
|
+
shouldInjectSpentDataHashesProp() {
|
|
1987
|
+
return this.checkShouldInject((method) => this.shouldAutoAppendSpentDataHashes(method).shouldAccessThis);
|
|
1988
|
+
}
|
|
1989
|
+
shouldInjectPrevTxHashPreimageProp() {
|
|
1990
|
+
return this.checkShouldInject((method) => this.shouldAutoAppendPrevTxHashPreimageArgs(method).shouldAccessThis);
|
|
1991
|
+
}
|
|
1992
|
+
shouldInjectCurStateProp() {
|
|
1993
|
+
return this.checkShouldInject((method) => this.shouldAutoAppendStateArgs(method).shouldAccessThis);
|
|
1994
|
+
}
|
|
1995
|
+
isStateful() {
|
|
1996
|
+
return this._stateTypeSymbols.has(this.currentContractName);
|
|
1997
|
+
}
|
|
1998
|
+
accessChange() {
|
|
1999
|
+
let ret = false;
|
|
2000
|
+
this.methodInfos.forEach((info) => {
|
|
2001
|
+
if (info.isPublic && info.accessInfo.accessChange) {
|
|
2002
|
+
ret = true;
|
|
2003
|
+
}
|
|
2004
|
+
});
|
|
2005
|
+
return ret;
|
|
2006
|
+
}
|
|
2007
|
+
accessPrevouts() {
|
|
2008
|
+
let ret = false;
|
|
2009
|
+
this.methodInfos.forEach((info) => {
|
|
2010
|
+
if (info.isPublic && info.accessInfo.accessPrevouts) {
|
|
2011
|
+
ret = true;
|
|
2012
|
+
}
|
|
2013
|
+
});
|
|
2014
|
+
return ret;
|
|
2015
|
+
}
|
|
2016
|
+
_shouldAutoAppend(node, shouldFn) {
|
|
2017
|
+
const methodInfo = this.findMethodInfo(node.name.getText());
|
|
2018
|
+
if (!methodInfo) {
|
|
2019
|
+
throw new types_1.UnknownError(`No method info found for \`${node.name.getText()}\``, this.getRange(node.name));
|
|
2020
|
+
}
|
|
2021
|
+
const [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis] = shouldFn(methodInfo);
|
|
2022
|
+
// addtional check: if a private method is accessing , but no public method is accessing, then it should not be injected and throw an error
|
|
2023
|
+
if (shouldAccessThis && !methodInfo.isPublic) {
|
|
2024
|
+
let addtionalCheckPassed = shouldAccessThis ? false : true;
|
|
2025
|
+
for (const info of this.methodInfos.values()) {
|
|
2026
|
+
const [shouldAppendArguments_] = shouldFn(info);
|
|
2027
|
+
if (info.isPublic && shouldAppendArguments_) {
|
|
2028
|
+
addtionalCheckPassed = true;
|
|
2029
|
+
break;
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
if (!addtionalCheckPassed) {
|
|
2033
|
+
throw new types_1.TranspileError(`Cannot access \`this.ctx\`, \`this.state\`, \`this.changeInfo\`, or \`backtrace\` in a private method \`${node.name.getText()}\`, because the private method is not called by any public method`, this.getRange(node.name));
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
return { shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis };
|
|
2037
|
+
}
|
|
2038
|
+
shouldAutoAppendChangeAmount(node) {
|
|
2039
|
+
return this._shouldAutoAppend(node, (methodInfo) => {
|
|
2040
|
+
const { accessChange, accessChangeInSubCall } = methodInfo.accessInfo;
|
|
2041
|
+
const { isPublic } = methodInfo;
|
|
2042
|
+
const shouldAppendArguments = accessChange && isPublic;
|
|
2043
|
+
const shouldAppendThisAssignment = accessChange && accessChangeInSubCall && isPublic;
|
|
2044
|
+
const shouldAccessThis = (!isPublic || accessChangeInSubCall) && accessChange;
|
|
2045
|
+
return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2048
|
+
shouldAutoAppendPrevouts(node) {
|
|
2049
|
+
return this._shouldAutoAppend(node, (methodInfo) => {
|
|
2050
|
+
const { accessPrevouts, accessPrevoutsInSubCall } = methodInfo.accessInfo;
|
|
2051
|
+
const { isPublic } = methodInfo;
|
|
2052
|
+
const shouldAppendArguments = accessPrevouts && isPublic;
|
|
2053
|
+
const shouldAppendThisAssignment = accessPrevouts && accessPrevoutsInSubCall && isPublic;
|
|
2054
|
+
const shouldAccessThis = (!isPublic || accessPrevoutsInSubCall) && accessPrevouts;
|
|
2055
|
+
return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
shouldAutoAppendPrevout(node) {
|
|
2059
|
+
return this._shouldAutoAppend(node, (methodInfo) => {
|
|
2060
|
+
const { accessPrevout, accessPrevoutInSubCall } = methodInfo.accessInfo;
|
|
2061
|
+
const { isPublic } = methodInfo;
|
|
2062
|
+
const shouldAppendArguments = accessPrevout && isPublic;
|
|
2063
|
+
const shouldAppendThisAssignment = accessPrevout && accessPrevoutInSubCall && isPublic;
|
|
2064
|
+
const shouldAccessThis = (!isPublic || accessPrevoutInSubCall) && accessPrevout;
|
|
2065
|
+
return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
shouldAutoAppendSpentScripts(node) {
|
|
2069
|
+
return this._shouldAutoAppend(node, (methodInfo) => {
|
|
2070
|
+
const { accessSpentScripts, accessSpentScriptsInSubCall } = methodInfo.accessInfo;
|
|
2071
|
+
const { isPublic } = methodInfo;
|
|
2072
|
+
const shouldAppendArguments = accessSpentScripts && isPublic;
|
|
2073
|
+
const shouldAppendThisAssignment = accessSpentScripts && accessSpentScriptsInSubCall && isPublic;
|
|
2074
|
+
const shouldAccessThis = (!isPublic || accessSpentScriptsInSubCall) && accessSpentScripts;
|
|
2075
|
+
return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
|
|
2076
|
+
});
|
|
2077
|
+
}
|
|
2078
|
+
shouldAutoAppendSpentAmounts(node) {
|
|
2079
|
+
return this._shouldAutoAppend(node, (methodInfo) => {
|
|
2080
|
+
const { accessSpentAmounts, accessSpentAmountsInSubCall } = methodInfo.accessInfo;
|
|
2081
|
+
const { isPublic } = methodInfo;
|
|
2082
|
+
const shouldAppendArguments = accessSpentAmounts && isPublic;
|
|
2083
|
+
const shouldAppendThisAssignment = accessSpentAmounts && accessSpentAmountsInSubCall && isPublic;
|
|
2084
|
+
const shouldAccessThis = (!isPublic || accessSpentAmountsInSubCall) && accessSpentAmounts;
|
|
2085
|
+
return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
|
|
2086
|
+
});
|
|
2087
|
+
}
|
|
2088
|
+
shouldAutoAppendSpentDataHashes(node) {
|
|
2089
|
+
return this._shouldAutoAppend(node, (methodInfo) => {
|
|
2090
|
+
const { accessSpentDataHashes, accessSpentDataHashesInSubCall } = methodInfo.accessInfo;
|
|
2091
|
+
const { isPublic } = methodInfo;
|
|
2092
|
+
const shouldAppendArguments = accessSpentDataHashes && isPublic;
|
|
2093
|
+
const shouldAppendThisAssignment = accessSpentDataHashes && accessSpentDataHashesInSubCall && isPublic;
|
|
2094
|
+
const shouldAccessThis = (!isPublic || accessSpentDataHashesInSubCall) && accessSpentDataHashes;
|
|
2095
|
+
return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
shouldAutoAppendStateArgs(node) {
|
|
2099
|
+
return this._shouldAutoAppend(node, (methodInfo) => {
|
|
2100
|
+
const { accessState, accessStateInSubCall } = methodInfo.accessInfo;
|
|
2101
|
+
const { isPublic } = methodInfo;
|
|
2102
|
+
const shouldAppendArguments = accessState && isPublic;
|
|
2103
|
+
const shouldAppendThisAssignment = accessState && accessStateInSubCall && isPublic;
|
|
2104
|
+
const shouldAccessThis = (!isPublic || accessStateInSubCall) && accessState;
|
|
2105
|
+
return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
|
|
2106
|
+
});
|
|
2107
|
+
}
|
|
2108
|
+
shouldAutoAppendPrevTxHashPreimageArgs(node) {
|
|
2109
|
+
return this._shouldAutoAppend(node, (methodInfo) => {
|
|
2110
|
+
const { accessBacktrace, accessBacktraceInSubCall } = methodInfo.accessInfo;
|
|
2111
|
+
const { isPublic } = methodInfo;
|
|
2112
|
+
const shouldAppendArguments = accessBacktrace && isPublic;
|
|
2113
|
+
const shouldAppendThisAssignment = accessBacktrace && accessBacktraceInSubCall && isPublic;
|
|
2114
|
+
const shouldAccessThis = (!isPublic || accessBacktraceInSubCall) && accessBacktrace;
|
|
2115
|
+
return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
2118
|
+
static isStaticMethod(node) {
|
|
2119
|
+
if (typescript_1.default.isMethodDeclaration(node) && (0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.StaticKeyword)) {
|
|
2120
|
+
return true;
|
|
2121
|
+
}
|
|
2122
|
+
return false;
|
|
2123
|
+
}
|
|
2124
|
+
static isPublicMethod(node) {
|
|
2125
|
+
if (typescript_1.default.isMethodDeclaration(node) &&
|
|
2126
|
+
(0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.PublicKeyword) &&
|
|
2127
|
+
this.findDecorator(node, DecoratorName.Method)) {
|
|
2128
|
+
return true;
|
|
2129
|
+
}
|
|
2130
|
+
return false;
|
|
2131
|
+
}
|
|
2132
|
+
shouldAutoAppendSighashPreimage(node) {
|
|
2133
|
+
// const hasSigHashPreimageParameters = node.parameters.find(
|
|
2134
|
+
// (p) => p.type?.getText() === 'SigHashPreimage',
|
|
2135
|
+
// );
|
|
2136
|
+
// if (hasSigHashPreimageParameters) {
|
|
2137
|
+
// return false;
|
|
2138
|
+
// }
|
|
2139
|
+
const methodInfo = this.findMethodInfo(node.name.getText());
|
|
2140
|
+
if (!methodInfo) {
|
|
2141
|
+
throw new types_1.UnknownError(`No method info found for \`${node.name.getText()}\``, this.getRange(node.name));
|
|
2142
|
+
}
|
|
2143
|
+
const { accessSHPreimage, accessSHPreimageInSubCall } = methodInfo.accessInfo;
|
|
2144
|
+
const { isPublic } = methodInfo;
|
|
2145
|
+
const shouldAppendArguments = accessSHPreimage && isPublic;
|
|
2146
|
+
const shouldAppendThisAssignment = accessSHPreimage && accessSHPreimageInSubCall && isPublic;
|
|
2147
|
+
const shouldAccessThis = (!isPublic || accessSHPreimageInSubCall) && accessSHPreimage;
|
|
2148
|
+
return { shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis };
|
|
2149
|
+
}
|
|
2150
|
+
transformParameter(node, toSection) {
|
|
2151
|
+
return toSection
|
|
2152
|
+
.appendWith(this, (toSec) => {
|
|
2153
|
+
if (!node.type) {
|
|
2154
|
+
throw new types_1.TranspileError(`Untransformable parameter: '${node.getText()}', all parameters should be typed explicitly`, this.getRange(node));
|
|
2155
|
+
}
|
|
2156
|
+
return this.transformEnclosingTypeNode(node.type, toSec);
|
|
2157
|
+
})
|
|
2158
|
+
.append(' ')
|
|
2159
|
+
.append(node.name.getText(), this.getCoordinates(node.name.getStart()));
|
|
2160
|
+
}
|
|
2161
|
+
transformStatement(node, toSection) {
|
|
2162
|
+
switch (node.kind) {
|
|
2163
|
+
case typescript_1.default.SyntaxKind.Block: {
|
|
2164
|
+
return toSection
|
|
2165
|
+
.append('{')
|
|
2166
|
+
.appendWith(this, (blockSec) => {
|
|
2167
|
+
node.statements.forEach((stmt) => {
|
|
2168
|
+
blockSec.append('\n').appendWith(this, (stmtsSec) => {
|
|
2169
|
+
return this.transformStatement(stmt, stmtsSec);
|
|
2170
|
+
});
|
|
2171
|
+
});
|
|
2172
|
+
return blockSec;
|
|
2173
|
+
}, true)
|
|
2174
|
+
.append('\n}');
|
|
2175
|
+
}
|
|
2176
|
+
case typescript_1.default.SyntaxKind.ExpressionStatement: {
|
|
2177
|
+
const s = node;
|
|
2178
|
+
if (s.expression.kind === typescript_1.default.SyntaxKind.CallExpression &&
|
|
2179
|
+
/^super(\s*)\(.*\)/.test(s.expression.getText())) {
|
|
2180
|
+
return toSection;
|
|
2181
|
+
}
|
|
2182
|
+
else if (s.expression.kind === typescript_1.default.SyntaxKind.CallExpression &&
|
|
2183
|
+
/^console\./.test(s.expression.getText())) {
|
|
2184
|
+
return toSection;
|
|
2185
|
+
}
|
|
2186
|
+
else if (s.expression.kind === typescript_1.default.SyntaxKind.CallExpression &&
|
|
2187
|
+
/^this\.debug\./.test(s.expression.getText())) {
|
|
2188
|
+
return toSection;
|
|
2189
|
+
}
|
|
2190
|
+
else if (s.expression.kind === typescript_1.default.SyntaxKind.CallExpression &&
|
|
2191
|
+
/^this\.init\(/.test(s.expression.getText())) {
|
|
2192
|
+
return toSection;
|
|
2193
|
+
}
|
|
2194
|
+
else if (s.expression.kind === typescript_1.default.SyntaxKind.CallExpression &&
|
|
2195
|
+
/^this\.checkInputState\(/.test(s.expression.getText())) {
|
|
2196
|
+
return this.transformCallCheckInputState(s.expression, toSection);
|
|
2197
|
+
}
|
|
2198
|
+
return this.transformExpression(s.expression, toSection).append(';');
|
|
2199
|
+
}
|
|
2200
|
+
case typescript_1.default.SyntaxKind.VariableStatement: {
|
|
2201
|
+
const stmt = node;
|
|
2202
|
+
if (stmt.declarationList.declarations.length > 1) {
|
|
2203
|
+
throw new types_1.TranspileError(`Untransformable statement: '${node.getText()}'`, this.getRange(node));
|
|
2204
|
+
}
|
|
2205
|
+
const d = stmt.declarationList.declarations[0];
|
|
2206
|
+
if (!d.initializer) {
|
|
2207
|
+
throw new types_1.TranspileError(`Untransformable statement: '${node.getText()}'`, this.getRange(node));
|
|
2208
|
+
}
|
|
2209
|
+
if (this.isCtcDeclaration(d)) {
|
|
2210
|
+
return toSection;
|
|
2211
|
+
}
|
|
2212
|
+
else {
|
|
2213
|
+
// use `d.type` as type context node if it exists,
|
|
2214
|
+
// otherwise use `d.initializer` to provide type context, so we can leverage the type inference.
|
|
2215
|
+
if (d.type) {
|
|
2216
|
+
return this.transformEnclosingTypeNode(d.type, toSection)
|
|
2217
|
+
.append(` ${d.name.getText()}`, this.getCoordinates(d.name.getStart()))
|
|
2218
|
+
.append(' = ')
|
|
2219
|
+
.appendWith(this, (toSec) => {
|
|
2220
|
+
return this.transformExpression(d.initializer, toSec);
|
|
2221
|
+
})
|
|
2222
|
+
.append(';');
|
|
2223
|
+
}
|
|
2224
|
+
const type = this._checker.getTypeAtLocation(d.initializer);
|
|
2225
|
+
if (type.flags === typescript_1.default.TypeFlags.String) {
|
|
2226
|
+
// fix https://github.com/sCrypt-Inc/scrypt-ts/issues/390
|
|
2227
|
+
const coordinates = this.getCoordinates(d.initializer.getStart());
|
|
2228
|
+
return toSection
|
|
2229
|
+
.append('bytes', coordinates)
|
|
2230
|
+
.append(` ${d.name.getText()}`, this.getCoordinates(d.name.getStart()))
|
|
2231
|
+
.append(' = ')
|
|
2232
|
+
.appendWith(this, (toSec) => {
|
|
2233
|
+
return this.transformExpression(d.initializer, toSec);
|
|
2234
|
+
})
|
|
2235
|
+
.append(';');
|
|
2236
|
+
}
|
|
2237
|
+
return this.transformEnclosingTypeNode(d.initializer, toSection)
|
|
2238
|
+
.append(` ${d.name.getText()}`, this.getCoordinates(d.name.getStart()))
|
|
2239
|
+
.append(' = ')
|
|
2240
|
+
.appendWith(this, (toSec) => {
|
|
2241
|
+
return this.transformExpression(d.initializer, toSec);
|
|
2242
|
+
})
|
|
2243
|
+
.append(';');
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
case typescript_1.default.SyntaxKind.ReturnStatement: {
|
|
2247
|
+
const s = node;
|
|
2248
|
+
if (!s.expression) {
|
|
2249
|
+
// If only "return", then the method must be a void type.
|
|
2250
|
+
// The return stmt gets handled elsewhere.
|
|
2251
|
+
return toSection;
|
|
2252
|
+
}
|
|
2253
|
+
return toSection
|
|
2254
|
+
.append(`return `, this.getCoordinates(s.getStart()))
|
|
2255
|
+
.appendWith(this, (toSec) => {
|
|
2256
|
+
return this.transformExpression(s.expression, toSec);
|
|
2257
|
+
})
|
|
2258
|
+
.append(';');
|
|
2259
|
+
}
|
|
2260
|
+
case typescript_1.default.SyntaxKind.IfStatement: {
|
|
2261
|
+
const s = node;
|
|
2262
|
+
toSection
|
|
2263
|
+
.append(`if(`, this.getCoordinates(s.getStart()))
|
|
2264
|
+
.appendWith(this, (toSec) => {
|
|
2265
|
+
return this.transformExpression(s.expression, toSec);
|
|
2266
|
+
})
|
|
2267
|
+
.append(`) `)
|
|
2268
|
+
.appendWith(this, (toSec) => {
|
|
2269
|
+
return this.transformStatement(s.thenStatement, toSec);
|
|
2270
|
+
});
|
|
2271
|
+
if (s.elseStatement) {
|
|
2272
|
+
toSection.append(` else `).appendWith(this, (toSec) => {
|
|
2273
|
+
return this.transformStatement(s.elseStatement, toSec);
|
|
2274
|
+
});
|
|
2275
|
+
}
|
|
2276
|
+
return toSection;
|
|
2277
|
+
}
|
|
2278
|
+
case typescript_1.default.SyntaxKind.ForStatement: {
|
|
2279
|
+
const s = node;
|
|
2280
|
+
let inductionVar = undefined;
|
|
2281
|
+
if (s.initializer?.kind === typescript_1.default.SyntaxKind.VariableDeclarationList) {
|
|
2282
|
+
const ivDeclare = s.initializer.declarations[0];
|
|
2283
|
+
// initializer expr must match `let $i = 0;`
|
|
2284
|
+
if (ivDeclare.initializer.getText() === '0' ||
|
|
2285
|
+
ivDeclare.initializer.getText() === '0n') {
|
|
2286
|
+
inductionVar = ivDeclare.name;
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
if (!inductionVar) {
|
|
2290
|
+
throw new types_1.TranspileError(`\`for\` statement in \`@method\` should have induction variable declaration as: 'for(let $i = 0; ...; ...)'`, this.getRange(s));
|
|
2291
|
+
}
|
|
2292
|
+
let loopCount = undefined;
|
|
2293
|
+
if (s.condition?.kind === typescript_1.default.SyntaxKind.BinaryExpression) {
|
|
2294
|
+
const cond = s.condition;
|
|
2295
|
+
// condition expr must match `$i < $constNum;`
|
|
2296
|
+
const condVarName = cond.left.getText();
|
|
2297
|
+
if (condVarName === inductionVar.getText()) {
|
|
2298
|
+
if (cond.operatorToken.kind !== typescript_1.default.SyntaxKind.LessThanToken) {
|
|
2299
|
+
throw new types_1.TranspileError(`\`for\` statement in \`@method\` should only have a \`<\`(lessthan) operator: 'for(...; $i < $constNum; ...)'`, this.getRange(cond.operatorToken));
|
|
2300
|
+
}
|
|
2301
|
+
if (this.isCtcExpression(cond.right)) {
|
|
2302
|
+
if (this.isParameterNode(cond.right)) {
|
|
2303
|
+
loopCount = cond.right.getText();
|
|
2304
|
+
}
|
|
2305
|
+
else {
|
|
2306
|
+
loopCount = this.evalCtcExpression(cond.right);
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
if (loopCount === undefined) {
|
|
2312
|
+
throw new types_1.TranspileError(`\`for\` statement in \`@method\` should have condition expression as: 'for(...; $i < $constNum; ...)'`, this.getRange(s));
|
|
2313
|
+
}
|
|
2314
|
+
let postIncIV = false;
|
|
2315
|
+
// incrementor expr must match `$i++`
|
|
2316
|
+
if (s.incrementor?.kind === typescript_1.default.SyntaxKind.PostfixUnaryExpression) {
|
|
2317
|
+
const inc = s.incrementor;
|
|
2318
|
+
if (inc.operator === typescript_1.default.SyntaxKind.PlusPlusToken &&
|
|
2319
|
+
inc.operand.getText() === inductionVar.getText()) {
|
|
2320
|
+
postIncIV = true;
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
if (!postIncIV) {
|
|
2324
|
+
throw new types_1.TranspileError(`\`for\` statement in \`@method\` should have incrementor expression as: 'for(...; ...; $i++)'`, this.getRange(s.incrementor));
|
|
2325
|
+
}
|
|
2326
|
+
return toSection
|
|
2327
|
+
.append('loop (')
|
|
2328
|
+
.append(loopCount)
|
|
2329
|
+
.append(') : ')
|
|
2330
|
+
.append(`${inductionVar.getText()} `, this.getCoordinates(inductionVar.getStart()))
|
|
2331
|
+
.appendWith(this, (toSec) => {
|
|
2332
|
+
return this.transformStatement(s.statement, toSec);
|
|
2333
|
+
});
|
|
2334
|
+
}
|
|
2335
|
+
default: {
|
|
2336
|
+
throw new types_1.TranspileError(`Untransformable statement: '${node.getText()}'`, this.getRange(node));
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
transformExpression(node, toSection) {
|
|
2341
|
+
const srcLoc = this.getCoordinates(node.getStart());
|
|
2342
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
2343
|
+
const map = new Map();
|
|
2344
|
+
map.set(typescript_1.default.SyntaxKind.NumericLiteral, (node, toSection) => toSection.append(`${node.getText()}`, srcLoc));
|
|
2345
|
+
map.set(typescript_1.default.SyntaxKind.StringLiteral, (node, toSection) => this.transformStringLiteralExpression(node, toSection));
|
|
2346
|
+
map.set(typescript_1.default.SyntaxKind.BigIntLiteral, (node, toSection) => this.transformBigIntLiteralExpression(node, toSection));
|
|
2347
|
+
map.set(typescript_1.default.SyntaxKind.ArrayLiteralExpression, (node, toSection) => this.transformArrayLiteralExpression(node, toSection));
|
|
2348
|
+
map.set(typescript_1.default.SyntaxKind.ObjectLiteralExpression, (node, toSection) => this.transformObjectLiteralExpression(node, toSection));
|
|
2349
|
+
map.set(typescript_1.default.SyntaxKind.CallExpression, (node, toSection) => this.transformCallExpression(node, toSection));
|
|
2350
|
+
map.set(typescript_1.default.SyntaxKind.Identifier, (node, toSection) => this.transformIdentifierExpression(node, toSection));
|
|
2351
|
+
map.set(typescript_1.default.SyntaxKind.BinaryExpression, (node, toSection) => this.transformBinaryExpression(node, toSection));
|
|
2352
|
+
map.set(typescript_1.default.SyntaxKind.PropertyAccessExpression, (node, toSection) => this.transformPropertyAccessExpression(node, toSection));
|
|
2353
|
+
map.set(typescript_1.default.SyntaxKind.ArrowFunction, (node, toSection) => this.transformArrowFunctionExpression(node, toSection));
|
|
2354
|
+
map.set(typescript_1.default.SyntaxKind.NewExpression, (node, toSection) => this.transformNewExpression(node, toSection));
|
|
2355
|
+
map.set(typescript_1.default.SyntaxKind.ThisKeyword, (_node, toSection) => toSection.append('this', srcLoc));
|
|
2356
|
+
map.set(typescript_1.default.SyntaxKind.TrueKeyword, (_node, toSection) => toSection.append('true', srcLoc));
|
|
2357
|
+
map.set(typescript_1.default.SyntaxKind.FalseKeyword, (_node, toSection) => toSection.append('false', srcLoc));
|
|
2358
|
+
map.set(typescript_1.default.SyntaxKind.SuperKeyword, (_node, toSection) => toSection);
|
|
2359
|
+
map.set(typescript_1.default.SyntaxKind.ElementAccessExpression, (node, toSection) => this.transformElementAccessExpression(node, toSection));
|
|
2360
|
+
map.set(typescript_1.default.SyntaxKind.ConditionalExpression, (node, toSection) => this.transformConditionalExpression(node, toSection));
|
|
2361
|
+
map.set(typescript_1.default.SyntaxKind.ParenthesizedExpression, (node, toSection) => this.transformParenthesizedExpression(node, toSection));
|
|
2362
|
+
map.set(typescript_1.default.SyntaxKind.PostfixUnaryExpression, (node, toSection) => this.transformPostfixUnaryExpression(node, toSection));
|
|
2363
|
+
map.set(typescript_1.default.SyntaxKind.PrefixUnaryExpression, (node, toSection) => this.transformPrefixUnaryExpression(node, toSection));
|
|
2364
|
+
map.set(typescript_1.default.SyntaxKind.AsExpression, (node, toSection) => toSection.appendWith(this, (toSec) => this.transformExpression(node.expression, toSec)));
|
|
2365
|
+
const func = map.get(node.kind);
|
|
2366
|
+
if (func === undefined) {
|
|
2367
|
+
throw new types_1.TranspileError(`Untransformable expression kind ${typescript_1.default.SyntaxKind[node.kind]}: '${node.getText()}'`, this.getRange(node));
|
|
2368
|
+
}
|
|
2369
|
+
return func(node, toSection);
|
|
2370
|
+
}
|
|
2371
|
+
// transform the type enclosed in the node.
|
|
2372
|
+
transformEnclosingTypeNode(node, toSection) {
|
|
2373
|
+
const type = this._checker.getTypeAtLocation(node);
|
|
2374
|
+
return this.transformType(type, node, toSection);
|
|
2375
|
+
}
|
|
2376
|
+
isByteStringNode(node) {
|
|
2377
|
+
const type = this._checker.getTypeAtLocation(node);
|
|
2378
|
+
const typeString = this.type2ResolvedName(type);
|
|
2379
|
+
return typeString === 'ByteString' || typeString === 'OpCodeType';
|
|
2380
|
+
}
|
|
2381
|
+
// `typeStrCtx` is the type's literal name or the text of the node which encloses the type.
|
|
2382
|
+
transformType(type, node, toSection) {
|
|
2383
|
+
const typeStrCtx = node.getText();
|
|
2384
|
+
const coordinates = this.getCoordinates(node.getStart());
|
|
2385
|
+
switch (type.flags) {
|
|
2386
|
+
case typescript_1.default.TypeFlags.Union + typescript_1.default.TypeFlags.Boolean: // This is the real internal type of `boolean`, it's a union of `true` and `false`
|
|
2387
|
+
case typescript_1.default.TypeFlags.BooleanLiteral:
|
|
2388
|
+
case typescript_1.default.TypeFlags.Boolean: {
|
|
2389
|
+
return toSection.append('bool', coordinates);
|
|
2390
|
+
}
|
|
2391
|
+
case typescript_1.default.TypeFlags.Union + typescript_1.default.TypeFlags.EnumLiteral:
|
|
2392
|
+
case typescript_1.default.TypeFlags.BigIntLiteral:
|
|
2393
|
+
case typescript_1.default.TypeFlags.BigInt: {
|
|
2394
|
+
return toSection.append('int', coordinates);
|
|
2395
|
+
}
|
|
2396
|
+
case typescript_1.default.TypeFlags.NumberLiteral:
|
|
2397
|
+
case typescript_1.default.TypeFlags.Number: {
|
|
2398
|
+
if (typescript_1.default.isParameter(node.parent)) {
|
|
2399
|
+
return toSection.append('static const int', coordinates);
|
|
2400
|
+
}
|
|
2401
|
+
throw new types_1.TranspileError(`Untransformable type \`number\` here, please use type \`bigint\` instead`, this.getRange(node));
|
|
2402
|
+
}
|
|
2403
|
+
case typescript_1.default.TypeFlags.Intersection:
|
|
2404
|
+
case typescript_1.default.TypeFlags.Object: {
|
|
2405
|
+
const typeString = this.type2ResolvedName(type);
|
|
2406
|
+
// for declared object literal type, like `x : {prop: number}`
|
|
2407
|
+
if (typeString === '__type') {
|
|
2408
|
+
throw new types_1.TranspileError(`Untransformable literal object type: '${typeStrCtx}'`, this.getRange(node));
|
|
2409
|
+
}
|
|
2410
|
+
// for inferred object literal type, like `x = {prop: 1}`
|
|
2411
|
+
if (typeString === '__object') {
|
|
2412
|
+
throw new types_1.TranspileError(`Untransformable literal object type: '${typeStrCtx}'`, this.getRange(node));
|
|
2413
|
+
}
|
|
2414
|
+
// for bigint & number wrapper
|
|
2415
|
+
if (typeString === 'BigInt') {
|
|
2416
|
+
return toSection.append('int', coordinates);
|
|
2417
|
+
}
|
|
2418
|
+
if (typeString === 'Number') {
|
|
2419
|
+
throw new types_1.TranspileError(`Untransformable type \`${typeStrCtx}\` here, please use type \`bigint\` instead`, this.getRange(node));
|
|
2420
|
+
}
|
|
2421
|
+
// for string wrapper
|
|
2422
|
+
if (typeString === 'String') {
|
|
2423
|
+
throw new types_1.TranspileError(`Untransformable type \`${typeStrCtx}\` here, please use type \`ByteString\` instead`, this.getRange(node));
|
|
2424
|
+
}
|
|
2425
|
+
const isFixedArray = (type) => {
|
|
2426
|
+
if (type.isUnionOrIntersection() && type.types.length === 2) {
|
|
2427
|
+
const t1 = this._checker.typeToString(type.types[0]);
|
|
2428
|
+
const t2 = this._checker.typeToString(type.types[1]);
|
|
2429
|
+
if (t1.endsWith('[]') && /\{\slength:\s((\d+)|(number)|(any));\s\}/.test(t2)) {
|
|
2430
|
+
return true;
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
return false;
|
|
2434
|
+
};
|
|
2435
|
+
if (isFixedArray(type)) {
|
|
2436
|
+
const getBaseElemTypeAndLengths = (typeRef, arrLens) => {
|
|
2437
|
+
if (typeRef.isUnionOrIntersection() && isFixedArray(typeRef)) {
|
|
2438
|
+
const FixedArrayLenString = this._checker.typeToString(typeRef.types[1]);
|
|
2439
|
+
const m = /(\d)+/.exec(FixedArrayLenString);
|
|
2440
|
+
if (m) {
|
|
2441
|
+
const fst = this._checker.getTypeArguments(typeRef.types[0]);
|
|
2442
|
+
return getBaseElemTypeAndLengths(fst[0], arrLens.concat(parseInt(m[0])));
|
|
2443
|
+
}
|
|
2444
|
+
else {
|
|
2445
|
+
throw new types_1.TranspileError(`Untransformable type \`Array\`, please use type \`FixedArray\` instead`, this.getRange(node));
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
else {
|
|
2449
|
+
return { typeRef: typeRef, arrLens };
|
|
2450
|
+
}
|
|
2451
|
+
};
|
|
2452
|
+
const getBaseElemTypeAndLengthsIncludeCTC = (tNode, arrLens) => {
|
|
2453
|
+
const typeRef = this._checker.getTypeAtLocation(tNode);
|
|
2454
|
+
if (typeRef.isUnionOrIntersection() && isFixedArray(typeRef)) {
|
|
2455
|
+
const FixedArrayLenString = this._checker.typeToString(typeRef.types[1]);
|
|
2456
|
+
if (typescript_1.default.isTypeReferenceNode(tNode)) {
|
|
2457
|
+
if (FixedArrayLenString === '{ length: number; }' &&
|
|
2458
|
+
tNode.typeArguments &&
|
|
2459
|
+
tNode.typeArguments.length == 2 &&
|
|
2460
|
+
typescript_1.default.isTypeQueryNode(tNode.typeArguments[1])) {
|
|
2461
|
+
if (this.isParameterNode(tNode.typeArguments[1].exprName)) {
|
|
2462
|
+
return getBaseElemTypeAndLengthsIncludeCTC(tNode.typeArguments[0], arrLens.concat(tNode.typeArguments[1].exprName.getText()));
|
|
2463
|
+
}
|
|
2464
|
+
else {
|
|
2465
|
+
throw new types_1.TranspileError(`Untransformable type \`Array\`, please use type \`FixedArray\` instead`, this.getRange(node));
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
else if (FixedArrayLenString === '{ length: any; }' &&
|
|
2469
|
+
tNode.typeArguments &&
|
|
2470
|
+
tNode.typeArguments.length == 2 &&
|
|
2471
|
+
typescript_1.default.isUnionTypeNode(tNode.typeArguments[1]) &&
|
|
2472
|
+
typescript_1.default.isTypeQueryNode(tNode.typeArguments[1].types[0])) {
|
|
2473
|
+
if (this.isParameterNode(tNode.typeArguments[1].types[0].exprName)) {
|
|
2474
|
+
return getBaseElemTypeAndLengthsIncludeCTC(tNode.typeArguments[0], arrLens.concat(tNode.typeArguments[1].types[0].exprName.getText()));
|
|
2475
|
+
}
|
|
2476
|
+
else {
|
|
2477
|
+
throw new types_1.TranspileError(`Untransformable type \`Array\`, please use type \`FixedArray\` instead`, this.getRange(node));
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
else {
|
|
2481
|
+
const m = /(\d)+/.exec(FixedArrayLenString);
|
|
2482
|
+
if (m) {
|
|
2483
|
+
if (tNode.typeArguments && tNode.typeArguments[0]) {
|
|
2484
|
+
return getBaseElemTypeAndLengthsIncludeCTC(tNode.typeArguments[0], arrLens.concat(parseInt(m[0])));
|
|
2485
|
+
}
|
|
2486
|
+
else {
|
|
2487
|
+
const fst = this._checker.getTypeArguments(typeRef.types[0]);
|
|
2488
|
+
return getBaseElemTypeAndLengths(fst[0], arrLens.concat(parseInt(m[0])));
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
else {
|
|
2492
|
+
throw new types_1.TranspileError(`Untransformable type \`Array\`, please use type \`FixedArray\` instead`, this.getRange(node));
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
else {
|
|
2497
|
+
const m = /(\d)+/.exec(FixedArrayLenString);
|
|
2498
|
+
if (m) {
|
|
2499
|
+
const fst = this._checker.getTypeArguments(typeRef.types[0]);
|
|
2500
|
+
return getBaseElemTypeAndLengths(fst[0], arrLens.concat(parseInt(m[0])));
|
|
2501
|
+
}
|
|
2502
|
+
else {
|
|
2503
|
+
throw new types_1.TranspileError(`Untransformable type \`Array\`, please use type \`FixedArray\` instead`, this.getRange(node));
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
else {
|
|
2508
|
+
return { typeRef: typeRef, arrLens };
|
|
2509
|
+
}
|
|
2510
|
+
};
|
|
2511
|
+
const { typeRef: baseElemType, arrLens } = getBaseElemTypeAndLengthsIncludeCTC(node, []);
|
|
2512
|
+
this.transformType(baseElemType, node, toSection).append(`${arrLens.map((i) => '[' + i + ']').join('')}`, coordinates);
|
|
2513
|
+
}
|
|
2514
|
+
else if (typeString === 'Array' || typeString === '[]') {
|
|
2515
|
+
const isOldFixedArray = (typeRef) => {
|
|
2516
|
+
if (typeRef.typeArguments.length > 0) {
|
|
2517
|
+
const typeString = this.type2ResolvedName(typeRef.typeArguments[0]);
|
|
2518
|
+
return typeString.includes('Flavor') && typeString.includes('FixedArray');
|
|
2519
|
+
}
|
|
2520
|
+
else {
|
|
2521
|
+
return false;
|
|
2522
|
+
}
|
|
2523
|
+
};
|
|
2524
|
+
if (isOldFixedArray(type)) {
|
|
2525
|
+
const getBaseElemTypeAndLengths = (typeRef, arrLens) => {
|
|
2526
|
+
if (typeRef.typeArguments.length > 0) {
|
|
2527
|
+
const innerTypeRef = typeRef.typeArguments[0];
|
|
2528
|
+
if (innerTypeRef.aliasTypeArguments.length > 1) {
|
|
2529
|
+
const FixedArrayString = this.type2ResolvedName(innerTypeRef.aliasTypeArguments[1]);
|
|
2530
|
+
const m = /(\d)+/.exec(FixedArrayString);
|
|
2531
|
+
if (m) {
|
|
2532
|
+
return getBaseElemTypeAndLengths(innerTypeRef.aliasTypeArguments[0], arrLens.concat(parseInt(m[0])));
|
|
2533
|
+
}
|
|
2534
|
+
throw new types_1.TranspileError(`Untransformable type \`Array\`, please use type \`FixedArray\` instead`, this.getRange(node));
|
|
2535
|
+
}
|
|
2536
|
+
else {
|
|
2537
|
+
return getBaseElemTypeAndLengths(innerTypeRef, arrLens.concat(typeRef.typeArguments.length));
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
else {
|
|
2541
|
+
return { typeRef, arrLens };
|
|
2542
|
+
}
|
|
2543
|
+
};
|
|
2544
|
+
const { typeRef: baseElemType, arrLens } = getBaseElemTypeAndLengths(type, []);
|
|
2545
|
+
this.transformType(baseElemType, node, toSection).append(`${arrLens.map((i) => '[' + i + ']').join('')}`, coordinates);
|
|
2546
|
+
return toSection;
|
|
2547
|
+
}
|
|
2548
|
+
else {
|
|
2549
|
+
throw new types_1.TranspileError(`Untransformable type \`Array\`, please use type \`FixedArray\` instead`, this.getRange(node));
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
else if (typeString.startsWith('[') && typeString.endsWith(']')) {
|
|
2553
|
+
// built-in type `FixedArray` goes here.
|
|
2554
|
+
const getBaseElemTypeAndLengths = (typeRef, arrLens) => {
|
|
2555
|
+
if (typeRef.typeArguments.length > 0) {
|
|
2556
|
+
const innerTypeRef = typeRef.typeArguments[0];
|
|
2557
|
+
return getBaseElemTypeAndLengths(innerTypeRef, arrLens.concat(typeRef.typeArguments.length));
|
|
2558
|
+
}
|
|
2559
|
+
else {
|
|
2560
|
+
return { typeRef, arrLens };
|
|
2561
|
+
}
|
|
2562
|
+
};
|
|
2563
|
+
const { typeRef: baseElemType, arrLens } = getBaseElemTypeAndLengths(type, []);
|
|
2564
|
+
this.transformType(baseElemType, node, toSection).append(`${arrLens.map((i) => '[' + i + ']').join('')}`, coordinates);
|
|
2565
|
+
}
|
|
2566
|
+
else {
|
|
2567
|
+
const t = (0, utils_1.toBuiltinsTypes)(typeString);
|
|
2568
|
+
if (t) {
|
|
2569
|
+
toSection.append(t, coordinates);
|
|
2570
|
+
}
|
|
2571
|
+
else {
|
|
2572
|
+
// all user defined or std types go here.
|
|
2573
|
+
toSection.append(typeString, coordinates);
|
|
2574
|
+
if (type.symbol) {
|
|
2575
|
+
this.saveSymbol(typeString, type.symbol);
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
break;
|
|
2580
|
+
}
|
|
2581
|
+
case typescript_1.default.TypeFlags.Any: {
|
|
2582
|
+
toSection.append('auto', coordinates);
|
|
2583
|
+
break;
|
|
2584
|
+
}
|
|
2585
|
+
case typescript_1.default.TypeFlags.String: {
|
|
2586
|
+
throw new types_1.TranspileError(`Untransformable type \`${typeStrCtx}\` here, please use type \`ByteString\` instead`, this.getRange(node));
|
|
2587
|
+
}
|
|
2588
|
+
default: {
|
|
2589
|
+
if (typescript_1.default.isTypeReferenceNode(node)) {
|
|
2590
|
+
// for boolean wrapper
|
|
2591
|
+
if (this.type2ResolvedName(type) === 'Bool') {
|
|
2592
|
+
return toSection.append('bool', coordinates);
|
|
2593
|
+
}
|
|
2594
|
+
throw new types_1.TranspileError(`Untransformable type : '${typeStrCtx}'`, this.getRange(node));
|
|
2595
|
+
}
|
|
2596
|
+
else {
|
|
2597
|
+
throw new types_1.TranspileError(`Untransformable type : '${typeStrCtx}', missing explicitly declared type`, this.getRange(node));
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
return toSection;
|
|
2602
|
+
}
|
|
2603
|
+
saveSymbol(symbolName, symbol) {
|
|
2604
|
+
// skip importing or local declaration for these keyword symbols.
|
|
2605
|
+
if (['SmartContract', 'OpCode'].includes(symbolName)) {
|
|
2606
|
+
return;
|
|
2607
|
+
}
|
|
2608
|
+
const symbolSourceFile = this.findDeclarationFile(symbol);
|
|
2609
|
+
if (symbolSourceFile === this._srcFile) {
|
|
2610
|
+
this._localTypeSymbols.set(symbolName, symbol);
|
|
2611
|
+
}
|
|
2612
|
+
else {
|
|
2613
|
+
this._importedTypeSymbols.set(symbolName, symbol);
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
transformModifiers(node, toSection) {
|
|
2617
|
+
const modifiers = typescript_1.default.getModifiers(node);
|
|
2618
|
+
if (modifiers) {
|
|
2619
|
+
modifiers.forEach((modifier) => {
|
|
2620
|
+
switch (modifier.kind) {
|
|
2621
|
+
case typescript_1.default.SyntaxKind.PublicKeyword: {
|
|
2622
|
+
toSection.append('public ', this.getCoordinates(modifier.getStart()));
|
|
2623
|
+
break;
|
|
2624
|
+
}
|
|
2625
|
+
case typescript_1.default.SyntaxKind.PrivateKeyword: {
|
|
2626
|
+
toSection.append('private ', this.getCoordinates(modifier.getStart()));
|
|
2627
|
+
break;
|
|
2628
|
+
}
|
|
2629
|
+
case typescript_1.default.SyntaxKind.ProtectedKeyword: {
|
|
2630
|
+
// transform to nothing in scrypt
|
|
2631
|
+
break;
|
|
2632
|
+
}
|
|
2633
|
+
case typescript_1.default.SyntaxKind.StaticKeyword: {
|
|
2634
|
+
toSection.append('static ', this.getCoordinates(modifier.getStart()));
|
|
2635
|
+
break;
|
|
2636
|
+
}
|
|
2637
|
+
case typescript_1.default.SyntaxKind.ReadonlyKeyword: {
|
|
2638
|
+
toSection.append('const ', this.getCoordinates(modifier.getStart()));
|
|
2639
|
+
break;
|
|
2640
|
+
}
|
|
2641
|
+
default: {
|
|
2642
|
+
throw new types_1.TranspileError(`Untransformable modifier kind ${modifier.kind}: '${modifier.getText()}'`, this.getRange(modifier));
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
});
|
|
2646
|
+
}
|
|
2647
|
+
return toSection;
|
|
2648
|
+
}
|
|
2649
|
+
static findDecorator(node, decorator) {
|
|
2650
|
+
if (typescript_1.default.canHaveDecorators(node)) {
|
|
2651
|
+
return (typescript_1.default.getDecorators(node) || []).find((dec) => {
|
|
2652
|
+
return dec.expression.getText().match(new RegExp(`^${decorator}\\((.*)?\\)$`));
|
|
2653
|
+
});
|
|
2654
|
+
}
|
|
2655
|
+
return undefined;
|
|
2656
|
+
}
|
|
2657
|
+
static isProperty(node) {
|
|
2658
|
+
if (typescript_1.default.isPropertyDeclaration(node)) {
|
|
2659
|
+
const decorator = Transpiler.findDecorator(node, DecoratorName.Prop);
|
|
2660
|
+
if (decorator) {
|
|
2661
|
+
return true;
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
return false;
|
|
2665
|
+
}
|
|
2666
|
+
static isMethod(node) {
|
|
2667
|
+
if (typescript_1.default.isMethodDeclaration(node)) {
|
|
2668
|
+
const decorator = Transpiler.findDecorator(node, DecoratorName.Method);
|
|
2669
|
+
if (decorator) {
|
|
2670
|
+
return true;
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
return false;
|
|
2674
|
+
}
|
|
2675
|
+
isNonProp(node) {
|
|
2676
|
+
if (typescript_1.default.isPropertyDeclaration(node)) {
|
|
2677
|
+
const decorator = Transpiler.findDecorator(node, DecoratorName.Prop);
|
|
2678
|
+
if (!decorator) {
|
|
2679
|
+
return true;
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
return false;
|
|
2683
|
+
}
|
|
2684
|
+
isStaticReadOnlyNonProp(node) {
|
|
2685
|
+
if (typescript_1.default.isPropertyDeclaration(node)) {
|
|
2686
|
+
const decorator = Transpiler.findDecorator(node, DecoratorName.Prop);
|
|
2687
|
+
return (!decorator &&
|
|
2688
|
+
(0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.StaticKeyword) &&
|
|
2689
|
+
(0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.ReadonlyKeyword));
|
|
2690
|
+
}
|
|
2691
|
+
return false;
|
|
2692
|
+
}
|
|
2693
|
+
static isStateProperty(node) {
|
|
2694
|
+
if (typescript_1.default.isPropertyDeclaration(node)) {
|
|
2695
|
+
const decorator = Transpiler.findDecorator(node, DecoratorName.Prop);
|
|
2696
|
+
if (decorator) {
|
|
2697
|
+
return /^prop\((true)+\)$/.test(decorator.expression.getText());
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
return false;
|
|
2701
|
+
}
|
|
2702
|
+
static getClassDeclaration(node) {
|
|
2703
|
+
if (!node.parent) {
|
|
2704
|
+
return undefined;
|
|
2705
|
+
}
|
|
2706
|
+
if (typescript_1.default.isClassDeclaration(node.parent)) {
|
|
2707
|
+
return node.parent;
|
|
2708
|
+
}
|
|
2709
|
+
return Transpiler.getClassDeclaration(node.parent);
|
|
2710
|
+
}
|
|
2711
|
+
static getMethodDeclaration(node) {
|
|
2712
|
+
if (!node.parent) {
|
|
2713
|
+
return undefined;
|
|
2714
|
+
}
|
|
2715
|
+
if (typescript_1.default.isMethodDeclaration(node.parent)) {
|
|
2716
|
+
return node.parent;
|
|
2717
|
+
}
|
|
2718
|
+
return Transpiler.getMethodDeclaration(node.parent);
|
|
2719
|
+
}
|
|
2720
|
+
static getIfStatement(node) {
|
|
2721
|
+
if (!node.parent) {
|
|
2722
|
+
return undefined;
|
|
2723
|
+
}
|
|
2724
|
+
if (typescript_1.default.isIfStatement(node.parent)) {
|
|
2725
|
+
return node.parent;
|
|
2726
|
+
}
|
|
2727
|
+
return Transpiler.getIfStatement(node.parent);
|
|
2728
|
+
}
|
|
2729
|
+
findMethodDeclaration(name) {
|
|
2730
|
+
const m = this._currentContract.members.find((m) => {
|
|
2731
|
+
return Transpiler.isMethod(m) && m.name.getText() === name;
|
|
2732
|
+
});
|
|
2733
|
+
if (m) {
|
|
2734
|
+
return m;
|
|
2735
|
+
}
|
|
2736
|
+
if (this.currentbaseContract) {
|
|
2737
|
+
return this.currentbaseContract.members.find((m) => {
|
|
2738
|
+
return Transpiler.isMethod(m) && m.name.getText() === name;
|
|
2739
|
+
});
|
|
2740
|
+
}
|
|
2741
|
+
return undefined;
|
|
2742
|
+
}
|
|
2743
|
+
isNonPropReferences(node, name) {
|
|
2744
|
+
return node.members
|
|
2745
|
+
.filter((member) => this.isNonProp(member))
|
|
2746
|
+
.map((m) => {
|
|
2747
|
+
const p = m;
|
|
2748
|
+
return p.name.getText();
|
|
2749
|
+
})
|
|
2750
|
+
.includes(name);
|
|
2751
|
+
}
|
|
2752
|
+
allPropertyDeclaration(node) {
|
|
2753
|
+
if (typescript_1.default.isClassDeclaration(node)) {
|
|
2754
|
+
return node.members.filter((member) => Transpiler.isProperty(member));
|
|
2755
|
+
}
|
|
2756
|
+
return [];
|
|
2757
|
+
}
|
|
2758
|
+
isCtcBinaryExpression(node) {
|
|
2759
|
+
const operators = [
|
|
2760
|
+
typescript_1.default.SyntaxKind.PlusToken,
|
|
2761
|
+
typescript_1.default.SyntaxKind.MinusToken,
|
|
2762
|
+
typescript_1.default.SyntaxKind.AsteriskToken,
|
|
2763
|
+
];
|
|
2764
|
+
return (operators.indexOf(node.operatorToken.kind) !== -1 &&
|
|
2765
|
+
this.isCtcExpression(node.left) &&
|
|
2766
|
+
this.isCtcExpression(node.right));
|
|
2767
|
+
}
|
|
2768
|
+
isCtcPrefixUnaryExpression(node) {
|
|
2769
|
+
const operators = [typescript_1.default.SyntaxKind.PlusToken, typescript_1.default.SyntaxKind.MinusToken];
|
|
2770
|
+
return operators.indexOf(node.operator) !== -1 && this.isCtcExpression(node.operand);
|
|
2771
|
+
}
|
|
2772
|
+
isCtcParenthesizedExpression(node) {
|
|
2773
|
+
return this.isCtcExpression(node.expression);
|
|
2774
|
+
}
|
|
2775
|
+
isCtcExpression(node) {
|
|
2776
|
+
if (!node || !typescript_1.default.isExpression(node)) {
|
|
2777
|
+
return false;
|
|
2778
|
+
}
|
|
2779
|
+
if ((0, utils_1.isNumberLiteralExpr)(node)) {
|
|
2780
|
+
return true;
|
|
2781
|
+
}
|
|
2782
|
+
if (typescript_1.default.isIdentifier(node) || typescript_1.default.isPropertyAccessExpression(node)) {
|
|
2783
|
+
return this.isCtcIdentifierOrProperty(node);
|
|
2784
|
+
}
|
|
2785
|
+
if (typescript_1.default.isBinaryExpression(node)) {
|
|
2786
|
+
return this.isCtcBinaryExpression(node);
|
|
2787
|
+
}
|
|
2788
|
+
if (typescript_1.default.isPrefixUnaryExpression(node)) {
|
|
2789
|
+
return this.isCtcPrefixUnaryExpression(node);
|
|
2790
|
+
}
|
|
2791
|
+
if (typescript_1.default.isParenthesizedExpression(node)) {
|
|
2792
|
+
return this.isCtcParenthesizedExpression(node);
|
|
2793
|
+
}
|
|
2794
|
+
return false;
|
|
2795
|
+
}
|
|
2796
|
+
isCtcDeclaration(node) {
|
|
2797
|
+
if (typescript_1.default.isPropertyDeclaration(node) && node['initializer']) {
|
|
2798
|
+
if ((0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.StaticKeyword) &&
|
|
2799
|
+
(0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.ReadonlyKeyword)) {
|
|
2800
|
+
return this.isCtcExpression(node['initializer']);
|
|
2801
|
+
}
|
|
2802
|
+
else if ((0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.ConstKeyword)) {
|
|
2803
|
+
return this.isCtcExpression(node['initializer']);
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
else if (typescript_1.default.isVariableDeclaration(node) &&
|
|
2807
|
+
node['initializer'] &&
|
|
2808
|
+
typescript_1.default.getCombinedNodeFlags(node) === typescript_1.default.NodeFlags.Const) {
|
|
2809
|
+
return this.isCtcExpression(node['initializer']);
|
|
2810
|
+
}
|
|
2811
|
+
else if (typescript_1.default.isParameter(node) && node.type) {
|
|
2812
|
+
return node.type?.kind === typescript_1.default.SyntaxKind.NumberKeyword;
|
|
2813
|
+
}
|
|
2814
|
+
return false;
|
|
2815
|
+
}
|
|
2816
|
+
isCtcIdentifierOrProperty(node) {
|
|
2817
|
+
const symbol = this._checker.getSymbolAtLocation(node);
|
|
2818
|
+
if (symbol) {
|
|
2819
|
+
if (symbol.valueDeclaration) {
|
|
2820
|
+
// local ctc
|
|
2821
|
+
return this.isCtcDeclaration(symbol.valueDeclaration);
|
|
2822
|
+
}
|
|
2823
|
+
// imported ctc
|
|
2824
|
+
return this.getImportedCtcValue(symbol) !== undefined;
|
|
2825
|
+
}
|
|
2826
|
+
return false;
|
|
2827
|
+
}
|
|
2828
|
+
isParameterNode(node) {
|
|
2829
|
+
const symbol = this._checker.getSymbolAtLocation(node);
|
|
2830
|
+
if (!symbol || !symbol.valueDeclaration) {
|
|
2831
|
+
return false;
|
|
2832
|
+
}
|
|
2833
|
+
return typescript_1.default.isParameter(symbol.valueDeclaration);
|
|
2834
|
+
}
|
|
2835
|
+
evalCtcBinaryExpression(node) {
|
|
2836
|
+
const left = this.evalCtcExpression(node.left);
|
|
2837
|
+
const right = this.evalCtcExpression(node.right);
|
|
2838
|
+
const operator = node.operatorToken.getText();
|
|
2839
|
+
return eval(`BigInt("${left}") ${operator} BigInt("${right}")`).toString().replace('n', '');
|
|
2840
|
+
}
|
|
2841
|
+
evalCtcPrefixUnaryExpression(node) {
|
|
2842
|
+
const operand = this.evalCtcExpression(node.operand);
|
|
2843
|
+
const operator = node.operator === typescript_1.default.SyntaxKind.MinusToken ? '-' : '';
|
|
2844
|
+
return eval(`${operator}BigInt("${operand}")`).toString().replace('n', '');
|
|
2845
|
+
}
|
|
2846
|
+
evalCtcParenthesizedExpression(node) {
|
|
2847
|
+
return this.evalCtcExpression(node.expression);
|
|
2848
|
+
}
|
|
2849
|
+
evalCtcExpression(node) {
|
|
2850
|
+
if ((0, utils_1.isNumberLiteralExpr)(node)) {
|
|
2851
|
+
return eval(node.getText()).toString().replace('n', '');
|
|
2852
|
+
}
|
|
2853
|
+
if (typescript_1.default.isIdentifier(node) || typescript_1.default.isPropertyAccessExpression(node)) {
|
|
2854
|
+
const symbol = this._checker.getSymbolAtLocation(node);
|
|
2855
|
+
if (symbol.valueDeclaration) {
|
|
2856
|
+
return this.evalCtcExpression(symbol.valueDeclaration['initializer']);
|
|
2857
|
+
}
|
|
2858
|
+
const ctcValue = this.getImportedCtcValue(symbol);
|
|
2859
|
+
if (ctcValue) {
|
|
2860
|
+
return ctcValue;
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
if (typescript_1.default.isBinaryExpression(node)) {
|
|
2864
|
+
return this.evalCtcBinaryExpression(node);
|
|
2865
|
+
}
|
|
2866
|
+
if (typescript_1.default.isPrefixUnaryExpression(node)) {
|
|
2867
|
+
return this.evalCtcPrefixUnaryExpression(node);
|
|
2868
|
+
}
|
|
2869
|
+
if (typescript_1.default.isParenthesizedExpression(node)) {
|
|
2870
|
+
return this.evalCtcParenthesizedExpression(node);
|
|
2871
|
+
}
|
|
2872
|
+
throw new types_1.TranspileError(`Cannot eval a non-CTC expression: '${node.getText()}'`, this.getRange(node));
|
|
2873
|
+
}
|
|
2874
|
+
getImportedCtcValue(symbol) {
|
|
2875
|
+
if (symbol.declarations) {
|
|
2876
|
+
const symbolDecl = (symbol.declarations ?? [])[0];
|
|
2877
|
+
if (symbolDecl && typescript_1.default.isImportSpecifier(symbolDecl)) {
|
|
2878
|
+
const importDecl = symbolDecl.parent.parent.parent;
|
|
2879
|
+
if (typescript_1.default.isImportDeclaration(importDecl)) {
|
|
2880
|
+
const moduleSpecifier = symbolDecl.parent.parent.parent.moduleSpecifier
|
|
2881
|
+
.getText()
|
|
2882
|
+
.replace(/['"]/g, '');
|
|
2883
|
+
const resolvedMoudle = typescript_1.default.resolveModuleName(moduleSpecifier, this._srcFile.fileName, this._compilerOptions, this._host);
|
|
2884
|
+
const ctcExportFile = resolvedMoudle.resolvedModule?.resolvedFileName;
|
|
2885
|
+
if (ctcExportFile) {
|
|
2886
|
+
const nodeType = this._checker.getTypeAtLocation(symbolDecl);
|
|
2887
|
+
if (nodeType.isNumberLiteral()) {
|
|
2888
|
+
return nodeType.value.toString().replace('n', '');
|
|
2889
|
+
}
|
|
2890
|
+
// for bigint literal
|
|
2891
|
+
if (nodeType.isLiteral() && nodeType.value.base10Value) {
|
|
2892
|
+
const negative = nodeType.value.negative ? '-' : '';
|
|
2893
|
+
return (negative +
|
|
2894
|
+
nodeType.value.base10Value.toString().replace('n', ''));
|
|
2895
|
+
}
|
|
2896
|
+
return Transpiler.topCtcs.get(`${(0, utils_1.sha1)(ctcExportFile)}:${symbolDecl.propertyName?.getText() || symbolDecl.name.getText()}`);
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
return undefined;
|
|
2902
|
+
}
|
|
2903
|
+
transformCtcExpr(expr, toSection) {
|
|
2904
|
+
const coordinates = this.getCoordinates(expr.getStart());
|
|
2905
|
+
const ctcValue = this.evalCtcExpression(expr).toString().replace('n', '');
|
|
2906
|
+
toSection.append(ctcValue, coordinates);
|
|
2907
|
+
return toSection;
|
|
2908
|
+
}
|
|
2909
|
+
transformCtcNode(node, toSection) {
|
|
2910
|
+
if (this.isParameterNode(node)) {
|
|
2911
|
+
// ctc parameter
|
|
2912
|
+
const coordinates = this.getCoordinates(node.getStart());
|
|
2913
|
+
toSection.append(node.getText(), coordinates);
|
|
2914
|
+
return toSection;
|
|
2915
|
+
}
|
|
2916
|
+
const symbol = this._checker.getSymbolAtLocation(node);
|
|
2917
|
+
if (symbol && symbol.valueDeclaration) {
|
|
2918
|
+
// local ctc
|
|
2919
|
+
return this.transformCtcExpr(symbol.valueDeclaration['initializer'], toSection);
|
|
2920
|
+
}
|
|
2921
|
+
if (symbol) {
|
|
2922
|
+
const ctcValue = this.getImportedCtcValue(symbol);
|
|
2923
|
+
if (ctcValue) {
|
|
2924
|
+
// imported ctc
|
|
2925
|
+
return toSection.append(ctcValue, this.getCoordinates(node.getStart()));
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
throw new types_1.TranspileError(`Cannot find ctc value: '${node.getText()}'`, this.getRange(node));
|
|
2929
|
+
}
|
|
2930
|
+
queryPropertyInitializedInStmt(node) {
|
|
2931
|
+
if (typescript_1.default.isExpressionStatement(node) && typescript_1.default.isBinaryExpression(node.expression)) {
|
|
2932
|
+
if (node.expression.operatorToken.getText() === '=' &&
|
|
2933
|
+
typescript_1.default.isPropertyAccessExpression(node.expression.left) &&
|
|
2934
|
+
node.expression.left.expression.getText() === 'this') {
|
|
2935
|
+
const symbol = this._checker.getSymbolAtLocation(node.expression.left.name);
|
|
2936
|
+
if (symbol &&
|
|
2937
|
+
symbol.valueDeclaration &&
|
|
2938
|
+
typescript_1.default.isPropertyDeclaration(symbol.valueDeclaration)) {
|
|
2939
|
+
return symbol.valueDeclaration;
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
return undefined;
|
|
2944
|
+
}
|
|
2945
|
+
hasProperties(node) {
|
|
2946
|
+
return (node.members.findIndex((m) => typescript_1.default.isPropertyDeclaration(m) &&
|
|
2947
|
+
typeof Transpiler.findDecorator(m, DecoratorName.Prop) !== 'undefined') > -1);
|
|
2948
|
+
}
|
|
2949
|
+
static isStaticProperty(node) {
|
|
2950
|
+
if (Transpiler.isProperty(node)) {
|
|
2951
|
+
return (0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.StaticKeyword);
|
|
2952
|
+
}
|
|
2953
|
+
return false;
|
|
2954
|
+
}
|
|
2955
|
+
static isReadonlyProperty(node) {
|
|
2956
|
+
if (Transpiler.isProperty(node)) {
|
|
2957
|
+
return (0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.ReadonlyKeyword);
|
|
2958
|
+
}
|
|
2959
|
+
return false;
|
|
2960
|
+
}
|
|
2961
|
+
onlyHasStaticProperties(node) {
|
|
2962
|
+
return node.members
|
|
2963
|
+
.filter((m) => typescript_1.default.isPropertyDeclaration(m))
|
|
2964
|
+
.every((m) => {
|
|
2965
|
+
return (Transpiler.findDecorator(m, DecoratorName.Prop) === undefined ||
|
|
2966
|
+
Transpiler.isStaticProperty(m));
|
|
2967
|
+
});
|
|
2968
|
+
}
|
|
2969
|
+
hasConstructor(node) {
|
|
2970
|
+
return node.members.findIndex((m) => typescript_1.default.isConstructorDeclaration(m)) > -1;
|
|
2971
|
+
}
|
|
2972
|
+
getConstructor(node) {
|
|
2973
|
+
return node.members.find((m) => typescript_1.default.isConstructorDeclaration(m));
|
|
2974
|
+
}
|
|
2975
|
+
checkConstructor(node) {
|
|
2976
|
+
if (this.hasProperties(node)) {
|
|
2977
|
+
if (this.onlyHasStaticProperties(node)) {
|
|
2978
|
+
return;
|
|
2979
|
+
}
|
|
2980
|
+
if (!this.hasConstructor(node)) {
|
|
2981
|
+
throw new types_1.TranspileError(`Untransformable contract: a smart contract must have an explicit constructor if it has at least one @prop.`, this.getRange(node));
|
|
2982
|
+
}
|
|
2983
|
+
if (this.isInherited(node)) {
|
|
2984
|
+
this.checkSetConstructorStmt(this.getConstructor(node));
|
|
2985
|
+
}
|
|
2986
|
+
else {
|
|
2987
|
+
this.checkSuperStmt(this.getConstructor(node));
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
getImportedPath(fulSymbolPath) {
|
|
2992
|
+
const filePath = path.relative(path.dirname(this._scryptFullPath), fulSymbolPath);
|
|
2993
|
+
if (!filePath.startsWith('.')) {
|
|
2994
|
+
return `./${filePath}`;
|
|
2995
|
+
}
|
|
2996
|
+
return filePath;
|
|
2997
|
+
}
|
|
2998
|
+
resolvePackageDir(symbolFileFullPath) {
|
|
2999
|
+
let searchDir = path.dirname(symbolFileFullPath);
|
|
3000
|
+
do {
|
|
3001
|
+
const locatePath = path.join(searchDir, 'package.json');
|
|
3002
|
+
if ((0, fs_1.existsSync)(locatePath)) {
|
|
3003
|
+
return path.dirname(locatePath);
|
|
3004
|
+
}
|
|
3005
|
+
if (path.join(searchDir, '..') === searchDir) {
|
|
3006
|
+
return undefined;
|
|
3007
|
+
}
|
|
3008
|
+
searchDir = path.join(searchDir, '..');
|
|
3009
|
+
// eslint-disable-next-line no-constant-condition
|
|
3010
|
+
} while (true);
|
|
3011
|
+
}
|
|
3012
|
+
getThirdPartyPackageName(packageDir) {
|
|
3013
|
+
const packageJson = JSON.parse((0, fs_1.readFileSync)(path.join(packageDir, 'package.json'), 'utf8'));
|
|
3014
|
+
return packageJson.name;
|
|
3015
|
+
}
|
|
3016
|
+
saveThirdPartySymbol(symbol, symbolName, fullSymbolPath) {
|
|
3017
|
+
const symbolFile = this.findDeclarationFile(symbol);
|
|
3018
|
+
if (!symbolFile)
|
|
3019
|
+
return;
|
|
3020
|
+
const symbolFileFullPath = symbolFile.fileName;
|
|
3021
|
+
const symbolPath = this.getRelativePathFromArtifacts(fullSymbolPath);
|
|
3022
|
+
let symbolDec = symbol.declarations[0];
|
|
3023
|
+
symbolDec = symbolDec['name'] || symbolDec;
|
|
3024
|
+
this._indexer.addSymbols([
|
|
3025
|
+
{
|
|
3026
|
+
name: symbolName,
|
|
3027
|
+
srcRange: {
|
|
3028
|
+
fileName: symbolFileFullPath,
|
|
3029
|
+
start: symbolFile.getLineAndCharacterOfPosition(symbolDec.getStart()),
|
|
3030
|
+
end: symbolFile.getLineAndCharacterOfPosition(symbolDec.getEnd()),
|
|
3031
|
+
},
|
|
3032
|
+
},
|
|
3033
|
+
], symbolPath);
|
|
3034
|
+
}
|
|
3035
|
+
saveLocalSymbol(symbol, symbolName, fullSymbolPath) {
|
|
3036
|
+
const symbolFile = this.findDeclarationFile(symbol);
|
|
3037
|
+
if (!symbolFile)
|
|
3038
|
+
return;
|
|
3039
|
+
const symbolFileFullPath = symbolFile.fileName;
|
|
3040
|
+
const symbolPath = this.getRelativePathFromTsRoot(fullSymbolPath);
|
|
3041
|
+
let symbolDec = symbol.declarations[0];
|
|
3042
|
+
symbolDec = symbolDec['name'] || symbolDec;
|
|
3043
|
+
this._indexer.addSymbols([
|
|
3044
|
+
{
|
|
3045
|
+
name: symbolName,
|
|
3046
|
+
srcRange: {
|
|
3047
|
+
fileName: symbolFileFullPath,
|
|
3048
|
+
start: symbolFile.getLineAndCharacterOfPosition(symbolDec.getStart()),
|
|
3049
|
+
end: symbolFile.getLineAndCharacterOfPosition(symbolDec.getEnd()),
|
|
3050
|
+
},
|
|
3051
|
+
},
|
|
3052
|
+
], symbolPath);
|
|
3053
|
+
}
|
|
3054
|
+
transformImports(allmissSym, relinker) {
|
|
3055
|
+
const importsSection = new EmittedSection();
|
|
3056
|
+
const imports = new Set();
|
|
3057
|
+
const missSymbols = new Map();
|
|
3058
|
+
const fromPkgName = relinker_1.Relinker.getFilePackageInfo(this._scryptFullPath).packageName;
|
|
3059
|
+
Array.from(this._importedTypeSymbols.entries()).forEach((entry) => {
|
|
3060
|
+
const symbolName = entry[0];
|
|
3061
|
+
const symbol = entry[1];
|
|
3062
|
+
const symbolFile = this.findDeclarationFile(symbol);
|
|
3063
|
+
if (!symbolFile)
|
|
3064
|
+
return;
|
|
3065
|
+
const symbolFileFullPath = symbolFile.fileName;
|
|
3066
|
+
if (this.isFromThirdParty(symbolFileFullPath)) {
|
|
3067
|
+
const thirdPartyBaseDir = this.resolvePackageDir(symbolFileFullPath);
|
|
3068
|
+
if (!thirdPartyBaseDir) {
|
|
3069
|
+
importsSection.errors.push(new types_1.TranspileError(`Invalid symbol '${symbolName}', missing \`package.json\``, {
|
|
3070
|
+
fileName: symbolFileFullPath,
|
|
3071
|
+
start: { line: -1, character: -1 },
|
|
3072
|
+
end: { line: -1, character: -1 },
|
|
3073
|
+
}));
|
|
3074
|
+
return;
|
|
3075
|
+
}
|
|
3076
|
+
const thirdPartyIndexFile = indexer_1.Indexer.queryIndexFile(path.dirname(symbolFileFullPath), thirdPartyBaseDir);
|
|
3077
|
+
if (!thirdPartyIndexFile) {
|
|
3078
|
+
if (symbol.flags !== typescript_1.default.SymbolFlags.TypeLiteral) {
|
|
3079
|
+
const message = symbolName === 'IContext'
|
|
3080
|
+
? '`IContext` is not allowed to be defined in the contract'
|
|
3081
|
+
: `Invalid symbol '${symbolName}', missing \`scrypt.index.json\` in the third party root directory ${thirdPartyBaseDir}`;
|
|
3082
|
+
importsSection.errors.push(new types_1.TranspileError(message, {
|
|
3083
|
+
fileName: symbolFileFullPath,
|
|
3084
|
+
start: { line: -1, character: -1 },
|
|
3085
|
+
end: { line: -1, character: -1 },
|
|
3086
|
+
}));
|
|
3087
|
+
return;
|
|
3088
|
+
}
|
|
3089
|
+
else {
|
|
3090
|
+
// case: builtin types from scrypt-ts-btc which does not have an index file
|
|
3091
|
+
// const symbols = missSymbols.get(symbolFile) || new Map<string, ts.Symbol>();
|
|
3092
|
+
// symbols.set(symbolName, symbol);
|
|
3093
|
+
// missSymbols.set(symbolFile, symbols);
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
else {
|
|
3097
|
+
const thirdPartyIndexer = new indexer_1.Indexer(thirdPartyIndexFile);
|
|
3098
|
+
const thirdPartyFullPath = thirdPartyIndexer.getFullPath(symbolName);
|
|
3099
|
+
if (!thirdPartyFullPath) {
|
|
3100
|
+
importsSection.errors.push(new types_1.TranspileError(symbolName === 'IContext'
|
|
3101
|
+
? '`IContext` is not allowed to be defined in the contract'
|
|
3102
|
+
: `Invalid symbol '${symbolName}', missing index info of symbol ${symbolName} in the third party indexer file '${thirdPartyIndexFile}'`, {
|
|
3103
|
+
fileName: symbolFileFullPath,
|
|
3104
|
+
start: { line: -1, character: -1 },
|
|
3105
|
+
end: { line: -1, character: -1 },
|
|
3106
|
+
}));
|
|
3107
|
+
return;
|
|
3108
|
+
}
|
|
3109
|
+
else {
|
|
3110
|
+
this.saveThirdPartySymbol(symbol, symbolName, thirdPartyFullPath);
|
|
3111
|
+
imports.add(thirdPartyIndexer.getPackageFilePath(this.getThirdPartyPackageName(thirdPartyBaseDir), symbolName));
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
else {
|
|
3116
|
+
// local imports
|
|
3117
|
+
let symbolPath = this._indexer.getFullPath(symbolName);
|
|
3118
|
+
if (symbolPath && (0, fs_1.existsSync)(symbolPath)) {
|
|
3119
|
+
imports.add(this.getImportedPath(symbolPath));
|
|
3120
|
+
}
|
|
3121
|
+
else {
|
|
3122
|
+
this.saveLocalSymbol(symbol, symbolName, symbolFileFullPath);
|
|
3123
|
+
symbolPath = this._indexer.getFullPath(symbolName);
|
|
3124
|
+
imports.add(this.getImportedPath(symbolPath));
|
|
3125
|
+
const symbols = missSymbols.get(symbolFile) || new Map();
|
|
3126
|
+
symbols.set(symbolName, symbol);
|
|
3127
|
+
missSymbols.set(symbolFile, symbols);
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
});
|
|
3131
|
+
// merge all miss import local symbols
|
|
3132
|
+
if (missSymbols.size > 0) {
|
|
3133
|
+
missSymbols.forEach((value, key) => {
|
|
3134
|
+
if (allmissSym.has(key)) {
|
|
3135
|
+
const map = allmissSym.get(key);
|
|
3136
|
+
value.forEach((v, k) => {
|
|
3137
|
+
map.set(k, v);
|
|
3138
|
+
});
|
|
3139
|
+
}
|
|
3140
|
+
else {
|
|
3141
|
+
allmissSym.set(key, value);
|
|
3142
|
+
}
|
|
3143
|
+
});
|
|
3144
|
+
// generate scrypt file for missing Imported local symbol
|
|
3145
|
+
allmissSym.forEach((symbols, symbolFile) => {
|
|
3146
|
+
const transpiler = new Transpiler(symbolFile, this._host, this._checker, this._tsRootDir, this._scryptOutDir, this._indexer, this._compilerOptions);
|
|
3147
|
+
transpiler.setLocalSymbols(symbols);
|
|
3148
|
+
transpiler.transform(allmissSym, relinker);
|
|
3149
|
+
// imports.add(transpiler._scryptRelativePath);
|
|
3150
|
+
});
|
|
3151
|
+
}
|
|
3152
|
+
// only import builtins for non-builtin packages
|
|
3153
|
+
if (fromPkgName !== BUILDIN_PACKAGE_NAME) {
|
|
3154
|
+
this.importAllBuiltins(imports);
|
|
3155
|
+
}
|
|
3156
|
+
imports.forEach((import_) => {
|
|
3157
|
+
importsSection
|
|
3158
|
+
.append('import ')
|
|
3159
|
+
.append(`"${import_.replaceAll('\\', '/')}"`)
|
|
3160
|
+
.append(';')
|
|
3161
|
+
.append('\n');
|
|
3162
|
+
});
|
|
3163
|
+
return importsSection;
|
|
3164
|
+
}
|
|
3165
|
+
loadBuiltinIndexer() {
|
|
3166
|
+
const scryptTsBtcDir = (0, utils_1.findPackageDir)(BUILDIN_PACKAGE_NAME, process.cwd());
|
|
3167
|
+
const builtinIndexerFile = indexer_1.Indexer.queryIndexFile(scryptTsBtcDir, path.join(scryptTsBtcDir, '..'));
|
|
3168
|
+
if (!builtinIndexerFile) {
|
|
3169
|
+
throw new Error(`Missing \`scrypt.index.json\` for the built-in types & libraries in the directory \`${scryptTsBtcDir}\``);
|
|
3170
|
+
}
|
|
3171
|
+
this._builtinIndexer = new indexer_1.Indexer(builtinIndexerFile);
|
|
3172
|
+
}
|
|
3173
|
+
importAllBuiltins(imports) {
|
|
3174
|
+
if (!this._currentContract) {
|
|
3175
|
+
// skip import builtins for non-contract file
|
|
3176
|
+
return;
|
|
3177
|
+
}
|
|
3178
|
+
const scryptTsBtcDir = (0, utils_1.findPackageDir)(BUILDIN_PACKAGE_NAME, process.cwd());
|
|
3179
|
+
const pkgName = this.getThirdPartyPackageName(scryptTsBtcDir);
|
|
3180
|
+
new Set(this._builtinIndexer.symbolInfos.keys()).forEach((symbolName) => {
|
|
3181
|
+
if (this._accessBuiltinsSymbols.has(symbolName)) {
|
|
3182
|
+
const builtinFullPath = this._builtinIndexer.getPackageFilePath(pkgName, symbolName);
|
|
3183
|
+
imports.add(builtinFullPath);
|
|
3184
|
+
}
|
|
3185
|
+
});
|
|
3186
|
+
}
|
|
3187
|
+
transformTypeLiteralAndInterfaces() {
|
|
3188
|
+
const structSecs = [];
|
|
3189
|
+
const enumSecs = [];
|
|
3190
|
+
this._localTypeSymbols.forEach((symbol, symbolTypeName) => {
|
|
3191
|
+
const symbolDec = symbol.declarations[0];
|
|
3192
|
+
if ([
|
|
3193
|
+
typescript_1.default.SyntaxKind.TypeLiteral,
|
|
3194
|
+
typescript_1.default.SyntaxKind.InterfaceDeclaration,
|
|
3195
|
+
typescript_1.default.SyntaxKind.TypeAliasDeclaration,
|
|
3196
|
+
].includes(symbolDec.kind)) {
|
|
3197
|
+
const stSec = new EmittedSection().appendWith(this, (stSec) => {
|
|
3198
|
+
return stSec
|
|
3199
|
+
.append('\nstruct ')
|
|
3200
|
+
.append(`${symbolTypeName} {`, this.getCoordinates(symbolDec.getStart()))
|
|
3201
|
+
.appendWith(this, (fieldsSec) => {
|
|
3202
|
+
let members;
|
|
3203
|
+
if (typescript_1.default.SyntaxKind.TypeAliasDeclaration === symbolDec.kind) {
|
|
3204
|
+
// TODO: resolve recursive type alias
|
|
3205
|
+
const symbol = this._checker.getTypeAtLocation(symbolDec.type).symbol;
|
|
3206
|
+
members = symbol.members;
|
|
3207
|
+
}
|
|
3208
|
+
else {
|
|
3209
|
+
members = symbol.members;
|
|
3210
|
+
}
|
|
3211
|
+
members.forEach((memSymbol) => {
|
|
3212
|
+
fieldsSec.append('\n').appendWith(this, (fieldSec) => {
|
|
3213
|
+
return this.transformPropertySignature(memSymbol.valueDeclaration, fieldSec);
|
|
3214
|
+
});
|
|
3215
|
+
});
|
|
3216
|
+
return fieldsSec;
|
|
3217
|
+
}, true)
|
|
3218
|
+
.append('\n}');
|
|
3219
|
+
});
|
|
3220
|
+
structSecs.push(stSec);
|
|
3221
|
+
}
|
|
3222
|
+
else if (typescript_1.default.isEnumDeclaration(symbolDec)) {
|
|
3223
|
+
const enumDeclaration = symbolDec;
|
|
3224
|
+
const enumSec = new EmittedSection().appendWith(this, (enumSec) => {
|
|
3225
|
+
return enumSec
|
|
3226
|
+
.append('\nlibrary ')
|
|
3227
|
+
.append(`${symbolTypeName} {`, this.getCoordinates(symbolDec.getStart()))
|
|
3228
|
+
.appendWith(this, (enumMembersSec) => {
|
|
3229
|
+
let prevEnumMemValue = -1;
|
|
3230
|
+
enumDeclaration.members.forEach((enumMember) => {
|
|
3231
|
+
enumMembersSec
|
|
3232
|
+
.append('\n')
|
|
3233
|
+
.append(`static const int ${enumMember.name.getText()} = `)
|
|
3234
|
+
.appendWith(this, (enumMemberSec) => {
|
|
3235
|
+
if (enumMember.initializer) {
|
|
3236
|
+
const type = this._checker.getTypeAtLocation(enumMember.initializer);
|
|
3237
|
+
if (type.flags !== typescript_1.default.TypeFlags.NumberLiteral) {
|
|
3238
|
+
throw new types_1.TranspileError(`Untransformable enum member: '${symbolTypeName}.${enumMember.name.getText()}', only allowed number literal in enum`, this.getRange(enumMember));
|
|
3239
|
+
}
|
|
3240
|
+
prevEnumMemValue = parseInt(enumMember.initializer.getText());
|
|
3241
|
+
return this.transformExpression(enumMember.initializer, enumMemberSec);
|
|
3242
|
+
}
|
|
3243
|
+
else {
|
|
3244
|
+
prevEnumMemValue++;
|
|
3245
|
+
enumMemberSec.append(`${prevEnumMemValue}`);
|
|
3246
|
+
}
|
|
3247
|
+
return enumMemberSec;
|
|
3248
|
+
})
|
|
3249
|
+
.append(';');
|
|
3250
|
+
});
|
|
3251
|
+
return enumMembersSec;
|
|
3252
|
+
}, true)
|
|
3253
|
+
.append('\n}');
|
|
3254
|
+
});
|
|
3255
|
+
enumSecs.push(enumSec);
|
|
3256
|
+
}
|
|
3257
|
+
});
|
|
3258
|
+
return EmittedSection.join(...structSecs.concat(enumSecs));
|
|
3259
|
+
}
|
|
3260
|
+
toScryptBinary(node, operator) {
|
|
3261
|
+
switch (operator) {
|
|
3262
|
+
case '==':
|
|
3263
|
+
case '!=':
|
|
3264
|
+
case '+':
|
|
3265
|
+
case '-':
|
|
3266
|
+
case '+=':
|
|
3267
|
+
case '-=':
|
|
3268
|
+
case '<':
|
|
3269
|
+
case '>':
|
|
3270
|
+
case '<=':
|
|
3271
|
+
case '>=':
|
|
3272
|
+
case '&&':
|
|
3273
|
+
case '||':
|
|
3274
|
+
case '*':
|
|
3275
|
+
case '/':
|
|
3276
|
+
case '%':
|
|
3277
|
+
case '=':
|
|
3278
|
+
return operator;
|
|
3279
|
+
case '===':
|
|
3280
|
+
return '==';
|
|
3281
|
+
case '!==':
|
|
3282
|
+
return '!=';
|
|
3283
|
+
default:
|
|
3284
|
+
throw new types_1.TranspileError(`Untransformable binary operator: '${operator}'`, this.getRange(node));
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
// SyntaxKind.PlusPlusToken | SyntaxKind.MinusMinusToken | SyntaxKind.PlusToken | SyntaxKind.MinusToken | SyntaxKind.TildeToken | SyntaxKind.ExclamationToken
|
|
3288
|
+
toScryptUnary(node, operator) {
|
|
3289
|
+
switch (operator) {
|
|
3290
|
+
case typescript_1.default.SyntaxKind.PlusPlusToken:
|
|
3291
|
+
return '++';
|
|
3292
|
+
case typescript_1.default.SyntaxKind.MinusMinusToken:
|
|
3293
|
+
return '--';
|
|
3294
|
+
case typescript_1.default.SyntaxKind.MinusToken:
|
|
3295
|
+
return '-';
|
|
3296
|
+
case typescript_1.default.SyntaxKind.ExclamationToken:
|
|
3297
|
+
return '!';
|
|
3298
|
+
default:
|
|
3299
|
+
throw new types_1.TranspileError(`Untransformable prefix unary operator kind ${typescript_1.default.SyntaxKind[operator]} in: ${node.getText()}`, this.getRange(node));
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
type2ResolvedName(type) {
|
|
3303
|
+
// for basic types
|
|
3304
|
+
if (!type.symbol) {
|
|
3305
|
+
const typeName = this._checker.typeToString(type, undefined, typescript_1.default.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope);
|
|
3306
|
+
return typeName;
|
|
3307
|
+
}
|
|
3308
|
+
let typeName = type.symbol.escapedName.toString();
|
|
3309
|
+
// get scrypt struct name which might be a type alias or interface
|
|
3310
|
+
if (typeName === '__type') {
|
|
3311
|
+
typeName = type.aliasSymbol?.escapedName.toString() || typeName;
|
|
3312
|
+
}
|
|
3313
|
+
return typeName === 'boolean' ? 'Bool' : typeName;
|
|
3314
|
+
}
|
|
3315
|
+
getResolvedTypeName(node) {
|
|
3316
|
+
const type = this._checker.getTypeAtLocation(node);
|
|
3317
|
+
return this.type2ResolvedName(type);
|
|
3318
|
+
}
|
|
3319
|
+
// check if a call expression is a method call to a certain class's instance.
|
|
3320
|
+
isCertainClassPropertyAccessExpr(expr, clazz) {
|
|
3321
|
+
const objExpr = expr.expression;
|
|
3322
|
+
return this.getResolvedTypeName(objExpr) === clazz;
|
|
3323
|
+
}
|
|
3324
|
+
transformStringLiteralExpression(node, toSection) {
|
|
3325
|
+
if (node.parent.kind === typescript_1.default.SyntaxKind.CallExpression) {
|
|
3326
|
+
const srcLoc = this.getCoordinates(node.getStart());
|
|
3327
|
+
const parent = node.parent;
|
|
3328
|
+
if ((0, utils_1.allowByteStringLiteral)(parent)) {
|
|
3329
|
+
if (parent.expression.getText() === 'toByteString') {
|
|
3330
|
+
let literal = parent.arguments[0].getText();
|
|
3331
|
+
literal = literal.substring(1, literal.length - 1);
|
|
3332
|
+
if (parent.arguments.length == 1) {
|
|
3333
|
+
// one argument --> hex literal
|
|
3334
|
+
if (!/^([0-9a-fA-F]{2})*$/.test(literal)) {
|
|
3335
|
+
throw new types_1.TranspileError(`\`${literal}\` is not a valid hex literal`, this.getRange(node));
|
|
3336
|
+
}
|
|
3337
|
+
toSection.append(`b'${literal}'`, srcLoc);
|
|
3338
|
+
}
|
|
3339
|
+
else {
|
|
3340
|
+
// two arguments
|
|
3341
|
+
const isUtf8 = parent.arguments[1].getText();
|
|
3342
|
+
if (isUtf8 === 'false') {
|
|
3343
|
+
// hex literal
|
|
3344
|
+
if (!/^([0-9a-fA-F]{2})*$/.test(literal)) {
|
|
3345
|
+
throw new types_1.TranspileError(`\`${literal}\` is not a valid hex literal`, this.getRange(node));
|
|
3346
|
+
}
|
|
3347
|
+
toSection.append(`b'${literal}'`, srcLoc);
|
|
3348
|
+
}
|
|
3349
|
+
else if (isUtf8 === 'true') {
|
|
3350
|
+
// utf8 literal
|
|
3351
|
+
// Auto escape double quotes.
|
|
3352
|
+
const escaped = literal.replace(/"/g, '\\"');
|
|
3353
|
+
toSection.append(`"${escaped}"`, srcLoc);
|
|
3354
|
+
}
|
|
3355
|
+
else {
|
|
3356
|
+
throw new types_1.TranspileError('Only boolean literal can be passed to the second parameter of `toByteString`', this.getRange(node));
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
else {
|
|
3361
|
+
try {
|
|
3362
|
+
(0, utils_1.checkByteStringLiteral)(node);
|
|
3363
|
+
}
|
|
3364
|
+
catch (error) {
|
|
3365
|
+
throw new types_1.TranspileError(error.message, this.getRange(node));
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
else {
|
|
3370
|
+
throw new types_1.TranspileError(`String literal ${node.getText()} is not allowed here, please use \`toByteString\` instead`, this.getRange(node));
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
else {
|
|
3374
|
+
throw new types_1.TranspileError(`String literal ${node.getText()} is not allowed here, please use \`toByteString\` instead`, this.getRange(node));
|
|
3375
|
+
}
|
|
3376
|
+
return toSection;
|
|
3377
|
+
}
|
|
3378
|
+
transformBigIntLiteralExpression(node, toSection) {
|
|
3379
|
+
let text = node.getText();
|
|
3380
|
+
text = text.replaceAll(/[_n]/g, '');
|
|
3381
|
+
return toSection.append(`${text}`, this.getCoordinates(node.getStart()));
|
|
3382
|
+
}
|
|
3383
|
+
transformArrayLiteralExpression(node, toSection) {
|
|
3384
|
+
const e = node;
|
|
3385
|
+
return toSection
|
|
3386
|
+
.append('[')
|
|
3387
|
+
.appendWith(this, (toSec) => {
|
|
3388
|
+
e.elements.forEach((arg, index) => {
|
|
3389
|
+
toSec
|
|
3390
|
+
.appendWith(this, (toSec) => this.transformExpression(arg, toSec))
|
|
3391
|
+
.append(index < e.elements.length - 1 ? ', ' : '');
|
|
3392
|
+
});
|
|
3393
|
+
return toSec;
|
|
3394
|
+
})
|
|
3395
|
+
.append(']');
|
|
3396
|
+
}
|
|
3397
|
+
transformObjectLiteralExpression(node, toSection) {
|
|
3398
|
+
const e = node;
|
|
3399
|
+
toSection.append('{', this.getCoordinates(e.getStart()));
|
|
3400
|
+
const type = this._checker.getContextualType(e);
|
|
3401
|
+
if (type) {
|
|
3402
|
+
const members = [];
|
|
3403
|
+
type.getProperties().forEach((property) => members.push(property.getName()));
|
|
3404
|
+
members.forEach((member, index) => {
|
|
3405
|
+
const property = e.properties.find((property) => property.name.getText() === member);
|
|
3406
|
+
if (property) {
|
|
3407
|
+
const _property = property;
|
|
3408
|
+
if (_property.initializer) {
|
|
3409
|
+
toSection
|
|
3410
|
+
.appendWith(this, (toSec) => this.transformExpression(_property.initializer, toSec))
|
|
3411
|
+
.append(index < members.length - 1 ? ', ' : '');
|
|
3412
|
+
}
|
|
3413
|
+
else {
|
|
3414
|
+
toSection
|
|
3415
|
+
.append(_property.name.getText())
|
|
3416
|
+
.append(index < members.length - 1 ? ', ' : '');
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
else {
|
|
3420
|
+
const structname = type.symbol === undefined
|
|
3421
|
+
? this._checker.typeToString(type, undefined, typescript_1.default.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope)
|
|
3422
|
+
: (type.aliasSymbol === undefined
|
|
3423
|
+
? type.symbol.escapedName
|
|
3424
|
+
: type.aliasSymbol.escapedName).toString();
|
|
3425
|
+
throw new types_1.TranspileError(`field not found for struct '${structname}': '${property.getText()}'`, this.getRange(e));
|
|
3426
|
+
}
|
|
3427
|
+
});
|
|
3428
|
+
}
|
|
3429
|
+
else {
|
|
3430
|
+
throw new types_1.TranspileError(`Untransformable expression: '${e.getText()}'`, this.getRange(e));
|
|
3431
|
+
}
|
|
3432
|
+
return toSection.append('}');
|
|
3433
|
+
}
|
|
3434
|
+
transformIdentifierExpression(node, toSection) {
|
|
3435
|
+
const nodeType = this._checker.getTypeAtLocation(node);
|
|
3436
|
+
if (nodeType.symbol &&
|
|
3437
|
+
[typescript_1.default.SymbolFlags.Class, typescript_1.default.SymbolFlags.ConstEnum, typescript_1.default.SymbolFlags.RegularEnum].includes(nodeType.symbol.getFlags())) {
|
|
3438
|
+
this.saveSymbol(nodeType.symbol.getName(), nodeType.symbol);
|
|
3439
|
+
// if (node.getText() === 'Utils') {
|
|
3440
|
+
// toSection.append('__scrypt_Utils', this.getCoordinates(node.getStart()));
|
|
3441
|
+
// return toSection;
|
|
3442
|
+
// } else if (node.getText() === 'SigHashUtils') {
|
|
3443
|
+
// toSection.append('ContextUtils', this.getCoordinates(node.getStart()));
|
|
3444
|
+
// return toSection;
|
|
3445
|
+
// }
|
|
3446
|
+
}
|
|
3447
|
+
// if (node.getText() === 'ZEROSAT') {
|
|
3448
|
+
// toSection.append('__scrypt_Utils.ZEROSAT', this.getCoordinates(node.getStart()));
|
|
3449
|
+
// return toSection;
|
|
3450
|
+
// }
|
|
3451
|
+
if (this.isCtcIdentifierOrProperty(node)) {
|
|
3452
|
+
return this.transformCtcNode(node, toSection);
|
|
3453
|
+
}
|
|
3454
|
+
if (this.isTranspilingConstructor(node) && this.isTranspilingBaseContract(node)) {
|
|
3455
|
+
const mapedNode = this._constructorParametersMap.get(node.getText());
|
|
3456
|
+
if (mapedNode) {
|
|
3457
|
+
if (typescript_1.default.isParameter(mapedNode)) {
|
|
3458
|
+
toSection.append(mapedNode.name.getText(), this.getCoordinates(node.getStart()));
|
|
3459
|
+
return toSection;
|
|
3460
|
+
}
|
|
3461
|
+
else if (typescript_1.default.isExpression(mapedNode)) {
|
|
3462
|
+
toSection.appendWith(this, (toSec) => this.transformExpression(mapedNode, toSec));
|
|
3463
|
+
return toSection;
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
toSection.append(node.getText(), this.getCoordinates(node.getStart()));
|
|
3468
|
+
return toSection;
|
|
3469
|
+
}
|
|
3470
|
+
isBooleanType(node) {
|
|
3471
|
+
return (['true', 'false', 'never', 'boolean', 'Bool'].indexOf(this.getResolvedTypeName(node)) !== -1);
|
|
3472
|
+
}
|
|
3473
|
+
transformBinaryExpression(node, toSection) {
|
|
3474
|
+
const e = node;
|
|
3475
|
+
const operator = this.toScryptBinary(e, e.operatorToken.getText());
|
|
3476
|
+
if (e.operatorToken.getText() === '&&' || e.operatorToken.getText() === '||') {
|
|
3477
|
+
// require both operands are boolean type
|
|
3478
|
+
if (!this.isBooleanType(e.left) || !this.isBooleanType(e.right)) {
|
|
3479
|
+
throw new types_1.TranspileError(`\`${node.getText()}\` is not allowed, both operands of \`${e.operatorToken.getText()}\` must be boolean type`, this.getRange(node));
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
const srcLoc = this.getCoordinates(e.getStart());
|
|
3483
|
+
return this.transformBinaryOperation(operator, e.left, e.right, srcLoc, toSection);
|
|
3484
|
+
}
|
|
3485
|
+
transformSpecialPropertyAccessExpression(node, toSection) {
|
|
3486
|
+
const text = node.getText().replaceAll('?', '');
|
|
3487
|
+
let isSpecial = true;
|
|
3488
|
+
let shouldAccessThis = false;
|
|
3489
|
+
switch (text) {
|
|
3490
|
+
case 'SigHash.ALL':
|
|
3491
|
+
toSection.append('SigHash.ALL | SigHash.FORKID', this.getCoordinates(node.name.getStart()));
|
|
3492
|
+
break;
|
|
3493
|
+
case 'SigHash.NONE':
|
|
3494
|
+
toSection.append('SigHash.NONE | SigHash.FORKID', this.getCoordinates(node.name.getStart()));
|
|
3495
|
+
break;
|
|
3496
|
+
case 'SigHash.SINGLE':
|
|
3497
|
+
toSection.append('SigHash.SINGLE | SigHash.FORKID', this.getCoordinates(node.name.getStart()));
|
|
3498
|
+
break;
|
|
3499
|
+
case 'SigHash.ANYONECANPAY_ALL':
|
|
3500
|
+
toSection.append('SigHash.ALL | SigHash.ANYONECANPAY | SigHash.FORKID', this.getCoordinates(node.name.getStart()));
|
|
3501
|
+
break;
|
|
3502
|
+
case 'SigHash.ANYONECANPAY_NONE':
|
|
3503
|
+
toSection.append('SigHash.NONE | SigHash.ANYONECANPAY | SigHash.FORKID', this.getCoordinates(node.name.getStart()));
|
|
3504
|
+
break;
|
|
3505
|
+
case 'SigHash.ANYONECANPAY_SINGLE':
|
|
3506
|
+
toSection.append('SigHash.SINGLE | SigHash.ANYONECANPAY | SigHash.FORKID', this.getCoordinates(node.name.getStart()));
|
|
3507
|
+
break;
|
|
3508
|
+
case 'this.changeInfo':
|
|
3509
|
+
shouldAccessThis = this.shouldAutoAppendChangeAmount(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3510
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_ChangeInfo}`);
|
|
3511
|
+
break;
|
|
3512
|
+
// ctx: SH Preimage
|
|
3513
|
+
case 'this.ctx.nVersion':
|
|
3514
|
+
shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3515
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.nVersion`);
|
|
3516
|
+
break;
|
|
3517
|
+
case 'this.ctx.hashPrevouts':
|
|
3518
|
+
shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3519
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.hashPrevouts`);
|
|
3520
|
+
break;
|
|
3521
|
+
case 'this.ctx.spentScriptHash':
|
|
3522
|
+
shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3523
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.spentScriptHash`);
|
|
3524
|
+
break;
|
|
3525
|
+
case 'this.ctx.spentDataHash':
|
|
3526
|
+
shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3527
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.spentDataHash`);
|
|
3528
|
+
break;
|
|
3529
|
+
case 'this.ctx.value':
|
|
3530
|
+
shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3531
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.value`);
|
|
3532
|
+
break;
|
|
3533
|
+
case 'this.ctx.nSequence':
|
|
3534
|
+
shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3535
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.nSequence`);
|
|
3536
|
+
break;
|
|
3537
|
+
case 'this.ctx.hashSpentAmounts':
|
|
3538
|
+
shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3539
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.hashSpentAmounts`);
|
|
3540
|
+
break;
|
|
3541
|
+
case 'this.ctx.hashSpentScriptHashes':
|
|
3542
|
+
shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3543
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.hashSpentScriptHashes`);
|
|
3544
|
+
break;
|
|
3545
|
+
case 'this.ctx.hashSpentDataHashes':
|
|
3546
|
+
shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3547
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.hashSpentDataHashes`);
|
|
3548
|
+
break;
|
|
3549
|
+
case 'this.ctx.hashSequences':
|
|
3550
|
+
shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3551
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.hashSequences`);
|
|
3552
|
+
break;
|
|
3553
|
+
case 'this.ctx.hashOutputs':
|
|
3554
|
+
shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3555
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.hashOutputs`);
|
|
3556
|
+
break;
|
|
3557
|
+
case 'this.ctx.inputIndex':
|
|
3558
|
+
shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3559
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.inputIndex`);
|
|
3560
|
+
break;
|
|
3561
|
+
case 'this.ctx.nLockTime':
|
|
3562
|
+
shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3563
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.nLockTime`);
|
|
3564
|
+
break;
|
|
3565
|
+
case 'this.ctx.sigHashType':
|
|
3566
|
+
shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3567
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.sigHashType`);
|
|
3568
|
+
break;
|
|
3569
|
+
// ParamCtx
|
|
3570
|
+
case 'this.ctx.prevouts':
|
|
3571
|
+
// toSection.append(`${InjectedParam_Prevouts}`);
|
|
3572
|
+
shouldAccessThis = this.shouldAutoAppendPrevouts(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3573
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_Prevouts}`);
|
|
3574
|
+
break;
|
|
3575
|
+
case 'this.ctx.spentScriptHashes':
|
|
3576
|
+
// toSection.append(`${InjectedParam_SpentScriptHashes}`);
|
|
3577
|
+
shouldAccessThis = this.shouldAutoAppendSpentScripts(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3578
|
+
toSection.append(shouldAccessThis
|
|
3579
|
+
? `this.${snippets_1.InjectedParam_SpentScriptHashes}`
|
|
3580
|
+
: `${snippets_1.InjectedParam_SpentScriptHashes}`);
|
|
3581
|
+
break;
|
|
3582
|
+
case 'this.ctx.spentDataHashes':
|
|
3583
|
+
// toSection.append(`${InjectedParam_SpentScriptHashes}`);
|
|
3584
|
+
shouldAccessThis = this.shouldAutoAppendSpentDataHashes(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3585
|
+
toSection.append(shouldAccessThis
|
|
3586
|
+
? `this.${snippets_1.InjectedParam_SpentDataHashes}`
|
|
3587
|
+
: `${snippets_1.InjectedParam_SpentDataHashes}`);
|
|
3588
|
+
break;
|
|
3589
|
+
case 'this.ctx.spentAmounts':
|
|
3590
|
+
// toSection.append(`${InjectedParam_SpentAmounts}`);
|
|
3591
|
+
shouldAccessThis = this.shouldAutoAppendSpentAmounts(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3592
|
+
toSection.append(shouldAccessThis
|
|
3593
|
+
? `this.${snippets_1.InjectedParam_SpentAmounts}`
|
|
3594
|
+
: `${snippets_1.InjectedParam_SpentAmounts}`);
|
|
3595
|
+
break;
|
|
3596
|
+
case 'this.ctx.prevout':
|
|
3597
|
+
// toSection.append(`${InjectedParam_Prevout}`);
|
|
3598
|
+
shouldAccessThis = this.shouldAutoAppendPrevouts(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3599
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_Prevout}`);
|
|
3600
|
+
break;
|
|
3601
|
+
case 'this.ctx.inputCount':
|
|
3602
|
+
shouldAccessThis = this.shouldAutoAppendSpentAmounts(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3603
|
+
toSection.append(shouldAccessThis
|
|
3604
|
+
? snippets_1.ACCESS_INPUT_COUNT.accessThis
|
|
3605
|
+
: snippets_1.ACCESS_INPUT_COUNT.accessArgument);
|
|
3606
|
+
break;
|
|
3607
|
+
case 'this.ctx':
|
|
3608
|
+
throw new types_1.TranspileError(`Direct access to \`this.ctx\` is prohibited, only access to \`this.ctx.*\` is allowed, such as: \`this.ctx.shaSpentAmounts\` or \`this.ctx.inputIndex\``, this.getRange(node));
|
|
3609
|
+
case 'this.state':
|
|
3610
|
+
shouldAccessThis = this.shouldAutoAppendStateArgs(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
3611
|
+
toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedProp_NextState}`);
|
|
3612
|
+
break;
|
|
3613
|
+
default: {
|
|
3614
|
+
isSpecial = false;
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
3617
|
+
return isSpecial;
|
|
3618
|
+
}
|
|
3619
|
+
transformCTCPropertyAccessExpression(node, toSection) {
|
|
3620
|
+
if (typescript_1.default.isIdentifier(node.name) && typescript_1.default.isIdentifier(node.expression)) {
|
|
3621
|
+
if (this.isCtcIdentifierOrProperty(node)) {
|
|
3622
|
+
this.transformCtcNode(node, toSection);
|
|
3623
|
+
return true;
|
|
3624
|
+
}
|
|
3625
|
+
}
|
|
3626
|
+
return false;
|
|
3627
|
+
}
|
|
3628
|
+
transformPropertyAccessExpression(node, toSection) {
|
|
3629
|
+
if (this.transformSpecialPropertyAccessExpression(node, toSection)) {
|
|
3630
|
+
return toSection;
|
|
3631
|
+
}
|
|
3632
|
+
if (this.transformCTCPropertyAccessExpression(node, toSection)) {
|
|
3633
|
+
return toSection;
|
|
3634
|
+
}
|
|
3635
|
+
const name = node.name.getText();
|
|
3636
|
+
const cls = Transpiler.getClassDeclaration(node);
|
|
3637
|
+
if (this.isNonPropReferences(cls, name)) {
|
|
3638
|
+
throw new types_1.TranspileError(`Cannot access Non-Prop property '${name}' in a \`@method()\` function`, this.getRange(node));
|
|
3639
|
+
}
|
|
3640
|
+
if (typescript_1.default.isIdentifier(node.expression)) {
|
|
3641
|
+
const leftSide = node.expression.getText();
|
|
3642
|
+
if (leftSide === this.currentbaseContractName) {
|
|
3643
|
+
toSection.append(`${this.currentContractName}.${node.name.getText()}`, this.getCoordinates(node.name.getStart()));
|
|
3644
|
+
return toSection;
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
toSection
|
|
3648
|
+
.appendWith(this, (toSec) => this.transformExpression(node.expression, toSec))
|
|
3649
|
+
.append(`.${node.name.getText()}`, this.getCoordinates(node.name.getStart()));
|
|
3650
|
+
return toSection;
|
|
3651
|
+
}
|
|
3652
|
+
transformArrowFunctionExpression(node, toSection) {
|
|
3653
|
+
const e = node;
|
|
3654
|
+
if (typescript_1.default.isBlock(e.body)) {
|
|
3655
|
+
if (e.parameters.length > 1) {
|
|
3656
|
+
throw new types_1.TranspileError(`Untransformable expression kind ${typescript_1.default.SyntaxKind[e.kind]}: '${e.getText()}'`, this.getRange(e));
|
|
3657
|
+
}
|
|
3658
|
+
if (e.parameters.length === 1) {
|
|
3659
|
+
toSection.append(` : ${e.parameters[0].name.getText()}`);
|
|
3660
|
+
}
|
|
3661
|
+
toSection.append(' ');
|
|
3662
|
+
return this.transformStatement(e.body, toSection);
|
|
3663
|
+
}
|
|
3664
|
+
throw new types_1.TranspileError(`Untransformable expression kind ${typescript_1.default.SyntaxKind[e.kind]}: '${e.getText()}'`, this.getRange(e));
|
|
3665
|
+
}
|
|
3666
|
+
transformNewExpression(node, toSection) {
|
|
3667
|
+
const e = node;
|
|
3668
|
+
const clsText = e.expression.getText();
|
|
3669
|
+
const scryptType = (0, utils_1.getBuiltInType)(clsText);
|
|
3670
|
+
if (scryptType) {
|
|
3671
|
+
toSection.append(scryptType, this.getCoordinates(e.getStart())).append('(');
|
|
3672
|
+
if (clsText === 'SigHashType' && e.arguments[0].kind) {
|
|
3673
|
+
const t = parseInt(e.arguments[0].getText());
|
|
3674
|
+
toSection.append(`b'${(0, utils_1.number2hex)(t)}'`);
|
|
3675
|
+
}
|
|
3676
|
+
else {
|
|
3677
|
+
e.arguments.forEach((arg, index) => {
|
|
3678
|
+
toSection
|
|
3679
|
+
.appendWith(this, (toSec) => this.transformExpression(arg, toSec))
|
|
3680
|
+
.append(index < e.arguments.length - 1 ? ', ' : '');
|
|
3681
|
+
});
|
|
3682
|
+
}
|
|
3683
|
+
toSection.append(')');
|
|
3684
|
+
}
|
|
3685
|
+
else {
|
|
3686
|
+
const type = this.getResolvedType(e.expression);
|
|
3687
|
+
const classDec = type.getSymbol().declarations[0];
|
|
3688
|
+
// if this extends `SmartContract` or `SmartLibrary`
|
|
3689
|
+
if (this.isLibrary(classDec)) {
|
|
3690
|
+
toSection
|
|
3691
|
+
.append('new ')
|
|
3692
|
+
.append(e.expression.getText(), this.getCoordinates(e.expression.getStart()))
|
|
3693
|
+
.append('(')
|
|
3694
|
+
.appendWith(this, (toSec) => {
|
|
3695
|
+
e.arguments.forEach((arg, index) => {
|
|
3696
|
+
toSec
|
|
3697
|
+
.appendWith(this, (ts) => this.transformExpression(arg, ts))
|
|
3698
|
+
.append(index < e.arguments.length - 1 ? ', ' : '');
|
|
3699
|
+
});
|
|
3700
|
+
return toSec;
|
|
3701
|
+
})
|
|
3702
|
+
.append(')');
|
|
3703
|
+
}
|
|
3704
|
+
else if (this.isContract(classDec)) {
|
|
3705
|
+
throw new types_1.TranspileError(`contract \`${e.expression.getText()}\` can not be initialized inside a @method, only supports instantiation of library`, this.getRange(e));
|
|
3706
|
+
}
|
|
3707
|
+
else {
|
|
3708
|
+
throw new types_1.TranspileError(`Untransformable expression kind ${typescript_1.default.SyntaxKind[e.kind]}: '${e.getText()}'`, this.getRange(e));
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
return toSection;
|
|
3712
|
+
}
|
|
3713
|
+
transformElementAccessExpression(node, toSection) {
|
|
3714
|
+
const e = node;
|
|
3715
|
+
if (this.getResolvedTypeName(e.expression) === 'ByteString') {
|
|
3716
|
+
// ByteString[] is not allowed
|
|
3717
|
+
throw new types_1.TranspileError(`\`${node.getText()}\` is not allowed here, slice ByteString is not supported`, this.getRange(e));
|
|
3718
|
+
}
|
|
3719
|
+
return toSection
|
|
3720
|
+
.appendWith(this, (toSec) => this.transformExpression(e.expression, toSec))
|
|
3721
|
+
.append('[')
|
|
3722
|
+
.appendWith(this, (toSec) => this.transformExpression(e.argumentExpression, toSec))
|
|
3723
|
+
.append(']');
|
|
3724
|
+
}
|
|
3725
|
+
transformConditionalExpression(node, toSection) {
|
|
3726
|
+
const e = node;
|
|
3727
|
+
return toSection
|
|
3728
|
+
.appendWith(this, (toSec) => this.transformExpression(e.condition, toSec))
|
|
3729
|
+
.append(' ? ')
|
|
3730
|
+
.appendWith(this, (toSec) => this.transformExpression(e.whenTrue, toSec))
|
|
3731
|
+
.append(' : ')
|
|
3732
|
+
.appendWith(this, (toSec) => this.transformExpression(e.whenFalse, toSec));
|
|
3733
|
+
}
|
|
3734
|
+
transformParenthesizedExpression(node, toSection) {
|
|
3735
|
+
const e = node;
|
|
3736
|
+
return toSection
|
|
3737
|
+
.append('(')
|
|
3738
|
+
.appendWith(this, (toSec) => this.transformExpression(e.expression, toSec))
|
|
3739
|
+
.append(')');
|
|
3740
|
+
}
|
|
3741
|
+
transformPostfixUnaryExpression(node, toSection) {
|
|
3742
|
+
const e = node;
|
|
3743
|
+
return toSection
|
|
3744
|
+
.appendWith(this, (toSec) => this.transformExpression(e.operand, toSec))
|
|
3745
|
+
.append(`${e.operator == typescript_1.default.SyntaxKind.PlusPlusToken ? '++' : '--'}`, this.getCoordinates(e.getStart()));
|
|
3746
|
+
}
|
|
3747
|
+
transformPrefixUnaryExpression(node, toSection) {
|
|
3748
|
+
const e = node;
|
|
3749
|
+
if (e.operator === typescript_1.default.SyntaxKind.ExclamationToken) {
|
|
3750
|
+
// require boolean
|
|
3751
|
+
if (!this.isBooleanType(e.operand)) {
|
|
3752
|
+
throw new types_1.TranspileError(`\`${node.getText()}\` is not allowed, operand of \`!\` must be boolean type`, this.getRange(node));
|
|
3753
|
+
}
|
|
3754
|
+
}
|
|
3755
|
+
return toSection
|
|
3756
|
+
.append(`${this.toScryptUnary(e, e.operator)}`, this.getCoordinates(e.getStart()))
|
|
3757
|
+
.appendWith(this, (toSec) => this.transformExpression(e.operand, toSec));
|
|
3758
|
+
}
|
|
3759
|
+
transformCallExpression(node, toSection) {
|
|
3760
|
+
const e = node;
|
|
3761
|
+
if (e.expression.kind === typescript_1.default.SyntaxKind.PropertyAccessExpression) {
|
|
3762
|
+
// E.g. "str1.slice(2, 6)"
|
|
3763
|
+
return this.transformPropertyAccessCallExpression(e, toSection);
|
|
3764
|
+
}
|
|
3765
|
+
if (e.expression.kind === typescript_1.default.SyntaxKind.Identifier) {
|
|
3766
|
+
// E.g. "assert(...)"
|
|
3767
|
+
return this.transformIdentifierCallExpression(e, toSection);
|
|
3768
|
+
}
|
|
3769
|
+
return this.transformDefaultCallExpression(e, toSection);
|
|
3770
|
+
}
|
|
3771
|
+
transformBinaryOperation(operator, left, right, srcLoc, toSection, parenthesized = false) {
|
|
3772
|
+
if (parenthesized) {
|
|
3773
|
+
toSection.append('(', srcLoc);
|
|
3774
|
+
}
|
|
3775
|
+
toSection
|
|
3776
|
+
.appendWith(this, (toSec) => this.transformExpression(left, toSec))
|
|
3777
|
+
.append(` ${operator} `, srcLoc)
|
|
3778
|
+
.appendWith(this, (toSec) => this.transformExpression(right, toSec));
|
|
3779
|
+
if (parenthesized) {
|
|
3780
|
+
toSection.append(')', srcLoc);
|
|
3781
|
+
}
|
|
3782
|
+
return toSection;
|
|
3783
|
+
}
|
|
3784
|
+
transformPropertyAccessCallExpression(node, toSection) {
|
|
3785
|
+
const expr = node.expression;
|
|
3786
|
+
const name = expr.name.getText();
|
|
3787
|
+
const leftSide = expr.expression.getText();
|
|
3788
|
+
if (leftSide === 'this') {
|
|
3789
|
+
if (name === 'checkSig') {
|
|
3790
|
+
return this.transformCallCheckSig(node, toSection);
|
|
3791
|
+
}
|
|
3792
|
+
if (name === 'checkMultiSig') {
|
|
3793
|
+
return this.transformCallCheckMultiSig(node, toSection);
|
|
3794
|
+
}
|
|
3795
|
+
if (name === 'checkPreimage' || name === 'checkPreimageSigHashType') {
|
|
3796
|
+
return this.transformCallCheckPreimage(node, toSection);
|
|
3797
|
+
}
|
|
3798
|
+
if (name === 'buildChangeOutput') {
|
|
3799
|
+
return this.transformCallBuildChangeOutput(node, toSection);
|
|
3800
|
+
}
|
|
3801
|
+
if (name === 'relTimeLock') {
|
|
3802
|
+
return this.transformCallCSV(node, toSection);
|
|
3803
|
+
}
|
|
3804
|
+
if (name === 'absTimeLock') {
|
|
3805
|
+
return this.transformCallCLTV(node, toSection);
|
|
3806
|
+
}
|
|
3807
|
+
if (name === 'checkInputState') {
|
|
3808
|
+
return this.transformCallCheckInputState(node, toSection);
|
|
3809
|
+
}
|
|
3810
|
+
if (name === 'backtraceToOutpoint') {
|
|
3811
|
+
return this.transformCallBacktraceToOutpoint(node, toSection);
|
|
3812
|
+
}
|
|
3813
|
+
if (name === 'backtraceToScript') {
|
|
3814
|
+
return this.transformCallBacktraceToScript(node, toSection);
|
|
3815
|
+
}
|
|
3816
|
+
if (name === 'checkOutputs') {
|
|
3817
|
+
return this.transformCallCheckOutputs(node, toSection);
|
|
3818
|
+
}
|
|
3819
|
+
return this.transformDefaultCallExpression(node, toSection);
|
|
3820
|
+
}
|
|
3821
|
+
if (name === 'slice') {
|
|
3822
|
+
return this.transformCallSlice(node);
|
|
3823
|
+
}
|
|
3824
|
+
return this.transformDefaultCallExpression(node, toSection);
|
|
3825
|
+
}
|
|
3826
|
+
transformIdentifierCallExpression(node, toSection) {
|
|
3827
|
+
const srcLoc = this.getCoordinates(node.getStart());
|
|
3828
|
+
const name = node.expression.getText();
|
|
3829
|
+
if (name === 'assert') {
|
|
3830
|
+
return toSection
|
|
3831
|
+
.append('require(', srcLoc)
|
|
3832
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
|
|
3833
|
+
.append(')');
|
|
3834
|
+
}
|
|
3835
|
+
if (name === 'BigInt' || name === 'Number') {
|
|
3836
|
+
toSection.append('(', srcLoc);
|
|
3837
|
+
node.arguments.forEach((arg) => {
|
|
3838
|
+
toSection.appendWith(this, (toSec) => this.transformExpression(arg, toSec));
|
|
3839
|
+
});
|
|
3840
|
+
return toSection.append(')');
|
|
3841
|
+
}
|
|
3842
|
+
if (name === 'and') {
|
|
3843
|
+
return this.transformBinaryOperation('&', node.arguments[0], node.arguments[1], srcLoc, toSection, true);
|
|
3844
|
+
}
|
|
3845
|
+
if (name === 'or') {
|
|
3846
|
+
return this.transformBinaryOperation('|', node.arguments[0], node.arguments[1], srcLoc, toSection, true);
|
|
3847
|
+
}
|
|
3848
|
+
if (name === 'xor') {
|
|
3849
|
+
return this.transformBinaryOperation('^', node.arguments[0], node.arguments[1], srcLoc, toSection, true);
|
|
3850
|
+
}
|
|
3851
|
+
if (name === 'invert') {
|
|
3852
|
+
return toSection
|
|
3853
|
+
.append('~', srcLoc)
|
|
3854
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec));
|
|
3855
|
+
}
|
|
3856
|
+
if (name === 'lshift') {
|
|
3857
|
+
return this.transformBinaryOperation('<<', node.arguments[0], node.arguments[1], srcLoc, toSection, true);
|
|
3858
|
+
}
|
|
3859
|
+
if (name === 'rshift') {
|
|
3860
|
+
return this.transformBinaryOperation('>>', node.arguments[0], node.arguments[1], srcLoc, toSection, true);
|
|
3861
|
+
}
|
|
3862
|
+
if (name === 'equals') {
|
|
3863
|
+
return this.transformBinaryOperation('==', node.arguments[0], node.arguments[1], srcLoc, toSection);
|
|
3864
|
+
}
|
|
3865
|
+
if (name === 'toByteString') {
|
|
3866
|
+
const arg0 = node.arguments[0];
|
|
3867
|
+
if (arg0.kind === typescript_1.default.SyntaxKind.StringLiteral || this.isByteStringNode(arg0)) {
|
|
3868
|
+
return toSection.appendWith(this, (toSec) => this.transformExpression(arg0, toSec));
|
|
3869
|
+
}
|
|
3870
|
+
throw new types_1.TranspileError('Only string literal can be passed to the first parameter of `toByteString`', this.getRange(node));
|
|
3871
|
+
}
|
|
3872
|
+
if (name === 'PubKeyHash' || name === 'Addr') {
|
|
3873
|
+
return toSection
|
|
3874
|
+
.append('Ripemd160', srcLoc)
|
|
3875
|
+
.append('(')
|
|
3876
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
|
|
3877
|
+
.append(')');
|
|
3878
|
+
}
|
|
3879
|
+
// if (name === 'SigHashType') {
|
|
3880
|
+
// return this.transformCallSigHashType(node, toSection);
|
|
3881
|
+
// }
|
|
3882
|
+
if (name === 'reverseByteString') {
|
|
3883
|
+
toSection.append('reverseBytes(', srcLoc);
|
|
3884
|
+
node.arguments.forEach((arg, index) => {
|
|
3885
|
+
toSection
|
|
3886
|
+
.appendWith(this, (toSec) => this.transformExpression(arg, toSec))
|
|
3887
|
+
.append(index < node.arguments.length - 1 ? ', ' : '');
|
|
3888
|
+
});
|
|
3889
|
+
return toSection.append(')');
|
|
3890
|
+
}
|
|
3891
|
+
if (name === 'intToByteString') {
|
|
3892
|
+
let func = node.arguments.length === 1 ? 'pack' : 'num2bin';
|
|
3893
|
+
toSection.append(`${func}(`, srcLoc);
|
|
3894
|
+
node.arguments.forEach((arg, index) => {
|
|
3895
|
+
toSection
|
|
3896
|
+
.appendWith(this, (toSec) => this.transformExpression(arg, toSec))
|
|
3897
|
+
.append(index < node.arguments.length - 1 ? ', ' : '');
|
|
3898
|
+
});
|
|
3899
|
+
return toSection.append(')');
|
|
3900
|
+
}
|
|
3901
|
+
if (name === 'byteStringToInt') {
|
|
3902
|
+
toSection.append(`unpack(`, srcLoc);
|
|
3903
|
+
node.arguments.forEach((arg, index) => {
|
|
3904
|
+
toSection
|
|
3905
|
+
.appendWith(this, (toSec) => this.transformExpression(arg, toSec))
|
|
3906
|
+
.append(index < node.arguments.length - 1 ? ', ' : '');
|
|
3907
|
+
});
|
|
3908
|
+
return toSection.append(')');
|
|
3909
|
+
}
|
|
3910
|
+
if (name === 'fill') {
|
|
3911
|
+
return this.transformCallFill(node, toSection);
|
|
3912
|
+
}
|
|
3913
|
+
if (name === 'slice') {
|
|
3914
|
+
return this.transformIdentifierCallSlice(node, toSection);
|
|
3915
|
+
}
|
|
3916
|
+
if (name === 'pubKey2Addr') {
|
|
3917
|
+
return toSection
|
|
3918
|
+
.append('hash160', srcLoc)
|
|
3919
|
+
.append('(')
|
|
3920
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
|
|
3921
|
+
.append(')');
|
|
3922
|
+
}
|
|
3923
|
+
return this.transformDefaultCallExpression(node, toSection);
|
|
3924
|
+
}
|
|
3925
|
+
transformDefaultCallExpression(node, toSection) {
|
|
3926
|
+
const funcType = this._checker.getTypeAtLocation(node.expression);
|
|
3927
|
+
if (funcType.flags === typescript_1.default.TypeFlags.Any) {
|
|
3928
|
+
throw new types_1.TranspileError(`Calling function which returns \`any\` is not allowed`, this.getRange(node.expression));
|
|
3929
|
+
}
|
|
3930
|
+
const text = node.getText().replaceAll('?', '');
|
|
3931
|
+
switch (text) {
|
|
3932
|
+
case 'this.ctx.serialize()':
|
|
3933
|
+
toSection.append(`this.${snippets_1.InjectedParam_SHPreimage}`, this.getCoordinates(node.getStart()));
|
|
3934
|
+
break;
|
|
3935
|
+
case 'this.insertCodeSeparator()':
|
|
3936
|
+
{
|
|
3937
|
+
const ifStmt = Transpiler.getIfStatement(node);
|
|
3938
|
+
if (ifStmt) {
|
|
3939
|
+
throw new types_1.TranspileError(`insertCodeSeparator() cannot be called in a if statement`, this.getRange(node));
|
|
3940
|
+
}
|
|
3941
|
+
const md = Transpiler.getMethodDeclaration(node);
|
|
3942
|
+
if (md) {
|
|
3943
|
+
if (!Transpiler.isPublicMethod(md)) {
|
|
3944
|
+
throw new types_1.TranspileError(`non-public methods cannot call insertCodeSeparator()`, this.getRange(node));
|
|
3945
|
+
}
|
|
3946
|
+
const methodInfo = this.findMethodInfo(md.name.getText());
|
|
3947
|
+
if (methodInfo) {
|
|
3948
|
+
methodInfo.codeSeparatorCount++;
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
toSection.append(`***`, this.getCoordinates(node.getStart()));
|
|
3952
|
+
toSection.skipNextAppend = true; // "***" is not closed using semicolon
|
|
3953
|
+
}
|
|
3954
|
+
break;
|
|
3955
|
+
default:
|
|
3956
|
+
toSection
|
|
3957
|
+
.appendWith(this, (toSec) => this.transformExpression(node.expression, toSec))
|
|
3958
|
+
.append('(')
|
|
3959
|
+
.appendWith(this, (toSec) => {
|
|
3960
|
+
const methodDec = funcType.symbol.declarations[0];
|
|
3961
|
+
const inValidParams = methodDec.parameters.find((p) => p.type?.getText() === 'SmartContract');
|
|
3962
|
+
if (inValidParams) {
|
|
3963
|
+
throw new types_1.TranspileError(`Untransformable parameter: '${node.getText()}'`, this.getRange(node));
|
|
3964
|
+
}
|
|
3965
|
+
node.arguments.forEach((arg, index) => {
|
|
3966
|
+
toSec
|
|
3967
|
+
.appendWith(this, (ts) => this.transformExpression(arg, ts))
|
|
3968
|
+
.append(index < node.arguments.length - 1 ? ', ' : '');
|
|
3969
|
+
});
|
|
3970
|
+
return toSec;
|
|
3971
|
+
})
|
|
3972
|
+
.append(')');
|
|
3973
|
+
break;
|
|
3974
|
+
}
|
|
3975
|
+
return toSection;
|
|
3976
|
+
}
|
|
3977
|
+
transformCallSlice(node) {
|
|
3978
|
+
// disable ByteString.slice()
|
|
3979
|
+
const msg = `\`${node.getText()}\` is not allowed here, slice ByteString is not supported`;
|
|
3980
|
+
throw new types_1.TranspileError(msg, this.getRange(node));
|
|
3981
|
+
}
|
|
3982
|
+
transformIdentifierCallSlice(node, toSection) {
|
|
3983
|
+
toSection
|
|
3984
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
|
|
3985
|
+
.append('[')
|
|
3986
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[1], toSec))
|
|
3987
|
+
.append(' : ');
|
|
3988
|
+
if (node.arguments.length === 3) {
|
|
3989
|
+
toSection.appendWith(this, (toSec) => this.transformExpression(node.arguments[2], toSec));
|
|
3990
|
+
}
|
|
3991
|
+
else {
|
|
3992
|
+
toSection
|
|
3993
|
+
.append('len(')
|
|
3994
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
|
|
3995
|
+
.append(')');
|
|
3996
|
+
}
|
|
3997
|
+
return toSection.append(']');
|
|
3998
|
+
}
|
|
3999
|
+
transformCallCSV(node, toSection) {
|
|
4000
|
+
const arg = node.arguments[0];
|
|
4001
|
+
this._accessBuiltinsSymbols.add('__scrypt_ASM');
|
|
4002
|
+
return toSection
|
|
4003
|
+
.append(`__scrypt_ASM.csv(`, this.getCoordinates(node.getStart()))
|
|
4004
|
+
.appendWith(this, (toSec) => this.transformExpression(arg, toSec))
|
|
4005
|
+
.append(`)`);
|
|
4006
|
+
}
|
|
4007
|
+
transformCallCLTV(node, toSection) {
|
|
4008
|
+
const arg = node.arguments[0];
|
|
4009
|
+
this._accessBuiltinsSymbols.add('__scrypt_ASM');
|
|
4010
|
+
return toSection
|
|
4011
|
+
.append(`__scrypt_ASM.cltv(`, this.getCoordinates(node.getStart()))
|
|
4012
|
+
.appendWith(this, (toSec) => this.transformExpression(arg, toSec))
|
|
4013
|
+
.append(`)`);
|
|
4014
|
+
}
|
|
4015
|
+
transformCallCheckSig(node, toSection) {
|
|
4016
|
+
const srcLoc = this.getCoordinates(node.getStart());
|
|
4017
|
+
toSection.appendWith(this, (toSec) => {
|
|
4018
|
+
const _e = node.expression;
|
|
4019
|
+
return this.transformExpression(_e.name, toSec);
|
|
4020
|
+
});
|
|
4021
|
+
toSection.append('(', srcLoc);
|
|
4022
|
+
const args = node.arguments.slice(0, 2);
|
|
4023
|
+
args.forEach((arg, index) => {
|
|
4024
|
+
toSection
|
|
4025
|
+
.appendWith(this, (toSec) => this.transformExpression(arg, toSec))
|
|
4026
|
+
.append(index < args.length - 1 ? ', ' : '');
|
|
4027
|
+
});
|
|
4028
|
+
return toSection.append(')', srcLoc);
|
|
4029
|
+
}
|
|
4030
|
+
transformCallCheckMultiSig(node, toSection) {
|
|
4031
|
+
const srcLoc = this.getCoordinates(node.getStart());
|
|
4032
|
+
toSection.appendWith(this, (toSec) => {
|
|
4033
|
+
const _e = node.expression;
|
|
4034
|
+
return this.transformExpression(_e.name, toSec);
|
|
4035
|
+
});
|
|
4036
|
+
toSection.append('(', srcLoc);
|
|
4037
|
+
const args = node.arguments.slice(0, 2);
|
|
4038
|
+
args.forEach((arg, index) => {
|
|
4039
|
+
toSection
|
|
4040
|
+
.appendWith(this, (toSec) => this.transformExpression(arg, toSec))
|
|
4041
|
+
.append(index < args.length - 1 ? ', ' : '');
|
|
4042
|
+
});
|
|
4043
|
+
return toSection.append(')', srcLoc);
|
|
4044
|
+
}
|
|
4045
|
+
transformCallCheckPreimage(node, toSection) {
|
|
4046
|
+
const srcLoc = this.getCoordinates(node.getStart());
|
|
4047
|
+
toSection.appendWith(this, (toSec) => {
|
|
4048
|
+
const _e = node.expression;
|
|
4049
|
+
toSec.append('Tx.');
|
|
4050
|
+
return this.transformExpression(_e.name, toSec);
|
|
4051
|
+
});
|
|
4052
|
+
toSection.append('(', srcLoc);
|
|
4053
|
+
node.arguments.forEach((arg, index) => {
|
|
4054
|
+
toSection
|
|
4055
|
+
.appendWith(this, (toSec) => this.transformExpression(arg, toSec))
|
|
4056
|
+
.append(index < node.arguments.length - 1 ? ', ' : '');
|
|
4057
|
+
});
|
|
4058
|
+
return toSection.append(')', srcLoc);
|
|
4059
|
+
}
|
|
4060
|
+
transformCallBuildPublicKeyHashOutput(node, toSection, name) {
|
|
4061
|
+
if (node.arguments.length !== 2) {
|
|
4062
|
+
throw new types_1.TranspileError(`Invalid arguments length for ${name}`, this.getRange(node));
|
|
4063
|
+
}
|
|
4064
|
+
return toSection
|
|
4065
|
+
.append(`Utils.buildOutput(Utils.buildPublicKeyHashScript(`, this.getCoordinates(node.getStart()))
|
|
4066
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
|
|
4067
|
+
.append('), ')
|
|
4068
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[1], toSec))
|
|
4069
|
+
.append(')');
|
|
4070
|
+
}
|
|
4071
|
+
transformCallBuildAddressScript(node, toSection) {
|
|
4072
|
+
if (node.arguments.length !== 1) {
|
|
4073
|
+
throw new types_1.TranspileError(`Invalid arguments length for buildAddressScript`, this.getRange(node));
|
|
4074
|
+
}
|
|
4075
|
+
return toSection
|
|
4076
|
+
.append(`Utils.buildPublicKeyHashScript(`, this.getCoordinates(node.getStart()))
|
|
4077
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
|
|
4078
|
+
.append(')');
|
|
4079
|
+
}
|
|
4080
|
+
transformCallFill(node, toSection) {
|
|
4081
|
+
const _length = node.arguments[1];
|
|
4082
|
+
const isCtc = this.isCtcExpression(_length);
|
|
4083
|
+
if (_length.kind !== typescript_1.default.SyntaxKind.NumericLiteral && !isCtc) {
|
|
4084
|
+
throw new types_1.TranspileError('Only compiled-time constant can be passed to the second parameter of `fill`', this.getRange(node));
|
|
4085
|
+
}
|
|
4086
|
+
return toSection
|
|
4087
|
+
.append('repeat(', this.getCoordinates(node.getStart()))
|
|
4088
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
|
|
4089
|
+
.append(', ')
|
|
4090
|
+
.appendWith(this, (toSec) => isCtc && !this.isParameterNode(node.arguments[1])
|
|
4091
|
+
? this.transformCtcExpr(_length, toSec)
|
|
4092
|
+
: this.transformExpression(_length, toSec))
|
|
4093
|
+
.append(')');
|
|
4094
|
+
}
|
|
4095
|
+
transformCallSigHashType(node, toSection) {
|
|
4096
|
+
return toSection
|
|
4097
|
+
.append('SigHashType', this.getCoordinates(node.getStart()))
|
|
4098
|
+
.append('(')
|
|
4099
|
+
.appendWith(this, (toSec) => {
|
|
4100
|
+
if (node.arguments[0].kind !== typescript_1.default.SyntaxKind.NumericLiteral) {
|
|
4101
|
+
throw new types_1.TranspileError(`Only support numeric literal: [65,66,67,193,194,195]`, this.getRange(node));
|
|
4102
|
+
}
|
|
4103
|
+
const n = node.arguments[0];
|
|
4104
|
+
const val = parseInt(n.getText());
|
|
4105
|
+
if ([65, 66, 67, 193, 194, 195].includes(val)) {
|
|
4106
|
+
return toSec.append(`b'${val.toString(16)}'`);
|
|
4107
|
+
}
|
|
4108
|
+
throw new types_1.TranspileError(`Only support numeric literal: [65,66,67,193,194,195]`, this.getRange(node));
|
|
4109
|
+
})
|
|
4110
|
+
.append(')');
|
|
4111
|
+
}
|
|
4112
|
+
transformCallBuildChangeOutput(_node, toSection) {
|
|
4113
|
+
const methodNode = this.getMethodContainsTheNode(_node);
|
|
4114
|
+
const { shouldAccessThis } = this.shouldAutoAppendChangeAmount(methodNode);
|
|
4115
|
+
this._accessBuiltinsSymbols.add('TxUtils');
|
|
4116
|
+
return toSection
|
|
4117
|
+
.append('\n')
|
|
4118
|
+
.append(shouldAccessThis
|
|
4119
|
+
? snippets_1.CALL_BUILD_CHANGE_OUTPUT.accessThis
|
|
4120
|
+
: snippets_1.CALL_BUILD_CHANGE_OUTPUT.accessArgument);
|
|
4121
|
+
}
|
|
4122
|
+
transformCallCheckInputState(node, toSection) {
|
|
4123
|
+
const dynamicInputIndex = this.isDynamicIndex(node.arguments[0]);
|
|
4124
|
+
const randomId = Math.random().toString(36).substring(2, 15);
|
|
4125
|
+
const inputIndexVar = `__scrypt_ts_input_index_${randomId}`;
|
|
4126
|
+
if (dynamicInputIndex) {
|
|
4127
|
+
// declare a new input index variable
|
|
4128
|
+
toSection
|
|
4129
|
+
.append(`int ${inputIndexVar} = `, this.getCoordinates(node.arguments[0].getStart()))
|
|
4130
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
|
|
4131
|
+
.append(';\n');
|
|
4132
|
+
}
|
|
4133
|
+
// call `StateUtils.checkInputState`
|
|
4134
|
+
this._accessBuiltinsSymbols.add('StateUtils');
|
|
4135
|
+
return toSection
|
|
4136
|
+
.append('\n')
|
|
4137
|
+
.append(`StateUtils.checkInputState(`)
|
|
4138
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
|
|
4139
|
+
.append(', ')
|
|
4140
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[1], toSec))
|
|
4141
|
+
.append(', ')
|
|
4142
|
+
.appendWith(this, (toSec) => {
|
|
4143
|
+
const shouldAccessThis = this.shouldAutoAppendStateArgs(this.getMethodContainsTheNode(node)).shouldAccessThis;
|
|
4144
|
+
toSec.append(shouldAccessThis
|
|
4145
|
+
? `this.${snippets_1.InjectedParam_SpentDataHashes}`
|
|
4146
|
+
: `${snippets_1.InjectedParam_SpentDataHashes}`);
|
|
4147
|
+
return toSec;
|
|
4148
|
+
})
|
|
4149
|
+
.append(');')
|
|
4150
|
+
.append('\n');
|
|
4151
|
+
}
|
|
4152
|
+
transformCallBacktraceToOutpoint(node, toSection) {
|
|
4153
|
+
const methodNode = this.getMethodContainsTheNode(node);
|
|
4154
|
+
const { shouldAccessThis } = this.shouldAutoAppendSighashPreimage(methodNode);
|
|
4155
|
+
this._accessBuiltinsSymbols.add('Backtrace');
|
|
4156
|
+
const spentScriptHashes = `${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SpentScriptHashes}`;
|
|
4157
|
+
const inputIndex = `${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.inputIndex`;
|
|
4158
|
+
return toSection
|
|
4159
|
+
.append('Backtrace.verifyFromOutpoint(')
|
|
4160
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
|
|
4161
|
+
.append(', ')
|
|
4162
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[1], toSec))
|
|
4163
|
+
.append(', ')
|
|
4164
|
+
.append(`${spentScriptHashes}[${inputIndex} * 32 : (${inputIndex} + 1) * 32]`)
|
|
4165
|
+
.append(', ')
|
|
4166
|
+
.appendWith(this, (toSec) => {
|
|
4167
|
+
return this.transformAccessPrevTxHashPreimage(node, toSec).append(`.inputList`);
|
|
4168
|
+
})
|
|
4169
|
+
.append(')');
|
|
4170
|
+
}
|
|
4171
|
+
transformCallBacktraceToScript(node, toSection) {
|
|
4172
|
+
const methodNode = this.getMethodContainsTheNode(node);
|
|
4173
|
+
const { shouldAccessThis } = this.shouldAutoAppendSighashPreimage(methodNode);
|
|
4174
|
+
this._accessBuiltinsSymbols.add('Backtrace');
|
|
4175
|
+
const spentScriptHashes = `${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SpentScriptHashes}`;
|
|
4176
|
+
const inputIndex = `${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.inputIndex`;
|
|
4177
|
+
return toSection
|
|
4178
|
+
.append('Backtrace.verifyFromScript(')
|
|
4179
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
|
|
4180
|
+
.append(', ')
|
|
4181
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[1], toSec))
|
|
4182
|
+
.append(', ')
|
|
4183
|
+
.append(`${spentScriptHashes}[${inputIndex} * 32 : (${inputIndex} + 1) * 32]`)
|
|
4184
|
+
.append(', ')
|
|
4185
|
+
.appendWith(this, (toSec) => {
|
|
4186
|
+
return this.transformAccessPrevTxHashPreimage(node, toSec).append(`.inputList`);
|
|
4187
|
+
})
|
|
4188
|
+
.append(')');
|
|
4189
|
+
}
|
|
4190
|
+
// private transformAccessCurInputStateProof(
|
|
4191
|
+
// node: ts.Node,
|
|
4192
|
+
// toSection: EmittedSection,
|
|
4193
|
+
// ): EmittedSection {
|
|
4194
|
+
// const methodName = `${this._currentContract.name.escapedText.toString()}.${this._currentMethodName}`;
|
|
4195
|
+
// const methodInfo = this.methodInfos.get(methodName);
|
|
4196
|
+
// if (!methodInfo) {
|
|
4197
|
+
// throw new Error(`Method info not found for ${methodName}`);
|
|
4198
|
+
// }
|
|
4199
|
+
// const methodNode = this.getMethodContainsTheNode(node);
|
|
4200
|
+
// const { shouldAccessThis } = this.shouldAutoAppendStateArgs(methodNode);
|
|
4201
|
+
// const expr = methodInfo.accessInfo.accessInputStateProofs
|
|
4202
|
+
// ? `${shouldAccessThis ? INPUT_STATE_PROOF_EXPR.accessThis(InjectedParam_InputIndexVal) : INPUT_STATE_PROOF_EXPR.accessArgument(InjectedParam_InputIndexVal)}`
|
|
4203
|
+
// : `${shouldAccessThis ? 'this.' : ''}${InjectedParam_InputStateProof}`;
|
|
4204
|
+
// return toSection.append(expr);
|
|
4205
|
+
// }
|
|
4206
|
+
transformAccessPrevTxHashPreimage(node, toSection) {
|
|
4207
|
+
const methodNode = this.getMethodContainsTheNode(node);
|
|
4208
|
+
const methodInfo = this.findMethodInfo(methodNode.name.getText());
|
|
4209
|
+
if (!methodInfo) {
|
|
4210
|
+
throw new Error(`Method info not found for ${methodNode.name.getText()}`);
|
|
4211
|
+
}
|
|
4212
|
+
const { shouldAccessThis } = this.shouldAutoAppendPrevTxHashPreimageArgs(methodNode);
|
|
4213
|
+
const expr = shouldAccessThis ? `this.${snippets_1.InjectedProp_PrevTxHashPreimage}` : snippets_1.InjectedProp_PrevTxHashPreimage;
|
|
4214
|
+
return toSection.append(expr);
|
|
4215
|
+
}
|
|
4216
|
+
isDynamicIndex(node) {
|
|
4217
|
+
const indexExpr = this.unwrapNumberConversion(node);
|
|
4218
|
+
return (!this.isCtcExpression(indexExpr) &&
|
|
4219
|
+
!typescript_1.default.isForStatement(this._checker.getSymbolAtLocation(indexExpr)?.declarations[0]?.parent?.parent));
|
|
4220
|
+
}
|
|
4221
|
+
unwrapNumberConversion(node) {
|
|
4222
|
+
// BigInt(x) => x, Number(x) => x
|
|
4223
|
+
if (typescript_1.default.isCallExpression(node) && ['BigInt', 'Number'].includes(node.expression.getText())) {
|
|
4224
|
+
return node.arguments[0];
|
|
4225
|
+
}
|
|
4226
|
+
return node;
|
|
4227
|
+
}
|
|
4228
|
+
transformCallCheckOutputs(node, toSection) {
|
|
4229
|
+
return toSection
|
|
4230
|
+
.append(`hash256(`)
|
|
4231
|
+
.appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
|
|
4232
|
+
.append(`) == ${snippets_1.InjectedParam_SHPreimage}.hashOutputs`);
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
exports.Transpiler = Transpiler;
|
|
4236
|
+
Transpiler.topCtcs = new Map();
|
|
4237
|
+
Transpiler.contractAst = new Map();
|
|
4238
|
+
Transpiler.scryptFileExt = 'scrypt.tpl';
|
|
4239
|
+
//# sourceMappingURL=transpiler.js.map
|