From b0315ec663b2750b61b607af4d6eb1a658897ff3 Mon Sep 17 00:00:00 2001 From: quininer Date: Mon, 3 Nov 2025 20:08:23 +0800 Subject: [PATCH 1/3] Add regression test for function --- _packages/api/test/api.test.ts | 46 ++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/_packages/api/test/api.test.ts b/_packages/api/test/api.test.ts index a01b029fac..14110c63f1 100644 --- a/_packages/api/test/api.test.ts +++ b/_packages/api/test/api.test.ts @@ -11,6 +11,13 @@ import { isTemplateHead, isTemplateMiddle, isTemplateTail, + isFunctionDeclaration, + isExpressionStatement, + isCallExpression, + isPropertyAccessExpression, + isIdentifier, + isStringLiteral, + SyntaxKind, } from "@typescript/ast"; import assert from "node:assert"; import { @@ -151,6 +158,45 @@ test("Dispose", () => { }); }); +test("Function", () => { + const currentFiles = { + "/tsconfig.json": "{}", + "/src/index.ts": `function foo() { + console.log("hello", "world") + }`, + }; + + let api = spawnAPI(currentFiles); + const project = api.loadProject("/tsconfig.json"); + const sourceFile = project.getSourceFile("/src/index.ts"); + + let func = sourceFile?.statements[0]!; + assert.ok(isFunctionDeclaration(func)); + + let body = func.body!; + let expr = body.statements[0]!; + assert.ok(isExpressionStatement(expr)); + let call_expr = expr.expression; + assert.ok(isCallExpression(call_expr)); + + let callee = call_expr.expression; + assert.ok(isPropertyAccessExpression(callee)); + let left = callee.expression; + let right = callee.name; + assert.ok(isIdentifier(left)); + assert.ok(isIdentifier(right)); + assert.equal(left.text, "console"); + assert.equal(right.text, "log"); + + let args = call_expr.arguments; + let arg0 = args[0]; + let arg1 = args[1]; + assert.ok(isStringLiteral(arg0)); + assert.ok(isStringLiteral(arg1)); + assert.equal(arg0.text, "hello"); + assert.equal(arg1.text, "world"); +}); + test("Benchmarks", async () => { await runBenchmarks(/*singleIteration*/ true); }); From c706a2996256b307a5a31a79c01ec383c3f191df Mon Sep 17 00:00:00 2001 From: quininer Date: Mon, 3 Nov 2025 20:25:29 +0800 Subject: [PATCH 2/3] fix function params encode --- internal/api/encoder/encoder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/encoder/encoder.go b/internal/api/encoder/encoder.go index 2c40c6cb80..d353939cae 100644 --- a/internal/api/encoder/encoder.go +++ b/internal/api/encoder/encoder.go @@ -440,7 +440,7 @@ func getChildrenPropertyMask(node *ast.Node) uint8 { return (boolToByte(n.DotDotDotToken != nil) << 0) | (boolToByte(n.PropertyName != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.Initializer != nil) << 3) case ast.KindFunctionDeclaration: n := node.AsFunctionDeclaration() - return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.AsteriskToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.TypeParameters != nil) << 3) | (boolToByte(n.Parameters != nil) << 4) | (boolToByte(n.Type != nil) << 5) | (boolToByte(n.Body != nil) << 6) + return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.AsteriskToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.TypeParameters != nil) << 3) | (boolToByte(n.Parameters != nil && len(n.Parameters.Nodes) > 0) << 4) | (boolToByte(n.Type != nil) << 5) | (boolToByte(n.Body != nil) << 6) case ast.KindInterfaceDeclaration: n := node.AsInterfaceDeclaration() return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.HeritageClauses != nil) << 3) | (boolToByte(n.Members != nil) << 4) From da56f163200ee7880c2134cf821ef08372383f7b Mon Sep 17 00:00:00 2001 From: quininer Date: Mon, 3 Nov 2025 20:39:03 +0800 Subject: [PATCH 3/3] fix child at index --- _packages/api/src/node.ts | 26 +++++++++++++++++++++++++- _packages/api/test/api.test.ts | 1 - 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/_packages/api/src/node.ts b/_packages/api/src/node.ts index 3145e84ae8..bfd5cbb469 100644 --- a/_packages/api/src/node.ts +++ b/_packages/api/src/node.ts @@ -390,6 +390,29 @@ export class RemoteNode extends RemoteNodeBase implements Node { return this.decoder.decode(text); } + private getChildAtNodeIndex(parent: number, index: number): RemoteNode | RemoteNodeList | undefined { + let next = parent + 1; + let count = 0; + while (next !== 0) { + let child = new RemoteNode(this.view, this.decoder, next, this); + + if (child.parent.index !== parent) { + return undefined; + } + + if (count === index) { + if (child.kind == KIND_NODE_LIST) { + return new RemoteNodeList(this.view, this.decoder, child.index, this); + } else { + return child; + } + } + + count += 1; + next = child.next; + } + } + private getOrCreateChildAtNodeIndex(index: number): RemoteNode | RemoteNodeList { const pos = this.view.getUint32(this.offsetNodes + index * NODE_LEN + NODE_OFFSET_POS, true); let child = (this._children ??= new Map()).get(pos); @@ -487,7 +510,8 @@ export class RemoteNode extends RemoteNodeBase implements Node { // were present, we would have `parameters = children[5]`, but since `postfixToken` and `astersiskToken` are // missing, we have `parameters = children[5 - 2]`. const propertyIndex = order - popcount8[~(mask | ((0xff << order) & 0xff)) & 0xff]; - return this.getOrCreateChildAtNodeIndex(this.index + 1 + propertyIndex); + return this.getChildAtNodeIndex(this.index, propertyIndex); + // return this.getOrCreateChildAtNodeIndex(this.index + 1 + propertyIndex); } __print(): string { diff --git a/_packages/api/test/api.test.ts b/_packages/api/test/api.test.ts index 14110c63f1..549e849229 100644 --- a/_packages/api/test/api.test.ts +++ b/_packages/api/test/api.test.ts @@ -17,7 +17,6 @@ import { isPropertyAccessExpression, isIdentifier, isStringLiteral, - SyntaxKind, } from "@typescript/ast"; import assert from "node:assert"; import {