From 9b3e8df37f8cac2454f4d84ba7f4edb33f99c71b Mon Sep 17 00:00:00 2001 From: Nikita Skovoroda Date: Fri, 24 Nov 2023 07:11:27 +0300 Subject: [PATCH] fix: dynamic recursion should always return dynamic deltas --- src/compile.js | 32 ++++++++++++++++++++++---------- test/JSON-Schema-Test-Suite | 2 +- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/compile.js b/src/compile.js index 74be07b..5381b5e 100644 --- a/src/compile.js +++ b/src/compile.js @@ -191,6 +191,7 @@ const compileSchema = (schema, root, opts, scope, basePathRoot = '') => { const recursiveLog = [] const getMeta = () => rootMeta.get(root) const basePathStack = basePathRoot ? [basePathRoot] : [] + const recursiveDelta = root.$dynamicAnchor || root.$recursiveAnchor const visit = (errors, history, current, node, schemaPath, trace = {}, { constProp } = {}) => { // e.g. top-level data and property names, OR already checked by present() in history, OR in keys and not undefined const isSub = history.length > 0 && history[history.length - 1].prop === current @@ -254,7 +255,10 @@ const compileSchema = (schema, root, opts, scope, basePathRoot = '') => { // evaluated tracing const stat = initTracing() - const evaluateDelta = (delta) => applyDelta(stat, delta) + const evaluateDelta = (delta) => { + applyDelta(stat, delta) + if (recursiveDelta && node === root) evaluateDeltaDynamic(delta, true) + } if (typeof node === 'boolean') { if (node === true) { @@ -375,24 +379,29 @@ const compileSchema = (schema, root, opts, scope, basePathRoot = '') => { items: needUnevaluated('unevaluatedItems') ? gensym('evaluatedItems') : null, props: needUnevaluated('unevaluatedProperties') ? gensym('evaluatedProps') : null, }) + // RECHECK! const dyn = Object.freeze({ item: local.item || trace.item, items: local.items || trace.items, props: local.props || trace.props, }) + // RECHECK! const canSkipDynamic = () => (!dyn.items || stat.items === Infinity) && (!dyn.props || stat.properties.includes(true)) - const evaluateDeltaDynamic = (delta) => { - // Skips applying those that have already been proved statically - if (dyn.item && delta.item && stat.items !== Infinity) + const evaluateDeltaDynamic = (delta, noskip = false) => { + // Skips applying those that have already been proven statically (if not enforced for dynamic recursion) + if (dyn.item && delta.item && (noskip || stat.items !== Infinity)) { fun.write('%s.push(%s)', dyn.item, delta.item) - if (dyn.items && delta.items > stat.items) fun.write('%s.push(%d)', dyn.items, delta.items) - if (dyn.props && (delta.properties || []).includes(true) && !stat.properties.includes(true)) { - fun.write('%s[0].push(true)', dyn.props) + } + if (dyn.items && delta.items && (noskip || delta.items > stat.items)) { + fun.write('%s.push(%d)', dyn.items, delta.items) + } + if (dyn.props && (delta.properties || []).includes(true)) { + if (noskip || !stat.properties.includes(true)) fun.write('%s[0].push(true)', dyn.props) } else if (dyn.props) { const inStat = (properties, patterns) => inProperties(stat, { properties, patterns }) - const properties = (delta.properties || []).filter((x) => !inStat([x], [])) - const patterns = (delta.patterns || []).filter((x) => !inStat([], [x])) + const properties = (delta.properties || []).filter((x) => noskip || !inStat([x], [])) + const patterns = (delta.patterns || []).filter((x) => noskip || !inStat([], [x])) if (properties.length > 0) fun.write('%s[0].push(...%j)', dyn.props, properties) if (patterns.length > 0) fun.write('%s[1].push(...%j)', dyn.props, patterns) for (const sym of delta.propertiesVars || []) fun.write('%s[0].push(%s)', dyn.props, sym) @@ -1351,7 +1360,10 @@ const compileSchema = (schema, root, opts, scope, basePathRoot = '') => { if (refsNeedFullValidation.has(funname)) throw new Error('Unexpected: unvalidated cyclic ref') // evaluated: return dynamic for refs - if (opts[optDynamic] && (isDynamic(stat).items || isDynamic(stat).properties)) { + if ( + (opts[optDynamic] && (isDynamic(stat).items || isDynamic(stat).properties)) || + recursiveDelta + ) { if (!local) throw new Error('Failed to trace dynamic properties') // Unreachable fun.write('validate.evaluatedDynamic = [%s, %s, %s]', local.item, local.items, local.props) } diff --git a/test/JSON-Schema-Test-Suite b/test/JSON-Schema-Test-Suite index cda4281..2834c63 160000 --- a/test/JSON-Schema-Test-Suite +++ b/test/JSON-Schema-Test-Suite @@ -1 +1 @@ -Subproject commit cda4281c46226b980cbf9db4610f03d9f718e533 +Subproject commit 2834c6306c3c72983ab2a6572d3dc7f3bb4d4c83