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
48 changes: 45 additions & 3 deletions src/codegen/stdlib/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -595,14 +595,27 @@ export class JsonGenerator {
);
}

private extractInterfaceFromLlvmType(llvmType: string): string | null {
if (llvmType.startsWith("%") && llvmType.endsWith("*")) {
return llvmType.slice(1, -1);
}
return null;
}

private resolveInterfaceType(arg: Expression): string | null {
if (arg.type === "variable") {
const varNode = arg as { type: string; name: string };
return (
const fromSymbol =
this.ctx.symbolTable.getInterfaceType(varNode.name) ||
this.ctx.symbolTable.getRawInterfaceType(varNode.name) ||
null
);
null;
if (fromSymbol) return fromSymbol;
const llvmType = this.ctx.getVariableType(varNode.name);
if (llvmType) {
const extracted = this.extractInterfaceFromLlvmType(llvmType);
if (extracted && this.ctx.interfaceStructGenHasInterface(extracted)) return extracted;
}
return null;
}
if (arg.type === "index_access") {
const indexAccess = arg as { type: string; object: Expression; index: Expression };
Expand All @@ -619,6 +632,24 @@ export class JsonGenerator {
return null;
}
}
if (arg.type === "member_access") {
const memberAccess = arg as { type: string; object: Expression; property: string };
const objType = this.resolveInterfaceType(memberAccess.object);
if (objType && this.ctx.interfaceStructGenHasInterface(objType)) {
const fieldCount = this.ctx.interfaceStructGenGetFieldCount(objType);
for (let i = 0; i < fieldCount; i++) {
const rawName = this.ctx.interfaceStructGenGetFieldName(objType, i);
const fName =
rawName.charAt(rawName.length - 1) === "?"
? rawName.substring(0, rawName.length - 1)
: rawName;
if (fName === memberAccess.property) {
const fTsType = this.ctx.interfaceStructGenGetFieldTsType(objType, i);
if (this.ctx.interfaceStructGenHasInterface(fTsType)) return fTsType;
}
}
}
}
return null;
}

Expand Down Expand Up @@ -697,6 +728,17 @@ export class JsonGenerator {
"@csyyjson_obj_add_bool",
`i8* ${jsonDoc}, i8* ${jsonObj}, i8* ${nameConst}, i32 ${boolInt}`,
);
} else if (this.ctx.interfaceStructGenHasInterface(fieldTsType)) {
const nestedPtr = this.ctx.emitLoad("i8*", fieldPtr);
const nestedFieldCount = this.ctx.interfaceStructGenGetFieldCount(fieldTsType);
const nestedStructType = this.buildStructType(fieldTsType, nestedFieldCount);
const nestedTyped = this.ctx.emitBitcast(nestedPtr, "i8*", `${nestedStructType}*`);
const subObj = this.ctx.emitCall(
"i8*",
"@csyyjson_obj_add_obj",
`i8* ${jsonDoc}, i8* ${jsonObj}, i8* ${nameConst}`,
);
this.emitAddFieldsToJsonObj(nestedTyped, nestedStructType, fieldTsType, jsonDoc, subObj);
} else {
const val = this.ctx.emitLoad("double", fieldPtr);
this.ctx.emitCallVoid(
Expand Down
24 changes: 24 additions & 0 deletions tests/fixtures/builtins/json-stringify-nested-interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// @test-description: json stringify nested interface fields

interface Address {
street: string;
zip: string;
}

interface Person {
name: string;
age: number;
address: Address;
}

const addr: Address = { street: "123 Main St", zip: "90210" };
const person: Person = { name: "Alice", age: 30, address: addr };

const json = JSON.stringify(person);
const parsed = JSON.parse<Person>(json);

if (parsed.name === "Alice" && parsed.age === 30) {
console.log("TEST_PASSED");
} else {
console.log("FAIL");
}
18 changes: 18 additions & 0 deletions tests/fixtures/builtins/json-stringify-parse-roundtrip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// @test-description: json stringify of a json parse result (llvm type tracking)

interface Config {
host: string;
port: number;
debug: boolean;
}

const raw = '{"host":"localhost","port":8080,"debug":1}';
const cfg = JSON.parse<Config>(raw);
const out = JSON.stringify(cfg);
const cfg2 = JSON.parse<Config>(out);

if (cfg2.host === "localhost" && cfg2.port === 8080) {
console.log("TEST_PASSED");
} else {
console.log("FAIL: " + cfg2.host + " " + cfg2.port);
}
Loading