Comparing version
{ | ||
"input": [ | ||
"./assembly/__tests__/*.spec.ts" | ||
], | ||
"input": ["./assembly/__tests__/*.spec.ts"], | ||
"outDir": "./build", | ||
@@ -11,3 +9,3 @@ "config": "none", | ||
"buildOptions": { | ||
"args": [], | ||
"args": ["--transform ./transform"], | ||
"target": "wasi" | ||
@@ -21,2 +19,2 @@ }, | ||
} | ||
} | ||
} |
import { JSON } from "json-as"; | ||
import { describe, expect, run } from "as-test/assembly"; | ||
import { | ||
describe, | ||
expect, | ||
run | ||
} from "as-test/assembly"; | ||
import { DerivedObject, Null, ObjWithStrangeKey, ObjectWithFloat, OmitIf, Player, Vec3 } from "./types"; | ||
DerivedObject, | ||
Null, | ||
ObjWithStrangeKey, | ||
ObjectWithFloat, | ||
OmitIf, | ||
Player, | ||
Vec3, | ||
} from "./types"; | ||
describe("Should serialize strings", () => { | ||
expect(JSON.stringify("abcdefg")).toBe('"abcdefg"'); | ||
expect( | ||
JSON.stringify("abcdefg") | ||
).toBe("\"abcdefg\""); | ||
expect(JSON.stringify('st"ring" w""ith quotes"')).toBe( | ||
'"st\\"ring\\" w\\"\\"ith quotes\\""', | ||
); | ||
expect( | ||
JSON.stringify('st"ring" w""ith quotes"') | ||
).toBe('"st\\"ring\\" w\\"\\"ith quotes\\""'); | ||
expect( | ||
JSON.stringify('string \"with random spa\nces and \nnewlines\n\n\n') | ||
JSON.stringify('string "with random spa\nces and \nnewlines\n\n\n'), | ||
).toBe('"string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"'); | ||
expect( | ||
JSON.stringify('string with colon : comma , brace [ ] bracket { } and quote " and other quote \\"') | ||
).toBe('"string with colon : comma , brace [ ] bracket { } and quote \\" and other quote \\\\\\""') | ||
JSON.stringify( | ||
'string with colon : comma , brace [ ] bracket { } and quote " and other quote \\"', | ||
), | ||
).toBe( | ||
'"string with colon : comma , brace [ ] bracket { } and quote \\" and other quote \\\\\\""', | ||
); | ||
}); | ||
describe("Should serialize integers", () => { | ||
expect(JSON.stringify(0)).toBe("0"); | ||
expect( | ||
JSON.stringify(0) | ||
).toBe("0"); | ||
expect(JSON.stringify<u32>(100)).toBe("100"); | ||
expect( | ||
JSON.stringify<u32>(100) | ||
).toBe("100"); | ||
expect(JSON.stringify<u64>(101)).toBe("101"); | ||
expect( | ||
JSON.stringify<u64>(101) | ||
).toBe("101"); | ||
expect(JSON.stringify<i32>(-100)).toBe("-100"); | ||
expect( | ||
JSON.stringify<i32>(-100) | ||
).toBe("-100"); | ||
expect( | ||
JSON.stringify<i64>(-101) | ||
).toBe("-101"); | ||
expect(JSON.stringify<i64>(-101)).toBe("-101"); | ||
}); | ||
describe("Should serialize floats", () => { | ||
expect(JSON.stringify<f64>(7.23)).toBe("7.23"); | ||
expect( | ||
JSON.stringify<f64>(7.23) | ||
).toBe("7.23"); | ||
expect(JSON.stringify<f64>(10e2)).toBe("1000.0"); | ||
expect( | ||
JSON.stringify<f64>(10e2) | ||
).toBe("1000.0"); | ||
expect(JSON.stringify<f64>(123456e-5)).toBe("1.23456"); | ||
expect( | ||
JSON.stringify<f64>(123456e-5) | ||
).toBe("1.23456"); | ||
expect(JSON.stringify<f64>(0.0)).toBe("0.0"); | ||
expect( | ||
JSON.stringify<f64>(0.0) | ||
).toBe("0.0"); | ||
expect(JSON.stringify<f64>(-7.23)).toBe("-7.23"); | ||
expect( | ||
JSON.stringify<f64>(-7.23) | ||
).toBe("-7.23"); | ||
expect(JSON.stringify<f64>(1e-6)).toBe("0.000001"); | ||
expect( | ||
JSON.stringify<f64>(1e-6) | ||
).toBe("0.000001"); | ||
expect(JSON.stringify<f64>(1e-7)).toBe("1e-7"); | ||
expect( | ||
JSON.stringify<f64>(1e-7) | ||
).toBe("1e-7"); | ||
expect(JSON.parse<f64>("1E-7")).toBe(1e-7); | ||
expect( | ||
JSON.parse<f64>("1E-7") | ||
).toBe(1e-7); | ||
expect(JSON.stringify<f64>(1e20)).toBe("100000000000000000000.0"); | ||
expect( | ||
JSON.stringify<f64>(1e20) | ||
).toBe("100000000000000000000.0"); | ||
expect(JSON.stringify<f64>(1e21)).toBe("1e+21"); | ||
expect( | ||
JSON.stringify<f64>(1e21) | ||
).toBe("1e+21"); | ||
expect(JSON.parse<f64>("1E+21")).toBe(1e21); | ||
expect( | ||
JSON.parse<f64>("1E+21") | ||
).toBe(1e21); | ||
expect(JSON.parse<f64>("1e21")).toBe(1e21); | ||
expect( | ||
JSON.parse<f64>("1e21") | ||
).toBe(1e21); | ||
expect( | ||
JSON.parse<f64>("1E21") | ||
).toBe(1e21); | ||
expect(JSON.parse<f64>("1E21")).toBe(1e21); | ||
}); | ||
describe("Should serialize booleans", () => { | ||
expect(JSON.stringify<bool>(true)).toBe("true"); | ||
expect( | ||
JSON.stringify<bool>(true) | ||
).toBe("true"); | ||
expect(JSON.stringify<bool>(false)).toBe("false"); | ||
expect( | ||
JSON.stringify<bool>(false) | ||
).toBe("false"); | ||
expect(JSON.stringify<boolean>(true)).toBe("true"); | ||
expect( | ||
JSON.stringify<boolean>(true) | ||
).toBe("true"); | ||
expect( | ||
JSON.stringify<boolean>(false) | ||
).toBe("false"); | ||
expect(JSON.stringify<boolean>(false)).toBe("false"); | ||
}); | ||
describe("Should serialize class inheritance", () => { | ||
const obj = new DerivedObject("1", "2"); | ||
expect( | ||
JSON.stringify(obj) | ||
).toBe("{\"a\":\"1\",\"b\":\"2\"}"); | ||
expect(JSON.stringify(obj)).toBe('{"a":"1","b":"2"}'); | ||
}); | ||
describe("Should serialize nulls", () => { | ||
expect( | ||
JSON.stringify<Null>(null) | ||
).toBe("null"); | ||
expect(JSON.stringify<Null>(null)).toBe("null"); | ||
}); | ||
describe("Should serialize integer arrays", () => { | ||
expect(JSON.stringify<u32[]>([0, 100, 101])).toBe("[0,100,101]"); | ||
expect( | ||
JSON.stringify<u32[]>([0, 100, 101]) | ||
).toBe("[0,100,101]"); | ||
expect(JSON.stringify<u64[]>([0, 100, 101])).toBe("[0,100,101]"); | ||
expect( | ||
JSON.stringify<u64[]>([0, 100, 101]) | ||
).toBe("[0,100,101]"); | ||
expect(JSON.stringify<i32[]>([0, 100, 101, -100, -101])).toBe( | ||
"[0,100,101,-100,-101]", | ||
); | ||
expect( | ||
JSON.stringify<i32[]>([0, 100, 101, -100, -101]) | ||
).toBe("[0,100,101,-100,-101]"); | ||
expect( | ||
JSON.stringify<i64[]>([0, 100, 101, -100, -101]) | ||
).toBe("[0,100,101,-100,-101]"); | ||
expect(JSON.stringify<i64[]>([0, 100, 101, -100, -101])).toBe( | ||
"[0,100,101,-100,-101]", | ||
); | ||
}); | ||
describe("Should serialize float arrays", () => { | ||
expect( | ||
JSON.stringify<f64[]>([7.23, 10e2, 10e2, 123456e-5, 123456e-5, 0.0, 7.23]) | ||
JSON.stringify<f64[]>([7.23, 10e2, 10e2, 123456e-5, 123456e-5, 0.0, 7.23]), | ||
).toBe("[7.23,1000.0,1000.0,1.23456,1.23456,0.0,7.23]"); | ||
expect( | ||
JSON.stringify<f64[]>([1e21, 1e22, 1e-7, 1e-8, 1e-9]) | ||
).toBe("[1e+21,1e+22,1e-7,1e-8,1e-9]"); | ||
expect(JSON.stringify<f64[]>([1e21, 1e22, 1e-7, 1e-8, 1e-9])).toBe( | ||
"[1e+21,1e+22,1e-7,1e-8,1e-9]", | ||
); | ||
}); | ||
describe("Should serialize boolean arrays", () => { | ||
expect(JSON.stringify<bool[]>([true, false])).toBe("[true,false]"); | ||
expect( | ||
JSON.stringify<bool[]>([true, false]) | ||
).toBe("[true,false]"); | ||
expect( | ||
JSON.stringify<boolean[]>([true, false]) | ||
).toBe("[true,false]"); | ||
expect(JSON.stringify<boolean[]>([true, false])).toBe("[true,false]"); | ||
}); | ||
describe("Should serialize string arrays", () => { | ||
expect( | ||
JSON.stringify<string[]>(['string \"with random spa\nces and \nnewlines\n\n\n']) | ||
JSON.stringify<string[]>([ | ||
'string "with random spa\nces and \nnewlines\n\n\n', | ||
]), | ||
).toBe('["string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"]'); | ||
}); | ||
describe("Should serialize nested integer arrays", () => { | ||
expect( | ||
JSON.stringify<i64[][]>([[100, 101], [-100, -101], [0]]) | ||
).toBe("[[100,101],[-100,-101],[0]]"); | ||
expect(JSON.stringify<i64[][]>([[100, 101], [-100, -101], [0]])).toBe( | ||
"[[100,101],[-100,-101],[0]]", | ||
); | ||
}); | ||
describe("Should serialize nested float arrays", () => { | ||
expect( | ||
@@ -219,21 +147,15 @@ JSON.stringify<f64[][]>([ | ||
[7.23], | ||
]) | ||
]), | ||
).toBe("[[7.23],[1000.0],[1000.0],[1.23456],[1.23456],[0.0],[7.23]]"); | ||
}); | ||
describe("Should serialize nested boolean arrays", () => { | ||
expect(JSON.stringify<bool[][]>([[true], [false]])).toBe("[[true],[false]]"); | ||
expect( | ||
JSON.stringify<bool[][]>([[true], [false]]) | ||
).toBe("[[true],[false]]"); | ||
expect( | ||
JSON.stringify<boolean[][]>([[true], [false]]) | ||
).toBe("[[true],[false]]"); | ||
expect(JSON.stringify<boolean[][]>([[true], [false]])).toBe( | ||
"[[true],[false]]", | ||
); | ||
}); | ||
describe("Should serialize object arrays", () => { | ||
expect( | ||
@@ -251,9 +173,7 @@ JSON.stringify<Vec3[]>([ | ||
}, | ||
]) | ||
]), | ||
).toBe('[{"x":3.4,"y":1.2,"z":8.3},{"x":3.4,"y":-2.1,"z":9.3}]'); | ||
}); | ||
describe("Should serialize objects", () => { | ||
expect( | ||
@@ -264,3 +184,3 @@ JSON.stringify<Vec3>({ | ||
z: 8.3, | ||
}) | ||
}), | ||
).toBe('{"x":3.4,"y":1.2,"z":8.3}'); | ||
@@ -280,140 +200,98 @@ | ||
isVerified: true, | ||
}) | ||
).toBe('{"firstName":"Emmet","lastName":"West","lastActive":[8,27,2022],"age":23,"pos":{"x":3.4,"y":1.2,"z":8.3},"isVerified":true}'); | ||
}), | ||
).toBe( | ||
'{"firstName":"Emmet","lastName":"West","lastActive":[8,27,2022],"age":23,"pos":{"x":3.4,"y":1.2,"z":8.3},"isVerified":true}', | ||
); | ||
expect( | ||
JSON.stringify<ObjectWithFloat>({ f: 7.23 }) | ||
).toBe('{"f":7.23}'); | ||
expect(JSON.stringify<ObjectWithFloat>({ f: 7.23 })).toBe('{"f":7.23}'); | ||
expect( | ||
JSON.stringify<ObjectWithFloat>({ f: 0.000001 }) | ||
).toBe('{"f":0.000001}'); | ||
expect(JSON.stringify<ObjectWithFloat>({ f: 0.000001 })).toBe( | ||
'{"f":0.000001}', | ||
); | ||
expect( | ||
JSON.stringify<ObjectWithFloat>({ f: 1e-7 }) | ||
).toBe('{"f":1e-7}'); | ||
expect(JSON.stringify<ObjectWithFloat>({ f: 1e-7 })).toBe('{"f":1e-7}'); | ||
expect( | ||
JSON.stringify<ObjectWithFloat>({ f: 1e20 }) | ||
).toBe('{"f":100000000000000000000.0}'); | ||
expect(JSON.stringify<ObjectWithFloat>({ f: 1e20 })).toBe( | ||
'{"f":100000000000000000000.0}', | ||
); | ||
expect( | ||
JSON.stringify<ObjectWithFloat>({ f: 1e21 }) | ||
).toBe('{"f":1e+21}'); | ||
expect(JSON.stringify<ObjectWithFloat>({ f: 1e21 })).toBe('{"f":1e+21}'); | ||
expect( | ||
JSON.stringify<ObjWithStrangeKey<string>>({ data: "foo" }) | ||
).toBe('{"a\\\\\\t\\"\\u0002b`c":"foo"}'); | ||
expect(JSON.stringify<ObjWithStrangeKey<string>>({ data: "foo" })).toBe( | ||
'{"a\\\\\\t\\"\\u0002b`c":"foo"}', | ||
); | ||
}); | ||
describe("Should serialize @omit'ed objects", () => { | ||
expect( | ||
JSON.stringify(<OmitIf>{ | ||
y: 1 | ||
}) | ||
y: 1, | ||
}), | ||
).toBe('{"x":1,"y":1,"z":1}'); | ||
}); | ||
describe("Should deserialize strings", () => { | ||
expect(JSON.parse<string>('"abcdefg"')).toBe("abcdefg"); | ||
expect( | ||
JSON.parse<string>("\"abcdefg\"") | ||
).toBe("abcdefg"); | ||
expect( | ||
JSON.parse<string>('"\\"st\\\\\\"ring\\\\\\" w\\\\\\"\\\\\\"ith quotes\\\\\\"\\\""') | ||
JSON.parse<string>( | ||
'"\\"st\\\\\\"ring\\\\\\" w\\\\\\"\\\\\\"ith quotes\\\\\\"\\""', | ||
), | ||
).toBe('"st\\"ring\\" w\\"\\"ith quotes\\""'); | ||
expect( | ||
JSON.parse<string>('"\\"string \\\\\\"with random spa\\\\nces and \\\\nnewlines\\\\n\\\\n\\\\n\\""') | ||
JSON.parse<string>( | ||
'"\\"string \\\\\\"with random spa\\\\nces and \\\\nnewlines\\\\n\\\\n\\\\n\\""', | ||
), | ||
).toBe('"string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"'); | ||
expect( | ||
JSON.parse<string>('"\\"string with colon : comma , brace [ ] bracket { } and quote \\\\\\" and other quote \\\\\\\\\\"\\\""') | ||
).toBe('"string with colon : comma , brace [ ] bracket { } and quote \\" and other quote \\\\""'); | ||
JSON.parse<string>( | ||
'"\\"string with colon : comma , brace [ ] bracket { } and quote \\\\\\" and other quote \\\\\\\\\\"\\""', | ||
), | ||
).toBe( | ||
'"string with colon : comma , brace [ ] bracket { } and quote \\" and other quote \\\\""', | ||
); | ||
}); | ||
describe("Should deserialize integers", () => { | ||
expect(JSON.parse<i32>("0")).toBe(<i32>0); | ||
expect( | ||
JSON.parse<i32>("0") | ||
).toBe(<i32>0); | ||
expect(JSON.parse<u32>("100")).toBe(<u32>100); | ||
expect( | ||
JSON.parse<u32>("100") | ||
).toBe(<u32>100); | ||
expect(JSON.parse<u64>("101")).toBe(<u64>101); | ||
expect( | ||
JSON.parse<u64>("101") | ||
).toBe(<u64>101); | ||
expect(JSON.parse<i32>("-100")).toBe(<i32>-100); | ||
expect( | ||
JSON.parse<i32>("-100") | ||
).toBe(<i32>-100); | ||
expect( | ||
JSON.parse<i64>("-101") | ||
).toBe(<i64>-101); | ||
expect(JSON.parse<i64>("-101")).toBe(<i64>-101); | ||
}); | ||
describe("Should deserialize floats", () => { | ||
expect(JSON.parse<f64>("7.23")).toBe(7.23); | ||
expect( | ||
JSON.parse<f64>("7.23") | ||
).toBe(7.23); | ||
expect(JSON.parse<f64>("1000.0")).toBe(1000.0); | ||
expect( | ||
JSON.parse<f64>("1000.0") | ||
).toBe(1000.0); | ||
expect(JSON.parse<f64>("1.23456")).toBe(1.23456); | ||
expect( | ||
JSON.parse<f64>("1.23456") | ||
).toBe(1.23456); | ||
expect(JSON.parse<f64>("0.0")).toBe(0.0); | ||
expect( | ||
JSON.parse<f64>("0.0") | ||
).toBe(0.0); | ||
expect(JSON.parse<f64>("-7.23")).toBe(-7.23); | ||
expect( | ||
JSON.parse<f64>("-7.23") | ||
).toBe(-7.23); | ||
expect(JSON.parse<f64>("0.000001")).toBe(0.000001); | ||
expect( | ||
JSON.parse<f64>("0.000001") | ||
).toBe(0.000001); | ||
expect(JSON.parse<f64>("1e-7")).toBe(1e-7); | ||
expect( | ||
JSON.parse<f64>("1e-7") | ||
).toBe(1e-7); | ||
expect(JSON.parse<f64>("100000000000000000000.0")).toBe(1e20); | ||
expect( | ||
JSON.parse<f64>("100000000000000000000.0") | ||
).toBe(1e20); | ||
expect( | ||
JSON.parse<f64>("1e+21") | ||
).toBe(1e21); | ||
expect(JSON.parse<f64>("1e+21")).toBe(1e21); | ||
}); | ||
describe("Should deserialize booleans", () => { | ||
expect(JSON.parse<boolean>("true")).toBe(true); | ||
expect( | ||
JSON.parse<boolean>("true") | ||
).toBe(true); | ||
expect( | ||
JSON.parse<boolean>("false") | ||
).toBe(false); | ||
expect(JSON.parse<boolean>("false")).toBe(false); | ||
}); | ||
describe("Should deserialize class inheritance", () => { | ||
const jsonStr = "{\"a\":\"1\",\"b\":\"2\"}"; | ||
const jsonStr = '{"a":"1","b":"2"}'; | ||
const obj = JSON.parse<DerivedObject>(jsonStr); | ||
@@ -427,117 +305,129 @@ | ||
describe("Should deserialize nulls", () => { | ||
expect( | ||
JSON.stringify(JSON.parse<Null>("null")) | ||
).toBe("null"); | ||
expect(JSON.stringify(JSON.parse<Null>("null"))).toBe("null"); | ||
}); | ||
describe("Should deserialize integer arrays", () => { | ||
expect(JSON.stringify(JSON.parse<u32[]>("[0,100,101]"))).toBe( | ||
JSON.stringify([0, 100, 101]), | ||
); | ||
expect( | ||
JSON.stringify(JSON.parse<u32[]>("[0,100,101]")) | ||
).toBe(JSON.stringify([0, 100, 101])); | ||
expect( | ||
JSON.stringify(JSON.parse<i32[]>("[0,100,101,-100,-101]")) | ||
).toBe(JSON.stringify([0, 100, 101, -100, -101])); | ||
expect(JSON.stringify(JSON.parse<i32[]>("[0,100,101,-100,-101]"))).toBe( | ||
JSON.stringify([0, 100, 101, -100, -101]), | ||
); | ||
}); | ||
describe("Should deserialize float arrays", () => { | ||
expect( | ||
JSON.stringify(JSON.parse<f64[]>("[7.23,1000.0,1000.0,1.23456,1.23456,0.0,7.23]")) | ||
JSON.stringify( | ||
JSON.parse<f64[]>("[7.23,1000.0,1000.0,1.23456,1.23456,0.0,7.23]"), | ||
), | ||
).toBe(JSON.stringify([7.23, 1000.0, 1000.0, 1.23456, 1.23456, 0.0, 7.23])); | ||
expect( | ||
JSON.stringify(JSON.parse<f64[]>("[1e+21,1e+22,1e-7,1e-8,1e-9]")) | ||
JSON.stringify(JSON.parse<f64[]>("[1e+21,1e+22,1e-7,1e-8,1e-9]")), | ||
).toBe(JSON.stringify([1e21, 1e22, 1e-7, 1e-8, 1e-9])); | ||
}); | ||
describe("Should deserialize boolean arrays", () => { | ||
expect( | ||
JSON.stringify(JSON.parse<boolean[]>("[true,false]")) | ||
).toBe(JSON.stringify([true, false])); | ||
expect(JSON.stringify(JSON.parse<boolean[]>("[true,false]"))).toBe( | ||
JSON.stringify([true, false]), | ||
); | ||
}); | ||
describe("Should deserialize string arrays", () => { | ||
expect( | ||
JSON.stringify(JSON.parse<string[]>('["string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"]')) | ||
).toBe(JSON.stringify([ 'string "with random spa\nces and \nnewlines\n\n\n' ])); | ||
JSON.stringify( | ||
JSON.parse<string[]>( | ||
'["string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"]', | ||
), | ||
), | ||
).toBe(JSON.stringify(['string "with random spa\nces and \nnewlines\n\n\n'])); | ||
}); | ||
describe("Should deserialize nested integer arrays", () => { | ||
expect( | ||
JSON.stringify(JSON.parse<i64[][]>("[[100,101],[-100,-101],[0]]")) | ||
JSON.stringify(JSON.parse<i64[][]>("[[100,101],[-100,-101],[0]]")), | ||
).toBe(JSON.stringify([[100, 101], [-100, -101], [0]])); | ||
}); | ||
describe("Should deserialize nested float arrays", () => { | ||
expect( | ||
JSON.stringify(JSON.parse<f64[][]>("[[7.23],[1000.0],[1000.0],[1.23456],[1.23456],[0.0],[7.23]]")) | ||
).toBe(JSON.stringify([[7.23], [1000.0], [1000.0], [1.23456], [1.23456], [0.0], [7.23]])); | ||
JSON.stringify( | ||
JSON.parse<f64[][]>( | ||
"[[7.23],[1000.0],[1000.0],[1.23456],[1.23456],[0.0],[7.23]]", | ||
), | ||
), | ||
).toBe( | ||
JSON.stringify([ | ||
[7.23], | ||
[1000.0], | ||
[1000.0], | ||
[1.23456], | ||
[1.23456], | ||
[0.0], | ||
[7.23], | ||
]), | ||
); | ||
}); | ||
describe("Should deserialize nested boolean arrays", () => { | ||
expect( | ||
JSON.stringify(JSON.parse<boolean[][]>("[[true],[false]]")) | ||
).toBe(JSON.stringify([[true], [false]])); | ||
expect(JSON.stringify(JSON.parse<boolean[][]>("[[true],[false]]"))).toBe( | ||
JSON.stringify([[true], [false]]), | ||
); | ||
}); | ||
describe("Should deserialize object arrays", () => { | ||
expect( | ||
JSON.stringify(JSON.parse<Vec3[]>('[{"x":3.4,"y":1.2,"z":8.3},{"x":3.4,"y":-2.1,"z":9.3}]')) | ||
).toBe(JSON.stringify(<Vec3[]>[ | ||
{ x: 3.4, y: 1.2, z: 8.3 }, | ||
{ x: 3.4, y: -2.1, z: 9.3 } | ||
])); | ||
JSON.stringify( | ||
JSON.parse<Vec3[]>( | ||
'[{"x":3.4,"y":1.2,"z":8.3},{"x":3.4,"y":-2.1,"z":9.3}]', | ||
), | ||
), | ||
).toBe( | ||
JSON.stringify(<Vec3[]>[ | ||
{ x: 3.4, y: 1.2, z: 8.3 }, | ||
{ x: 3.4, y: -2.1, z: 9.3 }, | ||
]), | ||
); | ||
}); | ||
describe("Should deserialize Objects", () => { | ||
expect(JSON.stringify(JSON.parse<Vec3>('{"x":3.4,"y":1.2,"z":8.3}'))).toBe( | ||
JSON.stringify(<Vec3>{ x: 3.4, y: 1.2, z: 8.3 }), | ||
); | ||
expect( | ||
JSON.stringify(JSON.parse<Vec3>('{"x":3.4,"y":1.2,"z":8.3}')) | ||
).toBe(JSON.stringify(<Vec3>{ x: 3.4, y: 1.2, z: 8.3 })); | ||
JSON.stringify( | ||
JSON.parse<Player>( | ||
'{"firstName":"Emmet","lastName":"West","lastActive":[8,27,2022],"age":23,"pos":{"x":3.4,"y":1.2,"z":8.3},"isVerified":true}', | ||
), | ||
), | ||
).toBe( | ||
JSON.stringify(<Player>{ | ||
firstName: "Emmet", | ||
lastName: "West", | ||
lastActive: [8, 27, 2022], | ||
age: 23, | ||
pos: { x: 3.4, y: 1.2, z: 8.3 }, | ||
isVerified: true, | ||
}), | ||
); | ||
expect( | ||
JSON.stringify(JSON.parse<Player>('{"firstName":"Emmet","lastName":"West","lastActive":[8,27,2022],"age":23,"pos":{"x":3.4,"y":1.2,"z":8.3},"isVerified":true}')) | ||
).toBe(JSON.stringify(<Player>{ | ||
firstName: "Emmet", | ||
lastName: "West", | ||
lastActive: [8, 27, 2022], | ||
age: 23, | ||
pos: { x: 3.4, y: 1.2, z: 8.3 }, | ||
isVerified: true | ||
})); | ||
expect(JSON.stringify(JSON.parse<ObjectWithFloat>('{"f":7.23}'))).toBe( | ||
JSON.stringify(<ObjectWithFloat>{ f: 7.23 }), | ||
); | ||
expect( | ||
JSON.stringify(JSON.parse<ObjectWithFloat>('{"f":7.23}')) | ||
).toBe(JSON.stringify(<ObjectWithFloat>{ f: 7.23 })); | ||
expect(JSON.stringify(JSON.parse<ObjectWithFloat>('{"f":0.000001}'))).toBe( | ||
JSON.stringify(<ObjectWithFloat>{ f: 0.000001 }), | ||
); | ||
expect( | ||
JSON.stringify(JSON.parse<ObjectWithFloat>('{"f":0.000001}')) | ||
).toBe(JSON.stringify(<ObjectWithFloat>{ f: 0.000001 })); | ||
expect( | ||
JSON.stringify(JSON.parse<ObjWithStrangeKey<string>>('{"a\\\\\\t\\"\\u0002b`c":"foo"}')) | ||
JSON.stringify( | ||
JSON.parse<ObjWithStrangeKey<string>>('{"a\\\\\\t\\"\\u0002b`c":"foo"}'), | ||
), | ||
).toBe(JSON.stringify(<ObjWithStrangeKey<string>>{ data: "foo" })); | ||
}); | ||
run({ | ||
log: true | ||
}); | ||
log: true, | ||
}); |
@@ -1,5 +0,4 @@ | ||
@json | ||
export class ObjWithString { | ||
s!: string; | ||
s!: string; | ||
} | ||
@@ -9,8 +8,8 @@ | ||
export class ObjWithStrangeKey<T> { | ||
@alias('a\\\t"\x02b`c') | ||
data!: T; | ||
@alias('a\\\t"\x02b`c') | ||
data!: T; | ||
} | ||
@json | ||
export class ObjectWithStringArray { | ||
sa!: string[]; | ||
sa!: string[]; | ||
} | ||
@@ -20,3 +19,3 @@ | ||
export class ObjectWithFloat { | ||
f!: f64; | ||
f!: f64; | ||
} | ||
@@ -26,3 +25,3 @@ | ||
export class ObjectWithFloatArray { | ||
fa!: f64[]; | ||
fa!: f64[]; | ||
} | ||
@@ -74,3 +73,3 @@ | ||
export class Nullable { } | ||
export class Nullable {} | ||
export type Null = Nullable | null; | ||
@@ -85,3 +84,3 @@ | ||
@omitnull() | ||
foo: string | null = null | ||
} | ||
foo: string | null = null; | ||
} |
@@ -6,282 +6,283 @@ import { itoa_buffered, dtoa_buffered } from "util/number"; | ||
const NEW_LINE_CHAR: u16 = 0x0A; // \n | ||
const NEW_LINE_CHAR: u16 = 0x0a; // \n | ||
// @ts-ignore: decorator | ||
function nextPowerOf2(n: u32): u32 { | ||
return 1 << 32 - clz(n - 1); | ||
return 1 << (32 - clz(n - 1)); | ||
} | ||
export class Sink { | ||
public buffer!: ArrayBuffer; | ||
public offset: u32 = 0; | ||
public buffer!: ArrayBuffer; | ||
public offset: u32 = 0; | ||
static withCapacity(capacity: i32): Sink { | ||
const sink = new Sink(); | ||
sink.buffer = changetype<ArrayBuffer>(__new( | ||
max<u32>(MIN_BUFFER_SIZE, <u32>capacity << 1), | ||
idof<ArrayBuffer>()) | ||
); | ||
return sink; | ||
} | ||
static withCapacity(capacity: i32): Sink { | ||
const sink = new Sink(); | ||
sink.buffer = changetype<ArrayBuffer>( | ||
__new( | ||
max<u32>(MIN_BUFFER_SIZE, (<u32>capacity) << 1), | ||
idof<ArrayBuffer>(), | ||
), | ||
); | ||
return sink; | ||
} | ||
static fromString(initial: string = "", capacity: i32 = MIN_BUFFER_LEN): Sink { | ||
const sink = new Sink(); | ||
const size = <u32>initial.length << 1; | ||
sink.buffer = changetype<ArrayBuffer>(__new( | ||
max<u32>(size, max<u32>(MIN_BUFFER_SIZE, <u32>capacity << 1)), | ||
idof<ArrayBuffer>()) | ||
); | ||
if (size) { | ||
memory.copy( | ||
changetype<usize>(sink.buffer), | ||
changetype<usize>(initial), | ||
size | ||
); | ||
sink.offset += size; | ||
} | ||
return sink; | ||
static fromString( | ||
initial: string = "", | ||
capacity: i32 = MIN_BUFFER_LEN, | ||
): Sink { | ||
const sink = new Sink(); | ||
const size = (<u32>initial.length) << 1; | ||
sink.buffer = changetype<ArrayBuffer>( | ||
__new( | ||
max<u32>(size, max<u32>(MIN_BUFFER_SIZE, (<u32>capacity) << 1)), | ||
idof<ArrayBuffer>(), | ||
), | ||
); | ||
if (size) { | ||
memory.copy( | ||
changetype<usize>(sink.buffer), | ||
changetype<usize>(initial), | ||
size, | ||
); | ||
sink.offset += size; | ||
} | ||
return sink; | ||
} | ||
static fromStringLiteral(initial: string = ""): Sink { | ||
const sink = new Sink(); | ||
const size = <u32>initial.length << 1; | ||
sink.buffer = changetype<ArrayBuffer>(__new( | ||
size, | ||
idof<ArrayBuffer>()) | ||
); | ||
if (size) { | ||
memory.copy( | ||
changetype<usize>(sink.buffer), | ||
changetype<usize>(initial), | ||
size | ||
); | ||
sink.offset += size; | ||
} | ||
return sink; | ||
static fromStringLiteral(initial: string = ""): Sink { | ||
const sink = new Sink(); | ||
const size = (<u32>initial.length) << 1; | ||
sink.buffer = changetype<ArrayBuffer>(__new(size, idof<ArrayBuffer>())); | ||
if (size) { | ||
memory.copy( | ||
changetype<usize>(sink.buffer), | ||
changetype<usize>(initial), | ||
size, | ||
); | ||
sink.offset += size; | ||
} | ||
return sink; | ||
} | ||
static fromBuffer(initial: ArrayBuffer, capacity: i32 = MIN_BUFFER_LEN): Sink { | ||
const sink = new Sink(); | ||
const size = <u32>initial.byteLength; | ||
sink.buffer = changetype<ArrayBuffer>(__new( | ||
max<u32>(size, max<u32>(MIN_BUFFER_SIZE, <u32>capacity << 1)), | ||
idof<ArrayBuffer>()) | ||
); | ||
if (size) { | ||
memory.copy( | ||
changetype<usize>(sink.buffer), | ||
changetype<usize>(initial), | ||
size | ||
); | ||
sink.offset = size; | ||
} | ||
return sink; | ||
static fromBuffer( | ||
initial: ArrayBuffer, | ||
capacity: i32 = MIN_BUFFER_LEN, | ||
): Sink { | ||
const sink = new Sink(); | ||
const size = <u32>initial.byteLength; | ||
sink.buffer = changetype<ArrayBuffer>( | ||
__new( | ||
max<u32>(size, max<u32>(MIN_BUFFER_SIZE, (<u32>capacity) << 1)), | ||
idof<ArrayBuffer>(), | ||
), | ||
); | ||
if (size) { | ||
memory.copy( | ||
changetype<usize>(sink.buffer), | ||
changetype<usize>(initial), | ||
size, | ||
); | ||
sink.offset = size; | ||
} | ||
return sink; | ||
} | ||
constructor() { } | ||
constructor() {} | ||
get length(): i32 { | ||
return this.offset >> 1; | ||
} | ||
get length(): i32 { | ||
return this.offset >> 1; | ||
} | ||
get capacity(): i32 { | ||
return this.buffer.byteLength >>> 1; | ||
get capacity(): i32 { | ||
return this.buffer.byteLength >>> 1; | ||
} | ||
reset(): void { | ||
this.offset = 0; | ||
} | ||
write(src: string, start: i32 = 0, end: i32 = i32.MAX_VALUE): Sink | null { | ||
let len = src.length as u32; | ||
if (start != 0 || end != i32.MAX_VALUE) { | ||
let from: i32; | ||
from = min<i32>(max(start, 0), len); | ||
end = min<i32>(max(end, 0), len); | ||
start = min<i32>(from, end); | ||
end = max<i32>(from, end); | ||
len = end - start; | ||
} | ||
reset(): void { | ||
this.offset = 0; | ||
} | ||
write(src: string, start: i32 = 0, end: i32 = i32.MAX_VALUE): Sink | null { | ||
let len = src.length as u32; | ||
if (start != 0 || end != i32.MAX_VALUE) { | ||
let from: i32; | ||
from = min<i32>(max(start, 0), len); | ||
end = min<i32>(max(end, 0), len); | ||
start = min<i32>(from, end); | ||
end = max<i32>(from, end); | ||
len = end - start; | ||
} | ||
if (!len) return null; | ||
if (!len) return null; | ||
let size = len << 1; | ||
this.ensureCapacity(size); | ||
let offset = this.offset; | ||
let size = len << 1; | ||
this.ensureCapacity(size); | ||
let offset = this.offset; | ||
memory.copy( | ||
changetype<usize>(this.buffer) + offset, | ||
changetype<usize>(src) + ((<usize>start) << 1), | ||
size, | ||
); | ||
this.offset = offset + size; | ||
return this; | ||
} | ||
memory.copy( | ||
changetype<usize>(this.buffer) + offset, | ||
changetype<usize>(src) + (<usize>start << 1), | ||
size | ||
); | ||
this.offset = offset + size; | ||
return this; | ||
writeLn(src: string = "", start: i32 = 0, end: i32 = i32.MAX_VALUE): Sink { | ||
let len = src.length as u32; | ||
if (start != 0 || end != i32.MAX_VALUE) { | ||
let from: i32; | ||
from = min<i32>(max(start, 0), len); | ||
end = min<i32>(max(end, 0), len); | ||
start = min<i32>(from, end); | ||
end = max<i32>(from, end); | ||
len = end - start; | ||
} | ||
writeLn(src: string = "", start: i32 = 0, end: i32 = i32.MAX_VALUE): Sink { | ||
let len = src.length as u32; | ||
if (start != 0 || end != i32.MAX_VALUE) { | ||
let from: i32; | ||
from = min<i32>(max(start, 0), len); | ||
end = min<i32>(max(end, 0), len); | ||
start = min<i32>(from, end); | ||
end = max<i32>(from, end); | ||
len = end - start; | ||
} | ||
let size = len << 1; | ||
this.ensureCapacity(size + 2); | ||
let offset = this.offset; | ||
let dest = changetype<usize>(this.buffer) + offset; | ||
if (size) | ||
memory.copy(dest, changetype<usize>(src) + ((<usize>start) << 1), size); | ||
store<u16>(dest + size, NEW_LINE_CHAR); | ||
this.offset = offset + (size + 2); | ||
return this; | ||
} | ||
let size = len << 1; | ||
this.ensureCapacity(size + 2); | ||
let offset = this.offset; | ||
let dest = changetype<usize>(this.buffer) + offset; | ||
if (size) memory.copy(dest, changetype<usize>(src) + (<usize>start << 1), size); | ||
store<u16>(dest + size, NEW_LINE_CHAR); | ||
this.offset = offset + (size + 2); | ||
return this; | ||
} | ||
writeCodePoint(code: i32): Sink { | ||
let hasSur = <u32>code > 0xffff; | ||
this.ensureCapacity(2 << i32(hasSur)); | ||
writeCodePoint(code: i32): Sink { | ||
let hasSur = <u32>code > 0xFFFF; | ||
this.ensureCapacity(2 << i32(hasSur)); | ||
let offset = this.offset; | ||
let dest = changetype<usize>(this.buffer) + offset; | ||
let offset = this.offset; | ||
let dest = changetype<usize>(this.buffer) + offset; | ||
if (!hasSur) { | ||
store<u16>(dest, <u16>code); | ||
this.offset = offset + 2; | ||
} else { | ||
assert(<u32>code <= 0x10FFFF); | ||
code -= 0x10000; | ||
let hi = (code & 0x03FF) | 0xDC00; | ||
let lo = code >>> 10 | 0xD800; | ||
store<u32>(dest, lo | hi << 16); | ||
this.offset = offset + 4; | ||
} | ||
return this; | ||
if (!hasSur) { | ||
store<u16>(dest, <u16>code); | ||
this.offset = offset + 2; | ||
} else { | ||
assert(<u32>code <= 0x10ffff); | ||
code -= 0x10000; | ||
let hi = (code & 0x03ff) | 0xdc00; | ||
let lo = (code >>> 10) | 0xd800; | ||
store<u32>(dest, lo | (hi << 16)); | ||
this.offset = offset + 4; | ||
} | ||
return this; | ||
} | ||
writeCodePoint16(code: i32): Sink { | ||
this.ensureCapacity(2); | ||
writeCodePoint16(code: i32): Sink { | ||
this.ensureCapacity(2); | ||
let offset = this.offset; | ||
let dest = changetype<usize>(this.buffer) + offset; | ||
let offset = this.offset; | ||
let dest = changetype<usize>(this.buffer) + offset; | ||
store<u16>(dest, <u16>code); | ||
this.offset = offset + 2; | ||
store<u16>(dest, <u16>code); | ||
this.offset = offset + 2; | ||
return this; | ||
} | ||
return this; | ||
} | ||
writeCodePointUnsafe(code: i32): Sink { | ||
this.ensureCapacity(2); | ||
writeCodePointUnsafe(code: i32): Sink { | ||
this.ensureCapacity(2); | ||
let offset = this.offset; | ||
let dest = changetype<usize>(this.buffer) + offset; | ||
let offset = this.offset; | ||
let dest = changetype<usize>(this.buffer) + offset; | ||
code -= 0x10000; | ||
let hi = (code & 0x03FF) | 0xDC00; | ||
let lo = code >>> 10 | 0xD800; | ||
store<u32>(dest, lo | hi << 16); | ||
this.offset = offset + 4; | ||
return this; | ||
} | ||
code -= 0x10000; | ||
let hi = (code & 0x03ff) | 0xdc00; | ||
let lo = (code >>> 10) | 0xd800; | ||
store<u32>(dest, lo | (hi << 16)); | ||
this.offset = offset + 4; | ||
return this; | ||
} | ||
writeNumber<T extends number>(value: T): Sink { | ||
let offset = this.offset; | ||
if (isInteger<T>()) { | ||
let maxCapacity = 0; | ||
// this also include size for sign | ||
if (sizeof<T>() == 1) { | ||
maxCapacity = 4 << 1; | ||
} else if (sizeof<T>() == 2) { | ||
maxCapacity = 6 << 1; | ||
} else if (sizeof<T>() == 4) { | ||
maxCapacity = 11 << 1; | ||
} else if (sizeof<T>() == 8) { | ||
maxCapacity = 21 << 1; | ||
} | ||
this.ensureCapacity(maxCapacity); | ||
offset += itoa_buffered( | ||
changetype<usize>(this.buffer) + offset, | ||
value | ||
) << 1; | ||
} else { | ||
this.ensureCapacity(32 << 1); | ||
offset += dtoa_buffered( | ||
changetype<usize>(this.buffer) + offset, | ||
value | ||
) << 1; | ||
} | ||
this.offset = offset; | ||
return this; | ||
writeNumber<T extends number>(value: T): Sink { | ||
let offset = this.offset; | ||
if (isInteger<T>()) { | ||
let maxCapacity = 0; | ||
// this also include size for sign | ||
if (sizeof<T>() == 1) { | ||
maxCapacity = 4 << 1; | ||
} else if (sizeof<T>() == 2) { | ||
maxCapacity = 6 << 1; | ||
} else if (sizeof<T>() == 4) { | ||
maxCapacity = 11 << 1; | ||
} else if (sizeof<T>() == 8) { | ||
maxCapacity = 21 << 1; | ||
} | ||
this.ensureCapacity(maxCapacity); | ||
offset += | ||
itoa_buffered(changetype<usize>(this.buffer) + offset, value) << 1; | ||
} else { | ||
this.ensureCapacity(32 << 1); | ||
offset += | ||
dtoa_buffered(changetype<usize>(this.buffer) + offset, value) << 1; | ||
} | ||
writeNumberUnsafe<T extends number>(value: T): Sink { | ||
let offset = this.offset; | ||
if (isInteger<T>()) { | ||
offset += itoa_buffered( | ||
changetype<usize>(this.buffer) + offset, | ||
value | ||
) << 1; | ||
} else { | ||
offset += dtoa_buffered( | ||
changetype<usize>(this.buffer) + offset, | ||
value | ||
) << 1; | ||
} | ||
this.offset = offset; | ||
return this; | ||
this.offset = offset; | ||
return this; | ||
} | ||
writeNumberUnsafe<T extends number>(value: T): Sink { | ||
let offset = this.offset; | ||
if (isInteger<T>()) { | ||
offset += | ||
itoa_buffered(changetype<usize>(this.buffer) + offset, value) << 1; | ||
} else { | ||
offset += | ||
dtoa_buffered(changetype<usize>(this.buffer) + offset, value) << 1; | ||
} | ||
writeIntegerUnsafe<T extends number>(value: T): Sink { | ||
let offset = this.offset; | ||
if (isInteger<T>()) { | ||
offset += itoa_buffered( | ||
changetype<usize>(this.buffer) + offset, | ||
value | ||
) << 1; | ||
} else { | ||
offset += dtoa_buffered( | ||
changetype<usize>(this.buffer) + offset, | ||
value | ||
) << 1; | ||
} | ||
this.offset = offset; | ||
return this; | ||
this.offset = offset; | ||
return this; | ||
} | ||
writeIntegerUnsafe<T extends number>(value: T): Sink { | ||
let offset = this.offset; | ||
if (isInteger<T>()) { | ||
offset += | ||
itoa_buffered(changetype<usize>(this.buffer) + offset, value) << 1; | ||
} else { | ||
offset += | ||
dtoa_buffered(changetype<usize>(this.buffer) + offset, value) << 1; | ||
} | ||
this.offset = offset; | ||
return this; | ||
} | ||
reserve(capacity: i32, clear: bool = false): void { | ||
if (clear) this.offset = 0; | ||
this.buffer = changetype<ArrayBuffer>(__renew( | ||
changetype<usize>(this.buffer), | ||
max<u32>(this.offset, max<u32>(MIN_BUFFER_SIZE, <u32>capacity << 1)) | ||
)); | ||
} | ||
reserve(capacity: i32, clear: bool = false): void { | ||
if (clear) this.offset = 0; | ||
this.buffer = changetype<ArrayBuffer>( | ||
__renew( | ||
changetype<usize>(this.buffer), | ||
max<u32>(this.offset, max<u32>(MIN_BUFFER_SIZE, (<u32>capacity) << 1)), | ||
), | ||
); | ||
} | ||
shrink(): void { | ||
this.buffer = changetype<ArrayBuffer>(__renew( | ||
changetype<usize>(this.buffer), | ||
max<u32>(this.offset, MIN_BUFFER_SIZE) | ||
)); | ||
} | ||
shrink(): void { | ||
this.buffer = changetype<ArrayBuffer>( | ||
__renew( | ||
changetype<usize>(this.buffer), | ||
max<u32>(this.offset, MIN_BUFFER_SIZE), | ||
), | ||
); | ||
} | ||
clear(): void { | ||
this.reserve(0, true); | ||
} | ||
clear(): void { | ||
this.reserve(0, true); | ||
} | ||
toString(): string { | ||
let size = this.offset; | ||
if (!size) return ""; | ||
let out = changetype<string>(__new(size, idof<string>())); | ||
memory.copy(changetype<usize>(out), changetype<usize>(this.buffer), size); | ||
return out; | ||
} | ||
toString(): string { | ||
let size = this.offset; | ||
if (!size) return ""; | ||
let out = changetype<string>(__new(size, idof<string>())); | ||
memory.copy(changetype<usize>(out), changetype<usize>(this.buffer), size); | ||
return out; | ||
} | ||
ensureCapacity(deltaBytes: u32): void { | ||
let buffer = this.buffer; | ||
let newSize = this.offset + deltaBytes; | ||
if (newSize > <u32>buffer.byteLength) { | ||
this.buffer = changetype<ArrayBuffer>(__renew( | ||
changetype<usize>(buffer), | ||
nextPowerOf2(newSize) | ||
)); | ||
} | ||
ensureCapacity(deltaBytes: u32): void { | ||
let buffer = this.buffer; | ||
let newSize = this.offset + deltaBytes; | ||
if (newSize > <u32>buffer.byteLength) { | ||
this.buffer = changetype<ArrayBuffer>( | ||
__renew(changetype<usize>(buffer), nextPowerOf2(newSize)), | ||
); | ||
} | ||
} | ||
} | ||
} |
@json | ||
export class Vec3 { | ||
x: f64 = 1.0; | ||
} | ||
} |
@@ -12,28 +12,32 @@ import { isMap } from "../custom/util"; | ||
export function deserializeArray<T extends unknown[]>(data: string): T { | ||
if (isString<valueof<T>>()) { | ||
return <T>deserializeStringArray(data); | ||
} else if (isBoolean<valueof<T>>()) { | ||
// @ts-ignore | ||
return deserializeBooleanArray<T>(data); | ||
} else if (isInteger<valueof<T>>()) { | ||
// @ts-ignore | ||
return deserializeIntegerArray<T>(data); | ||
} else if (isFloat<valueof<T>>()) { | ||
// @ts-ignore | ||
return deserializeFloatArray<T>(data); | ||
} else if (isArrayLike<valueof<T>>()) { | ||
// @ts-ignore | ||
return deserializeArrayArray<T>(data); | ||
} else if (isMap<valueof<T>>()) { | ||
return deserializeMapArray<T>(data); | ||
} else if (isManaged<valueof<T>>() || isReference<valueof<T>>()) { | ||
const type = changetype<nonnull<valueof<T>>>(0); | ||
// @ts-ignore | ||
if (isDefined(type.__DESERIALIZE)) { | ||
return deserializeObjectArray<T>(data); | ||
} | ||
throw new Error("Could not parse array of type " + nameof<T>() + "! Make sure to add the @json decorator over classes!"); | ||
} else { | ||
throw new Error("Could not parse array of type " + nameof<T>() + "!"); | ||
if (isString<valueof<T>>()) { | ||
return <T>deserializeStringArray(data); | ||
} else if (isBoolean<valueof<T>>()) { | ||
// @ts-ignore | ||
return deserializeBooleanArray<T>(data); | ||
} else if (isInteger<valueof<T>>()) { | ||
// @ts-ignore | ||
return deserializeIntegerArray<T>(data); | ||
} else if (isFloat<valueof<T>>()) { | ||
// @ts-ignore | ||
return deserializeFloatArray<T>(data); | ||
} else if (isArrayLike<valueof<T>>()) { | ||
// @ts-ignore | ||
return deserializeArrayArray<T>(data); | ||
} else if (isMap<valueof<T>>()) { | ||
return deserializeMapArray<T>(data); | ||
} else if (isManaged<valueof<T>>() || isReference<valueof<T>>()) { | ||
const type = changetype<nonnull<valueof<T>>>(0); | ||
// @ts-ignore | ||
if (isDefined(type.__DESERIALIZE)) { | ||
return deserializeObjectArray<T>(data); | ||
} | ||
} | ||
throw new Error( | ||
"Could not parse array of type " + | ||
nameof<T>() + | ||
"! Make sure to add the @json decorator over classes!", | ||
); | ||
} else { | ||
throw new Error("Could not parse array of type " + nameof<T>() + "!"); | ||
} | ||
} |
@@ -21,3 +21,2 @@ /** | ||
/** | ||
@@ -37,2 +36,2 @@ * Property decorator that allows a field to be omitted when equal to an Expression. | ||
*/ | ||
declare function flatten(fieldName: string = "value"): Function; | ||
declare function flatten(fieldName: string = "value"): Function; |
interface GeneratedInterface { | ||
__SERIALIZE(): string; | ||
} | ||
// @ts-ignore: Decoraor valid here | ||
// @ts-ignore: Decorator valid here | ||
@inline export function serializeObject<T extends GeneratedInterface>(data: T): string { | ||
return changetype<nonnull<T>>(data).__SERIALIZE(); | ||
} |
@@ -5,2 +5,4 @@ // import { JSON } from "."; | ||
class Message { | ||
@alias("raw_foo") | ||
public raw: JSON.Raw = "[1,2,3]"; | ||
constructor(role: string, content: string) { | ||
@@ -11,3 +13,2 @@ this._role = role; | ||
@alias("role") | ||
@@ -29,3 +30,3 @@ protected _role: string; | ||
} | ||
console.log(JSON.stringify(new Message("user", "foo"))) | ||
console.log(JSON.stringify(new UserMessage("foo"))); | ||
console.log(JSON.stringify(new Message("user", "foo"))); | ||
console.log(JSON.stringify(new UserMessage("foo"))); |
{ | ||
"extends": "assemblyscript/std/assembly.json", | ||
"include": [ | ||
"./**/*.ts" | ||
], | ||
"include": ["./**/*.ts"], | ||
"compilerOptions": { | ||
@@ -19,4 +17,4 @@ /* Visit https://aka.ms/tsconfig to read more about this file */ | ||
// "jsx": "preserve", /* Specify what JSX code is generated. */ | ||
"experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ | ||
"emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ | ||
"experimentalDecorators": true /* Enable experimental support for TC39 stage 2 draft decorators. */, | ||
"emitDecoratorMetadata": true /* Emit design-type metadata for decorated declarations in source files. */, | ||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ | ||
@@ -77,5 +75,5 @@ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ | ||
/* Type Checking */ | ||
"strict": false/* Enable all strict type-checking options. */, | ||
"strict": false /* Enable all strict type-checking options. */, | ||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ | ||
"strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ | ||
"strictNullChecks": true /* When type checking, take into account 'null' and 'undefined'. */, | ||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ | ||
@@ -87,4 +85,4 @@ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ | ||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ | ||
"noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ | ||
"noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ | ||
"noUnusedLocals": true /* Enable error reporting when local variables aren't read. */, | ||
"noUnusedParameters": true /* Raise an error when a function parameter isn't read. */, | ||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ | ||
@@ -102,2 +100,2 @@ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ | ||
} | ||
} | ||
} |
18
bench.js
@@ -29,5 +29,5 @@ import { Bench } from "tinybench"; | ||
const vec = { | ||
x: 3, | ||
y: 1, | ||
z: 8, | ||
x: 3, | ||
y: 1, | ||
z: 8, | ||
}; | ||
@@ -38,3 +38,3 @@ | ||
const bench = new Bench({ time: 1000 }) | ||
/*.add("stringify float", () => { | ||
/*.add("stringify float", () => { | ||
data = JSON.stringify(1.2345) | ||
@@ -71,9 +71,9 @@ }) | ||
*/ | ||
.add("Parse String", () => { | ||
data = JSON.parse('[[],[[]],[[],[[]]]]'); | ||
}) | ||
.todo("unimplemented .add"); | ||
.add("Parse String", () => { | ||
data = JSON.parse("[[],[[]],[[],[[]]]]"); | ||
}) | ||
.todo("unimplemented .add"); | ||
await bench.run(); | ||
console.table(bench.table()); | ||
console.table(bench.table()); |
import { readFileSync } from "fs"; | ||
import { WASI } from "wasi"; | ||
import { argv, env } from 'node:process'; | ||
import { argv, env } from "node:process"; | ||
@@ -8,6 +8,6 @@ const wasm = readFileSync("./benchmark.wasm"); | ||
const wasi = new WASI({ | ||
version: 'preview1', | ||
args: argv, | ||
env, | ||
preopens: {}, | ||
version: "preview1", | ||
args: argv, | ||
env, | ||
preopens: {}, | ||
}); | ||
@@ -18,2 +18,2 @@ | ||
wasi.start(instance); | ||
wasi.start(instance); |
@@ -7,24 +7,24 @@ import { bench } from "as-bench/assembly/bench"; | ||
class Vec3 { | ||
x: i32; | ||
y: i32; | ||
z: i32; | ||
__SERIALIZE_BS(): void { | ||
bs.write_128_u(i16x8(123, 34, 120, 34, 58, 49, 44, 34)); /* {"x":1," */ | ||
bs.write_128_u(i16x8(121, 34, 58, 50, 44, 34, 122, 34)); /* y":2,"z" */ | ||
bs.write_32_u(3342394); /* :3 */ | ||
bs.write_16_u(125); /* } */ | ||
} | ||
x: i32; | ||
y: i32; | ||
z: i32; | ||
__SERIALIZE_BS(): void { | ||
bs.write_128_u(i16x8(123, 34, 120, 34, 58, 49, 44, 34)); /* {"x":1," */ | ||
bs.write_128_u(i16x8(121, 34, 58, 50, 44, 34, 122, 34)); /* y":2,"z" */ | ||
bs.write_32_u(3342394); /* :3 */ | ||
bs.write_16_u(125); /* } */ | ||
} | ||
} | ||
const out = memory.data(1000); | ||
const vec: Vec3 = { | ||
x: 3, | ||
y: 1, | ||
z: 8, | ||
} | ||
x: 3, | ||
y: 1, | ||
z: 8, | ||
}; | ||
bench("Stringify Vec3", () => { | ||
vec.__SERIALIZE_BS(); | ||
//bs.reset() | ||
}) | ||
vec.__SERIALIZE_BS(); | ||
//bs.reset() | ||
}); | ||
bench("Stringify String", () => { | ||
serializeString('Hello World'); | ||
serializeString("Hello World"); | ||
}); | ||
@@ -78,2 +78,2 @@ /* | ||
blackbox(JSON.stringify<string[]>(["a", "b", "c"])); | ||
});*/ | ||
});*/ |
{ | ||
"extends": "assemblyscript/std/assembly.json", | ||
"include": [ | ||
"./**/*.ts" | ||
], | ||
"compilerOptions": { | ||
/* Visit https://aka.ms/tsconfig to read more about this file */ | ||
/* Projects */ | ||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ | ||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ | ||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ | ||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ | ||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ | ||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ | ||
/* Language and Environment */ | ||
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, | ||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ | ||
// "jsx": "preserve", /* Specify what JSX code is generated. */ | ||
"experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ | ||
"emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ | ||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ | ||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ | ||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ | ||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ | ||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ | ||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ | ||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ | ||
/* Modules */ | ||
"module": "commonjs" /* Specify what module code is generated. */, | ||
// "rootDir": "./", /* Specify the root folder within your source files. */ | ||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ | ||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ | ||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ | ||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ | ||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ | ||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */ | ||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ | ||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ | ||
// "resolveJsonModule": true, /* Enable importing .json files. */ | ||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */ | ||
/* JavaScript Support */ | ||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ | ||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ | ||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ | ||
/* Emit */ | ||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ | ||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */ | ||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ | ||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */ | ||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ | ||
// "outDir": "./", /* Specify an output folder for all emitted files. */ | ||
// "removeComments": true, /* Disable emitting comments. */ | ||
// "noEmit": true, /* Disable emitting files from a compilation. */ | ||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ | ||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ | ||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ | ||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ | ||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ | ||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ | ||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ | ||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ | ||
// "newLine": "crlf", /* Set the newline character for emitting files. */ | ||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ | ||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ | ||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ | ||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ | ||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */ | ||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ | ||
/* Interop Constraints */ | ||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ | ||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ | ||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, | ||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ | ||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, | ||
/* Type Checking */ | ||
"strict": false /* Enable all strict type-checking options. */, | ||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ | ||
"strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ | ||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ | ||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ | ||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ | ||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ | ||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ | ||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ | ||
"noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ | ||
"noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ | ||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ | ||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ | ||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ | ||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ | ||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ | ||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ | ||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ | ||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ | ||
/* Completeness */ | ||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ | ||
"skipLibCheck": true /* Skip type checking all .d.ts files. */ | ||
} | ||
} | ||
"extends": "assemblyscript/std/assembly.json", | ||
"include": ["./**/*.ts"], | ||
"compilerOptions": { | ||
/* Visit https://aka.ms/tsconfig to read more about this file */ | ||
/* Projects */ | ||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ | ||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ | ||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ | ||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ | ||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ | ||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ | ||
/* Language and Environment */ | ||
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, | ||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ | ||
// "jsx": "preserve", /* Specify what JSX code is generated. */ | ||
"experimentalDecorators": true /* Enable experimental support for TC39 stage 2 draft decorators. */, | ||
"emitDecoratorMetadata": true /* Emit design-type metadata for decorated declarations in source files. */, | ||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ | ||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ | ||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ | ||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ | ||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ | ||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ | ||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ | ||
/* Modules */ | ||
"module": "commonjs" /* Specify what module code is generated. */, | ||
// "rootDir": "./", /* Specify the root folder within your source files. */ | ||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ | ||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ | ||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ | ||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ | ||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ | ||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */ | ||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ | ||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ | ||
// "resolveJsonModule": true, /* Enable importing .json files. */ | ||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */ | ||
/* JavaScript Support */ | ||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ | ||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ | ||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ | ||
/* Emit */ | ||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ | ||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */ | ||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ | ||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */ | ||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ | ||
// "outDir": "./", /* Specify an output folder for all emitted files. */ | ||
// "removeComments": true, /* Disable emitting comments. */ | ||
// "noEmit": true, /* Disable emitting files from a compilation. */ | ||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ | ||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ | ||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ | ||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ | ||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ | ||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ | ||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ | ||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ | ||
// "newLine": "crlf", /* Set the newline character for emitting files. */ | ||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ | ||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ | ||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ | ||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ | ||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */ | ||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ | ||
/* Interop Constraints */ | ||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ | ||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ | ||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, | ||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ | ||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, | ||
/* Type Checking */ | ||
"strict": false /* Enable all strict type-checking options. */, | ||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ | ||
"strictNullChecks": true /* When type checking, take into account 'null' and 'undefined'. */, | ||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ | ||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ | ||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ | ||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ | ||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ | ||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ | ||
"noUnusedLocals": true /* Enable error reporting when local variables aren't read. */, | ||
"noUnusedParameters": true /* Raise an error when a function parameter isn't read. */, | ||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ | ||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ | ||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ | ||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ | ||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ | ||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ | ||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ | ||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ | ||
/* Completeness */ | ||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ | ||
"skipLibCheck": true /* Skip type checking all .d.ts files. */ | ||
} | ||
} |
@@ -1,1 +0,1 @@ | ||
export { JSON } from "./assembly/index"; | ||
export { JSON } from "./assembly/index"; |
{ | ||
"name": "json-as", | ||
"version": "0.9.17", | ||
"version": "0.9.18", | ||
"description": "The only JSON library you'll need for AssemblyScript. SIMD enabled", | ||
@@ -29,3 +29,3 @@ "types": "assembly/index.ts", | ||
"test:wasm3": "wasm3 ./build/test.wasm", | ||
"prettier": "as-prettier -w ." | ||
"prettier": "prettier -w ." | ||
}, | ||
@@ -41,8 +41,10 @@ "devDependencies": { | ||
"microtime": "^3.1.1", | ||
"prettier": "^3.3.2", | ||
"prettier": "^3.3.3", | ||
"tinybench": "^2.8.0", | ||
"typescript": "^5.5.3", | ||
"typescript": "^5.5.4", | ||
"visitor-as": "^0.11.4" | ||
}, | ||
"dependencies": { "as-virtual": "^0.2.0" }, | ||
"dependencies": { | ||
"as-virtual": "^0.2.0" | ||
}, | ||
"overrides": { | ||
@@ -49,0 +51,0 @@ "assemblyscript": "$assemblyscript" |
@@ -6,3 +6,3 @@ <h5 align="center"> | ||
|_____||_____||_____||_|___| |__|__||_____| | ||
v0.9.17 | ||
v0.9.18 | ||
</pre> | ||
@@ -122,48 +122,10 @@ </h5> | ||
It also supports arbitrary-length integers with [@hypercubed/as-mpz](https://github.com/Hypercubed/as-mpz) | ||
```js | ||
import { MpZ } from "@hypercubed/as-mpz"; | ||
const a = MpZ.from(18448972); | ||
const b = MpZ.from('7881297289452930'); | ||
const c = a.add(b); | ||
const serialized = JSON.stringify(c); | ||
// 7881297307901902 | ||
const parsed = JSON.parse<MpZ>(serialized); | ||
``` | ||
It also supports nullable primitive types, something that AssemblyScript usually can't do | ||
```js | ||
@json | ||
class Vec2 { | ||
x: f64 | null = 1.0; | ||
y: f32 | null = 2.0; | ||
} | ||
const vec: Vec2 = { | ||
x: 1.0, | ||
y: null | ||
} | ||
vec.y = 2.0; | ||
if (vec.y) { | ||
// do something | ||
} | ||
``` | ||
NOTE: There are a few quirks to using nullable primitives. First, you'll get a warning about usize (ignore it, its fine) and secondly, you'll get a wasm validation error if you do not have a `!` operator after accessing an element. Eg. `console.log(vec.y.toString())` fails, but `console.log(vec.y!.toString())` works. | ||
You can also add it to your `asconfig.json` | ||
{ | ||
// ... | ||
"options": { | ||
"transform": ["json-as/transform"], | ||
"disableWarning": [226] | ||
} | ||
// ... | ||
"options": { | ||
"transform": ["json-as/transform"] | ||
} | ||
} | ||
@@ -190,3 +152,3 @@ If you use this project in your codebase, consider dropping a [star](https://github.com/JairusSW/as-json). I would really appreciate it! | ||
My library beats JSON (written in C++) on all counts *and*, I see many places where I can pull at least a 60% uplift in performance if I implement it. | ||
My library beats JSON (written in C++) on all counts _and_, I see many places where I can pull at least a 60% uplift in performance if I implement it. | ||
@@ -197,33 +159,31 @@ Note: SIMD is in-development and only available on the `v1` branch on GitHub | ||
| Value | JavaScript (ops/s) | JSON-AS (ops/s) | JSON-AS (Pages) | JSON-AS (SIMD+Pages)| Max Throughput | | ||
|----------------------------|--------------------|--------------------|---------------------|---------------------|----------------| | ||
| "hello world" | 7,124,361 | 44,290,480 (6.2x) | 73,601,235 (10.3x) | NOT IMPLEMENTED | 1.91 GB/s | | ||
| 12345 | 9,611,677 | 66,900,642 (6.9x) | 145,924,333 (15.2x) | NOT IMPLEMENTED | 0.58 GB/s | | ||
| 1.2345 | 7,227,259 | 20,322,939 (2.8x) | NOT IMPLEMENTED | NOT IMPLEMENTED | 0.16 GB/s | | ||
| [[],[[]],[[],[[]]]] | 5,655,429 | 34,453,102 (6.0x) | NOT IMPLEMENTED | NOT IMPLEMENTED | 1.32 GB/s | | ||
| { x: f64, y: f64, z: f64 } | 3,878,604 | 44,557,996 (11.5x) | 113,203,242 (29.2x) | 172,023,231 (44.4x) | 8.61 GB/s | | ||
| Value | JavaScript (ops/s) | JSON-AS (ops/s) | JSON-AS (Pages) | JSON-AS (SIMD+Pages) | Max Throughput | | ||
| -------------------------- | ------------------ | ------------------ | ------------------- | -------------------- | -------------- | | ||
| "hello world" | 7,124,361 | 44,290,480 (6.2x) | 73,601,235 (10.3x) | NOT IMPLEMENTED | 1.91 GB/s | | ||
| 12345 | 9,611,677 | 66,900,642 (6.9x) | 145,924,333 (15.2x) | NOT IMPLEMENTED | 0.58 GB/s | | ||
| 1.2345 | 7,227,259 | 20,322,939 (2.8x) | NOT IMPLEMENTED | NOT IMPLEMENTED | 0.16 GB/s | | ||
| [[],[[]],[[],[[]]]] | 5,655,429 | 34,453,102 (6.0x) | NOT IMPLEMENTED | NOT IMPLEMENTED | 1.32 GB/s | | ||
| { x: f64, y: f64, z: f64 } | 3,878,604 | 44,557,996 (11.5x) | 113,203,242 (29.2x) | 172,023,231 (44.4x) | 8.61 GB/s | | ||
Deserialization Benchmarks: | ||
| Value | JavaScript (ops/s) | JSON-AS (ops/s) | Difference| | ||
|----------------------------|--------------------|-----------------|-----------| | ||
| "hello world" | 12,210,131 | 24,274,496 | + 98% | | ||
| "12345" | 21,376,873 | 254,640,930 | + 1,191% | | ||
| 1.2345 | 23,193,902 | 221,869,840 | + 987% | | ||
| [[],[[]],[[],[[]]]] | 4,777,227 | 74,921,123 | + 1,568% | | ||
| { x: f64, y: f64, z: f64 } | 10,973,723 | 25,214,019 | + 230% | | ||
| Value | JavaScript (ops/s) | JSON-AS (ops/s) | Difference | | ||
| -------------------------- | ------------------ | --------------- | ---------- | | ||
| "hello world" | 12,210,131 | 24,274,496 | + 98% | | ||
| "12345" | 21,376,873 | 254,640,930 | + 1,191% | | ||
| 1.2345 | 23,193,902 | 221,869,840 | + 987% | | ||
| [[],[[]],[[],[[]]]] | 4,777,227 | 74,921,123 | + 1,568% | | ||
| { x: f64, y: f64, z: f64 } | 10,973,723 | 25,214,019 | + 230% | | ||
And my PC specs: | ||
| Component | Specification | | ||
|-----------------|--------------------------------------| | ||
| Wasmer Version | v4.3.0 | | ||
| CPU | AMD Ryzen 7 7800x3D @ 6.00 GHz | | ||
| Memory | T-Force DDR5 6000 MHz | | ||
| OS | Ubuntu WSL2 | | ||
| Component | Specification | | ||
| -------------- | ------------------------------ | | ||
| Wasmer Version | v4.3.0 | | ||
| CPU | AMD Ryzen 7 7800x3D @ 6.00 GHz | | ||
| Memory | T-Force DDR5 6000 MHz | | ||
| OS | Ubuntu WSL2 | | ||
## Issues | ||
Please submit an issue to https://github.com/JairusSW/as-json/issues if you find anything wrong with this library | ||
Please submit an issue to https://github.com/JairusSW/as-json/issues if you find anything wrong with this library |
@@ -1,2 +0,5 @@ | ||
import { FieldDeclaration } from "assemblyscript/dist/assemblyscript.js"; | ||
import { | ||
FieldDeclaration, | ||
StringLiteralExpression, | ||
} from "assemblyscript/dist/assemblyscript.js"; | ||
import { toString, isStdlib } from "visitor-as/dist/utils.js"; | ||
@@ -6,456 +9,507 @@ import { BaseVisitor, SimpleParser } from "visitor-as/dist/index.js"; | ||
class JSONTransform extends BaseVisitor { | ||
constructor() { | ||
super(...arguments); | ||
this.schemasList = []; | ||
this.sources = new Set(); | ||
constructor() { | ||
super(...arguments); | ||
this.schemasList = []; | ||
this.sources = new Set(); | ||
} | ||
visitMethodDeclaration() {} | ||
visitClassDeclaration(node) { | ||
if (!node.decorators?.length) return; | ||
let found = false; | ||
for (const decorator of node.decorators) { | ||
const name = decorator.name.text; | ||
if (name === "json" || name === "serializable") { | ||
found = true; | ||
break; | ||
} | ||
} | ||
visitMethodDeclaration() { } | ||
visitClassDeclaration(node) { | ||
if (!node.decorators?.length) | ||
return; | ||
let found = false; | ||
for (const decorator of node.decorators) { | ||
const name = decorator.name.text; | ||
if (name === "json" || name === "serializable") { | ||
found = true; | ||
break; | ||
} | ||
if (!found) return; | ||
const schema = new SchemaData(); | ||
schema.node = node; | ||
schema.name = node.name.text; | ||
const members = [ | ||
...node.members.filter( | ||
(v) => v.kind === 54 /* NodeKind.FieldDeclaration */, | ||
), | ||
]; | ||
if (node.extendsType) { | ||
schema.parent = this.schemasList.find( | ||
(v) => v.name == node.extendsType?.name.identifier.text, | ||
); | ||
if (schema.parent?.members) { | ||
for (let i = schema.parent.members.length - 1; i >= 0; i--) { | ||
const replace = schema.members.find( | ||
(v) => v.name == schema.parent?.members[i]?.name, | ||
); | ||
if (!replace) { | ||
members.unshift(schema.parent?.members[i].node); | ||
} | ||
} | ||
if (!found) | ||
return; | ||
const schema = new SchemaData(); | ||
schema.node = node; | ||
schema.name = node.name.text; | ||
const members = [ | ||
...node.members.filter(v => v.kind === 54 /* NodeKind.FieldDeclaration */) | ||
]; | ||
if (node.extendsType) { | ||
schema.parent = this.schemasList.find((v) => v.name == node.extendsType?.name.identifier.text); | ||
if (schema.parent?.members) { | ||
for (let i = schema.parent.members.length - 1; i >= 0; i--) { | ||
const replace = schema.members.find((v) => v.name == schema.parent?.members[i]?.name); | ||
if (!replace) { | ||
//schema.members.unshift(schema.parent?.members[i]!); | ||
members.unshift(schema.parent?.members[i].node); | ||
} | ||
} | ||
} | ||
} | ||
if (!members.length) { | ||
let SERIALIZE_RAW_EMPTY = '__SERIALIZE(): string {\n return "{}";\n}'; | ||
//let SERIALIZE_PRETTY_EMPTY = "__SERIALIZE_PRETTY(): string {\n return \"{}\";\n}"; | ||
let INITIALIZE_EMPTY = "__INITIALIZE(): this {\n return this;\n}"; | ||
let DESERIALIZE_EMPTY = | ||
"__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n return false;\n}"; | ||
if (process.env["JSON_DEBUG"]) { | ||
console.log(SERIALIZE_RAW_EMPTY); | ||
//console.log(SERIALIZE_PRETTY_EMPTY); | ||
console.log(INITIALIZE_EMPTY); | ||
console.log(DESERIALIZE_EMPTY); | ||
} | ||
const SERIALIZE_RAW_METHOD_EMPTY = SimpleParser.parseClassMember( | ||
SERIALIZE_RAW_EMPTY, | ||
node, | ||
); | ||
//const SERIALIZE_PRETTY_METHOD = SimpleParser.parseClassMember(SERIALIZE_PRETTY, node); | ||
const INITIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember( | ||
INITIALIZE_EMPTY, | ||
node, | ||
); | ||
const DESERIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember( | ||
DESERIALIZE_EMPTY, | ||
node, | ||
); | ||
if (!node.members.find((v) => v.name.text == "__SERIALIZE")) | ||
node.members.push(SERIALIZE_RAW_METHOD_EMPTY); | ||
if (!node.members.find((v) => v.name.text == "__INITIALIZE")) | ||
node.members.push(INITIALIZE_METHOD_EMPTY); | ||
if (!node.members.find((v) => v.name.text == "__DESERIALIZE")) | ||
node.members.push(DESERIALIZE_METHOD_EMPTY); | ||
this.schemasList.push(schema); | ||
} | ||
for (const member of members) { | ||
const name = member.name; | ||
if (!(member instanceof FieldDeclaration)) continue; | ||
if (!member.type) { | ||
throw new Error( | ||
"Fields must be strongly typed! Found " + | ||
toString(member) + | ||
" at " + | ||
node.range.source.normalizedPath, | ||
); | ||
} | ||
const type = toString(member.type); | ||
if (type.startsWith("(") && type.includes("=>")) continue; | ||
const value = member.initializer ? toString(member.initializer) : null; | ||
if (member.flags == 32 /* CommonFlags.Static */) continue; | ||
if (member.flags === 512 /* CommonFlags.Private */) continue; | ||
if (member.flags === 1024 /* CommonFlags.Protected */) continue; | ||
const mem = new Property(); | ||
mem.name = name.text; | ||
mem.type = type; | ||
mem.value = value; | ||
mem.node = member; | ||
if (type == "JSON.Raw") { | ||
mem.flags.set(PropertyFlags.JSON_Raw, []); | ||
} | ||
if (member.decorators) { | ||
for (const decorator of member.decorators) { | ||
const decoratorName = decorator.name.text; | ||
const args = getArgs(decorator.args); | ||
switch (decoratorName) { | ||
case "alias": { | ||
if (!args.length) | ||
throw new Error( | ||
"Expected 1 argument but got zero at @alias in " + | ||
node.range.source.normalizedPath, | ||
); | ||
mem.alias = args[0]; | ||
mem.flags.set(PropertyFlags.Alias, args); | ||
break; | ||
} | ||
} | ||
if (!members.length) { | ||
let SERIALIZE_RAW_EMPTY = "__SERIALIZE(): string {\n return \"{}\";\n}"; | ||
//let SERIALIZE_PRETTY_EMPTY = "__SERIALIZE_PRETTY(): string {\n return \"{}\";\n}"; | ||
let INITIALIZE_EMPTY = "__INITIALIZE(): this {\n return this;\n}"; | ||
let DESERIALIZE_EMPTY = "__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n return false;\n}"; | ||
if (process.env["JSON_DEBUG"]) { | ||
console.log(SERIALIZE_RAW_EMPTY); | ||
//console.log(SERIALIZE_PRETTY_EMPTY); | ||
console.log(INITIALIZE_EMPTY); | ||
console.log(DESERIALIZE_EMPTY); | ||
case "omit": { | ||
mem.flags.set(PropertyFlags.Omit, args); | ||
break; | ||
} | ||
const SERIALIZE_RAW_METHOD_EMPTY = SimpleParser.parseClassMember(SERIALIZE_RAW_EMPTY, node); | ||
//const SERIALIZE_PRETTY_METHOD = SimpleParser.parseClassMember(SERIALIZE_PRETTY, node); | ||
const INITIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember(INITIALIZE_EMPTY, node); | ||
const DESERIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember(DESERIALIZE_EMPTY, node); | ||
if (!node.members.find(v => v.name.text == "__SERIALIZE")) | ||
node.members.push(SERIALIZE_RAW_METHOD_EMPTY); | ||
if (!node.members.find(v => v.name.text == "__INITIALIZE")) | ||
node.members.push(INITIALIZE_METHOD_EMPTY); | ||
if (!node.members.find(v => v.name.text == "__DESERIALIZE")) | ||
node.members.push(DESERIALIZE_METHOD_EMPTY); | ||
this.schemasList.push(schema); | ||
} | ||
for (const member of members) { | ||
const name = member.name; | ||
if (!(member instanceof FieldDeclaration)) | ||
continue; | ||
if (!member.type) { | ||
throw new Error("Fields must be strongly typed! Found " + toString(member) + " at " + node.range.source.normalizedPath); | ||
case "omitif": { | ||
if (!decorator.args?.length) | ||
throw new Error( | ||
"Expected 1 argument but got zero at @omitif in " + | ||
node.range.source.normalizedPath, | ||
); | ||
mem.flags.set(PropertyFlags.OmitIf, args); | ||
break; | ||
} | ||
const type = toString(member.type); | ||
if (type.startsWith("(") && type.includes("=>")) | ||
continue; | ||
const value = member.initializer ? toString(member.initializer) : null; | ||
if (member.flags == 32 /* CommonFlags.Static */) | ||
continue; | ||
if (member.flags === 512 /* CommonFlags.Private */) | ||
continue; | ||
if (member.flags === 1024 /* CommonFlags.Protected */) | ||
continue; | ||
if (member.decorators && member.decorators.find((v) => v.name.text == "omit")) | ||
continue; | ||
const mem = new Property(); | ||
mem.name = name.text; | ||
mem.type = type; | ||
mem.value = value; | ||
mem.node = member; | ||
if (member.decorators) { | ||
let decorator = null; | ||
if (decorator = member.decorators.find(v => v.name.text == "alias")) { | ||
if (decorator.name.text == "alias") { | ||
if (!decorator.args?.length) | ||
throw new Error("Expected 1 argument but got zero at @alias in " + node.range.source.normalizedPath); | ||
mem.flags.push(PropertyFlags.Alias); | ||
mem.alias = decorator.args[0].value; | ||
} | ||
} | ||
for (let i = 0; i < (member.decorators).length; i++) { | ||
const decorator = member.decorators[i]; | ||
if (decorator.name.text == "omitnull") { | ||
mem.flags.push(PropertyFlags.OmitNull); | ||
} | ||
else if (decorator.name.text == "omitif") { | ||
if (!decorator.args?.length) | ||
throw new Error("Expected 1 argument but got zero at @omitif in " + node.range.source.normalizedPath); | ||
mem.args?.push(decorator.args[0].value); | ||
mem.flags.push(PropertyFlags.OmitIf); | ||
} | ||
else if (decorator.name.text == "flatten") { | ||
if (!decorator.args?.length) | ||
throw new Error("Expected 1 argument but got zero at @flatten in " + node.range.source.normalizedPath); | ||
mem.flags.push(PropertyFlags.Flatten); | ||
mem.args = [decorator.args[0].value]; | ||
} | ||
} | ||
case "omitnull": { | ||
mem.flags.set(PropertyFlags.OmitNull, args); | ||
break; | ||
} | ||
if (!mem.flags.length) { | ||
mem.flags = [PropertyFlags.None]; | ||
if (type == "JSON.Raw") { | ||
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${this." + name.text + "}"; | ||
mem.deserialize = "this." + name.text + " = " + "data.substring(value_start, value_end);"; | ||
} | ||
else { | ||
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${__SERIALIZE<" + type + ">(this." + name.text + ")}"; | ||
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));"; | ||
} | ||
} | ||
if (mem.flags.includes(PropertyFlags.OmitNull)) { | ||
mem.serialize = "${changetype<usize>(this." + mem.name + ") == <usize>0" + " ? \"\" : '" + escapeString(JSON.stringify(mem.alias || mem.name)) + ":' + __SERIALIZE<" + type + ">(this." + name.text + ") + \",\"}"; | ||
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));"; | ||
} | ||
else if (mem.flags.includes(PropertyFlags.OmitIf)) { | ||
mem.serialize = "${" + mem.args[0] + " ? \"\" : '" + escapeString(JSON.stringify(mem.alias || mem.name)) + ":' + __SERIALIZE<" + type + ">(this." + name.text + ") + \",\"}"; | ||
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));"; | ||
} | ||
else if (mem.flags.includes(PropertyFlags.Alias)) { | ||
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${__SERIALIZE<" + type + ">(this." + name.text + ")}"; | ||
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));"; | ||
mem.name = name.text; | ||
} | ||
else if (mem.flags.includes(PropertyFlags.Flatten)) { | ||
const nullable = mem.node.type.isNullable; | ||
if (nullable) { | ||
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${this." + name.text + " ? __SERIALIZE(changetype<nonnull<" + type + ">>(this." + name.text + ")" + (mem.args?.length ? '.' + mem.args.join(".") : '') + ") : \"null\"}"; | ||
mem.deserialize = "if (value_end - value_start == 4 && load<u64>(changetype<usize>(data) + <usize>(value_start << 1)) == " + charCodeAt64("null", 0) + ") {\n this." + name.text + " = null;\n } else {\n this." + name.text + " = " + "__DESERIALIZE<" + type + ">('{\"" + mem.args[0] + "\":' + data.substring(value_start, value_end) + \"}\");\n }"; | ||
} | ||
else { | ||
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${this." + name.text + " ? __SERIALIZE(this." + name.text + (mem.args?.length ? '.' + mem.args.join(".") : '') + ") : \"null\"}"; | ||
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">('{\"" + mem.args[0] + "\":' + data.substring(value_start, value_end) + \"}\");"; | ||
} | ||
mem.name = name.text; | ||
} | ||
const t = mem.node.type.name?.identifier.text; | ||
if (this.schemasList.find(v => v.name == t)) { | ||
mem.initialize = "this." + name.text + " = changetype<nonnull<" + mem.type + ">>(__new(offsetof<nonnull<" + mem.type + ">>(), idof<nonnull<" + mem.type + ">>()));\n changetype<nonnull<" + mem.type + ">>(this." + name.text + ").__INITIALIZE()"; | ||
} | ||
else if (mem.value) { | ||
mem.initialize = "this." + name.text + " = " + mem.value; | ||
} | ||
else if (t === "Map") { | ||
mem.initialize = "this." + name.text + " = new " + mem.type + "()"; | ||
} | ||
else if (t === "string") { | ||
mem.initialize = "this." + name.text + " = \"\""; | ||
} | ||
else if (t === "Array") { | ||
mem.initialize = "this." + name.text + " = instantiate<" + mem.type + ">()"; | ||
} | ||
else if (t === "bool" || t === "boolean") { | ||
mem.initialize = "this." + name.text + " = false"; | ||
} | ||
else if (t === "JSON.Raw") { | ||
mem.initialize = "this." + name.text + " = \"\""; | ||
} | ||
else if (t === "u8" || | ||
t === "u16" || | ||
t === "u32" || | ||
t === "u64" || | ||
t === "i8" || | ||
t === "i16" || | ||
t === "i32" || | ||
t === "i64") { | ||
mem.initialize = "this." + name.text + " = 0"; | ||
} | ||
else if (t === "f32" || | ||
t === "f64") { | ||
mem.initialize = "this." + name.text + " = 0.0"; | ||
} | ||
schema.members.push(mem); | ||
} | ||
} | ||
let SERIALIZE_RAW = "__SERIALIZE(): string {\n let out = `{"; | ||
let SERIALIZE_PRETTY = "__SERIALIZE_PRETTY(): string {\n let out = `{"; | ||
let INITIALIZE = "__INITIALIZE(): this {\n"; | ||
let DESERIALIZE = "__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n const len = key_end - key_start;\n"; | ||
let indent = " "; | ||
if (!schema.members.length) | ||
return; | ||
found = false; | ||
if (schema.members[0]?.flags.includes(PropertyFlags.OmitNull) | ||
|| schema.members[0]?.flags.includes(PropertyFlags.OmitIf)) { | ||
SERIALIZE_RAW += schema.members[0]?.serialize; | ||
SERIALIZE_PRETTY += "\\n" + schema.members[0]?.serialize; | ||
} | ||
mem.generate(); | ||
if (this.schemasList.find((v) => v.name == type)) { | ||
mem.initialize = | ||
"this." + | ||
name.text + | ||
" = changetype<nonnull<" + | ||
mem.type + | ||
">>(__new(offsetof<nonnull<" + | ||
mem.type + | ||
">>(), idof<nonnull<" + | ||
mem.type + | ||
">>()));\n changetype<nonnull<" + | ||
mem.type + | ||
">>(this." + | ||
name.text + | ||
").__INITIALIZE()"; | ||
} else if (mem.value) { | ||
mem.initialize = "this." + name.text + " = " + mem.value; | ||
} else if (type === "Map") { | ||
mem.initialize = "this." + name.text + " = new " + mem.type + "()"; | ||
} else if (type === "string") { | ||
mem.initialize = "this." + name.text + ' = ""'; | ||
} else if (type === "Array") { | ||
mem.initialize = | ||
"this." + name.text + " = instantiate<" + mem.type + ">()"; | ||
} else if (type === "bool" || type === "boolean") { | ||
mem.initialize = "this." + name.text + " = false"; | ||
} else if (type === "JSON.Raw") { | ||
mem.initialize = "this." + name.text + ' = ""'; | ||
} else if ( | ||
type === "u8" || | ||
type === "u16" || | ||
type === "u32" || | ||
type === "u64" || | ||
type === "i8" || | ||
type === "i16" || | ||
type === "i32" || | ||
type === "i64" | ||
) { | ||
mem.initialize = "this." + name.text + " = 0"; | ||
} else if (type === "f32" || type === "f64") { | ||
mem.initialize = "this." + name.text + " = 0.0"; | ||
} | ||
schema.members.push(mem); | ||
} | ||
let SERIALIZE_RAW = "__SERIALIZE(): string {\n let out = `{"; | ||
let SERIALIZE_PRETTY = "__SERIALIZE_PRETTY(): string {\n let out = `{"; | ||
let INITIALIZE = "__INITIALIZE(): this {\n"; | ||
let DESERIALIZE = | ||
"__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n const len = key_end - key_start;\n"; | ||
let indent = " "; | ||
if (!schema.members.length) return; | ||
found = false; | ||
if ( | ||
schema.members[0]?.flags.has(PropertyFlags.OmitNull) || | ||
schema.members[0]?.flags.has(PropertyFlags.OmitIf) | ||
) { | ||
SERIALIZE_RAW += schema.members[0]?.serialize; | ||
SERIALIZE_PRETTY += "\\n" + schema.members[0]?.serialize; | ||
} else { | ||
SERIALIZE_RAW += schema.members[0]?.serialize + ","; | ||
SERIALIZE_PRETTY += "\\n" + schema.members[0]?.serialize + ",\\n"; | ||
found = true; | ||
} | ||
if (schema.members[0]?.initialize) | ||
INITIALIZE += " " + schema.members[0]?.initialize + ";\n"; | ||
for (let i = 1; i < schema.members.length; i++) { | ||
const member = schema.members[i]; | ||
if (member.initialize) INITIALIZE += " " + member.initialize + ";\n"; | ||
if ( | ||
member.flags.has(PropertyFlags.OmitNull) || | ||
member.flags.has(PropertyFlags.OmitIf) | ||
) { | ||
SERIALIZE_RAW += member.serialize; | ||
SERIALIZE_PRETTY += member.serialize; | ||
} else { | ||
SERIALIZE_RAW += member.serialize + ","; | ||
SERIALIZE_PRETTY += indent + member.serialize + ",\\n"; | ||
found = true; | ||
} | ||
} | ||
if (found) { | ||
SERIALIZE_RAW += | ||
"`;\n store<u16>(changetype<usize>(out) + ((out.length - 1) << 1), 125);\n return out;\n}"; | ||
SERIALIZE_PRETTY += | ||
"`;\n store<u32>(changetype<usize>(out) + ((out.length - 2) << 1), 8192010);\n return out;\n}"; | ||
} else { | ||
SERIALIZE_RAW += "`;\n};"; | ||
SERIALIZE_PRETTY += "`;\n};"; | ||
} | ||
INITIALIZE += " return this;\n}"; | ||
const sortedMembers = []; | ||
const _sorted = schema.members.sort( | ||
(a, b) => a.name.length - b.name.length, | ||
); | ||
let len = 0; | ||
let offset = 0; | ||
sortedMembers.push([_sorted[0]]); | ||
len = _sorted[0]?.name.length; | ||
for (let i = 1; i < _sorted.length; i++) { | ||
const member = _sorted[i]; | ||
if (member.alias?.length || member.name.length > len) { | ||
sortedMembers.push([member]); | ||
len = member.alias?.length || member.name.length; | ||
offset++; | ||
} else { | ||
sortedMembers[offset].push(member); | ||
} | ||
} | ||
let first = true; | ||
for (const memberSet of sortedMembers) { | ||
const firstMember = memberSet[0]; | ||
const name = encodeKey(firstMember.alias || firstMember.name); | ||
if (name.length === 1) { | ||
if (first) { | ||
DESERIALIZE += | ||
" if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
first = false; | ||
} else { | ||
DESERIALIZE += | ||
"else if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
} | ||
else { | ||
SERIALIZE_RAW += schema.members[0]?.serialize + ","; | ||
SERIALIZE_PRETTY += "\\n" + schema.members[0]?.serialize + ",\\n"; | ||
found = true; | ||
} else if (name.length === 2) { | ||
if (first) { | ||
DESERIALIZE += | ||
" if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
first = false; | ||
} else { | ||
DESERIALIZE += | ||
"else if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
} | ||
if (schema.members[0]?.initialize) | ||
INITIALIZE += " " + schema.members[0]?.initialize + ";\n"; | ||
for (let i = 1; i < schema.members.length; i++) { | ||
const member = schema.members[i]; | ||
if (member.initialize) | ||
INITIALIZE += " " + member.initialize + ";\n"; | ||
if (member.flags.includes(PropertyFlags.OmitNull) | ||
|| member.flags.includes(PropertyFlags.OmitIf)) { | ||
SERIALIZE_RAW += member.serialize; | ||
SERIALIZE_PRETTY += member.serialize; | ||
} | ||
else { | ||
SERIALIZE_RAW += member.serialize + ","; | ||
SERIALIZE_PRETTY += indent + member.serialize + ",\\n"; | ||
found = true; | ||
} | ||
} else if (name.length === 4) { | ||
if (first) { | ||
DESERIALIZE += | ||
" if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n"; | ||
first = false; | ||
} else { | ||
DESERIALIZE += | ||
"else if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n"; | ||
} | ||
if (found) { | ||
SERIALIZE_RAW += "`;\n store<u16>(changetype<usize>(out) + ((out.length - 1) << 1), 125);\n return out;\n}"; | ||
SERIALIZE_PRETTY += "`;\n store<u32>(changetype<usize>(out) + ((out.length - 2) << 1), 8192010);\n return out;\n}"; | ||
} else { | ||
if (first) { | ||
DESERIALIZE += " if (" + name.length + " === len) {\n"; | ||
first = false; | ||
} else { | ||
DESERIALIZE += "else if (" + name.length + " === len) {\n"; | ||
} | ||
else { | ||
SERIALIZE_RAW += "`;\n};"; | ||
SERIALIZE_PRETTY += "`;\n};"; | ||
} | ||
let f = true; | ||
for (let i = 0; i < memberSet.length; i++) { | ||
const member = memberSet[i]; | ||
if (!member.deserialize) continue; | ||
const name = encodeKey(member.alias || member.name); | ||
if (name.length === 1) { | ||
DESERIALIZE += ` case ${name.charCodeAt(0)}: {\n ${member.deserialize}\n return true;\n }\n`; | ||
} else if (name.length === 2) { | ||
DESERIALIZE += ` case ${charCodeAt32(name, 0)}: {\n ${member.deserialize}\n return true;\n }\n`; | ||
} else if (name.length === 4) { | ||
if (f) { | ||
f = false; | ||
DESERIALIZE += ` if (${charCodeAt64(name, 0)} === code) {\n ${member.deserialize}\n return true;\n }\n`; | ||
} else { | ||
DESERIALIZE = | ||
DESERIALIZE.slice(0, DESERIALIZE.length - 1) + | ||
`else if (${charCodeAt64(name, 0)} === code) {\n ${member.deserialize}\n return true;\n }\n`; | ||
} | ||
} else { | ||
if (f) { | ||
f = false; | ||
DESERIALIZE += ` if (0 == memory.compare(changetype<usize>("${escapeQuote(escapeSlash(name))}"), changetype<usize>(data) + (key_start << 1), ${name.length << 1})) {\n ${member.deserialize}\n return true;\n }\n`; | ||
} else { | ||
DESERIALIZE = | ||
DESERIALIZE.slice(0, DESERIALIZE.length - 1) + | ||
` else if (0 == memory.compare(changetype<usize>("${escapeQuote(escapeSlash(name))}"), changetype<usize>(data) + (key_start << 1), ${name.length << 1})) {\n ${member.deserialize}\n return true;\n }\n`; | ||
} | ||
} | ||
INITIALIZE += " return this;\n}"; | ||
const sortedMembers = []; | ||
const _sorted = schema.members.sort((a, b) => a.name.length - b.name.length); | ||
let len = 0; | ||
let offset = 0; | ||
sortedMembers.push([_sorted[0]]); | ||
len = _sorted[0]?.name.length; | ||
for (let i = 1; i < _sorted.length; i++) { | ||
const member = _sorted[i]; | ||
if (member.alias?.length || member.name.length > len) { | ||
sortedMembers.push([member]); | ||
len = member.alias?.length || member.name.length; | ||
offset++; | ||
} | ||
else { | ||
sortedMembers[offset].push(member); | ||
} | ||
} | ||
let first = true; | ||
for (const memberSet of sortedMembers) { | ||
const firstMember = memberSet[0]; | ||
const name = encodeKey(firstMember.alias || firstMember.name); | ||
if (name.length === 1) { | ||
if (first) { | ||
DESERIALIZE += " if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
first = false; | ||
} | ||
else { | ||
DESERIALIZE += "else if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
} | ||
} | ||
else if (name.length === 2) { | ||
if (first) { | ||
DESERIALIZE += " if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
first = false; | ||
} | ||
else { | ||
DESERIALIZE += "else if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
} | ||
} | ||
else if (name.length === 4) { | ||
if (first) { | ||
DESERIALIZE += " if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n"; | ||
first = false; | ||
} | ||
else { | ||
DESERIALIZE += "else if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n"; | ||
} | ||
} | ||
else { | ||
if (first) { | ||
DESERIALIZE += " if (" + name.length + " === len) {\n"; | ||
first = false; | ||
} | ||
else { | ||
DESERIALIZE += "else if (" + name.length + " === len) {\n"; | ||
} | ||
} | ||
let f = true; | ||
for (let i = 0; i < memberSet.length; i++) { | ||
const member = memberSet[i]; | ||
if (!member.deserialize) | ||
continue; | ||
const name = encodeKey(member.alias || member.name); | ||
if (name.length === 1) { | ||
DESERIALIZE += ` case ${name.charCodeAt(0)}: {\n ${member.deserialize}\n return true;\n }\n`; | ||
} | ||
else if (name.length === 2) { | ||
DESERIALIZE += ` case ${charCodeAt32(name, 0)}: {\n ${member.deserialize}\n return true;\n }\n`; | ||
} | ||
else if (name.length === 4) { | ||
if (f) { | ||
f = false; | ||
DESERIALIZE += ` if (${charCodeAt64(name, 0)} === code) {\n ${member.deserialize}\n return true;\n }\n`; | ||
} | ||
else { | ||
DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + `else if (${charCodeAt64(name, 0)} === code) {\n ${member.deserialize}\n return true;\n }\n`; | ||
} | ||
} | ||
else { | ||
if (f) { | ||
f = false; | ||
DESERIALIZE += ` if (0 == memory.compare(changetype<usize>("${escapeQuote(escapeSlash(name))}"), changetype<usize>(data) + (key_start << 1), ${name.length << 1})) {\n ${member.deserialize}\n return true;\n }\n`; | ||
} | ||
else { | ||
DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else if (0 == memory.compare(changetype<usize>("${escapeQuote(escapeSlash(name))}"), changetype<usize>(data) + (key_start << 1), ${name.length << 1})) {\n ${member.deserialize}\n return true;\n }\n`; | ||
} | ||
} | ||
} | ||
if (name.length < 3) { | ||
DESERIALIZE += ` default: {\n return false;\n }\n }\n`; | ||
} | ||
else if (name.length == 4) { | ||
DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else {\n return false;\n }\n`; | ||
} | ||
else { | ||
DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else {\n return false;\n }\n`; | ||
} | ||
DESERIALIZE += " } "; | ||
} | ||
DESERIALIZE += "\n return false;\n}"; | ||
//console.log(sortedMembers); | ||
if (process.env["JSON_DEBUG"]) { | ||
console.log(SERIALIZE_RAW); | ||
//console.log(SERIALIZE_PRETTY); | ||
console.log(INITIALIZE); | ||
console.log(DESERIALIZE); | ||
} | ||
const SERIALIZE_RAW_METHOD = SimpleParser.parseClassMember(SERIALIZE_RAW, node); | ||
//const SERIALIZE_PRETTY_METHOD = SimpleParser.parseClassMember(SERIALIZE_PRETTY, node); | ||
const INITIALIZE_METHOD = SimpleParser.parseClassMember(INITIALIZE, node); | ||
const DESERIALIZE_METHOD = SimpleParser.parseClassMember(DESERIALIZE, node); | ||
if (!node.members.find(v => v.name.text == "__SERIALIZE")) | ||
node.members.push(SERIALIZE_RAW_METHOD); | ||
if (!node.members.find(v => v.name.text == "__INITIALIZE")) | ||
node.members.push(INITIALIZE_METHOD); | ||
if (!node.members.find(v => v.name.text == "__DESERIALIZE")) | ||
node.members.push(DESERIALIZE_METHOD); | ||
this.schemasList.push(schema); | ||
} | ||
if (name.length < 3) { | ||
DESERIALIZE += ` default: {\n return false;\n }\n }\n`; | ||
} else if (name.length == 4) { | ||
DESERIALIZE = | ||
DESERIALIZE.slice(0, DESERIALIZE.length - 1) + | ||
` else {\n return false;\n }\n`; | ||
} else { | ||
DESERIALIZE = | ||
DESERIALIZE.slice(0, DESERIALIZE.length - 1) + | ||
` else {\n return false;\n }\n`; | ||
} | ||
DESERIALIZE += " } "; | ||
} | ||
visitSource(node) { | ||
super.visitSource(node); | ||
// Only add the import statement to sources that have JSON decorated classes. | ||
if (!this.sources.has(node)) { | ||
return; | ||
} | ||
DESERIALIZE += "\n return false;\n}"; | ||
//console.log(sortedMembers); | ||
if (process.env["JSON_DEBUG"]) { | ||
console.log(SERIALIZE_RAW); | ||
//console.log(SERIALIZE_PRETTY); | ||
console.log(INITIALIZE); | ||
console.log(DESERIALIZE); | ||
} | ||
const SERIALIZE_RAW_METHOD = SimpleParser.parseClassMember( | ||
SERIALIZE_RAW, | ||
node, | ||
); | ||
//const SERIALIZE_PRETTY_METHOD = SimpleParser.parseClassMember(SERIALIZE_PRETTY, node); | ||
const INITIALIZE_METHOD = SimpleParser.parseClassMember(INITIALIZE, node); | ||
const DESERIALIZE_METHOD = SimpleParser.parseClassMember(DESERIALIZE, node); | ||
if (!node.members.find((v) => v.name.text == "__SERIALIZE")) | ||
node.members.push(SERIALIZE_RAW_METHOD); | ||
if (!node.members.find((v) => v.name.text == "__INITIALIZE")) | ||
node.members.push(INITIALIZE_METHOD); | ||
if (!node.members.find((v) => v.name.text == "__DESERIALIZE")) | ||
node.members.push(DESERIALIZE_METHOD); | ||
this.schemasList.push(schema); | ||
} | ||
visitSource(node) { | ||
super.visitSource(node); | ||
// Only add the import statement to sources that have JSON decorated classes. | ||
if (!this.sources.has(node)) { | ||
return; | ||
} | ||
} | ||
} | ||
export default class Transformer extends Transform { | ||
// Trigger the transform after parse. | ||
afterParse(parser) { | ||
// Create new transform | ||
const transformer = new JSONTransform(); | ||
// Sort the sources so that user scripts are visited last | ||
const sources = parser.sources | ||
.filter((source) => !isStdlib(source)) | ||
.sort((_a, _b) => { | ||
const a = _a.internalPath; | ||
const b = _b.internalPath; | ||
if (a[0] === "~" && b[0] !== "~") { | ||
return -1; | ||
} | ||
else if (a[0] !== "~" && b[0] === "~") { | ||
return 1; | ||
} | ||
else { | ||
return 0; | ||
} | ||
}); | ||
// Loop over every source | ||
for (const source of sources) { | ||
// Ignore all lib and std. Visit everything else. | ||
if (!isStdlib(source)) { | ||
transformer.visit(source); | ||
} | ||
// Trigger the transform after parse. | ||
afterParse(parser) { | ||
// Create new transform | ||
const transformer = new JSONTransform(); | ||
// Sort the sources so that user scripts are visited last | ||
const sources = parser.sources | ||
.filter((source) => !isStdlib(source)) | ||
.sort((_a, _b) => { | ||
const a = _a.internalPath; | ||
const b = _b.internalPath; | ||
if (a[0] === "~" && b[0] !== "~") { | ||
return -1; | ||
} else if (a[0] !== "~" && b[0] === "~") { | ||
return 1; | ||
} else { | ||
return 0; | ||
} | ||
// Check that every parent and child class is hooked up correctly | ||
const schemas = transformer.schemasList; | ||
for (const schema of schemas) { | ||
if (schema.parent) { | ||
const parent = schemas.find((v) => v.name === schema.parent?.name); | ||
if (!parent) | ||
throw new Error(`Class ${schema.name} extends its parent class ${schema.parent}, but ${schema.parent} does not include a @json or @serializable decorator! Add the decorator and rebuild.`); | ||
} | ||
} | ||
}); | ||
// Loop over every source | ||
for (const source of sources) { | ||
// Ignore all lib and std. Visit everything else. | ||
if (!isStdlib(source)) { | ||
transformer.visit(source); | ||
} | ||
} | ||
// Check that every parent and child class is hooked up correctly | ||
const schemas = transformer.schemasList; | ||
for (const schema of schemas) { | ||
if (schema.parent) { | ||
const parent = schemas.find((v) => v.name === schema.parent?.name); | ||
if (!parent) | ||
throw new Error( | ||
`Class ${schema.name} extends its parent class ${schema.parent}, but ${schema.parent} does not include a @json or @serializable decorator! Add the decorator and rebuild.`, | ||
); | ||
} | ||
} | ||
} | ||
} | ||
var PropertyFlags; | ||
(function (PropertyFlags) { | ||
PropertyFlags[PropertyFlags["None"] = 0] = "None"; | ||
PropertyFlags[PropertyFlags["Omit"] = 1] = "Omit"; | ||
PropertyFlags[PropertyFlags["OmitNull"] = 2] = "OmitNull"; | ||
PropertyFlags[PropertyFlags["OmitIf"] = 3] = "OmitIf"; | ||
PropertyFlags[PropertyFlags["Alias"] = 4] = "Alias"; | ||
PropertyFlags[PropertyFlags["Flatten"] = 5] = "Flatten"; | ||
PropertyFlags[(PropertyFlags["Omit"] = 0)] = "Omit"; | ||
PropertyFlags[(PropertyFlags["OmitNull"] = 1)] = "OmitNull"; | ||
PropertyFlags[(PropertyFlags["OmitIf"] = 2)] = "OmitIf"; | ||
PropertyFlags[(PropertyFlags["Alias"] = 3)] = "Alias"; | ||
PropertyFlags[(PropertyFlags["JSON_Raw"] = 4)] = "JSON_Raw"; | ||
})(PropertyFlags || (PropertyFlags = {})); | ||
class Property { | ||
constructor() { | ||
this.name = ""; | ||
this.alias = null; | ||
this.type = ""; | ||
this.value = null; | ||
this.flags = []; | ||
this.args = []; | ||
this.serialize = null; | ||
this.deserialize = null; | ||
this.initialize = null; | ||
constructor() { | ||
this.name = ""; | ||
this.alias = null; | ||
this.type = ""; | ||
this.value = null; | ||
this.flags = new Map(); | ||
this.args = []; | ||
this.serialize = null; | ||
this.deserialize = null; | ||
this.initialize = null; | ||
this.right_s = ""; | ||
this.right_d = ""; | ||
} | ||
generate() { | ||
const name = this.name; | ||
const escapedName = escapeString(JSON.stringify(this.alias || this.name)); | ||
const type = this.type; | ||
if (this.flags.has(PropertyFlags.Omit)) return; | ||
if (this.flags.has(PropertyFlags.JSON_Raw)) { | ||
this.right_s = "this." + name; | ||
this.right_d = "data.substring(value_start, value_end);"; | ||
} else { | ||
this.right_s = "__SERIALIZE<" + type + ">(this." + name + ")"; | ||
this.right_d = | ||
"__DESERIALIZE<" + type + ">(data.substring(value_start, value_end))"; | ||
} | ||
if (this.flags.has(PropertyFlags.OmitIf)) { | ||
const condition = this.args[0]; | ||
this.serialize = | ||
"${" + | ||
condition + | ||
' ? "" : \'' + | ||
escapedName + | ||
":' + " + | ||
this.right_s + | ||
' + ","}'; | ||
this.deserialize = "this." + name + " = " + this.right_d + ";"; | ||
} else if (this.flags.has(PropertyFlags.OmitNull)) { | ||
this.serialize = | ||
"${changetype<usize>(this." + | ||
name + | ||
") == <usize>0" + | ||
' ? "" : \'' + | ||
escapedName + | ||
":' + " + | ||
this.right_s + | ||
' + ","}'; | ||
this.deserialize = "this." + name + " = " + this.right_d + ";"; | ||
} else { | ||
this.serialize = escapedName + ":${" + this.right_s + "}"; | ||
this.deserialize = "this." + name + " = " + this.right_d + ";"; | ||
} | ||
} | ||
} | ||
class SchemaData { | ||
constructor() { | ||
this.name = ""; | ||
this.members = []; | ||
this.parent = null; | ||
} | ||
constructor() { | ||
this.name = ""; | ||
this.members = []; | ||
this.parent = null; | ||
} | ||
} | ||
function charCodeAt32(data, offset) { | ||
return (data.charCodeAt(offset + 1) << 16) | data.charCodeAt(offset); | ||
return (data.charCodeAt(offset + 1) << 16) | data.charCodeAt(offset); | ||
} | ||
function charCodeAt64(data, offset) { | ||
if (offset + 3 >= data.length) { | ||
throw new Error("The string must have at least 4 characters from the specified offset."); | ||
} | ||
const firstCharCode = BigInt(data.charCodeAt(offset)); | ||
const secondCharCode = BigInt(data.charCodeAt(offset + 1)); | ||
const thirdCharCode = BigInt(data.charCodeAt(offset + 2)); | ||
const fourthCharCode = BigInt(data.charCodeAt(offset + 3)); | ||
const u64Value = (fourthCharCode << 48n) | (thirdCharCode << 32n) | (secondCharCode << 16n) | firstCharCode; | ||
return u64Value; | ||
if (offset + 3 >= data.length) { | ||
throw new Error( | ||
"The string must have at least 4 characters from the specified offset.", | ||
); | ||
} | ||
const firstCharCode = BigInt(data.charCodeAt(offset)); | ||
const secondCharCode = BigInt(data.charCodeAt(offset + 1)); | ||
const thirdCharCode = BigInt(data.charCodeAt(offset + 2)); | ||
const fourthCharCode = BigInt(data.charCodeAt(offset + 3)); | ||
const u64Value = | ||
(fourthCharCode << 48n) | | ||
(thirdCharCode << 32n) | | ||
(secondCharCode << 16n) | | ||
firstCharCode; | ||
return u64Value; | ||
} | ||
function encodeKey(key) { | ||
const data = JSON.stringify(key); | ||
return data.slice(1, data.length - 1); | ||
const data = JSON.stringify(key); | ||
return data.slice(1, data.length - 1); | ||
} | ||
function escapeString(data) { | ||
return data.replace(/\\/g, "\\\\") | ||
.replace(/\`/g, '\\`'); | ||
return data.replace(/\\/g, "\\\\").replace(/\`/g, "\\`"); | ||
} | ||
function escapeSlash(data) { | ||
return data.replace(/\\/g, "\\\\") | ||
.replace(/\`/g, '\\`'); | ||
return data.replace(/\\/g, "\\\\").replace(/\`/g, "\\`"); | ||
} | ||
function escapeQuote(data) { | ||
return data.replace(/\"/g, "\\\""); | ||
return data.replace(/\"/g, '\\"'); | ||
} | ||
function getArgs(args) { | ||
if (!args) return []; | ||
let out = []; | ||
for (const arg of args) { | ||
if (arg instanceof StringLiteralExpression) { | ||
out.push(arg.value); | ||
} | ||
} | ||
return out; | ||
} |
{ | ||
"name": "@json-as/transform", | ||
"version": "0.9.17", | ||
"version": "0.9.18", | ||
"description": "The only JSON library you'll need for AssemblyScript. SIMD enabled", | ||
@@ -5,0 +5,0 @@ "main": "./lib/index.js", |
@@ -5,7 +5,8 @@ import { | ||
IdentifierExpression, | ||
NamedTypeNode, | ||
StringLiteralExpression, | ||
Parser, | ||
Source, | ||
NodeKind | ||
NodeKind, | ||
Expression, | ||
CommonFlags, | ||
} from "assemblyscript/dist/assemblyscript.js"; | ||
@@ -16,4 +17,2 @@ | ||
import { Transform } from "assemblyscript/dist/transform.js"; | ||
import { CommonFlags } from "types:assemblyscript/src/common"; | ||
import { DecoratorNode } from "types:assemblyscript/src/ast"; | ||
@@ -25,3 +24,3 @@ class JSONTransform extends BaseVisitor { | ||
visitMethodDeclaration(): void { } | ||
visitMethodDeclaration(): void {} | ||
visitClassDeclaration(node: ClassDeclaration): void { | ||
@@ -45,3 +44,3 @@ if (!node.decorators?.length) return; | ||
const members = [ | ||
...node.members.filter(v => v.kind === NodeKind.FieldDeclaration) | ||
...node.members.filter((v) => v.kind === NodeKind.FieldDeclaration), | ||
]; | ||
@@ -51,3 +50,3 @@ | ||
schema.parent = this.schemasList.find( | ||
(v) => v.name == node.extendsType?.name.identifier.text | ||
(v) => v.name == node.extendsType?.name.identifier.text, | ||
) as SchemaData | null; | ||
@@ -58,6 +57,5 @@ | ||
const replace = schema.members.find( | ||
(v) => v.name == schema.parent?.members[i]?.name | ||
(v) => v.name == schema.parent?.members[i]?.name, | ||
); | ||
if (!replace) { | ||
//schema.members.unshift(schema.parent?.members[i]!); | ||
members.unshift(schema.parent?.members[i]!.node); | ||
@@ -70,3 +68,3 @@ } | ||
if (!members.length) { | ||
let SERIALIZE_RAW_EMPTY = "__SERIALIZE(): string {\n return \"{}\";\n}"; | ||
let SERIALIZE_RAW_EMPTY = '__SERIALIZE(): string {\n return "{}";\n}'; | ||
//let SERIALIZE_PRETTY_EMPTY = "__SERIALIZE_PRETTY(): string {\n return \"{}\";\n}"; | ||
@@ -76,3 +74,4 @@ | ||
let DESERIALIZE_EMPTY = "__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n return false;\n}"; | ||
let DESERIALIZE_EMPTY = | ||
"__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n return false;\n}"; | ||
@@ -86,10 +85,22 @@ if (process.env["JSON_DEBUG"]) { | ||
const SERIALIZE_RAW_METHOD_EMPTY = SimpleParser.parseClassMember(SERIALIZE_RAW_EMPTY, node); | ||
const SERIALIZE_RAW_METHOD_EMPTY = SimpleParser.parseClassMember( | ||
SERIALIZE_RAW_EMPTY, | ||
node, | ||
); | ||
//const SERIALIZE_PRETTY_METHOD = SimpleParser.parseClassMember(SERIALIZE_PRETTY, node); | ||
const INITIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember(INITIALIZE_EMPTY, node); | ||
const DESERIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember(DESERIALIZE_EMPTY, node); | ||
const INITIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember( | ||
INITIALIZE_EMPTY, | ||
node, | ||
); | ||
const DESERIALIZE_METHOD_EMPTY = SimpleParser.parseClassMember( | ||
DESERIALIZE_EMPTY, | ||
node, | ||
); | ||
if (!node.members.find(v => v.name.text == "__SERIALIZE")) node.members.push(SERIALIZE_RAW_METHOD_EMPTY); | ||
if (!node.members.find(v => v.name.text == "__INITIALIZE")) node.members.push(INITIALIZE_METHOD_EMPTY); | ||
if (!node.members.find(v => v.name.text == "__DESERIALIZE")) node.members.push(DESERIALIZE_METHOD_EMPTY); | ||
if (!node.members.find((v) => v.name.text == "__SERIALIZE")) | ||
node.members.push(SERIALIZE_RAW_METHOD_EMPTY); | ||
if (!node.members.find((v) => v.name.text == "__INITIALIZE")) | ||
node.members.push(INITIALIZE_METHOD_EMPTY); | ||
if (!node.members.find((v) => v.name.text == "__DESERIALIZE")) | ||
node.members.push(DESERIALIZE_METHOD_EMPTY); | ||
@@ -103,3 +114,8 @@ this.schemasList.push(schema); | ||
if (!member.type) { | ||
throw new Error("Fields must be strongly typed! Found " + toString(member) + " at " + node.range.source.normalizedPath); | ||
throw new Error( | ||
"Fields must be strongly typed! Found " + | ||
toString(member) + | ||
" at " + | ||
node.range.source.normalizedPath, | ||
); | ||
} | ||
@@ -113,3 +129,2 @@ const type = toString(member.type!); | ||
if (member.flags === CommonFlags.Protected) continue; | ||
if (member.decorators && member.decorators.find((v) => (<IdentifierExpression>v.name).text == "omit")) continue; | ||
@@ -122,24 +137,40 @@ const mem = new Property(); | ||
if (type == "JSON.Raw") { | ||
mem.flags.set(PropertyFlags.JSON_Raw, []); | ||
} | ||
if (member.decorators) { | ||
let decorator: DecoratorNode | null = null; | ||
if (decorator = member.decorators.find(v => (<IdentifierExpression>v.name).text == "alias") as DecoratorNode | null) { | ||
if ((<IdentifierExpression>decorator.name).text == "alias") { | ||
if (!decorator.args?.length) throw new Error("Expected 1 argument but got zero at @alias in " + node.range.source.normalizedPath); | ||
mem.flags.push(PropertyFlags.Alias); | ||
mem.alias = (decorator.args[0] as StringLiteralExpression).value; | ||
} | ||
} | ||
for (const decorator of member.decorators) { | ||
const decoratorName = (decorator.name as IdentifierExpression).text; | ||
for (let i = 0; i < (member.decorators).length; i++) { | ||
const decorator = member.decorators[i]!; | ||
if ((<IdentifierExpression>decorator.name).text == "omitnull") { | ||
mem.flags.push(PropertyFlags.OmitNull); | ||
} else if ((<IdentifierExpression>decorator.name).text == "omitif") { | ||
if (!decorator.args?.length) throw new Error("Expected 1 argument but got zero at @omitif in " + node.range.source.normalizedPath); | ||
mem.args?.push((decorator.args[0] as StringLiteralExpression).value); | ||
mem.flags.push(PropertyFlags.OmitIf); | ||
} else if ((<IdentifierExpression>decorator.name).text == "flatten") { | ||
if (!decorator.args?.length) throw new Error("Expected 1 argument but got zero at @flatten in " + node.range.source.normalizedPath); | ||
mem.flags.push(PropertyFlags.Flatten); | ||
mem.args = [(decorator.args[0] as StringLiteralExpression).value]; | ||
const args = getArgs(decorator.args); | ||
switch (decoratorName) { | ||
case "alias": { | ||
if (!args.length) | ||
throw new Error( | ||
"Expected 1 argument but got zero at @alias in " + | ||
node.range.source.normalizedPath, | ||
); | ||
mem.alias = args[0]!; | ||
mem.flags.set(PropertyFlags.Alias, args); | ||
break; | ||
} | ||
case "omit": { | ||
mem.flags.set(PropertyFlags.Omit, args); | ||
break; | ||
} | ||
case "omitif": { | ||
if (!decorator.args?.length) | ||
throw new Error( | ||
"Expected 1 argument but got zero at @omitif in " + | ||
node.range.source.normalizedPath, | ||
); | ||
mem.flags.set(PropertyFlags.OmitIf, args); | ||
break; | ||
} | ||
case "omitnull": { | ||
mem.flags.set(PropertyFlags.OmitNull, args); | ||
break; | ||
} | ||
} | ||
@@ -149,65 +180,44 @@ } | ||
if (!mem.flags.length) { | ||
mem.flags = [PropertyFlags.None]; | ||
if (type == "JSON.Raw") { | ||
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${this." + name.text + "}"; | ||
mem.deserialize = "this." + name.text + " = " + "data.substring(value_start, value_end);" | ||
} else { | ||
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${__SERIALIZE<" + type + ">(this." + name.text + ")}"; | ||
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));" | ||
} | ||
} | ||
mem.generate(); | ||
if (mem.flags.includes(PropertyFlags.OmitNull)) { | ||
mem.serialize = "${changetype<usize>(this." + mem.name + ") == <usize>0" + " ? \"\" : '" + escapeString(JSON.stringify(mem.alias || mem.name)) + ":' + __SERIALIZE<" + type + ">(this." + name.text + ") + \",\"}"; | ||
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));" | ||
} else if (mem.flags.includes(PropertyFlags.OmitIf)) { | ||
mem.serialize = "${" + mem.args![0]! + " ? \"\" : '" + escapeString(JSON.stringify(mem.alias || mem.name)) + ":' + __SERIALIZE<" + type + ">(this." + name.text + ") + \",\"}"; | ||
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));" | ||
} else if (mem.flags.includes(PropertyFlags.Alias)) { | ||
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${__SERIALIZE<" + type + ">(this." + name.text + ")}"; | ||
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));" | ||
mem.name = name.text; | ||
} else if (mem.flags.includes(PropertyFlags.Flatten)) { | ||
const nullable = (mem.node.type as NamedTypeNode).isNullable; | ||
if (nullable) { | ||
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${this." + name.text + " ? __SERIALIZE(changetype<nonnull<" + type + ">>(this." + name.text + ")" + (mem.args?.length ? '.' + mem.args.join(".") : '') + ") : \"null\"}"; | ||
mem.deserialize = "if (value_end - value_start == 4 && load<u64>(changetype<usize>(data) + <usize>(value_start << 1)) == " + charCodeAt64("null", 0) + ") {\n this." + name.text + " = null;\n } else {\n this." + name.text + " = " + "__DESERIALIZE<" + type + ">('{\"" + mem.args![0]! + "\":' + data.substring(value_start, value_end) + \"}\");\n }"; | ||
} else { | ||
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${this." + name.text + " ? __SERIALIZE(this." + name.text + (mem.args?.length ? '.' + mem.args.join(".") : '') + ") : \"null\"}"; | ||
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">('{\"" + mem.args![0]! + "\":' + data.substring(value_start, value_end) + \"}\");"; | ||
} | ||
mem.name = name.text; | ||
} | ||
const t = (mem.node.type as NamedTypeNode).name?.identifier.text; | ||
if (this.schemasList.find(v => v.name == t)) { | ||
mem.initialize = "this." + name.text + " = changetype<nonnull<" + mem.type + ">>(__new(offsetof<nonnull<" + mem.type + ">>(), idof<nonnull<" + mem.type + ">>()));\n changetype<nonnull<" + mem.type + ">>(this." + name.text + ").__INITIALIZE()"; | ||
if (this.schemasList.find((v) => v.name == type)) { | ||
mem.initialize = | ||
"this." + | ||
name.text + | ||
" = changetype<nonnull<" + | ||
mem.type + | ||
">>(__new(offsetof<nonnull<" + | ||
mem.type + | ||
">>(), idof<nonnull<" + | ||
mem.type + | ||
">>()));\n changetype<nonnull<" + | ||
mem.type + | ||
">>(this." + | ||
name.text + | ||
").__INITIALIZE()"; | ||
} else if (mem.value) { | ||
mem.initialize = "this." + name.text + " = " + mem.value; | ||
} else if (t === "Map") { | ||
mem.initialize = "this." + name.text + " = new " + mem.type + "()" | ||
} else if (t === "string") { | ||
mem.initialize = "this." + name.text + " = \"\""; | ||
} else if (t === "Array") { | ||
mem.initialize = "this." + name.text + " = instantiate<" + mem.type + ">()"; | ||
} else if (t === "bool" || t === "boolean") { | ||
} else if (type === "Map") { | ||
mem.initialize = "this." + name.text + " = new " + mem.type + "()"; | ||
} else if (type === "string") { | ||
mem.initialize = "this." + name.text + ' = ""'; | ||
} else if (type === "Array") { | ||
mem.initialize = | ||
"this." + name.text + " = instantiate<" + mem.type + ">()"; | ||
} else if (type === "bool" || type === "boolean") { | ||
mem.initialize = "this." + name.text + " = false"; | ||
} else if (t === "JSON.Raw") { | ||
mem.initialize = "this." + name.text + " = \"\""; | ||
} else if (type === "JSON.Raw") { | ||
mem.initialize = "this." + name.text + ' = ""'; | ||
} else if ( | ||
t === "u8" || | ||
t === "u16" || | ||
t === "u32" || | ||
t === "u64" || | ||
t === "i8" || | ||
t === "i16" || | ||
t === "i32" || | ||
t === "i64" | ||
type === "u8" || | ||
type === "u16" || | ||
type === "u32" || | ||
type === "u64" || | ||
type === "i8" || | ||
type === "i16" || | ||
type === "i32" || | ||
type === "i64" | ||
) { | ||
mem.initialize = "this." + name.text + " = 0"; | ||
} else if ( | ||
t === "f32" || | ||
t === "f64" | ||
) { | ||
} else if (type === "f32" || type === "f64") { | ||
mem.initialize = "this." + name.text + " = 0.0"; | ||
@@ -224,3 +234,4 @@ } | ||
let DESERIALIZE = "__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n const len = key_end - key_start;\n" | ||
let DESERIALIZE = | ||
"__DESERIALIZE(data: string, key_start: i32, key_end: i32, value_start: i32, value_end: i32): boolean {\n const len = key_end - key_start;\n"; | ||
let indent = " "; | ||
@@ -233,4 +244,4 @@ | ||
if ( | ||
schema.members[0]?.flags.includes(PropertyFlags.OmitNull) | ||
|| schema.members[0]?.flags.includes(PropertyFlags.OmitIf) | ||
schema.members[0]?.flags.has(PropertyFlags.OmitNull) || | ||
schema.members[0]?.flags.has(PropertyFlags.OmitIf) | ||
) { | ||
@@ -245,3 +256,4 @@ SERIALIZE_RAW += schema.members[0]?.serialize; | ||
if (schema.members[0]?.initialize) INITIALIZE += " " + schema.members[0]?.initialize + ";\n"; | ||
if (schema.members[0]?.initialize) | ||
INITIALIZE += " " + schema.members[0]?.initialize + ";\n"; | ||
@@ -252,4 +264,4 @@ for (let i = 1; i < schema.members.length; i++) { | ||
if ( | ||
member.flags.includes(PropertyFlags.OmitNull) | ||
|| member.flags.includes(PropertyFlags.OmitIf) | ||
member.flags.has(PropertyFlags.OmitNull) || | ||
member.flags.has(PropertyFlags.OmitIf) | ||
) { | ||
@@ -266,4 +278,6 @@ SERIALIZE_RAW += member.serialize; | ||
if (found) { | ||
SERIALIZE_RAW += "`;\n store<u16>(changetype<usize>(out) + ((out.length - 1) << 1), 125);\n return out;\n}"; | ||
SERIALIZE_PRETTY += "`;\n store<u32>(changetype<usize>(out) + ((out.length - 2) << 1), 8192010);\n return out;\n}"; | ||
SERIALIZE_RAW += | ||
"`;\n store<u16>(changetype<usize>(out) + ((out.length - 1) << 1), 125);\n return out;\n}"; | ||
SERIALIZE_PRETTY += | ||
"`;\n store<u32>(changetype<usize>(out) + ((out.length - 2) << 1), 8192010);\n return out;\n}"; | ||
} else { | ||
@@ -274,6 +288,8 @@ SERIALIZE_RAW += "`;\n};"; | ||
INITIALIZE += " return this;\n}" | ||
INITIALIZE += " return this;\n}"; | ||
const sortedMembers: Property[][] = []; | ||
const _sorted = schema.members.sort((a, b) => a.name.length - b.name.length); | ||
const _sorted = schema.members.sort( | ||
(a, b) => a.name.length - b.name.length, | ||
); | ||
let len = 0; | ||
@@ -287,3 +303,3 @@ let offset = 0; | ||
sortedMembers.push([member]); | ||
len = member.alias?.length || member.name.length | ||
len = member.alias?.length || member.name.length; | ||
offset++; | ||
@@ -301,20 +317,26 @@ } else { | ||
if (first) { | ||
DESERIALIZE += " if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
DESERIALIZE += | ||
" if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
first = false; | ||
} else { | ||
DESERIALIZE += "else if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
DESERIALIZE += | ||
"else if (1 === len) {\n switch (load<u16>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
} | ||
} else if (name.length === 2) { | ||
if (first) { | ||
DESERIALIZE += " if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
DESERIALIZE += | ||
" if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
first = false; | ||
} else { | ||
DESERIALIZE += "else if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
DESERIALIZE += | ||
"else if (2 === len) {\n switch (load<u32>(changetype<usize>(data) + (key_start << 1))) {\n"; | ||
} | ||
} else if (name.length === 4) { | ||
if (first) { | ||
DESERIALIZE += " if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n"; | ||
DESERIALIZE += | ||
" if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n"; | ||
first = false; | ||
} else { | ||
DESERIALIZE += "else if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n"; | ||
DESERIALIZE += | ||
"else if (4 === len) {\n const code = load<u64>(changetype<usize>(data) + (key_start << 1));\n"; | ||
} | ||
@@ -343,3 +365,5 @@ } else { | ||
} else { | ||
DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + `else if (${charCodeAt64(name, 0)} === code) {\n ${member.deserialize}\n return true;\n }\n`; | ||
DESERIALIZE = | ||
DESERIALIZE.slice(0, DESERIALIZE.length - 1) + | ||
`else if (${charCodeAt64(name, 0)} === code) {\n ${member.deserialize}\n return true;\n }\n`; | ||
} | ||
@@ -349,5 +373,7 @@ } else { | ||
f = false; | ||
DESERIALIZE += ` if (0 == memory.compare(changetype<usize>("${escapeQuote(escapeSlash(name))}"), changetype<usize>(data) + (key_start << 1), ${name.length << 1})) {\n ${member.deserialize}\n return true;\n }\n` | ||
DESERIALIZE += ` if (0 == memory.compare(changetype<usize>("${escapeQuote(escapeSlash(name))}"), changetype<usize>(data) + (key_start << 1), ${name.length << 1})) {\n ${member.deserialize}\n return true;\n }\n`; | ||
} else { | ||
DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else if (0 == memory.compare(changetype<usize>("${escapeQuote(escapeSlash(name))}"), changetype<usize>(data) + (key_start << 1), ${name.length << 1})) {\n ${member.deserialize}\n return true;\n }\n` | ||
DESERIALIZE = | ||
DESERIALIZE.slice(0, DESERIALIZE.length - 1) + | ||
` else if (0 == memory.compare(changetype<usize>("${escapeQuote(escapeSlash(name))}"), changetype<usize>(data) + (key_start << 1), ${name.length << 1})) {\n ${member.deserialize}\n return true;\n }\n`; | ||
} | ||
@@ -357,7 +383,11 @@ } | ||
if (name.length < 3) { | ||
DESERIALIZE += ` default: {\n return false;\n }\n }\n` | ||
DESERIALIZE += ` default: {\n return false;\n }\n }\n`; | ||
} else if (name.length == 4) { | ||
DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else {\n return false;\n }\n` | ||
DESERIALIZE = | ||
DESERIALIZE.slice(0, DESERIALIZE.length - 1) + | ||
` else {\n return false;\n }\n`; | ||
} else { | ||
DESERIALIZE = DESERIALIZE.slice(0, DESERIALIZE.length - 1) + ` else {\n return false;\n }\n` | ||
DESERIALIZE = | ||
DESERIALIZE.slice(0, DESERIALIZE.length - 1) + | ||
` else {\n return false;\n }\n`; | ||
} | ||
@@ -367,3 +397,3 @@ DESERIALIZE += " } "; | ||
DESERIALIZE += "\n return false;\n}" | ||
DESERIALIZE += "\n return false;\n}"; | ||
@@ -379,3 +409,6 @@ //console.log(sortedMembers); | ||
const SERIALIZE_RAW_METHOD = SimpleParser.parseClassMember(SERIALIZE_RAW, node); | ||
const SERIALIZE_RAW_METHOD = SimpleParser.parseClassMember( | ||
SERIALIZE_RAW, | ||
node, | ||
); | ||
//const SERIALIZE_PRETTY_METHOD = SimpleParser.parseClassMember(SERIALIZE_PRETTY, node); | ||
@@ -385,5 +418,8 @@ const INITIALIZE_METHOD = SimpleParser.parseClassMember(INITIALIZE, node); | ||
if (!node.members.find(v => v.name.text == "__SERIALIZE")) node.members.push(SERIALIZE_RAW_METHOD); | ||
if (!node.members.find(v => v.name.text == "__INITIALIZE")) node.members.push(INITIALIZE_METHOD); | ||
if (!node.members.find(v => v.name.text == "__DESERIALIZE")) node.members.push(DESERIALIZE_METHOD); | ||
if (!node.members.find((v) => v.name.text == "__SERIALIZE")) | ||
node.members.push(SERIALIZE_RAW_METHOD); | ||
if (!node.members.find((v) => v.name.text == "__INITIALIZE")) | ||
node.members.push(INITIALIZE_METHOD); | ||
if (!node.members.find((v) => v.name.text == "__DESERIALIZE")) | ||
node.members.push(DESERIALIZE_METHOD); | ||
@@ -435,3 +471,6 @@ this.schemasList.push(schema); | ||
const parent = schemas.find((v) => v.name === schema.parent?.name); | ||
if (!parent) throw new Error(`Class ${schema.name} extends its parent class ${schema.parent}, but ${schema.parent} does not include a @json or @serializable decorator! Add the decorator and rebuild.`); | ||
if (!parent) | ||
throw new Error( | ||
`Class ${schema.name} extends its parent class ${schema.parent}, but ${schema.parent} does not include a @json or @serializable decorator! Add the decorator and rebuild.`, | ||
); | ||
} | ||
@@ -443,3 +482,2 @@ } | ||
enum PropertyFlags { | ||
None, | ||
Omit, | ||
@@ -449,11 +487,14 @@ OmitNull, | ||
Alias, | ||
Flatten | ||
JSON_Raw, | ||
} | ||
class Property { | ||
public name: string = "" | ||
public name: string = ""; | ||
public alias: string | null = null; | ||
public type: string = ""; | ||
public value: string | null = null; | ||
public flags: PropertyFlags[] = []; | ||
public flags: Map<PropertyFlags, string[]> = new Map< | ||
PropertyFlags, | ||
string[] | ||
>(); | ||
public args: string[] | null = []; | ||
@@ -466,2 +507,48 @@ | ||
public node!: FieldDeclaration; | ||
private right_s: string = ""; | ||
private right_d: string = ""; | ||
public generate(): void { | ||
const name = this.name; | ||
const escapedName = escapeString(JSON.stringify(this.alias || this.name)); | ||
const type = this.type; | ||
if (this.flags.has(PropertyFlags.Omit)) return; | ||
if (this.flags.has(PropertyFlags.JSON_Raw)) { | ||
this.right_s = "this." + name; | ||
this.right_d = "data.substring(value_start, value_end);"; | ||
} else { | ||
this.right_s = "__SERIALIZE<" + type + ">(this." + name + ")"; | ||
this.right_d = | ||
"__DESERIALIZE<" + type + ">(data.substring(value_start, value_end))"; | ||
} | ||
if (this.flags.has(PropertyFlags.OmitIf)) { | ||
const condition = this.args![0]; | ||
this.serialize = | ||
"${" + | ||
condition + | ||
' ? "" : \'' + | ||
escapedName + | ||
":' + " + | ||
this.right_s + | ||
' + ","}'; | ||
this.deserialize = "this." + name + " = " + this.right_d + ";"; | ||
} else if (this.flags.has(PropertyFlags.OmitNull)) { | ||
this.serialize = | ||
"${changetype<usize>(this." + | ||
name + | ||
") == <usize>0" + | ||
' ? "" : \'' + | ||
escapedName + | ||
":' + " + | ||
this.right_s + | ||
' + ","}'; | ||
this.deserialize = "this." + name + " = " + this.right_d + ";"; | ||
} else { | ||
this.serialize = escapedName + ":${" + this.right_s + "}"; | ||
this.deserialize = "this." + name + " = " + this.right_d + ";"; | ||
} | ||
} | ||
} | ||
@@ -471,3 +558,3 @@ | ||
public name: string = ""; | ||
public members: Property[] = [] | ||
public members: Property[] = []; | ||
public parent: SchemaData | null = null; | ||
@@ -483,3 +570,5 @@ public node!: ClassDeclaration; | ||
if (offset + 3 >= data.length) { | ||
throw new Error("The string must have at least 4 characters from the specified offset."); | ||
throw new Error( | ||
"The string must have at least 4 characters from the specified offset.", | ||
); | ||
} | ||
@@ -492,3 +581,7 @@ | ||
const u64Value = (fourthCharCode << 48n) | (thirdCharCode << 32n) | (secondCharCode << 16n) | firstCharCode; | ||
const u64Value = | ||
(fourthCharCode << 48n) | | ||
(thirdCharCode << 32n) | | ||
(secondCharCode << 16n) | | ||
firstCharCode; | ||
@@ -504,13 +597,22 @@ return u64Value; | ||
function escapeString(data: string): string { | ||
return data.replace(/\\/g, "\\\\") | ||
.replace(/\`/g, '\\`'); | ||
return data.replace(/\\/g, "\\\\").replace(/\`/g, "\\`"); | ||
} | ||
function escapeSlash(data: string): string { | ||
return data.replace(/\\/g, "\\\\") | ||
.replace(/\`/g, '\\`'); | ||
return data.replace(/\\/g, "\\\\").replace(/\`/g, "\\`"); | ||
} | ||
function escapeQuote(data: string): string { | ||
return data.replace(/\"/g, "\\\""); | ||
} | ||
return data.replace(/\"/g, '\\"'); | ||
} | ||
function getArgs(args: Expression[] | null): string[] { | ||
if (!args) return []; | ||
let out: string[] = []; | ||
for (const arg of args) { | ||
if (arg instanceof StringLiteralExpression) { | ||
out.push(arg.value); | ||
} | ||
} | ||
return out; | ||
} |
@@ -7,3 +7,3 @@ { | ||
"outDir": "./lib/", | ||
"strict": true, | ||
@@ -13,3 +13,3 @@ "noImplicitAny": true, | ||
"strictFunctionTypes": true, | ||
"strictBindCallApply": true , | ||
"strictBindCallApply": true, | ||
"strictPropertyInitialization": true, | ||
@@ -16,0 +16,0 @@ "noImplicitThis": true, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
3820
3.13%201059
-3.77%186
-17.33%