UNPKG

assemblyscript-unittest-framework

Version:
539 lines (493 loc) 14.5 kB
import { Transform } from "assemblyscript/transform"; import { NodeKind, SourceKind, CommonFlags, DeclarationStatement, DeclaredElement, Node, Source, ParameterNode, AssertionExpression, BinaryExpression, CallExpression, ClassExpression, CommaExpression, ElementAccessExpression, FunctionExpression, InstanceOfExpression, NewExpression, ParenthesizedExpression, PropertyAccessExpression, TernaryExpression, UnaryPostfixExpression, UnaryPrefixExpression, BlockStatement, DoStatement, ExpressionStatement, ForStatement, ForOfStatement, IfStatement, ReturnStatement, SwitchStatement, ThrowStatement, TryStatement, VariableStatement, VoidStatement, WhileStatement, ClassDeclaration, EnumDeclaration, EnumValueDeclaration, FieldDeclaration, FunctionDeclaration, InterfaceDeclaration, MethodDeclaration, NamespaceDeclaration, VariableDeclaration, SwitchCase, Program, } from "assemblyscript"; // This interface is same as SourceFunctionInfo in src/interface.ts // Copy it here to avoid tsc compiling interface.js interface SourceFunctionInfo { name: string; range: [number, number]; } class SourceFunctionTransform extends Transform { functionInfos: SourceFunctionInfo[] = []; #elementsByDeclaration: Map<DeclarationStatement, DeclaredElement> = new Map(); afterInitialize(program: Program) { this.#elementsByDeclaration = program.elementsByDeclaration; // There will be two sources with SourceKind.UserEntry, ~lib/rt/index-incremental.ts should be filtered const entrySource = program.sources.find( (source) => source.sourceKind === SourceKind.UserEntry && !source.normalizedPath.startsWith("~lib/") ); this.visitNode(entrySource); this.functionInfos.reverse(); globalThis.functionInfos = this.functionInfos; } visitNode(node: Node) { // eslint-disable-next-line sonarjs/max-switch-cases switch (node.kind) { case NodeKind.Source: { this.visitSource(node as Source); break; } // types case NodeKind.NamedType: case NodeKind.FunctionType: case NodeKind.TypeName: case NodeKind.TypeParameter: { break; } case NodeKind.Parameter: { this.visitParameterNode(node as ParameterNode); break; } // Expressions case NodeKind.Identifier: case NodeKind.False: case NodeKind.Literal: case NodeKind.Null: case NodeKind.Omitted: case NodeKind.Super: case NodeKind.This: case NodeKind.True: case NodeKind.Constructor: case NodeKind.Compiled: { break; } case NodeKind.Assertion: { this.visitAssertionExpression(node as AssertionExpression); break; } case NodeKind.Binary: { this.visitBinaryExpression(node as BinaryExpression); break; } case NodeKind.Call: { this.visitCallExpression(node as CallExpression); break; } case NodeKind.Class: { this.visitClassExpression(node as ClassExpression); break; } case NodeKind.Comma: { this.visitCommaExpression(node as CommaExpression); break; } case NodeKind.ElementAccess: { this.visitElementAccessExpression(node as ElementAccessExpression); break; } case NodeKind.Function: { this.visitFunctionExpression(node as FunctionExpression); break; } case NodeKind.InstanceOf: { this.visitInstanceOfExpression(node as InstanceOfExpression); break; } case NodeKind.New: { this.visitNewExpression(node as NewExpression); break; } case NodeKind.Parenthesized: { this.visitParenthesizedExpression(node as ParenthesizedExpression); break; } case NodeKind.PropertyAccess: { this.visitPropertyAccessExpression(node as PropertyAccessExpression); break; } case NodeKind.Ternary: { this.visitTernaryExpression(node as TernaryExpression); break; } case NodeKind.UnaryPostfix: { this.visitUnaryPostfixExpression(node as UnaryPostfixExpression); break; } case NodeKind.UnaryPrefix: { this.visitUnaryPrefixExpression(node as UnaryPrefixExpression); break; } // statements: case NodeKind.Break: case NodeKind.Empty: case NodeKind.Export: case NodeKind.ExportDefault: case NodeKind.ExportImport: case NodeKind.Continue: case NodeKind.Import: case NodeKind.Module: { break; } case NodeKind.Block: { this.visitBlockStatement(node as BlockStatement); break; } case NodeKind.Do: { this.visitDoStatement(node as DoStatement); break; } case NodeKind.Expression: { this.visitExpressionStatement(node as ExpressionStatement); break; } case NodeKind.For: { this.visitForStatement(node as ForStatement); break; } case NodeKind.ForOf: { this.visitForOfStatement(node as ForOfStatement); break; } case NodeKind.If: { this.visitIfStatement(node as IfStatement); break; } case NodeKind.Return: { this.visitReturnStatement(node as ReturnStatement); break; } case NodeKind.Switch: { this.visitSwitchStatement(node as SwitchStatement); break; } case NodeKind.Throw: { this.visitThrowStatement(node as ThrowStatement); break; } case NodeKind.Try: { this.visitTryStatement(node as TryStatement); break; } case NodeKind.Variable: { this.visitVariableStatement(node as VariableStatement); break; } case NodeKind.Void: { this.visitVoidStatement(node as VoidStatement); break; } case NodeKind.While: { this.visitWhileStatement(node as WhileStatement); break; } // declaration statements case NodeKind.ImportDeclaration: case NodeKind.TypeDeclaration: { break; } case NodeKind.ClassDeclaration: { this.visitClassDeclaration(node as ClassDeclaration); break; } case NodeKind.EnumDeclaration: { this.visitEnumDeclaration(node as EnumDeclaration); break; } case NodeKind.EnumValueDeclaration: { this.visitEnumValueDeclaration(node as EnumValueDeclaration); break; } case NodeKind.FieldDeclaration: { this.visitFieldDeclaration(node as FieldDeclaration); break; } case NodeKind.FunctionDeclaration: { this.visitFunctionDeclaration(node as FunctionDeclaration); break; } case NodeKind.InterfaceDeclaration: { this.visitInterfaceDeclaration(node as InterfaceDeclaration); break; } case NodeKind.MethodDeclaration: { this.visitMethodDeclaration(node as MethodDeclaration); break; } case NodeKind.NamespaceDeclaration: { this.visitNamespaceDeclaration(node as NamespaceDeclaration); break; } case NodeKind.VariableDeclaration: { this.visitVariableDeclaration(node as VariableDeclaration); break; } // special case NodeKind.ExportMember: case NodeKind.IndexSignature: case NodeKind.Comment: case NodeKind.Decorator: { break; } case NodeKind.SwitchCase: { this.visitSwitchCase(node as SwitchCase); break; } } } visitSource(node: Source) { for (const statement of node.statements) { this.visitNode(statement); } } visitParameterNode(node: ParameterNode) { if (node.initializer) { this.visitNode(node.initializer); } } visitAssertionExpression(node: AssertionExpression) { this.visitNode(node.expression); } visitBinaryExpression(node: BinaryExpression) { this.visitNode(node.left); this.visitNode(node.right); } visitCallExpression(node: CallExpression) { this.visitNode(node.expression); for (const arg of node.args) { this.visitNode(arg); } } visitClassExpression(node: ClassExpression) { this.visitClassDeclaration(node.declaration); } visitCommaExpression(node: CommaExpression) { for (const expr of node.expressions) { this.visitNode(expr); } } visitElementAccessExpression(node: ElementAccessExpression) { this.visitNode(node.expression); this.visitNode(node.elementExpression); } visitFunctionExpression(node: FunctionExpression) { this.visitFunctionDeclaration(node.declaration); } visitInstanceOfExpression(node: InstanceOfExpression) { this.visitNode(node.expression); } visitNewExpression(node: NewExpression) { for (const arg of node.args) { this.visitNode(arg); } } visitParenthesizedExpression(node: ParenthesizedExpression) { this.visitNode(node.expression); } visitPropertyAccessExpression(node: PropertyAccessExpression) { this.visitNode(node.expression); } visitTernaryExpression(node: TernaryExpression) { this.visitNode(node.condition); this.visitNode(node.ifThen); this.visitNode(node.ifElse); } visitUnaryPostfixExpression(node: UnaryPostfixExpression) { this.visitNode(node.operand); } visitUnaryPrefixExpression(node: UnaryPrefixExpression) { this.visitNode(node.operand); } // eslint-disable-next-line sonarjs/no-identical-functions visitBlockStatement(node: BlockStatement) { for (const statement of node.statements) { this.visitNode(statement); } } visitDoStatement(node: DoStatement) { this.visitNode(node.body); this.visitNode(node.condition); } visitExpressionStatement(node: ExpressionStatement) { this.visitNode(node.expression); } visitForStatement(node: ForStatement) { if (node.initializer) { this.visitNode(node.initializer); } if (node.condition) { this.visitNode(node.condition); } if (node.incrementor) { this.visitNode(node.incrementor); } this.visitNode(node.body); } visitForOfStatement(node: ForOfStatement) { this.visitNode(node.variable); this.visitNode(node.iterable); this.visitNode(node.body); } visitIfStatement(node: IfStatement) { this.visitNode(node.condition); this.visitNode(node.ifTrue); if (node.ifFalse) { this.visitNode(node.ifFalse); } } visitReturnStatement(node: ReturnStatement) { if (node.value) { this.visitNode(node.value); } } visitSwitchStatement(node: SwitchStatement) { this.visitNode(node.condition); for (const switchCase of node.cases) { this.visitSwitchCase(switchCase); } } visitThrowStatement(node: ThrowStatement) { this.visitNode(node.value); } visitTryStatement(node: TryStatement) { for (const stat of node.bodyStatements) { this.visitNode(stat); } if (node.catchStatements) { for (const stat of node.catchStatements) { this.visitNode(stat); } } if (node.finallyStatements) { for (const stat of node.finallyStatements) { this.visitNode(stat); } } } visitVariableStatement(node: VariableStatement) { for (const declaration of node.declarations) { this.visitVariableDeclaration(declaration); } } visitVoidStatement(node: VoidStatement) { this.visitNode(node.expression); } visitWhileStatement(node: WhileStatement) { this.visitNode(node.condition); this.visitNode(node.body); } visitClassDeclaration(node: ClassDeclaration) { for (const member of node.members) { this.visitNode(member); } } visitEnumDeclaration(node: EnumDeclaration) { for (const value of node.values) { this.visitEnumValueDeclaration(value); } } // eslint-disable-next-line sonarjs/no-identical-functions visitEnumValueDeclaration(node: EnumValueDeclaration) { if (node.initializer) { this.visitNode(node.initializer); } } // eslint-disable-next-line sonarjs/no-identical-functions visitFieldDeclaration(node: FieldDeclaration) { if (node.initializer) { this.visitNode(node.initializer); } } visitFunctionDeclaration(node: FunctionDeclaration) { if (!(node.flags & (CommonFlags.Ambient | CommonFlags.Abstract))) { let startLine: number, endLine: number; // startLine is the first Line of Function.body, same as endLine if (node.body) { if (node.body.kind === NodeKind.Block && (node.body as BlockStatement).statements.length > 0) { const bodyStatement = (node.body as BlockStatement).statements; const startStat = bodyStatement[0]; startLine = startStat.range.source.lineAt(startStat.range.start); const endStat = bodyStatement.at(-1); endLine = endStat.range.source.lineAt(endStat.range.end); } else { if (node.flags & CommonFlags.Constructor) { // do not count constructor without any statements return; } const LineRange = node.body.range; startLine = LineRange.source.lineAt(LineRange.start); endLine = LineRange.source.lineAt(LineRange.end); } this.functionInfos.push({ name: this.#elementsByDeclaration.get(node)?.internalName ?? node.name.text, range: [startLine, endLine], }); } } if (node.body) { this.visitNode(node.body); } } visitInterfaceDeclaration(node: InterfaceDeclaration) { this.visitClassDeclaration(node); } visitMethodDeclaration(node: MethodDeclaration) { this.visitFunctionDeclaration(node); } // eslint-disable-next-line sonarjs/no-identical-functions visitNamespaceDeclaration(node: NamespaceDeclaration) { for (const member of node.members) { this.visitNode(member); } } // eslint-disable-next-line sonarjs/no-identical-functions visitVariableDeclaration(node: VariableDeclaration) { if (node.initializer) { this.visitNode(node.initializer); } } visitSwitchCase(node: SwitchCase) { if (node.label) { this.visitNode(node.label); } for (const stat of node.statements) { this.visitNode(stat); } } } export default SourceFunctionTransform;