Skip to content

Commit d574a7c

Browse files
authored
Fix $dynamicRef getting confused with a static component (#503)
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent 250c983 commit d574a7c

3 files changed

Lines changed: 131 additions & 8 deletions

File tree

src/compiler/compile_helpers.h

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,12 @@ inline auto schema_resource_id(const Context &context,
4343
std::distance(context.resources.cbegin(), iterator));
4444
}
4545

46-
// Instantiate a value-oriented step
47-
inline auto make(const InstructionIndex type, const Context &context,
48-
const SchemaContext &schema_context,
49-
const DynamicContext &dynamic_context, const Value &value)
46+
// Instantiate a value-oriented step with a custom resource
47+
inline auto make_with_resource(const InstructionIndex type,
48+
const Context &context,
49+
const SchemaContext &schema_context,
50+
const DynamicContext &dynamic_context,
51+
const Value &value, const std::string &resource)
5052
-> Instruction {
5153
return {
5254
type,
@@ -56,11 +58,20 @@ inline auto make(const InstructionIndex type, const Context &context,
5658
{dynamic_context.keyword}),
5759
dynamic_context.base_instance_location,
5860
to_uri(schema_context.relative_pointer, schema_context.base).recompose(),
59-
schema_resource_id(context, schema_context.base.recompose()),
61+
schema_resource_id(context, resource),
6062
value,
6163
{}};
6264
}
6365

66+
// Instantiate a value-oriented step
67+
inline auto make(const InstructionIndex type, const Context &context,
68+
const SchemaContext &schema_context,
69+
const DynamicContext &dynamic_context, const Value &value)
70+
-> Instruction {
71+
return make_with_resource(type, context, schema_context, dynamic_context,
72+
value, schema_context.base.recompose());
73+
}
74+
6475
// Instantiate an applicator step
6576
inline auto make(const InstructionIndex type, const Context &context,
6677
const SchemaContext &schema_context,

src/compiler/default_compiler_2020_12.h

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,22 @@ auto compiler_2020_12_core_dynamicref(const Context &context,
9797
// Note we don't need to even care about the static part of the dynamic
9898
// reference (if any), as even if we jump first there, we will still
9999
// look for the oldest dynamic anchor in the schema resource chain.
100-
return {make(sourcemeta::blaze::InstructionIndex::ControlDynamicAnchorJump,
101-
context, schema_context, dynamic_context,
102-
std::string{reference.fragment().value()})};
100+
101+
if (reference.is_fragment_only()) {
102+
return {make(sourcemeta::blaze::InstructionIndex::ControlDynamicAnchorJump,
103+
context, schema_context, dynamic_context,
104+
std::string{reference.fragment().value()})};
105+
} else {
106+
const auto base_resource{reference.recompose_without_fragment()};
107+
assert(base_resource.has_value());
108+
109+
// If the dynamic reference has a static component, we need to make sure we
110+
// append such static part as a resource before we begin the lookup
111+
return {make_with_resource(
112+
sourcemeta::blaze::InstructionIndex::ControlDynamicAnchorJump, context,
113+
schema_context, dynamic_context,
114+
std::string{reference.fragment().value()}, base_resource.value())};
115+
}
103116
}
104117

105118
} // namespace internal

test/evaluator/evaluator_2020_12_test.cc

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,6 +1440,105 @@ TEST(Evaluator_2020_12, dynamicRef_1) {
14401440
"The value was expected to be of type string");
14411441
}
14421442

1443+
TEST(Evaluator_2020_12, dynamicRef_2) {
1444+
const sourcemeta::core::JSON schema{sourcemeta::core::parse_json(R"JSON({
1445+
"$schema": "https://json-schema.org/draft/2020-12/schema",
1446+
"$dynamicRef": "https://example.com/target#meta",
1447+
"$defs": {
1448+
"target": {
1449+
"$id": "https://example.com/target",
1450+
"$dynamicAnchor": "meta",
1451+
"type": "boolean"
1452+
},
1453+
"other": {
1454+
"$id": "https://example.com/other",
1455+
"$dynamicAnchor": "meta",
1456+
"type": "number"
1457+
}
1458+
}
1459+
})JSON")};
1460+
1461+
const sourcemeta::core::JSON instance{true};
1462+
EVALUATE_WITH_TRACE_FAST_SUCCESS(schema, instance, 4);
1463+
1464+
EVALUATE_TRACE_PRE(0, ControlMark, "", "https://example.com/other", "");
1465+
EVALUATE_TRACE_PRE(1, ControlMark, "", "https://example.com/target", "");
1466+
EVALUATE_TRACE_PRE(2, ControlDynamicAnchorJump, "/$dynamicRef",
1467+
"#/$dynamicRef", "");
1468+
EVALUATE_TRACE_PRE(3, AssertionTypeStrict, "/$dynamicRef/type",
1469+
"https://example.com/target#/type", "");
1470+
1471+
EVALUATE_TRACE_POST_SUCCESS(0, ControlMark, "", "https://example.com/other",
1472+
"");
1473+
EVALUATE_TRACE_POST_SUCCESS(1, ControlMark, "", "https://example.com/target",
1474+
"");
1475+
EVALUATE_TRACE_POST_SUCCESS(2, AssertionTypeStrict, "/$dynamicRef/type",
1476+
"https://example.com/target#/type", "");
1477+
EVALUATE_TRACE_POST_SUCCESS(3, ControlDynamicAnchorJump, "/$dynamicRef",
1478+
"#/$dynamicRef", "");
1479+
1480+
EVALUATE_TRACE_POST_DESCRIBE(instance, 0,
1481+
"The schema location was marked for future use");
1482+
EVALUATE_TRACE_POST_DESCRIBE(instance, 1,
1483+
"The schema location was marked for future use");
1484+
EVALUATE_TRACE_POST_DESCRIBE(instance, 2,
1485+
"The value was expected to be of type boolean");
1486+
EVALUATE_TRACE_POST_DESCRIBE(
1487+
instance, 3,
1488+
"The boolean value was expected to validate against the first subschema "
1489+
"in scope that declared the dynamic anchor \"meta\"");
1490+
}
1491+
1492+
TEST(Evaluator_2020_12, dynamicRef_3) {
1493+
const sourcemeta::core::JSON schema{sourcemeta::core::parse_json(R"JSON({
1494+
"$schema": "https://json-schema.org/draft/2020-12/schema",
1495+
"$dynamicRef": "https://example.com/target#meta",
1496+
"$defs": {
1497+
"target": {
1498+
"$id": "https://example.com/target",
1499+
"$dynamicAnchor": "meta",
1500+
"type": "boolean"
1501+
},
1502+
"other": {
1503+
"$id": "https://example.com/other",
1504+
"$dynamicAnchor": "meta",
1505+
"type": "number"
1506+
}
1507+
}
1508+
})JSON")};
1509+
1510+
const sourcemeta::core::JSON instance{1};
1511+
EVALUATE_WITH_TRACE_FAST_FAILURE(schema, instance, 4);
1512+
1513+
EVALUATE_TRACE_PRE(0, ControlMark, "", "https://example.com/other", "");
1514+
EVALUATE_TRACE_PRE(1, ControlMark, "", "https://example.com/target", "");
1515+
EVALUATE_TRACE_PRE(2, ControlDynamicAnchorJump, "/$dynamicRef",
1516+
"#/$dynamicRef", "");
1517+
EVALUATE_TRACE_PRE(3, AssertionTypeStrict, "/$dynamicRef/type",
1518+
"https://example.com/target#/type", "");
1519+
1520+
EVALUATE_TRACE_POST_SUCCESS(0, ControlMark, "", "https://example.com/other",
1521+
"");
1522+
EVALUATE_TRACE_POST_SUCCESS(1, ControlMark, "", "https://example.com/target",
1523+
"");
1524+
EVALUATE_TRACE_POST_FAILURE(2, AssertionTypeStrict, "/$dynamicRef/type",
1525+
"https://example.com/target#/type", "");
1526+
EVALUATE_TRACE_POST_FAILURE(3, ControlDynamicAnchorJump, "/$dynamicRef",
1527+
"#/$dynamicRef", "");
1528+
1529+
EVALUATE_TRACE_POST_DESCRIBE(instance, 0,
1530+
"The schema location was marked for future use");
1531+
EVALUATE_TRACE_POST_DESCRIBE(instance, 1,
1532+
"The schema location was marked for future use");
1533+
EVALUATE_TRACE_POST_DESCRIBE(instance, 2,
1534+
"The value was expected to be of type boolean "
1535+
"but it was of type integer");
1536+
EVALUATE_TRACE_POST_DESCRIBE(
1537+
instance, 3,
1538+
"The integer value was expected to validate against the first subschema "
1539+
"in scope that declared the dynamic anchor \"meta\"");
1540+
}
1541+
14431542
TEST(Evaluator_2020_12, definitions_1) {
14441543
const sourcemeta::core::JSON schema{sourcemeta::core::parse_json(R"JSON({
14451544
"$schema": "https://json-schema.org/draft/2020-12/schema",

0 commit comments

Comments
 (0)