as-test 0.1.4 → 0.1.5
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/.github/workflows/{nodejs.yml → as-test.yml} +10 -9
- package/.prettierrc +3 -0
- package/CHANGELOG.md +3 -1
- package/README.md +18 -65
- package/as-test.config.json +22 -0
- package/asconfig.json +30 -33
- package/assembly/__tests__/example.spec.ts +70 -0
- package/assembly/coverage.ts +15 -15
- package/assembly/index.ts +195 -132
- package/assembly/reporters/tap.ts +30 -0
- package/assembly/src/expectation.ts +302 -277
- package/assembly/src/group.ts +33 -33
- package/assembly/src/node.ts +7 -7
- package/assembly/test.ts +43 -46
- package/assembly/tsconfig.json +96 -98
- package/assembly/util.ts +90 -87
- package/bin/build.js +119 -0
- package/bin/index.js +159 -0
- package/bin/init.js +40 -0
- package/bin/package.json +3 -0
- package/bin/run.js +53 -0
- package/bin/types.js +41 -0
- package/bin/util.js +23 -0
- package/cli/build.ts +154 -0
- package/cli/index.ts +201 -0
- package/cli/init.ts +47 -0
- package/cli/run.ts +68 -0
- package/cli/tsconfig.json +8 -0
- package/cli/types.ts +34 -0
- package/cli/util.ts +30 -0
- package/index.ts +1 -1
- package/jest.test.js +44 -5
- package/package.json +18 -15
- package/tests/test.tap +14 -0
- package/transform/lib/index.js +391 -397
- package/transform/package.json +4 -4
- package/transform/src/index.ts +474 -506
- package/transform/tsconfig.json +2 -2
- package/assembly/example.spec.ts +0 -5
- package/src/cli.ts +0 -0
package/transform/src/index.ts
CHANGED
|
@@ -1,625 +1,588 @@
|
|
|
1
1
|
import { Transform } from "assemblyscript/dist/transform.js";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
ObjectLiteralExpression,
|
|
25
|
-
Tokenizer
|
|
3
|
+
Parser,
|
|
4
|
+
Source,
|
|
5
|
+
SourceKind,
|
|
6
|
+
Statement,
|
|
7
|
+
Token,
|
|
8
|
+
BinaryExpression,
|
|
9
|
+
CommaExpression,
|
|
10
|
+
ParenthesizedExpression,
|
|
11
|
+
ParameterNode,
|
|
12
|
+
BlockStatement,
|
|
13
|
+
ExpressionStatement,
|
|
14
|
+
FunctionDeclaration,
|
|
15
|
+
IfStatement,
|
|
16
|
+
MethodDeclaration,
|
|
17
|
+
ReturnStatement,
|
|
18
|
+
SwitchCase,
|
|
19
|
+
TernaryExpression,
|
|
20
|
+
NodeKind,
|
|
21
|
+
ArrowKind,
|
|
22
|
+
Node,
|
|
23
|
+
Tokenizer,
|
|
26
24
|
} from "assemblyscript/dist/assemblyscript.js";
|
|
27
25
|
|
|
28
26
|
import { BaseVisitor, SimpleParser } from "visitor-as/dist/index.js";
|
|
29
27
|
import { isStdlib } from "visitor-as/dist/utils.js";
|
|
30
28
|
import { RangeTransform } from "visitor-as/dist/transformRange.js";
|
|
31
29
|
|
|
32
|
-
let ENABLED = false;
|
|
33
|
-
|
|
34
30
|
enum CoverType {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
Function,
|
|
32
|
+
Expression,
|
|
33
|
+
Block,
|
|
38
34
|
}
|
|
39
35
|
|
|
40
36
|
class CoverPoint {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
37
|
+
public file: string = "";
|
|
38
|
+
public hash: string = "";
|
|
39
|
+
public line: number = 0;
|
|
40
|
+
public column: number = 0;
|
|
41
|
+
public type!: CoverType;
|
|
42
|
+
public executed: boolean = false;
|
|
47
43
|
}
|
|
48
44
|
|
|
49
45
|
class CoverageTransform extends BaseVisitor {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
46
|
+
public mustImport: boolean = false;
|
|
47
|
+
public points: Map<string, CoverPoint> = new Map<string, CoverPoint>();
|
|
48
|
+
public globalStatements: Statement[] = [];
|
|
49
|
+
visitBinaryExpression(node: BinaryExpression): void {
|
|
50
|
+
super.visitBinaryExpression(node);
|
|
51
|
+
// @ts-ignore
|
|
52
|
+
if (node.visited) return;
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
node.visited = true;
|
|
55
|
+
const path = node.range.source.normalizedPath;
|
|
56
|
+
|
|
57
|
+
switch (node.operator) {
|
|
58
|
+
case Token.Bar_Bar:
|
|
59
|
+
case Token.Ampersand_Ampersand: {
|
|
60
|
+
const right = node.right;
|
|
61
|
+
const rightLc = getLineCol(node);
|
|
66
62
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
point.hash = hash(point);
|
|
63
|
+
const point = new CoverPoint();
|
|
64
|
+
point.line = rightLc?.line!;
|
|
65
|
+
point.column = rightLc?.column!;
|
|
66
|
+
point.file = path;
|
|
67
|
+
point.type = CoverType.Expression;
|
|
74
68
|
|
|
69
|
+
point.hash = hash(point);
|
|
75
70
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
71
|
+
const replacer = new RangeTransform(node);
|
|
72
|
+
const registerStmt = SimpleParser.parseTopLevelStatement(
|
|
73
|
+
`__REGISTER({
|
|
79
74
|
file: "${point.file}",
|
|
80
75
|
hash: "${point.hash}",
|
|
81
76
|
line: ${point.line},
|
|
82
77
|
column: ${point.column},
|
|
83
78
|
type: "Expression",
|
|
84
79
|
executed: false
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
});`,
|
|
81
|
+
);
|
|
82
|
+
replacer.visit(registerStmt);
|
|
88
83
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
84
|
+
let coverExpression = SimpleParser.parseExpression(
|
|
85
|
+
`(__COVER("${point.hash}"), $$REPLACE_ME)`,
|
|
86
|
+
) as ParenthesizedExpression;
|
|
87
|
+
replacer.visit(coverExpression);
|
|
93
88
|
|
|
94
|
-
|
|
89
|
+
(coverExpression.expression as CommaExpression).expressions[1] = right;
|
|
95
90
|
|
|
96
|
-
|
|
91
|
+
node.right = coverExpression;
|
|
97
92
|
|
|
98
|
-
|
|
93
|
+
this.globalStatements.push(registerStmt);
|
|
99
94
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
103
97
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
98
|
+
}
|
|
99
|
+
visitMethodDeclaration(node: MethodDeclaration): void {
|
|
100
|
+
super.visitMethodDeclaration(node);
|
|
101
|
+
// @ts-ignore
|
|
102
|
+
if (node.visited) return;
|
|
103
|
+
// @ts-ignore
|
|
104
|
+
node.visited = true;
|
|
105
|
+
if (node.body) {
|
|
106
|
+
// @ts-ignore
|
|
107
|
+
if (node.body.visited) return;
|
|
108
|
+
// @ts-ignore
|
|
109
|
+
node.body.visited = true;
|
|
110
|
+
const path = node.range.source.normalizedPath;
|
|
111
|
+
const funcLc = getLineCol(node);
|
|
112
|
+
|
|
113
|
+
const point = new CoverPoint();
|
|
114
|
+
point.line = funcLc?.line!;
|
|
115
|
+
point.column = funcLc?.column!;
|
|
116
|
+
point.file = path;
|
|
117
|
+
point.type = CoverType.Function;
|
|
118
|
+
|
|
119
|
+
point.hash = hash(point);
|
|
120
|
+
|
|
121
|
+
const replacer = new RangeTransform(node);
|
|
122
|
+
const registerStmt = SimpleParser.parseTopLevelStatement(
|
|
123
|
+
`__REGISTER({
|
|
130
124
|
file: "${point.file}",
|
|
131
125
|
hash: "${point.hash}",
|
|
132
126
|
line: ${point.line},
|
|
133
127
|
column: ${point.column},
|
|
134
128
|
type: "Function",
|
|
135
129
|
executed: false
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
|
|
130
|
+
})`,
|
|
131
|
+
);
|
|
132
|
+
replacer.visit(registerStmt);
|
|
139
133
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
134
|
+
const coverStmt = SimpleParser.parseStatement(
|
|
135
|
+
`__COVER("${point.hash}")`,
|
|
136
|
+
true,
|
|
137
|
+
);
|
|
138
|
+
replacer.visit(coverStmt);
|
|
145
139
|
|
|
146
|
-
|
|
147
|
-
|
|
140
|
+
const bodyBlock = node.body as BlockStatement;
|
|
141
|
+
bodyBlock.statements.unshift(coverStmt);
|
|
148
142
|
|
|
149
|
-
|
|
150
|
-
}
|
|
143
|
+
this.globalStatements.push(registerStmt);
|
|
151
144
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
145
|
+
}
|
|
146
|
+
visitParameter(node: ParameterNode): void {
|
|
147
|
+
// @ts-ignore
|
|
148
|
+
if (node.visited) return;
|
|
149
|
+
// @ts-ignore
|
|
150
|
+
node.visited = true;
|
|
151
|
+
const path = node.range.source.normalizedPath;
|
|
152
|
+
if (node.initializer) {
|
|
153
|
+
// @ts-ignore
|
|
154
|
+
if (node.initializer.visited) return;
|
|
155
|
+
// @ts-ignore
|
|
156
|
+
node.initializer.visited = true;
|
|
157
|
+
super.visitParameter(node);
|
|
158
|
+
const paramLc = getLineCol(node.initializer);
|
|
159
|
+
|
|
160
|
+
const point = new CoverPoint();
|
|
161
|
+
point.line = paramLc?.line!;
|
|
162
|
+
point.column = paramLc?.column!;
|
|
163
|
+
point.file = path;
|
|
164
|
+
point.type = CoverType.Expression;
|
|
165
|
+
|
|
166
|
+
point.hash = hash(point);
|
|
167
|
+
|
|
168
|
+
const replacer = new RangeTransform(node);
|
|
169
|
+
const registerStmt = SimpleParser.parseTopLevelStatement(
|
|
170
|
+
`__REGISTER({
|
|
178
171
|
file: "${point.file}",
|
|
179
172
|
hash: "${point.hash}",
|
|
180
173
|
line: ${point.line},
|
|
181
174
|
column: ${point.column},
|
|
182
175
|
type: "Expression",
|
|
183
176
|
executed: false
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
|
|
177
|
+
})`,
|
|
178
|
+
);
|
|
179
|
+
replacer.visit(registerStmt);
|
|
187
180
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
181
|
+
const coverExpression = SimpleParser.parseExpression(
|
|
182
|
+
`(__COVER("${point.hash}"), $$REPLACE_ME)`,
|
|
183
|
+
) as ParenthesizedExpression;
|
|
184
|
+
replacer.visit(coverExpression);
|
|
192
185
|
|
|
193
|
-
|
|
186
|
+
(coverExpression.expression as CommaExpression).expressions[1] =
|
|
187
|
+
node.initializer;
|
|
194
188
|
|
|
195
|
-
|
|
189
|
+
node.initializer = coverExpression;
|
|
196
190
|
|
|
197
|
-
|
|
198
|
-
}
|
|
191
|
+
this.globalStatements.push(registerStmt);
|
|
199
192
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
193
|
+
}
|
|
194
|
+
visitFunctionDeclaration(
|
|
195
|
+
node: FunctionDeclaration,
|
|
196
|
+
isDefault?: boolean | undefined,
|
|
197
|
+
): void {
|
|
198
|
+
super.visitFunctionDeclaration(node, isDefault);
|
|
199
|
+
// @ts-ignore
|
|
200
|
+
if (node.visited) return;
|
|
201
|
+
// @ts-ignore
|
|
202
|
+
node.visited = true;
|
|
203
|
+
if (node.body) {
|
|
204
|
+
// @ts-ignore
|
|
205
|
+
if (node.body.visited) return;
|
|
206
|
+
// @ts-ignore
|
|
207
|
+
node.body.visited = true;
|
|
208
|
+
|
|
209
|
+
const path = node.range.source.normalizedPath;
|
|
210
|
+
const funcLc = getLineCol(node);
|
|
211
|
+
const point = new CoverPoint();
|
|
212
|
+
point.line = funcLc?.line!;
|
|
213
|
+
point.column = funcLc?.column!;
|
|
214
|
+
point.file = path;
|
|
215
|
+
point.type = CoverType.Function;
|
|
216
|
+
|
|
217
|
+
point.hash = hash(point);
|
|
218
|
+
|
|
219
|
+
const replacer = new RangeTransform(node);
|
|
220
|
+
const registerStmt = SimpleParser.parseTopLevelStatement(
|
|
221
|
+
`__REGISTER({
|
|
226
222
|
file: "${point.file}",
|
|
227
223
|
hash: "${point.hash}",
|
|
228
224
|
line: ${point.line},
|
|
229
225
|
column: ${point.column},
|
|
230
226
|
type: "Function",
|
|
231
227
|
executed: false
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
|
|
228
|
+
})`,
|
|
229
|
+
);
|
|
230
|
+
replacer.visit(registerStmt);
|
|
235
231
|
|
|
236
|
-
|
|
232
|
+
this.globalStatements.push(registerStmt);
|
|
237
233
|
|
|
238
|
-
|
|
239
|
-
|
|
234
|
+
if (node.body.kind === NodeKind.Export) {
|
|
235
|
+
const coverStmt = SimpleParser.parseStatement(`{
|
|
240
236
|
__COVER("${point.hash}")
|
|
241
237
|
return $$REPLACE_ME
|
|
242
238
|
}`) as BlockStatement;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const bodyBlock = node.body as BlockStatement;
|
|
271
|
-
bodyBlock.statements.unshift(coverStmt);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
239
|
+
replacer.visit(coverStmt);
|
|
240
|
+
|
|
241
|
+
const bodyReturn = coverStmt.statements[1] as ReturnStatement;
|
|
242
|
+
const body = node.body as ExpressionStatement;
|
|
243
|
+
node.arrowKind = ArrowKind.Single;
|
|
244
|
+
bodyReturn.value = body.expression;
|
|
245
|
+
node.body = body;
|
|
246
|
+
} else {
|
|
247
|
+
const coverStmt = SimpleParser.parseStatement(
|
|
248
|
+
`__COVER("${point.hash}")`,
|
|
249
|
+
true,
|
|
250
|
+
);
|
|
251
|
+
replacer.visit(coverStmt);
|
|
252
|
+
|
|
253
|
+
if (node.body instanceof BlockStatement) {
|
|
254
|
+
node.body.statements.unshift(coverStmt);
|
|
255
|
+
console.log(node);
|
|
256
|
+
} else if (node.body instanceof ExpressionStatement) {
|
|
257
|
+
const expression = (node.body as ExpressionStatement).expression;
|
|
258
|
+
node.body = Node.createBlockStatement(
|
|
259
|
+
[Node.createReturnStatement(expression, expression.range)],
|
|
260
|
+
expression.range,
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
const bodyBlock = node.body as BlockStatement;
|
|
264
|
+
bodyBlock.statements.unshift(coverStmt);
|
|
274
265
|
}
|
|
266
|
+
}
|
|
275
267
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
268
|
+
}
|
|
269
|
+
visitIfStatement(node: IfStatement): void {
|
|
270
|
+
// @ts-ignore
|
|
271
|
+
if (node.visited) return;
|
|
272
|
+
// @ts-ignore
|
|
273
|
+
node.visited = true;
|
|
274
|
+
let visitIfTrue = false;
|
|
275
|
+
let visitIfFalse = false;
|
|
283
276
|
|
|
284
|
-
|
|
285
|
-
|
|
277
|
+
const ifTrue = node.ifTrue;
|
|
278
|
+
const ifFalse = node.ifFalse;
|
|
286
279
|
|
|
287
|
-
|
|
280
|
+
const path = node.range.source.normalizedPath;
|
|
288
281
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
282
|
+
if (ifTrue.kind !== NodeKind.Block) {
|
|
283
|
+
const trueLc = getLineCol(ifTrue);
|
|
284
|
+
const point = new CoverPoint();
|
|
292
285
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
286
|
+
point.line = trueLc?.line!;
|
|
287
|
+
point.column = trueLc?.column!;
|
|
288
|
+
point.file = path;
|
|
289
|
+
point.type = CoverType.Expression;
|
|
297
290
|
|
|
298
|
-
|
|
291
|
+
point.hash = hash(point);
|
|
299
292
|
|
|
293
|
+
const replacer = new RangeTransform(ifTrue);
|
|
300
294
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const registerStmt = SimpleParser.parseTopLevelStatement(
|
|
304
|
-
`__REGISTER({
|
|
295
|
+
const registerStmt = SimpleParser.parseTopLevelStatement(
|
|
296
|
+
`__REGISTER({
|
|
305
297
|
file: "${point.file}",
|
|
306
298
|
hash: "${point.hash}",
|
|
307
299
|
line: ${point.line},
|
|
308
300
|
column: ${point.column},
|
|
309
301
|
type: "Expression",
|
|
310
302
|
executed: false
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const coverStmt = SimpleParser.parseStatement(
|
|
316
|
-
`{__COVER("${point.hash}")};`,
|
|
317
|
-
true
|
|
318
|
-
) as BlockStatement;
|
|
319
|
-
replacer.visit(coverStmt);
|
|
303
|
+
})`,
|
|
304
|
+
);
|
|
305
|
+
replacer.visit(registerStmt);
|
|
320
306
|
|
|
321
|
-
|
|
322
|
-
|
|
307
|
+
const coverStmt = SimpleParser.parseStatement(
|
|
308
|
+
`{__COVER("${point.hash}")};`,
|
|
309
|
+
true,
|
|
310
|
+
) as BlockStatement;
|
|
311
|
+
replacer.visit(coverStmt);
|
|
323
312
|
|
|
324
|
-
|
|
313
|
+
coverStmt.statements.push(ifTrue);
|
|
314
|
+
node.ifTrue = coverStmt;
|
|
325
315
|
|
|
326
|
-
|
|
327
|
-
visitIfFalse = !!ifFalse;
|
|
328
|
-
}
|
|
316
|
+
this.globalStatements.push(registerStmt);
|
|
329
317
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
318
|
+
visitIfTrue = true;
|
|
319
|
+
visitIfFalse = !!ifFalse;
|
|
320
|
+
}
|
|
333
321
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
point.type = CoverType.Expression;
|
|
322
|
+
if (ifFalse && ifFalse.kind !== NodeKind.Block) {
|
|
323
|
+
const falseLc = getLineCol(ifFalse);
|
|
324
|
+
const point = new CoverPoint();
|
|
338
325
|
|
|
339
|
-
|
|
326
|
+
point.line = falseLc?.line!;
|
|
327
|
+
point.column = falseLc?.column!;
|
|
328
|
+
point.file = path;
|
|
329
|
+
point.type = CoverType.Expression;
|
|
340
330
|
|
|
331
|
+
point.hash = hash(point);
|
|
341
332
|
|
|
342
|
-
|
|
333
|
+
const replacer = new RangeTransform(ifTrue);
|
|
343
334
|
|
|
344
|
-
|
|
345
|
-
|
|
335
|
+
const registerStmt = SimpleParser.parseTopLevelStatement(
|
|
336
|
+
`__REGISTER({
|
|
346
337
|
file: "${point.file}",
|
|
347
338
|
hash: "${point.hash}",
|
|
348
339
|
line: ${point.line},
|
|
349
340
|
column: ${point.column},
|
|
350
341
|
type: "Expression",
|
|
351
342
|
executed: false
|
|
352
|
-
})
|
|
353
|
-
|
|
354
|
-
|
|
343
|
+
})`,
|
|
344
|
+
);
|
|
345
|
+
replacer.visit(registerStmt);
|
|
355
346
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
347
|
+
const coverStmt = SimpleParser.parseStatement(
|
|
348
|
+
`{__COVER("${point.hash}")};`,
|
|
349
|
+
true,
|
|
350
|
+
) as BlockStatement;
|
|
351
|
+
replacer.visit(coverStmt);
|
|
361
352
|
|
|
362
|
-
|
|
363
|
-
|
|
353
|
+
coverStmt.statements.push(ifFalse);
|
|
354
|
+
node.ifFalse = coverStmt;
|
|
364
355
|
|
|
365
|
-
|
|
356
|
+
this.globalStatements.push(registerStmt);
|
|
366
357
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
if (visitIfTrue || visitIfFalse) {
|
|
371
|
-
if (visitIfTrue) {
|
|
372
|
-
// @ts-ignore
|
|
373
|
-
if (ifTrue.visited) return;
|
|
374
|
-
// @ts-ignore
|
|
375
|
-
ifTrue.visited = true;
|
|
376
|
-
this._visit(ifTrue);
|
|
377
|
-
}
|
|
378
|
-
if (visitIfFalse) {
|
|
379
|
-
// @ts-ignore
|
|
380
|
-
if (ifFalse.visited) return;
|
|
381
|
-
// @ts-ignore
|
|
382
|
-
ifFalse.visited = true;
|
|
383
|
-
this._visit(ifFalse!);
|
|
384
|
-
}
|
|
385
|
-
} else {
|
|
386
|
-
super.visitIfStatement(node);
|
|
387
|
-
}
|
|
358
|
+
visitIfTrue = true;
|
|
359
|
+
visitIfFalse = true;
|
|
388
360
|
}
|
|
389
|
-
|
|
361
|
+
if (visitIfTrue || visitIfFalse) {
|
|
362
|
+
if (visitIfTrue) {
|
|
390
363
|
// @ts-ignore
|
|
391
|
-
if (
|
|
364
|
+
if (ifTrue.visited) return;
|
|
392
365
|
// @ts-ignore
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
366
|
+
ifTrue.visited = true;
|
|
367
|
+
this._visit(ifTrue);
|
|
368
|
+
}
|
|
369
|
+
if (visitIfFalse) {
|
|
370
|
+
// @ts-ignore
|
|
371
|
+
if (ifFalse.visited) return;
|
|
372
|
+
// @ts-ignore
|
|
373
|
+
ifFalse.visited = true;
|
|
374
|
+
this._visit(ifFalse!);
|
|
375
|
+
}
|
|
376
|
+
} else {
|
|
377
|
+
super.visitIfStatement(node);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
visitTernaryExpression(node: TernaryExpression): void {
|
|
381
|
+
// @ts-ignore
|
|
382
|
+
if (node.visited) return;
|
|
383
|
+
// @ts-ignore
|
|
384
|
+
node.visited = true;
|
|
385
|
+
super.visitTernaryExpression(node);
|
|
386
|
+
|
|
387
|
+
const trueExpression = node.ifThen;
|
|
388
|
+
const falseExpression = node.ifElse;
|
|
389
|
+
|
|
390
|
+
const path = node.range.source.normalizedPath;
|
|
391
|
+
{
|
|
392
|
+
const trueLc = getLineCol(trueExpression);
|
|
393
|
+
const point = new CoverPoint();
|
|
394
|
+
point.line = trueLc?.line!;
|
|
395
|
+
point.column = trueLc?.column!;
|
|
396
|
+
point.file = path;
|
|
397
|
+
point.type = CoverType.Expression;
|
|
398
|
+
|
|
399
|
+
point.hash = hash(point);
|
|
400
|
+
|
|
401
|
+
const replacer = new RangeTransform(trueExpression);
|
|
402
|
+
|
|
403
|
+
const registerStmt = SimpleParser.parseTopLevelStatement(
|
|
404
|
+
`__REGISTER({
|
|
415
405
|
file: "${point.file}",
|
|
416
406
|
hash: "${point.hash}",
|
|
417
407
|
line: ${point.line},
|
|
418
408
|
column: ${point.column},
|
|
419
409
|
type: "Expression",
|
|
420
410
|
executed: false
|
|
421
|
-
})
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
const coverExpression = SimpleParser.parseExpression(
|
|
426
|
-
`(__COVER("${point.hash}"), $$REPLACE_ME)`
|
|
427
|
-
) as ParenthesizedExpression;
|
|
428
|
-
replacer.visit(coverExpression);
|
|
411
|
+
})`,
|
|
412
|
+
);
|
|
413
|
+
replacer.visit(registerStmt);
|
|
429
414
|
|
|
430
|
-
|
|
431
|
-
|
|
415
|
+
const coverExpression = SimpleParser.parseExpression(
|
|
416
|
+
`(__COVER("${point.hash}"), $$REPLACE_ME)`,
|
|
417
|
+
) as ParenthesizedExpression;
|
|
418
|
+
replacer.visit(coverExpression);
|
|
432
419
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
const falseLc = getLineCol(falseExpression);
|
|
437
|
-
const point = new CoverPoint();
|
|
438
|
-
point.line = falseLc?.line!;
|
|
439
|
-
point.column = falseLc?.column!;
|
|
440
|
-
point.file = path;
|
|
441
|
-
point.type = CoverType.Expression;
|
|
420
|
+
(coverExpression.expression as CommaExpression).expressions[1] =
|
|
421
|
+
trueExpression;
|
|
422
|
+
node.ifThen = coverExpression;
|
|
442
423
|
|
|
443
|
-
|
|
424
|
+
this.globalStatements.push(registerStmt);
|
|
425
|
+
}
|
|
426
|
+
{
|
|
427
|
+
const falseLc = getLineCol(falseExpression);
|
|
428
|
+
const point = new CoverPoint();
|
|
429
|
+
point.line = falseLc?.line!;
|
|
430
|
+
point.column = falseLc?.column!;
|
|
431
|
+
point.file = path;
|
|
432
|
+
point.type = CoverType.Expression;
|
|
444
433
|
|
|
434
|
+
point.hash = hash(point);
|
|
445
435
|
|
|
446
|
-
|
|
436
|
+
const replacer = new RangeTransform(falseExpression);
|
|
447
437
|
|
|
448
|
-
|
|
449
|
-
|
|
438
|
+
const registerStmt = SimpleParser.parseTopLevelStatement(
|
|
439
|
+
`__REGISTER({
|
|
450
440
|
file: "${point.file}",
|
|
451
441
|
hash: "${point.hash}",
|
|
452
442
|
line: ${point.line},
|
|
453
443
|
column: ${point.column},
|
|
454
444
|
type: "Expression",
|
|
455
445
|
executed: false
|
|
456
|
-
})
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
446
|
+
})`,
|
|
447
|
+
);
|
|
448
|
+
replacer.visit(registerStmt);
|
|
449
|
+
|
|
450
|
+
const coverExpression = SimpleParser.parseExpression(
|
|
451
|
+
`(__COVER("${point.hash}"), $$REPLACE_ME)`,
|
|
452
|
+
) as ParenthesizedExpression;
|
|
453
|
+
replacer.visit(coverExpression);
|
|
454
|
+
|
|
455
|
+
(coverExpression.expression as CommaExpression).expressions[1] =
|
|
456
|
+
falseExpression;
|
|
457
|
+
node.ifElse = coverExpression;
|
|
458
|
+
this.globalStatements.push(registerStmt);
|
|
469
459
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
460
|
+
}
|
|
461
|
+
visitSwitchCase(node: SwitchCase): void {
|
|
462
|
+
// @ts-ignore
|
|
463
|
+
if (node.visited) return;
|
|
464
|
+
// @ts-ignore
|
|
465
|
+
node.visited = true;
|
|
466
|
+
const path = node.range.source.normalizedPath;
|
|
467
|
+
const caseLc = getLineCol(node);
|
|
468
|
+
|
|
469
|
+
const point = new CoverPoint();
|
|
470
|
+
point.line = caseLc?.line!;
|
|
471
|
+
point.column = caseLc?.column!;
|
|
472
|
+
point.file = path;
|
|
473
|
+
point.type = CoverType.Block;
|
|
474
|
+
|
|
475
|
+
point.hash = hash(point);
|
|
476
|
+
|
|
477
|
+
const replacer = new RangeTransform(node);
|
|
478
|
+
|
|
479
|
+
const registerStmt = SimpleParser.parseTopLevelStatement(
|
|
480
|
+
`__REGISTER({
|
|
491
481
|
file: "${point.file}",
|
|
492
482
|
hash: "${point.hash}",
|
|
493
483
|
line: ${point.line},
|
|
494
484
|
column: ${point.column},
|
|
495
485
|
type: "Block",
|
|
496
486
|
executed: false
|
|
497
|
-
})
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
const registerStmt = SimpleParser.parseTopLevelStatement(
|
|
531
|
-
`__REGISTER({
|
|
487
|
+
})`,
|
|
488
|
+
);
|
|
489
|
+
replacer.visit(registerStmt);
|
|
490
|
+
|
|
491
|
+
const coverStmt = SimpleParser.parseStatement(`__COVER("${point.hash}")`);
|
|
492
|
+
replacer.visit(coverStmt);
|
|
493
|
+
|
|
494
|
+
this.globalStatements.push(registerStmt);
|
|
495
|
+
super.visitSwitchCase(node);
|
|
496
|
+
node.statements.unshift(coverStmt);
|
|
497
|
+
}
|
|
498
|
+
visitBlockStatement(node: BlockStatement): void {
|
|
499
|
+
// @ts-ignore
|
|
500
|
+
if (node.visited) return;
|
|
501
|
+
// @ts-ignore
|
|
502
|
+
node.visited = true;
|
|
503
|
+
const path = node.range.source.normalizedPath;
|
|
504
|
+
|
|
505
|
+
const blockLc = getLineCol(node);
|
|
506
|
+
|
|
507
|
+
const point = new CoverPoint();
|
|
508
|
+
point.line = blockLc?.line!;
|
|
509
|
+
point.column = blockLc?.column!;
|
|
510
|
+
point.file = path;
|
|
511
|
+
point.type = CoverType.Block;
|
|
512
|
+
|
|
513
|
+
point.hash = hash(point);
|
|
514
|
+
|
|
515
|
+
const replacer = new RangeTransform(node);
|
|
516
|
+
|
|
517
|
+
const registerStmt = SimpleParser.parseTopLevelStatement(
|
|
518
|
+
`__REGISTER({
|
|
532
519
|
file: "${point.file}",
|
|
533
520
|
hash: "${point.hash}",
|
|
534
521
|
line: ${point.line},
|
|
535
522
|
column: ${point.column},
|
|
536
523
|
type: "Block",
|
|
537
524
|
executed: false
|
|
538
|
-
})
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
super.visitSource(node);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
class CoverageFinder extends BaseVisitor {
|
|
557
|
-
visitObjectLiteralExpression(node: ObjectLiteralExpression): void {
|
|
558
|
-
for (let i = 0; i < node.names.length; i++) {
|
|
559
|
-
const name = node.names[i]!;
|
|
560
|
-
if (name.text === "coverage") {
|
|
561
|
-
const v = node.values[i]!;
|
|
562
|
-
if (v.kind === NodeKind.True) {
|
|
563
|
-
ENABLED = true;
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
525
|
+
})`,
|
|
526
|
+
);
|
|
527
|
+
replacer.visit(registerStmt);
|
|
528
|
+
|
|
529
|
+
const coverStmt = SimpleParser.parseStatement(`__COVER("${point.hash}")`);
|
|
530
|
+
replacer.visit(coverStmt);
|
|
531
|
+
|
|
532
|
+
this.globalStatements.push(registerStmt);
|
|
533
|
+
super.visitBlockStatement(node);
|
|
534
|
+
node.statements.unshift(coverStmt);
|
|
535
|
+
}
|
|
536
|
+
visitSource(node: Source): void {
|
|
537
|
+
super.visitSource(node);
|
|
538
|
+
}
|
|
568
539
|
}
|
|
569
540
|
|
|
570
541
|
export default class Transformer extends Transform {
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
542
|
+
// Trigger the transform after parse.
|
|
543
|
+
afterParse(parser: Parser): void {
|
|
544
|
+
// Create new transform
|
|
545
|
+
const transformer = new CoverageTransform();
|
|
546
|
+
|
|
547
|
+
// Sort the sources so that user scripts are visited last
|
|
548
|
+
const sources = parser.sources
|
|
549
|
+
.filter((source) => !isStdlib(source))
|
|
550
|
+
.sort((_a, _b) => {
|
|
551
|
+
const a = _a.internalPath;
|
|
552
|
+
const b = _b.internalPath;
|
|
553
|
+
if (a[0] === "~" && b[0] !== "~") {
|
|
554
|
+
return -1;
|
|
555
|
+
} else if (a[0] !== "~" && b[0] === "~") {
|
|
556
|
+
return 1;
|
|
557
|
+
} else {
|
|
558
|
+
return 0;
|
|
578
559
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
for (const source of sources) {
|
|
601
|
-
if (source.isLibrary) continue;
|
|
602
|
-
if (source.simplePath === "coverage") continue;
|
|
603
|
-
// Ignore all lib and std. Visit everything else.
|
|
604
|
-
if (!isStdlib(source)) {
|
|
605
|
-
transformer.visit(source);
|
|
606
|
-
if (transformer.globalStatements.length) {
|
|
607
|
-
source.statements.unshift(...transformer.globalStatements);
|
|
608
|
-
const tokenizer = new Tokenizer(
|
|
609
|
-
new Source(
|
|
610
|
-
SourceKind.User,
|
|
611
|
-
source.normalizedPath,
|
|
612
|
-
"import { __REGISTER, __COVER } from \"as-test/assembly/coverage\";"
|
|
613
|
-
)
|
|
614
|
-
);
|
|
615
|
-
parser.currentSource = tokenizer.source;
|
|
616
|
-
source.statements.unshift(parser.parseTopLevelStatement(tokenizer)!);
|
|
617
|
-
parser.currentSource = source;
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
transformer.globalStatements = [];
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
// Loop over every source
|
|
563
|
+
for (const source of sources) {
|
|
564
|
+
if (source.isLibrary) continue;
|
|
565
|
+
if (source.simplePath === "coverage") continue;
|
|
566
|
+
// Ignore all lib and std. Visit everything else.
|
|
567
|
+
if (!isStdlib(source)) {
|
|
568
|
+
transformer.visit(source);
|
|
569
|
+
if (transformer.globalStatements.length) {
|
|
570
|
+
source.statements.unshift(...transformer.globalStatements);
|
|
571
|
+
const tokenizer = new Tokenizer(
|
|
572
|
+
new Source(
|
|
573
|
+
SourceKind.User,
|
|
574
|
+
source.normalizedPath,
|
|
575
|
+
'import { __REGISTER, __COVER } from "as-test/assembly/coverage";',
|
|
576
|
+
),
|
|
577
|
+
);
|
|
578
|
+
parser.currentSource = tokenizer.source;
|
|
579
|
+
source.statements.unshift(parser.parseTopLevelStatement(tokenizer)!);
|
|
580
|
+
parser.currentSource = source;
|
|
621
581
|
}
|
|
582
|
+
}
|
|
583
|
+
transformer.globalStatements = [];
|
|
622
584
|
}
|
|
585
|
+
}
|
|
623
586
|
}
|
|
624
587
|
|
|
625
588
|
/**
|
|
@@ -630,32 +593,37 @@ export default class Transformer extends Transform {
|
|
|
630
593
|
* @returns {number} The hash of the string
|
|
631
594
|
*/
|
|
632
595
|
function djb2Hash(str: string): number {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
596
|
+
const points = Array.from(str);
|
|
597
|
+
let h = 5381;
|
|
598
|
+
for (let p = 0; p < points.length; p++)
|
|
599
|
+
// h = (h * 31 + c) | 0;
|
|
600
|
+
h = ((h << 5) - h + points[p]!.codePointAt(0)!) | 0;
|
|
601
|
+
return h;
|
|
639
602
|
}
|
|
640
603
|
|
|
641
604
|
function hash(point: CoverPoint): string {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
605
|
+
const hsh = djb2Hash(
|
|
606
|
+
point.file +
|
|
607
|
+
point.line.toString() +
|
|
608
|
+
point.column.toString() +
|
|
609
|
+
point.type.toString(),
|
|
610
|
+
);
|
|
611
|
+
if (hsh < 0) {
|
|
612
|
+
const out = hsh.toString(16);
|
|
613
|
+
return "3" + out.slice(1);
|
|
614
|
+
} else {
|
|
615
|
+
return hsh.toString(16);
|
|
616
|
+
}
|
|
649
617
|
}
|
|
650
618
|
|
|
651
619
|
class LineColumn {
|
|
652
|
-
|
|
653
|
-
|
|
620
|
+
public line!: number;
|
|
621
|
+
public column!: number;
|
|
654
622
|
}
|
|
655
623
|
|
|
656
624
|
function getLineCol(node: Node): LineColumn {
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}
|
|
625
|
+
return {
|
|
626
|
+
line: node.range.source.lineAt(node.range.start),
|
|
627
|
+
column: node.range.source.columnAt(),
|
|
628
|
+
};
|
|
629
|
+
}
|