Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,15 @@ class EtsTsArrayParameterTest {

assertTrue(method.cfg.stmts.any { it is EtsIfStmt })
}

@Test
fun `rest parameter metadata survives frontend JSON and model conversion`() {
val dto = roundTrip("export function count(...values: number[]): number { return values.length; }")

val dtoMethod = dto.classes.flatMap { it.methods }.single { it.signature.name == "count" }
val method = dto.toEtsFile().classes.flatMap { it.methods }.single { it.name == "count" }

assertTrue(dtoMethod.signature.parameters.single().isRest)
assertTrue(method.signature.parameters.single().isRest)
}
}
1 change: 1 addition & 0 deletions jacodb-ets/ts-frontend/src/dto/signatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export interface MethodParameterDto {
name: string;
type: TypeDto;
isOptional?: boolean; // Kotlin default: false
/** Call consumers pack trailing raw arguments into the array read by ParameterRef. */
isRest?: boolean; // Kotlin default: false
}

Expand Down
38 changes: 38 additions & 0 deletions jacodb-ets/ts-frontend/test/rest-parameters.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { describe, expect, it } from "vitest";
import { consumers } from "./array-test-consumers";
import { lower, methodByName } from "./util";
import { serializeEtsFile } from "../src/serialize";

describe("rest parameter caller contract", () => {
it("keeps isRest through JSON and uses it for calls with zero and multiple raw arguments", () => {
const source = "function count(...values: number[]): number { return values.length; }";
const { file, diagnostics } = lower(source);
const roundTripped = JSON.parse(serializeEtsFile(file));
const method = methodByName(roundTripped, "count");
const { ir, native } = consumers(source, "count");

expect(diagnostics.messages).toEqual([]);
expect(method.signature.parameters).toHaveLength(1);
expect(method.signature.parameters[0].isRest).toBe(true);
for (const args of [[], [1], [1, 2]]) {
expect(ir(...args)).toBe(native(...args));
expect(ir(...args)).toBe(args.length);
}
});

it("passes raw arguments through frontend calls to the packing consumer", () => {
const source = "function count(first: number, ...rest: number[]) { return rest.length; } function invoke() { return count(1, 2, 3); }";
const { ir, native } = consumers(source, "invoke");

expect(ir()).toBe(native());
expect(ir()).toBe(2);
});

it("packs remaining raw arguments in order, including missing normal arguments", () => {
const { ir, native } = consumers("function f(first: number, ...rest: number[]) { return rest; }", "f");

for (const args of [[], [1], [1, 2], [1, 2, 3]]) {
expect(ir(...args)).toEqual(native(...args));
}
});
});
Loading