-
Notifications
You must be signed in to change notification settings - Fork 3
/
metaprogramming.ts
112 lines (92 loc) · 2.98 KB
/
metaprogramming.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { readFileSync } from 'fs'
import * as ts from 'typescript'
interface MethodCall {
name: string
args: Array<{ name: string, type: string }>
returnType: string,
call: string
}
export function findSmartContractMethodDefinitions(sourceFile: ts.SourceFile, targetClassName: string): MethodCall[] {
function getMethodCallsFromNodes(nodes: ts.Node[]): MethodCall[] {
let res: MethodCall[] = []
function findFunctionCall(node: ts.Node, name: string) {
let call
function recurse(node: ts.Node) {
if (node.kind == ts.SyntaxKind.CallExpression && node.getText().indexOf(name) == 0) {
let srcFileStr = node.getSourceFile().text
let start = node.getStart()
while (srcFileStr.charAt(start) != '\n' && start > 0) {
start -= 1
}
call = srcFileStr.slice(start + 1, node.getEnd() + 1).replace(/^\s*/, '')
}
ts.forEachChild(node, recurse)
}
ts.forEachChild(node, recurse)
return call
}
nodes.forEach((node: ts.MethodDeclaration) => {
let methodName = node.name.getText()
let args = node.parameters.map((p: ts.ParameterDeclaration) => {
return {
name: p.name.getText(),
type: p.type!.getText(),
}
})
let typeMatch = node.getText().match(/Promise<([^>]+)>/)
let returnType = typeMatch && typeMatch[1] || '<invalid>'
let call = findFunctionCall(node, 'this._call')
res.push({
name: methodName,
args,
returnType,
call,
})
})
return res
}
function findSmartContractCalls(node: ts.ClassDeclaration) {
let res: ts.MethodDeclaration[] = []
ts.forEachChild(node, (node: ts.Node) => {
if (node.kind == ts.SyntaxKind.MethodDeclaration) {
let methodText = node.getText()
if (methodText.indexOf('this._call') >= 0)
res.push(node as ts.MethodDeclaration)
}
})
return res
}
function findClass(sourceFile: ts.SourceFile, targetClassName: string) {
let res
function recurse(node: ts.Node) {
switch (node.kind) {
case ts.SyntaxKind.ClassDeclaration:
let clsDef = node as ts.ClassDeclaration
if (clsDef.name && clsDef.name.getText() == targetClassName)
res = clsDef
return
case ts.SyntaxKind.ForStatement:
case ts.SyntaxKind.ForInStatement:
case ts.SyntaxKind.WhileStatement:
case ts.SyntaxKind.DoStatement:
case ts.SyntaxKind.IfStatement:
case ts.SyntaxKind.BinaryExpression:
return
}
ts.forEachChild(node, recurse)
}
recurse(sourceFile)
return res
}
let cls = findClass(sourceFile, targetClassName)
let methodNodes = findSmartContractCalls(cls)
return getMethodCallsFromNodes(methodNodes)
}
export function loadSourceFile(name: string) {
return ts.createSourceFile(
name,
readFileSync(name).toString(),
ts.ScriptTarget.ES2016,
/*setParentNodes */ true
)
}