Skip to content

Commit

Permalink
Fix regression with transformed object serializing in unions
Browse files Browse the repository at this point in the history
  • Loading branch information
DZakh committed Jun 17, 2024
1 parent 05798b9 commit e1994ec
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 18 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rescript-schema",
"version": "7.0.0",
"version": "7.0.1",
"description": "The fastest composable parser/serializer for ReScript and TypeScript",
"keywords": [
"ReScript",
Expand Down
59 changes: 50 additions & 9 deletions packages/tests/src/core/S_union_test.res
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ module Advanced = {
t->U.assertCompiledCode(
~schema=shapeSchema,
~op=#serialize,
`i=>{let v1;try{let v0={"kind":e[2],"radius":i["radius"],};if(i["TAG"]!==e[0]){e[1](i["TAG"])}if(!v0||v0.constructor!==Object){e[3](v0)}v1=v0}catch(e0){try{let v2={"kind":e[6],"x":i["x"],};if(i["TAG"]!==e[4]){e[5](i["TAG"])}if(!v2||v2.constructor!==Object){e[7](v2)}v1=v2}catch(e1){try{let v3={"kind":e[10],"x":i["x"],"y":i["y"],};if(i["TAG"]!==e[8]){e[9](i["TAG"])}if(!v3||v3.constructor!==Object){e[11](v3)}v1=v3}catch(e2){e[12]([e0,e1,e2,])}}}return v1}`,
`i=>{let v0,v1,v2,v3;try{if(i["TAG"]!==e[0]){e[1](i["TAG"])}v0={"kind":e[2],"radius":i["radius"],};if(!v0||v0.constructor!==Object){e[3](v0)}v1=v0}catch(e0){try{if(i["TAG"]!==e[4]){e[5](i["TAG"])}v2={"kind":e[6],"x":i["x"],};if(!v2||v2.constructor!==Object){e[7](v2)}v1=v2}catch(e1){try{if(i["TAG"]!==e[8]){e[9](i["TAG"])}v3={"kind":e[10],"x":i["x"],"y":i["y"],};if(!v3||v3.constructor!==Object){e[11](v3)}v1=v3}catch(e2){e[12]([e0,e1,e2,])}}}return v1}`,
)
})
}
Expand Down Expand Up @@ -418,17 +418,17 @@ test("Compiled parse code snapshot", t => {
})

// It shouldn't compile since it throw InvalidOperation error
Failing.test("Compiled async parse code snapshot", t => {
let schema = S.union([
Failing.test("Compiled async parse code snapshot", _t => {
let _schema = S.union([
S.literal(0)->S.transform(_ => {asyncParser: i => () => Promise.resolve(i)}),
S.literal(1),
])

t->U.assertCompiledCode(
~schema,
~op=#parse,
`i=>{let v0=e[1](i),v1;try{i===0||e[0](i);throw v0}catch(v2){if(v2&&v2.s===s||v2===v0){try{i===1||e[2](i);v1=()=>Promise.resolve(i)}catch(v3){if(v3&&v3.s===s){v1=()=>Promise.any([v2===v0?v2():Promise.reject(v2),Promise.reject(v3)]).catch(t=>{e[3](t.errors)})}else{throw v3}}}else{throw v2}}return v1}`,
)
// t->U.assertCompiledCode(
// ~schema,
// ~op=#parse,
// `i=>{let v0=e[1](i),v1;try{i===0||e[0](i);throw v0}catch(v2){if(v2&&v2.s===s||v2===v0){try{i===1||e[2](i);v1=()=>Promise.resolve(i)}catch(v3){if(v3&&v3.s===s){v1=()=>Promise.any([v2===v0?v2():Promise.reject(v2),Promise.reject(v3)]).catch(t=>{e[3](t.errors)})}else{throw v3}}}else{throw v2}}return v1}`,
// )
})

test("Compiled serialize code snapshot", t => {
Expand Down Expand Up @@ -456,6 +456,47 @@ test("Compiled serialize code snapshot for unboxed variant", t => {
t->U.assertCompiledCode(
~schema,
~op=#serialize,
`i=>{let v0;try{if(typeof i!=="string"){e[0](i)}v0=i}catch(e0){try{let v1=e[1](i);if(typeof v1!=="string"){e[2](v1)}v0=v1}catch(e1){e[3]([e0,e1,])}}return v0}`,
`i=>{let v0,v1;try{if(typeof i!=="string"){e[0](i)}v0=i}catch(e0){try{v1=e[1](i);if(typeof v1!=="string"){e[2](v1)}v0=v1}catch(e1){e[3]([e0,e1,])}}return v0}`,
)
})

module CknittelBugReport = {
module A = {
@schema
type payload = {a?: string}

@schema
type t = {payload: payload}
}

module B = {
@schema
type payload = {b?: int}

@schema
type t = {payload: payload}
}

type value = A(A.t) | B(B.t)

test("Union serializing of objects with optional fields", t => {
let schema = S.union([A.schema->S.variant(m => A(m)), B.schema->S.variant(m => B(m))])

t->U.assertCompiledCode(
~schema,
~op=#serialize,
`i=>{let v4,v5,v10;try{let v0=i["TAG"],v1=i["_0"],v2=v1["payload"]["a"],v3;if(v0!==e[0]){e[1](v0)}if(v2!==void 0){v3=e[2](v2)}v4={"payload":{"a":v3,},};if(!v4||v4.constructor!==Object){e[3](v4)}v5=v4}catch(e0){try{let v6=i["TAG"],v7=i["_0"],v8=v7["payload"]["b"],v9;if(v6!==e[4]){e[5](v6)}if(v8!==void 0){v9=e[6](v8)}v10={"payload":{"b":v9,},};if(!v10||v10.constructor!==Object){e[7](v10)}v5=v10}catch(e1){e[8]([e0,e1,])}}return v5}`,
)

let x = {
B.payload: {
b: 42,
},
}
t->Assert.deepEqual(
B(x)->S.serializeToUnknownWith(schema),
Ok(%raw(`{"payload":{"b":42}}`)),
(),
)
})
}
6 changes: 4 additions & 2 deletions src/S_Core.bs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2143,11 +2143,13 @@ function factory$8(schemas) {
var errorVar = "e" + idx;
var bb = scope(b);
var itemOutput = schema.s(bb, input, schema, "");
b.c = b.c + ("try{" + allocateScope(bb));
var typeFilter = schema.f;
if (typeFilter !== undefined) {
bb.c = bb.c + typeFilterCode(bb, typeFilter, schema, itemOutput, "");
var code = typeFilterCode(b, typeFilter, schema, itemOutput, "");
b.c = b.c + code;
}
b.c = b.c + ("try{" + allocateScope(bb) + set(b, output, itemOutput) + "}catch(" + errorVar + "){");
b.c = b.c + (set(b, output, itemOutput) + "}catch(" + errorVar + "){");
codeEndRef = codeEndRef + "}";
errorCodeRef = errorCodeRef + errorVar + ",";
}
Expand Down
13 changes: 7 additions & 6 deletions src/S_Core.res
Original file line number Diff line number Diff line change
Expand Up @@ -2825,17 +2825,18 @@ module Union = {

let bb = b->B.scope
let itemOutput = bb->B.serialize(~schema, ~input, ~path=Path.empty)
b.code = b.code ++ `try{${bb->B.allocateScope}`

switch schema.maybeTypeFilter {
| Some(typeFilter) =>
bb.code =
bb.code ++
bb->B.typeFilterCode(~schema, ~typeFilter, ~input=itemOutput, ~path=Path.empty)
let code =
b->B.typeFilterCode(~schema, ~typeFilter, ~input=itemOutput, ~path=Path.empty)
b.code = b.code ++ code

| None => ()
}

b.code =
b.code ++
`try{${bb->B.allocateScope}${b->B.Val.set(output, itemOutput)}}catch(${errorVar}){`
b.code = b.code ++ `${b->B.Val.set(output, itemOutput)}}catch(${errorVar}){`
codeEndRef := codeEndRef.contents ++ "}"
errorCodeRef := errorCodeRef.contents ++ errorVar ++ ","
} catch {
Expand Down

1 comment on commit e1994ec

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: e1994ec Previous: f718dde Ratio
Parse string 817217053 ops/sec (±0.14%) 814044167 ops/sec (±0.15%) 1.00
Serialize string 816708223 ops/sec (±0.07%) 814166152 ops/sec (±0.12%) 1.00
Advanced object schema factory 457705 ops/sec (±0.74%) 457405 ops/sec (±0.90%) 1.00
Parse advanced object 44582352 ops/sec (±0.23%) 42985342 ops/sec (±0.72%) 0.96
Create and parse advanced object 35187 ops/sec (±0.55%) 34684 ops/sec (±1.48%) 0.99
Parse advanced strict object 20464360 ops/sec (±0.23%) 21540357 ops/sec (±0.57%) 1.05
Serialize advanced object 804069732 ops/sec (±0.25%) 800158749 ops/sec (±0.09%) 1.00

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.