diff --git a/lib/js-compiler.js b/lib/js-compiler.js index 025b4b7..1d28e9b 100644 --- a/lib/js-compiler.js +++ b/lib/js-compiler.js @@ -2246,15 +2246,13 @@ function genCode(schema, v, lines, ctx, knownType) { } } - // Build sub-schema validators as closure vars + // Build sub-schema checks inline so they share the parent helper scope. + const subChecks = [] for (let i = 0; i < ppEntries.length; i++) { const [, sub] = ppEntries[i] const subLines = [] genCode(sub, `_ppv`, subLines, ctx) - const fnBody = subLines.length === 0 ? `return true` : `${subLines.join(';')};return true` - const fnVar = `_ppf${pi}_${i}` - ctx.closureVars.push(fnVar) - ctx.closureVals.push(new Function('_ppv', fnBody)) + subChecks.push(subLines.join(';')) } const guard = isObj ? '' : `if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v}))` @@ -2268,13 +2266,11 @@ function genCode(schema, v, lines, ctx, knownType) { ctx._ppHandledAdditional = true ctx._ppHandledPropertyNames = !!pn const propKeys = Object.keys(schema.properties || {}) - let apFn = null + let apCheck = null if (apSchema) { const apLines = [] genCode(apSchema, '_apv', apLines, ctx) - apFn = `_apf${pi}` - ctx.closureVars.push(apFn) - ctx.closureVals.push(new Function('_apv', apLines.length === 0 ? 'return true' : `${apLines.join(';')};return true`)) + apCheck = apLines.join(';') } lines.push(`${guard}{for(const ${kVar} in ${v}){`) // propertyNames checks (merged into same loop) @@ -2308,14 +2304,14 @@ function genCode(schema, v, lines, ctx, knownType) { if (ppEntries.length > 0) { lines.push(`let _pm${pi}=false`) for (let i = 0; i < ppEntries.length; i++) { - lines.push(`if(${matchers[i].check}){_pm${pi}=true;if(!_ppf${pi}_${i}(${v}[${kVar}]))return false}`) + lines.push(`if(${matchers[i].check}){_pm${pi}=true;const _ppv=${v}[${kVar}];${subChecks[i]}}`) } } // A key that is neither declared nor matched is additional. switch on // the declared names (V8 compiles string cases to a jump table); no // switch at all when nothing is declared, since a switch with no case // clause is a syntax error. - const additional = apFn ? `if(!${apFn}(${v}[${kVar}]))return false` : `return false` + const additional = apCheck !== null ? `const _apv=${v}[${kVar}];${apCheck}` : `return false` const notMatched = ppEntries.length > 0 ? `if(!_pm${pi}){${additional}}` : additional if (propKeys.length) { const switchCases = propKeys.map(k => `case ${JSON.stringify(k)}:`).join('') @@ -2352,7 +2348,7 @@ function genCode(schema, v, lines, ctx, knownType) { } } for (let i = 0; i < ppEntries.length; i++) { - lines.push(`if(${matchers[i].check}&&!_ppf${pi}_${i}(${v}[${kVar}]))return false`) + lines.push(`if(${matchers[i].check}){const _ppv=${v}[${kVar}];${subChecks[i]}}`) } lines.push(`}}`) } diff --git a/tests/test_codegen_edge_shapes.js b/tests/test_codegen_edge_shapes.js index 9d295c2..ac9f846 100644 --- a/tests/test_codegen_edge_shapes.js +++ b/tests/test_codegen_edge_shapes.js @@ -109,6 +109,10 @@ checkShape('patternProperties with boolean values', { patternProperties: { '^f': [{ bar: 1 }, false], [{ zap: 1 }, true], ]); +checkShape('patternProperties value with a pattern', { patternProperties: { '^x': { type: 'string', pattern: '^https?://[^/\\s]+' } } }, [ + [{ x: 'https://example.com' }, true], + [{ x: 'not-a-url' }, false], +]); checkShape('dependentSchemas with boolean values', { dependentSchemas: { foo: true, bar: false } }, [ [{ foo: 1 }, true], [{ bar: 1 }, false], @@ -160,6 +164,10 @@ checkShape('additionalProperties schema with patternProperties', { properties: { [{ other: 1 }, false], [{ x1: 's' }, false], ]); +checkShape('additionalProperties pattern with patternProperties', { patternProperties: { '^x': { type: 'number' } }, additionalProperties: { type: 'string', pattern: '^https?://[^/\\s]+' } }, [ + [{ x: 1, other: 'https://example.com' }, true], + [{ other: 'not-a-url' }, false], +]); // Two patterns with additionalProperties: false. A key matching only the // second pattern must not be rejected by the first pattern's miss; this was