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