as-test 0.1.7 → 0.1.8

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