diff --git a/DEPENDENCIES b/DEPENDENCIES index bc15030..7dadbc1 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,5 +1,5 @@ vendorpull https://github.com/sourcemeta/vendorpull dea311b5bfb53b6926a4140267959ae334d3ecf4 noa https://github.com/sourcemeta/noa 7e26abce7a4e31e86a16ef2851702a56773ca527 -jsontoolkit https://github.com/sourcemeta/jsontoolkit 9a88c7331742360abc06317bbce4df6afd11c61b +jsontoolkit https://github.com/sourcemeta/jsontoolkit 2b5158f237584fff7e35d520c2e5a722d01fd678 hydra https://github.com/sourcemeta/hydra 3c53d3fdef79e9ba603d48470a508cc45472a0dc alterschema https://github.com/sourcemeta/alterschema a31722f04ae2d7e57f2fe5bbb0613670866c0840 diff --git a/src/command_compile.cc b/src/command_compile.cc index a06ef86..6e141d9 100644 --- a/src/command_compile.cc +++ b/src/command_compile.cc @@ -23,7 +23,8 @@ auto sourcemeta::jsonschema::cli::compile( const auto compiled_schema{sourcemeta::jsontoolkit::compile( schema, sourcemeta::jsontoolkit::default_schema_walker, resolver(options, options.contains("h") || options.contains("http")), - sourcemeta::jsontoolkit::default_schema_compiler)}; + sourcemeta::jsontoolkit::default_schema_compiler, + sourcemeta::jsontoolkit::SchemaCompilerCompilationMode::Optimized)}; const sourcemeta::jsontoolkit::JSON result{ sourcemeta::jsontoolkit::to_json(compiled_schema)}; diff --git a/src/command_fmt.cc b/src/command_fmt.cc index dfd8046..579d2c2 100644 --- a/src/command_fmt.cc +++ b/src/command_fmt.cc @@ -30,7 +30,7 @@ auto sourcemeta::jsonschema::cli::fmt( log_verbose(options) << "PASS: " << entry.first.string() << "\n"; } else { std::cerr << "FAIL: " << entry.first.string() << "\n"; - std::cerr << "Got: \n" + std::cerr << "Got:\n" << buffer.str() << "\nBut expected:\n" << expected.str() << "\n"; return EXIT_FAILURE; diff --git a/src/command_metaschema.cc b/src/command_metaschema.cc index 1ff2861..2afc592 100644 --- a/src/command_metaschema.cc +++ b/src/command_metaschema.cc @@ -37,7 +37,8 @@ auto sourcemeta::jsonschema::cli::metaschema( if (!cache.contains(dialect.value())) { const auto metaschema_template{sourcemeta::jsontoolkit::compile( metaschema, sourcemeta::jsontoolkit::default_schema_walker, - custom_resolver, sourcemeta::jsontoolkit::default_schema_compiler)}; + custom_resolver, sourcemeta::jsontoolkit::default_schema_compiler, + sourcemeta::jsontoolkit::SchemaCompilerCompilationMode::Optimized)}; cache.insert({dialect.value(), metaschema_template}); } diff --git a/src/command_test.cc b/src/command_test.cc index 78ad3a3..7e0d9ab 100644 --- a/src/command_test.cc +++ b/src/command_test.cc @@ -142,7 +142,8 @@ auto sourcemeta::jsonschema::cli::test( try { schema_template = sourcemeta::jsontoolkit::compile( schema.value(), sourcemeta::jsontoolkit::default_schema_walker, - test_resolver, sourcemeta::jsontoolkit::default_schema_compiler); + test_resolver, sourcemeta::jsontoolkit::default_schema_compiler, + sourcemeta::jsontoolkit::SchemaCompilerCompilationMode::Optimized); } catch (const sourcemeta::jsontoolkit::SchemaReferenceError &error) { if (error.location().empty() && error.id() == schema_uri.recompose()) { std::cout << "\n"; diff --git a/src/command_validate.cc b/src/command_validate.cc index 6d314f4..0b90c7e 100644 --- a/src/command_validate.cc +++ b/src/command_validate.cc @@ -50,7 +50,8 @@ auto sourcemeta::jsonschema::cli::validate( const auto benchmark{options.contains("b") || options.contains("benchmark")}; const auto schema_template{sourcemeta::jsontoolkit::compile( schema, sourcemeta::jsontoolkit::default_schema_walker, custom_resolver, - sourcemeta::jsontoolkit::default_schema_compiler)}; + sourcemeta::jsontoolkit::default_schema_compiler, + sourcemeta::jsontoolkit::SchemaCompilerCompilationMode::Optimized)}; bool result{true}; diff --git a/test/compile/pass_1.sh b/test/compile/pass_1.sh index 874cfdf..2f8e964 100755 --- a/test/compile/pass_1.sh +++ b/test/compile/pass_1.sh @@ -33,12 +33,13 @@ cat << 'EOF' > "$TMP/expected.json" "type": "instance", "location": "" }, + "report": true, "dynamic": false, "condition": [], "children": [ { - "category": "internal", - "type": "container", + "category": "logical", + "type": "and", "value": null, "schemaResource": "", "absoluteKeywordLocation": "#/properties", @@ -49,6 +50,7 @@ cat << 'EOF' > "$TMP/expected.json" "type": "instance", "location": "" }, + "report": false, "dynamic": false, "condition": [ { @@ -68,6 +70,7 @@ cat << 'EOF' > "$TMP/expected.json" "type": "instance", "location": "" }, + "report": false, "dynamic": false, "condition": [] } @@ -90,26 +93,7 @@ cat << 'EOF' > "$TMP/expected.json" "type": "instance", "location": "" }, - "dynamic": false, - "condition": [] - }, - { - "category": "annotation", - "type": "public", - "value": { - "category": "value", - "type": "json", - "value": "foo" - }, - "schemaResource": "", - "absoluteKeywordLocation": "#/properties", - "relativeSchemaLocation": "", - "relativeInstanceLocation": "", - "target": { - "category": "target", - "type": "instance", - "location": "" - }, + "report": true, "dynamic": false, "condition": [] } diff --git a/test/format_check_single_fail.sh b/test/format_check_single_fail.sh index a16733e..229bbeb 100755 --- a/test/format_check_single_fail.sh +++ b/test/format_check_single_fail.sh @@ -14,15 +14,26 @@ cat << 'EOF' > "$TMP/schema.json" } EOF -"$1" fmt "$TMP/schema.json" --check && CODE="$?" || CODE="$?" - -if [ "$CODE" = "0" ] -then - echo "FAIL" 1>&2 - exit 1 -else - echo "PASS" 1>&2 -fi +"$1" fmt "$TMP/schema.json" --check 2>"$TMP/stderr.txt" && CODE="$?" || CODE="$?" +test "$CODE" = "1" || exit 1 + +cat << EOF > "$TMP/error.txt" +FAIL: $(realpath "$TMP")/schema.json +Got: +{ + "type": 1, + "\$schema": "http://json-schema.org/draft-04/schema#" +} + +But expected: +{ + "\$schema": "http://json-schema.org/draft-04/schema#", + "type": 1 +} + +EOF + +diff "$TMP/stderr.txt" "$TMP/error.txt" cat << 'EOF' > "$TMP/expected.json" { diff --git a/vendor/jsontoolkit/CMakeLists.txt b/vendor/jsontoolkit/CMakeLists.txt index 224f26f..be6a37a 100644 --- a/vendor/jsontoolkit/CMakeLists.txt +++ b/vendor/jsontoolkit/CMakeLists.txt @@ -70,6 +70,7 @@ endif() if(PROJECT_IS_TOP_LEVEL) noa_target_clang_format(SOURCES src/*.h src/*.cc + benchmark/*.h benchmark/*.cc test/*.h test/*.cc) noa_target_clang_tidy(SOURCES src/*.h src/*.cc) @@ -112,5 +113,4 @@ if(JSONTOOLKIT_TESTS) find_package(GoogleBenchmark REQUIRED) add_subdirectory(benchmark) endif() - endif() diff --git a/vendor/jsontoolkit/src/jsonschema/CMakeLists.txt b/vendor/jsontoolkit/src/jsonschema/CMakeLists.txt index cd9f901..a1be00d 100644 --- a/vendor/jsontoolkit/src/jsonschema/CMakeLists.txt +++ b/vendor/jsontoolkit/src/jsonschema/CMakeLists.txt @@ -10,7 +10,7 @@ noa_library(NAMESPACE sourcemeta PROJECT jsontoolkit NAME jsonschema anchor.cc resolver.cc walker.cc bundle.cc compile.cc compile_evaluate.cc compile_json.cc compile_describe.cc - compile_helpers.h default_compiler.cc + compile_helpers.h default_compiler.cc trace.h default_compiler_2020_12.h default_compiler_2019_09.h diff --git a/vendor/jsontoolkit/src/jsonschema/compile.cc b/vendor/jsontoolkit/src/jsonschema/compile.cc index 48982d3..3017852 100644 --- a/vendor/jsontoolkit/src/jsonschema/compile.cc +++ b/vendor/jsontoolkit/src/jsonschema/compile.cc @@ -27,8 +27,8 @@ auto compile_subschema( return {}; } else { return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, - {}, SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, + SchemaCompilerValueNone{}, {}, SchemaCompilerTargetType::Instance)}; } } @@ -64,6 +64,7 @@ namespace sourcemeta::jsontoolkit { auto compile(const JSON &schema, const SchemaWalker &walker, const SchemaResolver &resolver, const SchemaCompiler &compiler, + const SchemaCompilerCompilationMode mode, const std::optional &default_dialect) -> SchemaCompilerTemplate { assert(is_schema(schema)); @@ -105,8 +106,8 @@ auto compile(const JSON &schema, const SchemaWalker &walker, } const sourcemeta::jsontoolkit::SchemaCompilerContext context{ - result, frame, references, walker, - resolver, compiler, uses_dynamic_scopes}; + mode, result, frame, references, + walker, resolver, compiler, uses_dynamic_scopes}; sourcemeta::jsontoolkit::SchemaCompilerSchemaContext schema_context{ empty_pointer, result, @@ -150,7 +151,7 @@ auto compile(const JSON &schema, const SchemaWalker &walker, {}}; compiler_template.push_back(make( - context, nested_schema_context, dynamic_context, + true, context, nested_schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{label}, compile(context, nested_schema_context, relative_dynamic_context, empty_pointer, empty_pointer, entry.first.second))); diff --git a/vendor/jsontoolkit/src/jsonschema/compile_describe.cc b/vendor/jsontoolkit/src/jsonschema/compile_describe.cc index 6dba65d..c231ec6 100644 --- a/vendor/jsontoolkit/src/jsonschema/compile_describe.cc +++ b/vendor/jsontoolkit/src/jsonschema/compile_describe.cc @@ -249,8 +249,8 @@ struct DescribeVisitor { for (const auto &child : step.children) { // Schema - if (std::holds_alternative(child)) { - const auto &substep{std::get(child)}; + if (std::holds_alternative(child)) { + const auto &substep{std::get(child)}; assert(substep.condition.size() == 1); assert(std::holds_alternative( substep.condition.front())); @@ -268,9 +268,9 @@ struct DescribeVisitor { // Properties } else { assert( - std::holds_alternative(child)); + std::holds_alternative(child)); const auto &substep{ - std::get(child)}; + std::get(child)}; assert(substep.condition.size() == 1); assert(std::holds_alternative( substep.condition.front())); @@ -386,24 +386,46 @@ struct DescribeVisitor { std::set all_dependencies; std::set required; for (const auto &child : step.children) { - assert(std::holds_alternative(child)); - const auto &substep{std::get(child)}; - assert(substep.condition.size() == 1); - assert(std::holds_alternative( - substep.condition.front())); - const auto &define{std::get( - substep.condition.front())}; - const auto &property{step_value(define)}; - all_dependencies.insert(property); - if (!this->target.defines(property)) { - continue; - } - present.insert(property); - const auto &requirements{step_value(substep)}; - for (const auto &requirement : requirements) { - if (this->valid || !this->target.defines(requirement)) { - required.insert(requirement); + if (std::holds_alternative(child)) { + const auto &substep{std::get(child)}; + assert(substep.condition.size() == 1); + assert(std::holds_alternative( + substep.condition.front())); + const auto &define{std::get( + substep.condition.front())}; + const auto &property{step_value(define)}; + all_dependencies.insert(property); + if (!this->target.defines(property)) { + continue; + } + + present.insert(property); + if (this->valid || !this->target.defines(step_value(substep))) { + required.insert(step_value(substep)); + } + } else { + assert( + std::holds_alternative(child)); + const auto &substep{ + std::get(child)}; + assert(substep.condition.size() == 1); + assert(std::holds_alternative( + substep.condition.front())); + const auto &define{std::get( + substep.condition.front())}; + const auto &property{step_value(define)}; + all_dependencies.insert(property); + if (!this->target.defines(property)) { + continue; + } + + present.insert(property); + const auto &requirements{step_value(substep)}; + for (const auto &requirement : requirements) { + if (this->valid || !this->target.defines(requirement)) { + required.insert(requirement); + } } } } @@ -470,8 +492,8 @@ struct DescribeVisitor { std::set present; std::set all_dependencies; for (const auto &child : step.children) { - assert(std::holds_alternative(child)); - const auto &substep{std::get(child)}; + assert(std::holds_alternative(child)); + const auto &substep{std::get(child)}; assert(substep.condition.size() == 1); assert(std::holds_alternative( substep.condition.front())); @@ -601,7 +623,7 @@ struct DescribeVisitor { return message.str(); } - auto operator()(const SchemaCompilerAnnotationPublic &) const -> std::string { + auto operator()(const SchemaCompilerAnnotationEmit &) const -> std::string { if (this->keyword == "if") { assert(this->annotation == JSON{true}); std::ostringstream message; @@ -882,10 +904,10 @@ struct DescribeVisitor { if (this->keyword == "unevaluatedProperties") { std::ostringstream message; if (step.children.size() == 1 && - std::holds_alternative( + std::holds_alternative( step.children.front()) && std::holds_alternative( - std::get(step.children.front()) + std::get(step.children.front()) .children.front())) { message << "The object value was not expected to define unevaluated " "properties"; @@ -900,10 +922,10 @@ struct DescribeVisitor { assert(this->keyword == "additionalProperties"); std::ostringstream message; if (step.children.size() == 1 && - std::holds_alternative( + std::holds_alternative( step.children.front()) && std::holds_alternative( - std::get(step.children.front()) + std::get(step.children.front()) .children.front())) { message << "The object value was not expected to define additional " "properties"; @@ -1507,33 +1529,24 @@ struct DescribeVisitor { return message.str(); } - // Internal steps that should never be described - // TODO: Can we get rid of these somehow? + // These steps are never described, at least not right now auto - operator()(const SchemaCompilerInternalSizeEqual &) const -> std::string { + operator()(const SchemaCompilerAssertionSizeEqual &) const -> std::string { return unknown(); } auto - operator()(const SchemaCompilerInternalContainer &) const -> std::string { + operator()(const SchemaCompilerAssertionNoAnnotation &) const -> std::string { return unknown(); } auto - operator()(const SchemaCompilerInternalAnnotation &) const -> std::string { + operator()(const SchemaCompilerAssertionAnnotation &) const -> std::string { return unknown(); } - auto operator()(const SchemaCompilerInternalNoAdjacentAnnotation &) const + auto operator()(const SchemaCompilerAssertionNoAdjacentAnnotation &) const -> std::string { return unknown(); } - auto - operator()(const SchemaCompilerInternalNoAnnotation &) const -> std::string { - return unknown(); - } - auto - operator()(const SchemaCompilerInternalDefinesAll &) const -> std::string { - return unknown(); - } }; } // namespace diff --git a/vendor/jsontoolkit/src/jsonschema/compile_evaluate.cc b/vendor/jsontoolkit/src/jsonschema/compile_evaluate.cc index 10d8320..c361f49 100644 --- a/vendor/jsontoolkit/src/jsonschema/compile_evaluate.cc +++ b/vendor/jsontoolkit/src/jsonschema/compile_evaluate.cc @@ -2,6 +2,8 @@ #include #include +#include "trace.h" + #include // std::min #include // assert #include // std::reference_wrapper @@ -121,20 +123,6 @@ class EvaluationContext { return this->instance_location_; } - auto instance_location(const sourcemeta::jsontoolkit::SchemaCompilerTarget - &target) const -> Pointer { - switch (target.first) { - case sourcemeta::jsontoolkit::SchemaCompilerTargetType::InstanceParent: - return this->instance_location().concat(target.second).initial(); - default: - return this->instance_location().concat(target.second); - } - } - - template auto instance_location(const T &step) const -> Pointer { - return this->instance_location(step.target); - } - auto target_type(const TargetType type) -> void { this->target_type_ = type; } auto target_type() const -> TargetType { return this->target_type_; } @@ -151,6 +139,8 @@ class EvaluationContext { this->evaluate_path().initial().concat(target.second)}; assert(target.first != SchemaCompilerTargetType::ParentAnnotations); if (target.first == SchemaCompilerTargetType::ParentAdjacentAnnotations) { + // TODO: This involves expensive pointer copies, allocations, and + // destructions return this->annotations(this->instance_location().initial(), schema_location); } else { @@ -158,6 +148,8 @@ class EvaluationContext { } } else if constexpr (std::is_same_v) { if (target.first == SchemaCompilerTargetType::ParentAnnotations) { + // TODO: This involves expensive pointer copies, allocations, and + // destructions return this->annotations(this->instance_location().initial()); } else { assert(target.first == SchemaCompilerTargetType::Annotations); @@ -165,19 +157,27 @@ class EvaluationContext { } } else { static_assert(std::is_same_v); + assert(target.second.empty()); + assert(target.first != + sourcemeta::jsontoolkit::SchemaCompilerTargetType::InstanceParent); switch (target.first) { case SchemaCompilerTargetType::Instance: if (this->target_type() == TargetType::Key) { - assert(!this->instance_location(target).empty()); - assert(this->instance_location(target).back().is_property()); - return this->value( - this->instance_location(target).back().to_property()); + assert(!this->instance_location().empty()); + assert(this->instance_location().back().is_property()); + return this->value(this->instance_location().back().to_property()); } assert(this->target_type() == TargetType::Value); - return get(instance, this->instance_location(target)); + + // TODO: This means that we traverse the instance into + // the current instance location EVERY single time. + // Can we be smarter? Maybe we keep a reference to the current + // instance location in this class that we manipulate through + // .push() and .pop() + return get(instance, this->instance_location()); case SchemaCompilerTargetType::InstanceBasename: - return this->value(this->instance_location(target).back().to_json()); + return this->value(this->instance_location().back().to_json()); default: // We should never get here assert(false); @@ -264,65 +264,73 @@ class EvaluationContext { TargetType target_type_ = TargetType::Value; }; -auto callback_noop( - const sourcemeta::jsontoolkit::SchemaCompilerEvaluationType, bool, - const sourcemeta::jsontoolkit::SchemaCompilerTemplate::value_type &, - const sourcemeta::jsontoolkit::Pointer &, - const sourcemeta::jsontoolkit::Pointer &, - const sourcemeta::jsontoolkit::JSON &) noexcept -> void {} - auto evaluate_step( const sourcemeta::jsontoolkit::SchemaCompilerTemplate::value_type &step, const sourcemeta::jsontoolkit::JSON &instance, const sourcemeta::jsontoolkit::SchemaCompilerEvaluationMode mode, - const sourcemeta::jsontoolkit::SchemaCompilerEvaluationCallback &callback, + const std::optional< + sourcemeta::jsontoolkit::SchemaCompilerEvaluationCallback> &callback, EvaluationContext &context) -> bool { + SOURCEMETA_TRACE_REGISTER_ID(trace_id); + using namespace sourcemeta::jsontoolkit; bool result{false}; -#define CALLBACK_PRE(current_instance_location) \ - callback(SchemaCompilerEvaluationType::Pre, true, step, \ - context.evaluate_path(), current_instance_location, \ - context.value(nullptr)); +#define CALLBACK_PRE(current_step, current_instance_location) \ + if (current_step.report && callback.has_value()) { \ + callback.value()(SchemaCompilerEvaluationType::Pre, true, step, \ + context.evaluate_path(), current_instance_location, \ + context.value(nullptr)); \ + } -#define CALLBACK_POST(current_step) \ - callback(SchemaCompilerEvaluationType::Post, result, step, \ - context.evaluate_path(), context.instance_location(), \ - context.value(nullptr)); \ +#define CALLBACK_POST(title, current_step) \ + if (current_step.report && callback.has_value()) { \ + callback.value()(SchemaCompilerEvaluationType::Post, result, step, \ + context.evaluate_path(), context.instance_location(), \ + context.value(nullptr)); \ + } \ context.pop(current_step); \ + SOURCEMETA_TRACE_END(trace_id, title); \ return result; -#define EVALUATE_CONDITION_GUARD(step, instance) \ +#define EVALUATE_CONDITION_GUARD(title, step, instance) \ for (const auto &child : step.condition) { \ if (!evaluate_step(child, instance, SchemaCompilerEvaluationMode::Fast, \ - callback_noop, context)) { \ + std::nullopt, context)) { \ context.pop(step); \ + SOURCEMETA_TRACE_END(trace_id, title); \ return true; \ } \ } if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionFail"); const auto &assertion{std::get(step)}; context.push(assertion); assert(std::holds_alternative(assertion.value)); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); - CALLBACK_POST(assertion); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionFail", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); + CALLBACK_POST("SchemaCompilerAssertionFail", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionDefines"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionDefines", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = target.is_object() && target.defines(value); - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionDefines", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionDefinesAll"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionDefinesAll", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; // Otherwise we are we even emitting this instruction? assert(value.size() > 1); @@ -337,12 +345,14 @@ auto evaluate_step( } } - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionDefinesAll", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionType"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionType", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; @@ -351,12 +361,14 @@ auto evaluate_step( result = target.type() == value || (value == JSON::Type::Integer && target.is_integer_real()); - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionType", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionTypeAny"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionTypeAny", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; // Otherwise we are we even emitting this instruction? assert(value.size() > 1); @@ -367,162 +379,192 @@ auto evaluate_step( result = value.contains(target.type()) || (value.contains(JSON::Type::Integer) && target.is_integer_real()); - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionTypeAny", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionTypeStrict"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionTypeStrict", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = target.type() == value; - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionTypeStrict", assertion); } else if (std::holds_alternative( step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionTypeStrictAny"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionTypeStrictAny", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; // Otherwise we are we even emitting this instruction? assert(value.size() > 1); const auto &target{ context.resolve_target(assertion.target, instance)}; result = value.contains(target.type()); - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionTypeStrictAny", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionRegex"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionRegex", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; assert(target.is_string()); result = std::regex_search(target.to_string(), value.first); - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionRegex", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionSizeGreater"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionSizeGreater", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = (target.is_array() || target.is_object() || target.is_string()) && (target.size() > value); - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionSizeGreater", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionSizeLess"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionSizeLess", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = (target.is_array() || target.is_object() || target.is_string()) && (target.size() < value); - CALLBACK_POST(assertion); - } else if (std::holds_alternative(step)) { - const auto &assertion{std::get(step)}; + CALLBACK_POST("SchemaCompilerAssertionSizeLess", assertion); + } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionSizeEqual"); + const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionSizeEqual", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = (target.is_array() || target.is_object() || target.is_string()) && (target.size() == value); - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionSizeEqual", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionEqual"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionEqual", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = (target == value); - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionEqual", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionEqualsAny"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionEqualsAny", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = value.contains(target); - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionEqualsAny", assertion); } else if (std::holds_alternative( step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionGreaterEqual"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionGreaterEqual", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = target >= value; - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionGreaterEqual", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionLessEqual"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionLessEqual", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = target <= value; - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionLessEqual", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionGreater"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionGreater", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = target > value; - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionGreater", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionLess"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionLess", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = target < value; - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionLess", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionUnique"); const auto &assertion{std::get(step)}; assert(std::holds_alternative(assertion.value)); context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionUnique", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &target{ context.resolve_target(assertion.target, instance)}; result = target.is_array() && target.unique(); - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionUnique", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionDivisible"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionDivisible", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; assert(value.is_number()); assert(target.is_number()); result = target.divisible_by(value); - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionDivisible", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionStringType"); const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionStringType", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); const auto value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; @@ -541,12 +583,79 @@ auto evaluate_step( assert(false); } - CALLBACK_POST(assertion); + CALLBACK_POST("SchemaCompilerAssertionStringType", assertion); + } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionAnnotation"); + const auto &assertion{std::get(step)}; + context.push(assertion); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionAnnotation", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); + const auto &value{context.resolve_value(assertion.value, instance)}; + const auto &target{ + context.resolve_target>(assertion.target, instance)}; + result = target.contains(value); + CALLBACK_POST("SchemaCompilerAssertionAnnotation", assertion); + } else if (std::holds_alternative< + SchemaCompilerAssertionNoAdjacentAnnotation>(step)) { + SOURCEMETA_TRACE_START(trace_id, + "SchemaCompilerAssertionNoAdjacentAnnotation"); + const auto &assertion{ + std::get(step)}; + context.push(assertion); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionNoAdjacentAnnotation", + assertion, instance); + CALLBACK_PRE(assertion, context.instance_location()); + const auto &value{context.resolve_value(assertion.value, instance)}; + const auto &target{ + context.resolve_target>(assertion.target, instance)}; + result = !target.contains(value); + CALLBACK_POST("SchemaCompilerAssertionNoAdjacentAnnotation", assertion); + } else if (std::holds_alternative( + step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionNoAnnotation"); + const auto &assertion{std::get(step)}; + context.push(assertion); + EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionNoAnnotation", assertion, + instance); + CALLBACK_PRE(assertion, context.instance_location()); + const auto &value{context.resolve_value(assertion.value, instance)}; + const auto &target{ + context.resolve_target( + assertion.target, instance)}; + result = true; + + if (!assertion.data.empty()) { + for (const auto &[schema_location, annotations] : target) { + assert(!schema_location.empty()); + const auto &keyword{schema_location.back()}; + if (keyword.is_property() && + assertion.data.contains(keyword.to_property()) && + annotations.contains(value) && + // Make sure its not a cousin annotation, which can + // never be seen + // TODO: Have a better function at Pointer to check + // for these "initial starts with" cases in a way + // that we don't have to copy pointers, which `.initial()` + // does. + schema_location.initial().starts_with( + context.evaluate_path().initial()) && + // We want to ignore certain annotations, like the ones + // inside "not" + !context.masked(schema_location)) { + result = false; + break; + } + } + } + + CALLBACK_POST("SchemaCompilerAssertionNoAnnotation", assertion); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerLogicalOr"); const auto &logical{std::get(step)}; context.push(logical); - EVALUATE_CONDITION_GUARD(logical, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerLogicalOr", logical, instance); + CALLBACK_PRE(logical, context.instance_location()); // This boolean value controls whether we should still evaluate // every disjunction even on fast mode const auto value{context.resolve_value(logical.value, instance)}; @@ -560,30 +669,30 @@ auto evaluate_step( } } - CALLBACK_POST(logical); + CALLBACK_POST("SchemaCompilerLogicalOr", logical); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerLogicalAnd"); const auto &logical{std::get(step)}; assert(std::holds_alternative(logical.value)); context.push(logical); - EVALUATE_CONDITION_GUARD(logical, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerLogicalAnd", logical, instance); + CALLBACK_PRE(logical, context.instance_location()); result = true; for (const auto &child : logical.children) { if (!evaluate_step(child, instance, mode, callback, context)) { result = false; - if (mode == SchemaCompilerEvaluationMode::Fast) { - break; - } + break; } } - CALLBACK_POST(logical); + CALLBACK_POST("SchemaCompilerLogicalAnd", logical); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerLogicalXor"); const auto &logical{std::get(step)}; assert(std::holds_alternative(logical.value)); context.push(logical); - EVALUATE_CONDITION_GUARD(logical, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerLogicalXor", logical, instance); + CALLBACK_PRE(logical, context.instance_location()); result = false; // TODO: Cache results of a given branch so we can avoid @@ -606,7 +715,7 @@ auto evaluate_step( // We don't need to report traces that part of the exhaustive // XOR search. We can treat those as internal - if (evaluate_step(*subiterator, instance, mode, callback_noop, + if (evaluate_step(*subiterator, instance, mode, std::nullopt, context)) { subresult = false; break; @@ -619,13 +728,14 @@ auto evaluate_step( } } - CALLBACK_POST(logical); + CALLBACK_POST("SchemaCompilerLogicalXor", logical); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerLogicalTry"); const auto &logical{std::get(step)}; assert(std::holds_alternative(logical.value)); context.push(logical); - EVALUATE_CONDITION_GUARD(logical, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerLogicalTry", logical, instance); + CALLBACK_PRE(logical, context.instance_location()); result = true; for (const auto &child : logical.children) { if (!evaluate_step(child, instance, mode, callback, context)) { @@ -633,13 +743,14 @@ auto evaluate_step( } } - CALLBACK_POST(logical); + CALLBACK_POST("SchemaCompilerLogicalTry", logical); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerLogicalNot"); const auto &logical{std::get(step)}; assert(std::holds_alternative(logical.value)); context.push(logical); - EVALUATE_CONDITION_GUARD(logical, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerLogicalNot", logical, instance); + CALLBACK_PRE(logical, context.instance_location()); // Ignore annotations produced inside "not" context.mask(); result = false; @@ -652,192 +763,100 @@ auto evaluate_step( } } - CALLBACK_POST(logical); - } else if (std::holds_alternative(step)) { - const auto &assertion{std::get(step)}; - context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - const auto &value{context.resolve_value(assertion.value, instance)}; - const auto &target{ - context.resolve_target>(assertion.target, instance)}; - result = target.contains(value); - - // We treat this step as transparent to the consumer - context.pop(assertion); - return result; - } else if (std::holds_alternative( - step)) { - const auto &assertion{ - std::get(step)}; - context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - const auto &value{context.resolve_value(assertion.value, instance)}; - const auto &target{ - context.resolve_target>(assertion.target, instance)}; - result = !target.contains(value); - - // We treat this step as transparent to the consumer - context.pop(assertion); - return result; - } else if (std::holds_alternative(step)) { - const auto &assertion{std::get(step)}; - context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - const auto &value{context.resolve_value(assertion.value, instance)}; - const auto &target{ - context.resolve_target( - assertion.target, instance)}; - result = true; - - if (!assertion.data.empty()) { - for (const auto &[schema_location, annotations] : target) { - assert(!schema_location.empty()); - const auto &keyword{schema_location.back()}; - if (keyword.is_property() && - assertion.data.contains(keyword.to_property()) && - annotations.contains(value) && - // Make sure its not a cousin annotation, which can - // never be seen - // TODO: Have a better function at Pointer to check - // for these "initial starts with" cases in a way - // that we don't have to copy pointers, which `.initial()` - // does. - schema_location.initial().starts_with( - context.evaluate_path().initial()) && - // We want to ignore certain annotations, like the ones - // inside "not" - !context.masked(schema_location)) { - result = false; - break; - } - } - } - - // We treat this step as transparent to the consumer - context.pop(assertion); - return result; - } else if (std::holds_alternative(step)) { - const auto &container{std::get(step)}; - assert(std::holds_alternative(container.value)); - context.push(container); - EVALUATE_CONDITION_GUARD(container, instance); - result = true; - for (const auto &child : container.children) { - if (!evaluate_step(child, instance, mode, callback, context)) { - result = false; - break; - } - } - - // We treat this step as transparent to the consumer - context.pop(container); - return result; - } else if (std::holds_alternative(step)) { - const auto &assertion{std::get(step)}; - context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion, instance); - const auto &value{context.resolve_value(assertion.value, instance)}; - const auto &target{ - context.resolve_target(assertion.target, instance)}; - assert(target.is_object()); - result = true; - for (const auto &property : value) { - if (!target.defines(property)) { - result = false; - break; - } - } - - // We treat this step as transparent to the consumer - context.pop(assertion); - return result; + CALLBACK_POST("SchemaCompilerLogicalNot", logical); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerControlLabel"); const auto &control{std::get(step)}; context.mark(control.id, control.children); context.push(control); - CALLBACK_PRE(context.instance_location()); + CALLBACK_PRE(control, context.instance_location()); result = true; for (const auto &child : control.children) { if (!evaluate_step(child, instance, mode, callback, context)) { result = false; - if (mode == SchemaCompilerEvaluationMode::Fast) { - break; - } + break; } } - CALLBACK_POST(control); + CALLBACK_POST("SchemaCompilerControlLabel", control); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerControlMark"); const auto &control{std::get(step)}; context.mark(control.id, control.children); + SOURCEMETA_TRACE_END(trace_id, "SchemaCompilerControlMark"); return true; } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerControlJump"); const auto &control{std::get(step)}; context.push(control); - CALLBACK_PRE(context.instance_location()); + CALLBACK_PRE(control, context.instance_location()); assert(control.children.empty()); result = true; for (const auto &child : context.jump(control.id)) { if (!evaluate_step(child, instance, mode, callback, context)) { result = false; - if (mode == SchemaCompilerEvaluationMode::Fast) { - break; - } + break; } } - CALLBACK_POST(control); + CALLBACK_POST("SchemaCompilerControlJump", control); } else if (std::holds_alternative( step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerControlDynamicAnchorJump"); const auto &control{std::get(step)}; context.push(control); - CALLBACK_PRE(context.instance_location()); + CALLBACK_PRE(control, context.instance_location()); const auto id{context.find_dynamic_anchor(control.id)}; result = id.has_value(); if (id.has_value()) { for (const auto &child : context.jump(id.value())) { if (!evaluate_step(child, instance, mode, callback, context)) { result = false; - if (mode == SchemaCompilerEvaluationMode::Fast) { - break; - } + break; } } } - CALLBACK_POST(control); - } else if (std::holds_alternative(step)) { - const auto &annotation{std::get(step)}; + CALLBACK_POST("SchemaCompilerControlDynamicAnchorJump", control); + } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAnnotationEmit"); + const auto &annotation{std::get(step)}; context.push(annotation); - EVALUATE_CONDITION_GUARD(annotation, instance); - const auto current_instance_location{context.instance_location(annotation)}; + EVALUATE_CONDITION_GUARD("SchemaCompilerAnnotationEmit", annotation, + instance); + // Annotations never fail + result = true; + assert(annotation.target.second.empty()); + + // TODO: Can we avoid a copy of the instance location here? + const auto current_instance_location{ + annotation.target.first == SchemaCompilerTargetType::InstanceParent + ? context.instance_location().initial() + : context.instance_location()}; + const auto value{ context.annotate(current_instance_location, context.resolve_value(annotation.value, instance))}; - // Annotations never fail - result = true; // As a safety guard, only emit the annotation if it didn't exist already. // Otherwise we risk confusing consumers - if (value.second) { - CALLBACK_PRE(current_instance_location); - callback(SchemaCompilerEvaluationType::Post, result, step, - context.evaluate_path(), current_instance_location, value.first); + if (value.second && callback.has_value()) { + CALLBACK_PRE(annotation, current_instance_location); + callback.value()(SchemaCompilerEvaluationType::Post, result, step, + context.evaluate_path(), current_instance_location, + value.first); } context.pop(annotation); + SOURCEMETA_TRACE_END(trace_id, "SchemaCompilerAnnotationEmit"); return result; } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerLoopProperties"); const auto &loop{std::get(step)}; context.push(loop); - EVALUATE_CONDITION_GUARD(loop, instance); - const auto value{context.resolve_value(loop.value, instance)}; - - // Setting the value to false means "don't report it" - if (value) { - CALLBACK_PRE(context.instance_location()); - } - + EVALUATE_CONDITION_GUARD("SchemaCompilerLoopProperties", loop, instance); + assert(std::holds_alternative(loop.value)); + CALLBACK_PRE(loop, context.instance_location()); const auto &target{context.resolve_target(loop.target, instance)}; assert(target.is_object()); result = true; @@ -846,13 +865,9 @@ auto evaluate_step( for (const auto &child : loop.children) { if (!evaluate_step(child, instance, mode, callback, context)) { result = false; - if (mode == SchemaCompilerEvaluationMode::Fast) { - context.pop(loop); - // For efficiently breaking from the outer loop too - goto evaluate_loop_properties_end; - } else { - break; - } + context.pop(loop); + // For efficiently breaking from the outer loop too + goto evaluate_loop_properties_end; } } @@ -860,18 +875,13 @@ auto evaluate_step( } evaluate_loop_properties_end: - // Setting the value to false means "don't report it" - if (!value) { - context.pop(loop); - return result; - } - - CALLBACK_POST(loop); + CALLBACK_POST("SchemaCompilerLoopProperties", loop); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerLoopKeys"); const auto &loop{std::get(step)}; context.push(loop); - EVALUATE_CONDITION_GUARD(loop, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerLoopKeys", loop, instance); + CALLBACK_PRE(loop, context.instance_location()); assert(std::holds_alternative(loop.value)); const auto &target{context.resolve_target(loop.target, instance)}; assert(target.is_object()); @@ -882,12 +892,8 @@ auto evaluate_step( for (const auto &child : loop.children) { if (!evaluate_step(child, instance, mode, callback, context)) { result = false; - if (mode == SchemaCompilerEvaluationMode::Fast) { - context.pop(loop); - goto evaluate_loop_keys_end; - } else { - break; - } + context.pop(loop); + goto evaluate_loop_keys_end; } } @@ -896,12 +902,13 @@ auto evaluate_step( evaluate_loop_keys_end: context.target_type(EvaluationContext::TargetType::Value); - CALLBACK_POST(loop); + CALLBACK_POST("SchemaCompilerLoopKeys", loop); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerLoopItems"); const auto &loop{std::get(step)}; context.push(loop); - EVALUATE_CONDITION_GUARD(loop, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerLoopItems", loop, instance); + CALLBACK_PRE(loop, context.instance_location()); const auto value{context.resolve_value(loop.value, instance)}; const auto &target{context.resolve_target(loop.target, instance)}; assert(target.is_array()); @@ -923,26 +930,25 @@ auto evaluate_step( for (const auto &child : loop.children) { if (!evaluate_step(child, instance, mode, callback, context)) { result = false; - if (mode == SchemaCompilerEvaluationMode::Fast) { - context.pop(loop); - CALLBACK_POST(loop); - } else { - break; - } + context.pop(loop); + CALLBACK_POST("SchemaCompilerLoopItems", loop); } } context.pop(loop); } - CALLBACK_POST(loop); + CALLBACK_POST("SchemaCompilerLoopItems", loop); } else if (std::holds_alternative( step)) { + SOURCEMETA_TRACE_START(trace_id, + "SchemaCompilerLoopItemsFromAnnotationIndex"); const auto &loop{ std::get(step)}; context.push(loop); - EVALUATE_CONDITION_GUARD(loop, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerLoopItemsFromAnnotationIndex", loop, + instance); + CALLBACK_PRE(loop, context.instance_location()); const auto &value{context.resolve_value(loop.value, instance)}; const auto &target{context.resolve_target(loop.target, instance)}; assert(target.is_array()); @@ -985,24 +991,21 @@ auto evaluate_step( for (const auto &child : loop.children) { if (!evaluate_step(child, instance, mode, callback, context)) { result = false; - if (mode == SchemaCompilerEvaluationMode::Fast) { - context.pop(loop); - CALLBACK_POST(loop); - } else { - break; - } + context.pop(loop); + CALLBACK_POST("SchemaCompilerLoopItemsFromAnnotationIndex", loop); } } context.pop(loop); } - CALLBACK_POST(loop); + CALLBACK_POST("SchemaCompilerLoopItemsFromAnnotationIndex", loop); } else if (std::holds_alternative(step)) { + SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerLoopContains"); const auto &loop{std::get(step)}; context.push(loop); - EVALUATE_CONDITION_GUARD(loop, instance); - CALLBACK_PRE(context.instance_location()); + EVALUATE_CONDITION_GUARD("SchemaCompilerLoopContains", loop, instance); + CALLBACK_PRE(loop, context.instance_location()); const auto &value{context.resolve_value(loop.value, instance)}; const auto minimum{std::get<0>(value)}; const auto &maximum{std::get<1>(value)}; @@ -1052,7 +1055,7 @@ auto evaluate_step( } } - CALLBACK_POST(loop); + CALLBACK_POST("SchemaCompilerLoopContains", loop); } #undef CALLBACK_PRE @@ -1063,21 +1066,19 @@ auto evaluate_step( return result; } -} // namespace - -namespace sourcemeta::jsontoolkit { - -auto evaluate(const SchemaCompilerTemplate &steps, const JSON &instance, - const SchemaCompilerEvaluationMode mode, - const SchemaCompilerEvaluationCallback &callback) -> bool { +inline auto evaluate_internal( + const sourcemeta::jsontoolkit::SchemaCompilerTemplate &steps, + const sourcemeta::jsontoolkit::JSON &instance, + const sourcemeta::jsontoolkit::SchemaCompilerEvaluationMode mode, + const std::optional< + sourcemeta::jsontoolkit::SchemaCompilerEvaluationCallback> &callback) + -> bool { EvaluationContext context; bool overall{true}; for (const auto &step : steps) { if (!evaluate_step(step, instance, mode, callback, context)) { overall = false; - if (mode == SchemaCompilerEvaluationMode::Fast) { - break; - } + break; } } @@ -1089,12 +1090,22 @@ auto evaluate(const SchemaCompilerTemplate &steps, const JSON &instance, return overall; } +} // namespace + +namespace sourcemeta::jsontoolkit { + +auto evaluate(const SchemaCompilerTemplate &steps, const JSON &instance, + const SchemaCompilerEvaluationMode mode, + const SchemaCompilerEvaluationCallback &callback) -> bool { + return evaluate_internal(steps, instance, mode, callback); +} + auto evaluate(const SchemaCompilerTemplate &steps, const JSON &instance) -> bool { - // Otherwise what's the point of an exhaustive - // evaluation if you don't get the results? - return evaluate(steps, instance, SchemaCompilerEvaluationMode::Fast, - callback_noop); + return evaluate_internal(steps, instance, + // Otherwise what's the point of an exhaustive + // evaluation if you don't get the results? + SchemaCompilerEvaluationMode::Fast, std::nullopt); } } // namespace sourcemeta::jsontoolkit diff --git a/vendor/jsontoolkit/src/jsonschema/compile_helpers.h b/vendor/jsontoolkit/src/jsonschema/compile_helpers.h index 8eb6c14..108029b 100644 --- a/vendor/jsontoolkit/src/jsonschema/compile_helpers.h +++ b/vendor/jsontoolkit/src/jsonschema/compile_helpers.h @@ -25,7 +25,7 @@ inline auto relative_schema_location( // Instantiate a value-oriented step template -auto make(const SchemaCompilerContext &context, +auto make(const bool report, const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context, // Take the value type from the "type" property of the step struct @@ -40,13 +40,14 @@ auto make(const SchemaCompilerContext &context, keyword_location(schema_context), schema_context.base.recompose(), context.uses_dynamic_scopes, + report, std::move(value), std::move(condition)}; } // Instantiate a value-oriented step with data template -auto make(const SchemaCompilerContext &context, +auto make(const bool report, const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context, // Take the value type from the "type" property of the step struct @@ -63,6 +64,7 @@ auto make(const SchemaCompilerContext &context, keyword_location(schema_context), schema_context.base.recompose(), context.uses_dynamic_scopes, + report, std::move(value), std::move(condition), std::move(data)}; @@ -70,7 +72,7 @@ auto make(const SchemaCompilerContext &context, // Instantiate an applicator step template -auto make(const SchemaCompilerContext &context, +auto make(const bool report, const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context, // Take the value type from the "value" property of the step struct @@ -83,6 +85,7 @@ auto make(const SchemaCompilerContext &context, keyword_location(schema_context), schema_context.base.recompose(), context.uses_dynamic_scopes, + report, std::move(value), std::move(children), std::move(condition)}; @@ -90,7 +93,7 @@ auto make(const SchemaCompilerContext &context, // Instantiate a control step template -auto make(const SchemaCompilerContext &context, +auto make(const bool report, const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context, // Take the value type from the "id" property of the step struct @@ -101,6 +104,7 @@ auto make(const SchemaCompilerContext &context, keyword_location(schema_context), schema_context.base.recompose(), context.uses_dynamic_scopes, + report, std::move(id), std::move(children)}; } @@ -129,7 +133,7 @@ inline auto type_condition(const SchemaCompilerContext &context, } return {make( - context, schema_context, relative_dynamic_context, type, {}, + true, context, schema_context, relative_dynamic_context, type, {}, SchemaCompilerTargetType::Instance)}; } diff --git a/vendor/jsontoolkit/src/jsonschema/compile_json.cc b/vendor/jsontoolkit/src/jsonschema/compile_json.cc index 0efc651..369d858 100644 --- a/vendor/jsontoolkit/src/jsonschema/compile_json.cc +++ b/vendor/jsontoolkit/src/jsonschema/compile_json.cc @@ -183,6 +183,7 @@ auto encode_step(const std::string_view category, const std::string_view type, result.assign("absoluteKeywordLocation", JSON{step.keyword_location}); result.assign("schemaResource", JSON{step.schema_resource}); result.assign("dynamic", JSON{step.dynamic}); + result.assign("report", JSON{step.report}); if constexpr (requires { step.id; }) { result.assign("id", JSON{step.id}); @@ -244,19 +245,17 @@ struct StepVisitor { HANDLE_STEP("assertion", "divisible", SchemaCompilerAssertionDivisible) HANDLE_STEP("assertion", "string-type", SchemaCompilerAssertionStringType) HANDLE_STEP("assertion", "equals-any", SchemaCompilerAssertionEqualsAny) - HANDLE_STEP("annotation", "public", SchemaCompilerAnnotationPublic) + HANDLE_STEP("annotation", "emit", SchemaCompilerAnnotationEmit) + HANDLE_STEP("assertion", "size-equal", SchemaCompilerAssertionSizeEqual) + HANDLE_STEP("assertion", "annotation", SchemaCompilerAssertionAnnotation) + HANDLE_STEP("assertion", "no-adjacent-annotation", + SchemaCompilerAssertionNoAdjacentAnnotation) + HANDLE_STEP("assertion", "no-annotation", SchemaCompilerAssertionNoAnnotation) HANDLE_STEP("logical", "or", SchemaCompilerLogicalOr) HANDLE_STEP("logical", "and", SchemaCompilerLogicalAnd) HANDLE_STEP("logical", "xor", SchemaCompilerLogicalXor) HANDLE_STEP("logical", "try", SchemaCompilerLogicalTry) HANDLE_STEP("logical", "not", SchemaCompilerLogicalNot) - HANDLE_STEP("internal", "size-equal", SchemaCompilerInternalSizeEqual) - HANDLE_STEP("internal", "annotation", SchemaCompilerInternalAnnotation) - HANDLE_STEP("internal", "no-adjacent-annotation", - SchemaCompilerInternalNoAdjacentAnnotation) - HANDLE_STEP("internal", "no-annotation", SchemaCompilerInternalNoAnnotation) - HANDLE_STEP("internal", "container", SchemaCompilerInternalContainer) - HANDLE_STEP("internal", "defines-all", SchemaCompilerInternalDefinesAll) HANDLE_STEP("loop", "properties", SchemaCompilerLoopProperties) HANDLE_STEP("loop", "keys", SchemaCompilerLoopKeys) HANDLE_STEP("loop", "items", SchemaCompilerLoopItems) @@ -300,9 +299,10 @@ auto compiler_template_format_compare(const JSON::String &left, {"target", 7}, {"location", 8}, {"id", 9}, - {"dynamic", 10}, - {"condition", 11}, - {"children", 12}}; + {"report", 10}, + {"dynamic", 11}, + {"condition", 12}, + {"children", 13}}; // We define and control all of these keywords, so if we are missing // some here, then we did something wrong? diff --git a/vendor/jsontoolkit/src/jsonschema/default_compiler_2019_09.h b/vendor/jsontoolkit/src/jsonschema/default_compiler_2019_09.h index 36ff223..588869c 100644 --- a/vendor/jsontoolkit/src/jsonschema/default_compiler_2019_09.h +++ b/vendor/jsontoolkit/src/jsonschema/default_compiler_2019_09.h @@ -25,19 +25,19 @@ auto compiler_2019_09_applicator_dependentschemas( } if (!entry.second.is_boolean() || !entry.second.to_boolean()) { - children.push_back(make( - context, schema_context, relative_dynamic_context, + children.push_back(make( + false, context, schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, compile(context, schema_context, relative_dynamic_context, {entry.first}, empty_pointer), {make( - context, schema_context, relative_dynamic_context, entry.first, - {}, SchemaCompilerTargetType::Instance)})); + true, context, schema_context, relative_dynamic_context, + entry.first, {}, SchemaCompilerTargetType::Instance)})); } } return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), type_condition(context, schema_context, JSON::Type::Object))}; } @@ -64,17 +64,29 @@ auto compiler_2019_09_validation_dependentrequired( properties.emplace(property.to_string()); } - children.push_back(make( - context, schema_context, relative_dynamic_context, - std::move(properties), - {make( - context, schema_context, relative_dynamic_context, entry.first, {}, - SchemaCompilerTargetType::Instance)}, - SchemaCompilerTargetType::Instance)); + if (properties.empty()) { + continue; + } else if (properties.size() == 1) { + children.push_back(make( + false, context, schema_context, relative_dynamic_context, + *(properties.cbegin()), + {make( + true, context, schema_context, relative_dynamic_context, + entry.first, {}, SchemaCompilerTargetType::Instance)}, + SchemaCompilerTargetType::Instance)); + } else { + children.push_back(make( + false, context, schema_context, relative_dynamic_context, + std::move(properties), + {make( + true, context, schema_context, relative_dynamic_context, + entry.first, {}, SchemaCompilerTargetType::Instance)}, + SchemaCompilerTargetType::Instance)); + } } return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), type_condition(context, schema_context, JSON::Type::Object))}; } @@ -84,8 +96,8 @@ auto compiler_2019_09_core_annotation( const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { - return {make( - context, schema_context, dynamic_context, + return {make( + true, context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), {}, SchemaCompilerTargetType::Instance)}; } @@ -122,8 +134,8 @@ auto compiler_2019_09_applicator_contains_conditional_annotate( if (maximum.has_value() && minimum > maximum.value()) { return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, + SchemaCompilerValueNone{}, {}, SchemaCompilerTargetType::Instance)}; } if (minimum == 0 && !maximum.has_value()) { @@ -135,8 +147,8 @@ auto compiler_2019_09_applicator_contains_conditional_annotate( empty_pointer, empty_pointer)}; if (annotate) { - children.push_back(make( - context, schema_context, relative_dynamic_context, + children.push_back(make( + true, context, schema_context, relative_dynamic_context, SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, empty_pointer}, {}, SchemaCompilerTargetType::InstanceParent)); @@ -148,7 +160,7 @@ auto compiler_2019_09_applicator_contains_conditional_annotate( } return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, SchemaCompilerValueRange{ minimum, maximum, // TODO: We only need to be exhaustive here if `unevaluatedItems` is @@ -157,8 +169,8 @@ auto compiler_2019_09_applicator_contains_conditional_annotate( annotate}, std::move(children), {make( - context, schema_context, relative_dynamic_context, JSON::Type::Array, - {}, SchemaCompilerTargetType::Instance)})}; + true, context, schema_context, relative_dynamic_context, + JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)})}; } auto compiler_2019_09_applicator_contains( @@ -222,20 +234,20 @@ auto compiler_2019_09_applicator_unevaluateditems( SchemaCompilerTemplate loop_children{compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer)}; - loop_children.push_back(make( - context, schema_context, relative_dynamic_context, JSON{true}, {}, + loop_children.push_back(make( + true, context, schema_context, relative_dynamic_context, JSON{true}, {}, SchemaCompilerTargetType::InstanceParent)); if (schema_context.vocabularies.contains( "https://json-schema.org/draft/2020-12/vocab/applicator")) { SchemaCompilerTemplate subcondition{ - make( - context, schema_context, relative_dynamic_context, + make( + false, context, schema_context, relative_dynamic_context, SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, empty_pointer}, {}, SchemaCompilerTargetType::ParentAnnotations, {"contains"})}; - children.push_back(make( - context, schema_context, relative_dynamic_context, + children.push_back(make( + false, context, schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, std::move(loop_children), std::move(subcondition))); } else { @@ -247,33 +259,33 @@ auto compiler_2019_09_applicator_unevaluateditems( "https://json-schema.org/draft/2019-09/vocab/applicator") && dependencies.contains("items")) { loop.push_back(make( - context, schema_context, relative_dynamic_context, + true, context, schema_context, relative_dynamic_context, SchemaCompilerValueString{"items"}, std::move(children), SchemaCompilerTemplate{})); } else if (schema_context.vocabularies.contains( "https://json-schema.org/draft/2020-12/vocab/applicator") && dependencies.contains("prefixItems")) { loop.push_back(make( - context, schema_context, relative_dynamic_context, + true, context, schema_context, relative_dynamic_context, SchemaCompilerValueString{"prefixItems"}, std::move(children), SchemaCompilerTemplate{})); } else { loop.push_back(make( - context, schema_context, relative_dynamic_context, + true, context, schema_context, relative_dynamic_context, SchemaCompilerValueUnsignedInteger{0}, std::move(children), SchemaCompilerTemplate{})); } SchemaCompilerTemplate condition{make( - context, schema_context, relative_dynamic_context, JSON::Type::Array, {}, - SchemaCompilerTargetType::Instance)}; - condition.push_back(make( - context, schema_context, relative_dynamic_context, JSON{true}, {}, + true, context, schema_context, relative_dynamic_context, + JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)}; + condition.push_back(make( + false, context, schema_context, relative_dynamic_context, JSON{true}, {}, SchemaCompilerTargetType::Annotations, std::move(dependencies))); - return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, - std::move(loop), std::move(condition))}; + return {make( + false, context, schema_context, dynamic_context, + SchemaCompilerValueNone{}, std::move(loop), std::move(condition))}; } auto compiler_2019_09_applicator_unevaluatedproperties( @@ -297,8 +309,8 @@ auto compiler_2019_09_applicator_unevaluatedproperties( dependencies.emplace("additionalProperties"); } - SchemaCompilerTemplate condition{make( - context, schema_context, relative_dynamic_context, + SchemaCompilerTemplate condition{make( + false, context, schema_context, relative_dynamic_context, SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, empty_pointer}, {}, SchemaCompilerTargetType::ParentAnnotations, @@ -307,21 +319,22 @@ auto compiler_2019_09_applicator_unevaluatedproperties( SchemaCompilerTemplate children{compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer)}; - children.push_back(make( - context, schema_context, relative_dynamic_context, + children.push_back(make( + true, context, schema_context, relative_dynamic_context, SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, empty_pointer}, {}, SchemaCompilerTargetType::InstanceParent)); - SchemaCompilerTemplate wrapper{make( - context, schema_context, relative_dynamic_context, + SchemaCompilerTemplate wrapper{make( + false, context, schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, std::move(children), std::move(condition))}; return {make( - context, schema_context, dynamic_context, true, {std::move(wrapper)}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + {std::move(wrapper)}, {make( - context, schema_context, relative_dynamic_context, JSON::Type::Object, - {}, SchemaCompilerTargetType::Instance)})}; + true, context, schema_context, relative_dynamic_context, + JSON::Type::Object, {}, SchemaCompilerTargetType::Instance)})}; } auto compiler_2019_09_core_recursiveref( @@ -338,7 +351,7 @@ auto compiler_2019_09_core_recursiveref( } return {make( - context, schema_context, dynamic_context, "", {})}; + true, context, schema_context, dynamic_context, "", {})}; } auto compiler_2019_09_applicator_anyof( diff --git a/vendor/jsontoolkit/src/jsonschema/default_compiler_2020_12.h b/vendor/jsontoolkit/src/jsonschema/default_compiler_2020_12.h index 46d4929..c91cf2f 100644 --- a/vendor/jsontoolkit/src/jsonschema/default_compiler_2020_12.h +++ b/vendor/jsontoolkit/src/jsonschema/default_compiler_2020_12.h @@ -67,7 +67,7 @@ auto compiler_2020_12_core_dynamicref( // reference (if any), as even if we jump first there, we will still // look for the oldest dynamic anchor in the schema resource chain. return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, std::string{reference.fragment().value()}, {})}; } diff --git a/vendor/jsontoolkit/src/jsonschema/default_compiler_draft4.h b/vendor/jsontoolkit/src/jsonschema/default_compiler_draft4.h index abcc872..8219487 100644 --- a/vendor/jsontoolkit/src/jsonschema/default_compiler_draft4.h +++ b/vendor/jsontoolkit/src/jsonschema/default_compiler_draft4.h @@ -36,7 +36,7 @@ auto compiler_draft4_core_ref(const SchemaCompilerContext &context, // The label is already registered, so just jump to it if (schema_context.labels.contains(label)) { return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{label}, {})}; } @@ -56,7 +56,7 @@ auto compiler_draft4_core_ref(const SchemaCompilerContext &context, // handler, without having to add logic to every single keyword to check // whether something points to them and add the "checkpoint" themselves. return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{label}, compile(context, std::move(new_schema_context), relative_dynamic_context, empty_pointer, empty_pointer, reference.destination))}; @@ -72,33 +72,33 @@ auto compiler_draft4_validation_type( schema_context.schema.at(dynamic_context.keyword).to_string()}; if (type == "null") { return {make( - context, schema_context, dynamic_context, JSON::Type::Null, {}, + true, context, schema_context, dynamic_context, JSON::Type::Null, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "boolean") { return {make( - context, schema_context, dynamic_context, JSON::Type::Boolean, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::Boolean, + {}, SchemaCompilerTargetType::Instance)}; } else if (type == "object") { return {make( - context, schema_context, dynamic_context, JSON::Type::Object, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::Object, + {}, SchemaCompilerTargetType::Instance)}; } else if (type == "array") { return {make( - context, schema_context, dynamic_context, JSON::Type::Array, {}, + true, context, schema_context, dynamic_context, JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "number") { return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, std::set{JSON::Type::Real, JSON::Type::Integer}, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "integer") { return {make( - context, schema_context, dynamic_context, JSON::Type::Integer, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::Integer, + {}, SchemaCompilerTargetType::Instance)}; } else if (type == "string") { return {make( - context, schema_context, dynamic_context, JSON::Type::String, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::String, + {}, SchemaCompilerTargetType::Instance)}; } else { return {}; } @@ -111,33 +111,33 @@ auto compiler_draft4_validation_type( schema_context.schema.at(dynamic_context.keyword).front().to_string()}; if (type == "null") { return {make( - context, schema_context, dynamic_context, JSON::Type::Null, {}, + true, context, schema_context, dynamic_context, JSON::Type::Null, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "boolean") { return {make( - context, schema_context, dynamic_context, JSON::Type::Boolean, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::Boolean, + {}, SchemaCompilerTargetType::Instance)}; } else if (type == "object") { return {make( - context, schema_context, dynamic_context, JSON::Type::Object, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::Object, + {}, SchemaCompilerTargetType::Instance)}; } else if (type == "array") { return {make( - context, schema_context, dynamic_context, JSON::Type::Array, {}, + true, context, schema_context, dynamic_context, JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "number") { return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, std::set{JSON::Type::Real, JSON::Type::Integer}, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "integer") { return {make( - context, schema_context, dynamic_context, JSON::Type::Integer, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::Integer, + {}, SchemaCompilerTargetType::Instance)}; } else if (type == "string") { return {make( - context, schema_context, dynamic_context, JSON::Type::String, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::String, + {}, SchemaCompilerTargetType::Instance)}; } else { return {}; } @@ -168,7 +168,7 @@ auto compiler_draft4_validation_type( assert(types.size() >= schema_context.schema.at(dynamic_context.keyword).size()); return {make( - context, schema_context, dynamic_context, std::move(types), {}, + true, context, schema_context, dynamic_context, std::move(types), {}, SchemaCompilerTargetType::Instance)}; } @@ -192,15 +192,23 @@ auto compiler_draft4_validation_required( properties.emplace(property.to_string()); } - return {make( - context, schema_context, dynamic_context, std::move(properties), - type_condition(context, schema_context, JSON::Type::Object), - SchemaCompilerTargetType::Instance)}; + if (properties.size() == 1) { + return {make( + true, context, schema_context, dynamic_context, + *(properties.cbegin()), + type_condition(context, schema_context, JSON::Type::Object), + SchemaCompilerTargetType::Instance)}; + } else { + return {make( + true, context, schema_context, dynamic_context, std::move(properties), + type_condition(context, schema_context, JSON::Type::Object), + SchemaCompilerTargetType::Instance)}; + } } else { assert( schema_context.schema.at(dynamic_context.keyword).front().is_string()); return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword).front().to_string(), type_condition(context, schema_context, JSON::Type::Object), SchemaCompilerTargetType::Instance)}; @@ -227,7 +235,7 @@ auto compiler_draft4_applicator_allof( } return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), SchemaCompilerTemplate{})}; } @@ -243,8 +251,8 @@ auto compiler_draft4_applicator_anyof_conditional_exhaustive( for (std::uint64_t index = 0; index < schema_context.schema.at(dynamic_context.keyword).size(); index++) { - disjunctors.push_back(make( - context, schema_context, relative_dynamic_context, + disjunctors.push_back(make( + false, context, schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, compile(context, schema_context, relative_dynamic_context, {static_cast(index)}), @@ -252,7 +260,7 @@ auto compiler_draft4_applicator_anyof_conditional_exhaustive( } return {make( - context, schema_context, dynamic_context, exhaustive, + true, context, schema_context, dynamic_context, exhaustive, std::move(disjunctors), SchemaCompilerTemplate{})}; } @@ -277,8 +285,8 @@ auto compiler_draft4_applicator_oneof( for (std::uint64_t index = 0; index < schema_context.schema.at(dynamic_context.keyword).size(); index++) { - disjunctors.push_back(make( - context, schema_context, relative_dynamic_context, + disjunctors.push_back(make( + false, context, schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, compile(context, schema_context, relative_dynamic_context, {static_cast(index)}), @@ -286,7 +294,7 @@ auto compiler_draft4_applicator_oneof( } return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(disjunctors), SchemaCompilerTemplate{})}; } @@ -300,26 +308,49 @@ auto compiler_draft4_applicator_properties( return {}; } + const auto loads_unevaluated_keywords = + schema_context.vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/applicator") || + schema_context.vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/unevaluated"); + SchemaCompilerTemplate children; for (auto &[key, subschema] : schema_context.schema.at(dynamic_context.keyword).as_object()) { auto substeps{compile(context, schema_context, relative_dynamic_context, {key}, {key})}; - substeps.push_back(make( - context, schema_context, relative_dynamic_context, JSON{key}, {}, - SchemaCompilerTargetType::Instance)); - children.push_back(make( - context, schema_context, relative_dynamic_context, - SchemaCompilerValueNone{}, std::move(substeps), - // TODO: As an optimization, avoid this condition if the subschema - // declares `required` and includes the given key - {make( - context, schema_context, relative_dynamic_context, key, {}, - SchemaCompilerTargetType::Instance)})); + + // We can avoid producing an annotation if we need to go fast + // and there is no other keyword that would rely on this annotation + if (context.mode != SchemaCompilerCompilationMode::Optimized || + loads_unevaluated_keywords || + schema_context.schema.defines("additionalProperties")) { + substeps.push_back(make( + true, context, schema_context, relative_dynamic_context, JSON{key}, + {}, SchemaCompilerTargetType::Instance)); + } + + // We can avoid this "defines" condition if the property is a required one + if (schema_context.schema.defines("required") && + schema_context.schema.at("required").is_array() && + schema_context.schema.at("required").contains(JSON{key})) { + // We can avoid the container too and just inline these steps + for (auto &&substep : substeps) { + children.push_back(std::move(substep)); + } + } else { + SchemaCompilerTemplate condition{make( + false, context, schema_context, relative_dynamic_context, key, {}, + SchemaCompilerTargetType::Instance)}; + children.push_back(make( + false, context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, std::move(substeps), + std::move(condition))); + } } return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), SchemaCompilerTemplate{})}; } @@ -333,6 +364,12 @@ auto compiler_draft4_applicator_patternproperties( return {}; } + const auto loads_unevaluated_keywords = + schema_context.vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/applicator") || + schema_context.vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/unevaluated"); + SchemaCompilerTemplate children; // For each regular expression and corresponding subschema in the object @@ -341,32 +378,35 @@ auto compiler_draft4_applicator_patternproperties( auto substeps{compile(context, schema_context, relative_dynamic_context, {entry.first}, {})}; - // TODO: As an optimization, only emit an annotation if - // `additionalProperties` is also declared in the same subschema - - // The evaluator will make sure the same annotation is not reported twice. - // For example, if the same property matches more than one subschema in - // `patternProperties` - substeps.push_back(make( - context, schema_context, relative_dynamic_context, - SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, - empty_pointer}, - {}, SchemaCompilerTargetType::InstanceParent)); + // We can avoid producing an annotation if we need to go fast + // and there is no other keyword that would rely on this annotation + if (context.mode != SchemaCompilerCompilationMode::Optimized || + loads_unevaluated_keywords || + schema_context.schema.defines("additionalProperties")) { + // The evaluator will make sure the same annotation is not reported twice. + // For example, if the same property matches more than one subschema in + // `patternProperties` + substeps.push_back(make( + true, context, schema_context, relative_dynamic_context, + SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, + empty_pointer}, + {}, SchemaCompilerTargetType::InstanceParent)); + } // The instance property matches the schema property regex SchemaCompilerTemplate loop_condition{make( - context, schema_context, relative_dynamic_context, + true, context, schema_context, relative_dynamic_context, SchemaCompilerValueRegex{ std::regex{entry.first, std::regex::ECMAScript}, entry.first}, {}, SchemaCompilerTargetType::InstanceBasename)}; // Loop over the instance properties children.push_back(make( - context, schema_context, relative_dynamic_context, // Treat this as an internal step - false, - {make( - context, schema_context, relative_dynamic_context, + false, context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, + {make( + false, context, schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, std::move(substeps), std::move(loop_condition))}, SchemaCompilerTemplate{})); @@ -374,14 +414,14 @@ auto compiler_draft4_applicator_patternproperties( // If the instance is an object... return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `object` already {make( - context, schema_context, relative_dynamic_context, JSON::Type::Object, - {}, SchemaCompilerTargetType::Instance)})}; + true, context, schema_context, relative_dynamic_context, + JSON::Type::Object, {}, SchemaCompilerTargetType::Instance)})}; } auto compiler_draft4_applicator_additionalproperties_conditional_annotation( @@ -393,15 +433,15 @@ auto compiler_draft4_applicator_additionalproperties_conditional_annotation( // was NOT collected as an annotation on either "properties" or // "patternProperties" SchemaCompilerTemplate conjunctions{ - make( - context, schema_context, relative_dynamic_context, + make( + false, context, schema_context, relative_dynamic_context, SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, empty_pointer}, {}, SchemaCompilerTargetType::ParentAdjacentAnnotations, Pointer{"properties"}), - make( - context, schema_context, relative_dynamic_context, + make( + false, context, schema_context, relative_dynamic_context, SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, empty_pointer}, {}, SchemaCompilerTargetType::ParentAdjacentAnnotations, @@ -413,26 +453,27 @@ auto compiler_draft4_applicator_additionalproperties_conditional_annotation( empty_pointer, empty_pointer)}; if (annotate) { - children.push_back(make( - context, schema_context, relative_dynamic_context, + children.push_back(make( + true, context, schema_context, relative_dynamic_context, SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, empty_pointer}, {}, SchemaCompilerTargetType::InstanceParent)); } - SchemaCompilerTemplate wrapper{make( - context, schema_context, relative_dynamic_context, + SchemaCompilerTemplate wrapper{make( + false, context, schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, std::move(children), {make( - context, schema_context, relative_dynamic_context, + true, context, schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, std::move(conjunctions), SchemaCompilerTemplate{})})}; return {make( - context, schema_context, dynamic_context, true, {std::move(wrapper)}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + {std::move(wrapper)}, {make( - context, schema_context, relative_dynamic_context, JSON::Type::Object, - {}, SchemaCompilerTargetType::Instance)})}; + true, context, schema_context, relative_dynamic_context, + JSON::Type::Object, {}, SchemaCompilerTargetType::Instance)})}; } auto compiler_draft4_applicator_additionalproperties( @@ -453,7 +494,7 @@ auto compiler_draft4_validation_pattern( const auto ®ex_string{ schema_context.schema.at(dynamic_context.keyword).to_string()}; return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, SchemaCompilerValueRegex{std::regex{regex_string, std::regex::ECMAScript}, regex_string}, type_condition(context, schema_context, JSON::Type::String), @@ -481,7 +522,7 @@ auto compiler_draft4_validation_format( if (format == "uri") { return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, SchemaCompilerValueStringType::URI, type_condition(context, schema_context, JSON::Type::String), SchemaCompilerTargetType::Instance)}; @@ -490,7 +531,7 @@ auto compiler_draft4_validation_format( #define COMPILE_FORMAT_REGEX(name, regular_expression) \ if (format == (name)) { \ return {make( \ - context, schema_context, dynamic_context, \ + true, context, schema_context, dynamic_context, \ SchemaCompilerValueRegex{ \ std::regex{(regular_expression), std::regex::ECMAScript}, \ (regular_expression)}, \ @@ -512,7 +553,7 @@ auto compiler_draft4_applicator_not( -> SchemaCompilerTemplate { return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer), SchemaCompilerTemplate{})}; @@ -561,48 +602,49 @@ auto compiler_draft4_applicator_items_array( // The first entry if (cursor == items_size) { if (annotate) { - subchildren.push_back(make( - context, schema_context, relative_dynamic_context, JSON{true}, - {make( - context, schema_context, relative_dynamic_context, cursor, {}, - SchemaCompilerTargetType::Instance)}, + subchildren.push_back(make( + true, context, schema_context, relative_dynamic_context, JSON{true}, + {make( + false, context, schema_context, relative_dynamic_context, + cursor, {}, SchemaCompilerTargetType::Instance)}, SchemaCompilerTargetType::Instance)); - subchildren.push_back(make( - context, schema_context, relative_dynamic_context, JSON{cursor - 1}, + subchildren.push_back(make( + true, context, schema_context, relative_dynamic_context, + JSON{cursor - 1}, {make( - context, schema_context, relative_dynamic_context, cursor, {}, - SchemaCompilerTargetType::Instance)}, + true, context, schema_context, relative_dynamic_context, cursor, + {}, SchemaCompilerTargetType::Instance)}, SchemaCompilerTargetType::Instance)); } - children.push_back(make( - context, schema_context, relative_dynamic_context, + children.push_back(make( + false, context, schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, std::move(subchildren), {make( - context, schema_context, relative_dynamic_context, cursor - 1, {}, - SchemaCompilerTargetType::Instance)})); + true, context, schema_context, relative_dynamic_context, + cursor - 1, {}, SchemaCompilerTargetType::Instance)})); } else { if (annotate) { - subchildren.push_back(make( - context, schema_context, relative_dynamic_context, JSON{cursor - 1}, - {}, SchemaCompilerTargetType::Instance)); + subchildren.push_back(make( + true, context, schema_context, relative_dynamic_context, + JSON{cursor - 1}, {}, SchemaCompilerTargetType::Instance)); } - children.push_back(make( - context, schema_context, relative_dynamic_context, + children.push_back(make( + false, context, schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, std::move(subchildren), - {make( - context, schema_context, relative_dynamic_context, cursor, {}, - SchemaCompilerTargetType::Instance)})); + {make( + false, context, schema_context, relative_dynamic_context, cursor, + {}, SchemaCompilerTargetType::Instance)})); } } return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), {make( - context, schema_context, relative_dynamic_context, JSON::Type::Array, - {}, SchemaCompilerTargetType::Instance)})}; + true, context, schema_context, relative_dynamic_context, + JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)})}; } auto compiler_draft4_applicator_items_conditional_annotation( @@ -613,23 +655,23 @@ auto compiler_draft4_applicator_items_conditional_annotation( if (is_schema(schema_context.schema.at(dynamic_context.keyword))) { SchemaCompilerTemplate children; children.push_back(make( - context, schema_context, relative_dynamic_context, + true, context, schema_context, relative_dynamic_context, SchemaCompilerValueUnsignedInteger{0}, compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer), SchemaCompilerTemplate{})); if (annotate) { - children.push_back(make( - context, schema_context, relative_dynamic_context, JSON{true}, {}, - SchemaCompilerTargetType::Instance)); + children.push_back(make( + true, context, schema_context, relative_dynamic_context, JSON{true}, + {}, SchemaCompilerTargetType::Instance)); } - return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, - std::move(children), + return {make( + false, context, schema_context, dynamic_context, + SchemaCompilerValueNone{}, std::move(children), {make( - context, schema_context, relative_dynamic_context, + true, context, schema_context, relative_dynamic_context, JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)})}; } @@ -652,28 +694,28 @@ auto compiler_draft4_applicator_additionalitems_from_cursor( const SchemaCompilerDynamicContext &dynamic_context, const std::size_t cursor, const bool annotate) -> SchemaCompilerTemplate { SchemaCompilerTemplate condition{make( - context, schema_context, relative_dynamic_context, JSON::Type::Array, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, relative_dynamic_context, + JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)}; condition.push_back(make( - context, schema_context, relative_dynamic_context, cursor, {}, + true, context, schema_context, relative_dynamic_context, cursor, {}, SchemaCompilerTargetType::Instance)); SchemaCompilerTemplate children{make( - context, schema_context, relative_dynamic_context, + true, context, schema_context, relative_dynamic_context, SchemaCompilerValueUnsignedInteger{cursor}, compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer), SchemaCompilerTemplate{})}; if (annotate) { - children.push_back(make( - context, schema_context, relative_dynamic_context, JSON{true}, {}, + children.push_back(make( + true, context, schema_context, relative_dynamic_context, JSON{true}, {}, SchemaCompilerTargetType::Instance)); } - return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, - std::move(children), std::move(condition))}; + return {make( + false, context, schema_context, dynamic_context, + SchemaCompilerValueNone{}, std::move(children), std::move(condition))}; } auto compiler_draft4_applicator_additionalitems_conditional_annotation( @@ -719,8 +761,8 @@ auto compiler_draft4_applicator_dependencies( schema_context.schema.at(dynamic_context.keyword).as_object()) { if (is_schema(entry.second)) { if (!entry.second.is_boolean() || !entry.second.to_boolean()) { - children.push_back(make( - context, schema_context, relative_dynamic_context, + children.push_back(make( + false, context, schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, compile(context, schema_context, relative_dynamic_context, {entry.first}, empty_pointer), @@ -728,8 +770,8 @@ auto compiler_draft4_applicator_dependencies( // TODO: As an optimization, avoid this condition if the subschema // declares `required` and includes the given key {make( - context, schema_context, relative_dynamic_context, entry.first, - {}, SchemaCompilerTargetType::Instance)})); + true, context, schema_context, relative_dynamic_context, + entry.first, {}, SchemaCompilerTargetType::Instance)})); } } else if (entry.second.is_array()) { std::set properties; @@ -738,20 +780,34 @@ auto compiler_draft4_applicator_dependencies( properties.emplace(property.to_string()); } - children.push_back(make( - context, schema_context, relative_dynamic_context, - std::move(properties), - // TODO: As an optimization, avoid this condition if the subschema - // declares `required` and includes the given key - {make( - context, schema_context, relative_dynamic_context, entry.first, - {}, SchemaCompilerTargetType::Instance)}, - SchemaCompilerTargetType::Instance)); + if (properties.empty()) { + continue; + } else if (properties.size() == 1) { + children.push_back(make( + false, context, schema_context, relative_dynamic_context, + *(properties.cbegin()), + // TODO: As an optimization, avoid this condition if the subschema + // declares `required` and includes the given key + {make( + true, context, schema_context, relative_dynamic_context, + entry.first, {}, SchemaCompilerTargetType::Instance)}, + SchemaCompilerTargetType::Instance)); + } else { + children.push_back(make( + false, context, schema_context, relative_dynamic_context, + std::move(properties), + // TODO: As an optimization, avoid this condition if the subschema + // declares `required` and includes the given key + {make( + true, context, schema_context, relative_dynamic_context, + entry.first, {}, SchemaCompilerTargetType::Instance)}, + SchemaCompilerTargetType::Instance)); + } } } return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), type_condition(context, schema_context, JSON::Type::Object))}; } @@ -765,7 +821,7 @@ auto compiler_draft4_validation_enum( if (schema_context.schema.at(dynamic_context.keyword).size() == 1) { return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword).front(), {}, SchemaCompilerTargetType::Instance)}; } @@ -777,7 +833,7 @@ auto compiler_draft4_validation_enum( } return {make( - context, schema_context, dynamic_context, std::move(options), + true, context, schema_context, dynamic_context, std::move(options), SchemaCompilerTemplate{}, SchemaCompilerTargetType::Instance)}; } @@ -792,7 +848,7 @@ auto compiler_draft4_validation_uniqueitems( } return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, type_condition(context, schema_context, JSON::Type::Array), SchemaCompilerTargetType::Instance)}; } @@ -809,7 +865,7 @@ auto compiler_draft4_validation_maxlength( // TODO: As an optimization, if `minLength` is set to the same number, do // a single size equality assertion return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{ static_cast( schema_context.schema.at(dynamic_context.keyword).as_integer()) + @@ -830,7 +886,7 @@ auto compiler_draft4_validation_minlength( // TODO: As an optimization, if `maxLength` is set to the same number, do // a single size equality assertion return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{ static_cast( schema_context.schema.at(dynamic_context.keyword).as_integer()) - @@ -851,7 +907,7 @@ auto compiler_draft4_validation_maxitems( // TODO: As an optimization, if `minItems` is set to the same number, do // a single size equality assertion return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{ static_cast( schema_context.schema.at(dynamic_context.keyword).as_integer()) + @@ -872,7 +928,7 @@ auto compiler_draft4_validation_minitems( // TODO: As an optimization, if `maxItems` is set to the same number, do // a single size equality assertion return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{ static_cast( schema_context.schema.at(dynamic_context.keyword).as_integer()) - @@ -893,7 +949,7 @@ auto compiler_draft4_validation_maxproperties( // TODO: As an optimization, if `minProperties` is set to the same number, do // a single size equality assertion return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{ static_cast( schema_context.schema.at(dynamic_context.keyword).as_integer()) + @@ -914,7 +970,7 @@ auto compiler_draft4_validation_minproperties( // TODO: As an optimization, if `maxProperties` is set to the same number, do // a single size equality assertion return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{ static_cast( schema_context.schema.at(dynamic_context.keyword).as_integer()) - @@ -930,17 +986,18 @@ auto compiler_draft4_validation_maximum( -> SchemaCompilerTemplate { assert(schema_context.schema.at(dynamic_context.keyword).is_number()); - // TODO: As an optimization, avoid this condition if the subschema - // declares `type` to `number` or `integer` already - SchemaCompilerTemplate condition{make( - context, schema_context, relative_dynamic_context, false, - {make( - context, schema_context, relative_dynamic_context, JSON::Type::Real, - {}, SchemaCompilerTargetType::Instance), - make( - context, schema_context, relative_dynamic_context, - JSON::Type::Integer, {}, SchemaCompilerTargetType::Instance)}, - SchemaCompilerTemplate{})}; + SchemaCompilerTemplate condition; + const auto is_numeric{ + schema_context.schema.defines("type") && + (schema_context.schema.at("type").is_string() && + (schema_context.schema.at("type").to_string() == "integer" || + schema_context.schema.at("type").to_string() == "number"))}; + if (!is_numeric) { + condition.push_back(make( + false, context, schema_context, relative_dynamic_context, + std::set{JSON::Type::Integer, JSON::Type::Real}, {}, + SchemaCompilerTargetType::Instance)); + } // TODO: As an optimization, if `minimum` is set to the same number, do // a single equality assertion @@ -950,12 +1007,12 @@ auto compiler_draft4_validation_maximum( schema_context.schema.at("exclusiveMaximum").is_boolean() && schema_context.schema.at("exclusiveMaximum").to_boolean()) { return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), std::move(condition), SchemaCompilerTargetType::Instance)}; } else { return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), std::move(condition), SchemaCompilerTargetType::Instance)}; } @@ -968,17 +1025,18 @@ auto compiler_draft4_validation_minimum( -> SchemaCompilerTemplate { assert(schema_context.schema.at(dynamic_context.keyword).is_number()); - // TODO: As an optimization, avoid this condition if the subschema - // declares `type` to `number` or `integer` already - SchemaCompilerTemplate condition{make( - context, schema_context, relative_dynamic_context, false, - {make( - context, schema_context, relative_dynamic_context, JSON::Type::Real, - {}, SchemaCompilerTargetType::Instance), - make( - context, schema_context, relative_dynamic_context, - JSON::Type::Integer, {}, SchemaCompilerTargetType::Instance)}, - SchemaCompilerTemplate{})}; + SchemaCompilerTemplate condition; + const auto is_numeric{ + schema_context.schema.defines("type") && + (schema_context.schema.at("type").is_string() && + (schema_context.schema.at("type").to_string() == "integer" || + schema_context.schema.at("type").to_string() == "number"))}; + if (!is_numeric) { + condition.push_back(make( + false, context, schema_context, relative_dynamic_context, + std::set{JSON::Type::Integer, JSON::Type::Real}, {}, + SchemaCompilerTargetType::Instance)); + } // TODO: As an optimization, if `maximum` is set to the same number, do // a single equality assertion @@ -988,12 +1046,12 @@ auto compiler_draft4_validation_minimum( schema_context.schema.at("exclusiveMinimum").is_boolean() && schema_context.schema.at("exclusiveMinimum").to_boolean()) { return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), std::move(condition), SchemaCompilerTargetType::Instance)}; } else { return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), std::move(condition), SchemaCompilerTargetType::Instance)}; } @@ -1010,17 +1068,17 @@ auto compiler_draft4_validation_multipleof( // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `number` or `integer` already SchemaCompilerTemplate condition{make( - context, schema_context, relative_dynamic_context, false, + true, context, schema_context, relative_dynamic_context, false, {make( - context, schema_context, relative_dynamic_context, JSON::Type::Real, - {}, SchemaCompilerTargetType::Instance), + true, context, schema_context, relative_dynamic_context, + JSON::Type::Real, {}, SchemaCompilerTargetType::Instance), make( - context, schema_context, relative_dynamic_context, + true, context, schema_context, relative_dynamic_context, JSON::Type::Integer, {}, SchemaCompilerTargetType::Instance)}, SchemaCompilerTemplate{})}; return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), std::move(condition), SchemaCompilerTargetType::Instance)}; } diff --git a/vendor/jsontoolkit/src/jsonschema/default_compiler_draft6.h b/vendor/jsontoolkit/src/jsonschema/default_compiler_draft6.h index 381c6e7..5c64796 100644 --- a/vendor/jsontoolkit/src/jsonschema/default_compiler_draft6.h +++ b/vendor/jsontoolkit/src/jsonschema/default_compiler_draft6.h @@ -19,33 +19,33 @@ auto compiler_draft6_validation_type( schema_context.schema.at(dynamic_context.keyword).to_string()}; if (type == "null") { return {make( - context, schema_context, dynamic_context, JSON::Type::Null, {}, + true, context, schema_context, dynamic_context, JSON::Type::Null, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "boolean") { return {make( - context, schema_context, dynamic_context, JSON::Type::Boolean, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::Boolean, + {}, SchemaCompilerTargetType::Instance)}; } else if (type == "object") { return {make( - context, schema_context, dynamic_context, JSON::Type::Object, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::Object, + {}, SchemaCompilerTargetType::Instance)}; } else if (type == "array") { return {make( - context, schema_context, dynamic_context, JSON::Type::Array, {}, + true, context, schema_context, dynamic_context, JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "number") { return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, std::set{JSON::Type::Real, JSON::Type::Integer}, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "integer") { return {make( - context, schema_context, dynamic_context, JSON::Type::Integer, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::Integer, + {}, SchemaCompilerTargetType::Instance)}; } else if (type == "string") { return {make( - context, schema_context, dynamic_context, JSON::Type::String, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::String, + {}, SchemaCompilerTargetType::Instance)}; } else { return {}; } @@ -58,33 +58,33 @@ auto compiler_draft6_validation_type( schema_context.schema.at(dynamic_context.keyword).front().to_string()}; if (type == "null") { return {make( - context, schema_context, dynamic_context, JSON::Type::Null, {}, + true, context, schema_context, dynamic_context, JSON::Type::Null, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "boolean") { return {make( - context, schema_context, dynamic_context, JSON::Type::Boolean, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::Boolean, + {}, SchemaCompilerTargetType::Instance)}; } else if (type == "object") { return {make( - context, schema_context, dynamic_context, JSON::Type::Object, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::Object, + {}, SchemaCompilerTargetType::Instance)}; } else if (type == "array") { return {make( - context, schema_context, dynamic_context, JSON::Type::Array, {}, + true, context, schema_context, dynamic_context, JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "number") { return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, std::set{JSON::Type::Real, JSON::Type::Integer}, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "integer") { return {make( - context, schema_context, dynamic_context, JSON::Type::Integer, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::Integer, + {}, SchemaCompilerTargetType::Instance)}; } else if (type == "string") { return {make( - context, schema_context, dynamic_context, JSON::Type::String, {}, - SchemaCompilerTargetType::Instance)}; + true, context, schema_context, dynamic_context, JSON::Type::String, + {}, SchemaCompilerTargetType::Instance)}; } else { return {}; } @@ -115,7 +115,7 @@ auto compiler_draft6_validation_type( assert(types.size() >= schema_context.schema.at(dynamic_context.keyword).size()); return {make( - context, schema_context, dynamic_context, std::move(types), {}, + true, context, schema_context, dynamic_context, std::move(types), {}, SchemaCompilerTargetType::Instance)}; } @@ -128,7 +128,7 @@ auto compiler_draft6_validation_const( const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), {}, SchemaCompilerTargetType::Instance)}; } @@ -143,17 +143,17 @@ auto compiler_draft6_validation_exclusivemaximum( // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `number` or `integer` already SchemaCompilerTemplate condition{make( - context, schema_context, relative_dynamic_context, false, + true, context, schema_context, relative_dynamic_context, false, {make( - context, schema_context, relative_dynamic_context, JSON::Type::Real, - {}, SchemaCompilerTargetType::Instance), + true, context, schema_context, relative_dynamic_context, + JSON::Type::Real, {}, SchemaCompilerTargetType::Instance), make( - context, schema_context, relative_dynamic_context, + true, context, schema_context, relative_dynamic_context, JSON::Type::Integer, {}, SchemaCompilerTargetType::Instance)}, SchemaCompilerTemplate{})}; return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), std::move(condition), SchemaCompilerTargetType::Instance)}; } @@ -168,17 +168,17 @@ auto compiler_draft6_validation_exclusiveminimum( // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `number` or `integer` already SchemaCompilerTemplate condition{make( - context, schema_context, relative_dynamic_context, false, + true, context, schema_context, relative_dynamic_context, false, {make( - context, schema_context, relative_dynamic_context, JSON::Type::Real, - {}, SchemaCompilerTargetType::Instance), + true, context, schema_context, relative_dynamic_context, + JSON::Type::Real, {}, SchemaCompilerTargetType::Instance), make( - context, schema_context, relative_dynamic_context, + true, context, schema_context, relative_dynamic_context, JSON::Type::Integer, {}, SchemaCompilerTargetType::Instance)}, SchemaCompilerTemplate{})}; return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), std::move(condition), SchemaCompilerTargetType::Instance)}; } @@ -189,7 +189,7 @@ auto compiler_draft6_applicator_contains( const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { return {make( - context, schema_context, dynamic_context, + true, context, schema_context, dynamic_context, SchemaCompilerValueRange{1, std::nullopt, false}, compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer), @@ -197,8 +197,8 @@ auto compiler_draft6_applicator_contains( // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `array` already {make( - context, schema_context, relative_dynamic_context, JSON::Type::Array, - {}, SchemaCompilerTargetType::Instance)})}; + true, context, schema_context, relative_dynamic_context, + JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)})}; } auto compiler_draft6_validation_propertynames( @@ -207,15 +207,15 @@ auto compiler_draft6_validation_propertynames( const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer), // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `object` already {make( - context, schema_context, relative_dynamic_context, JSON::Type::Object, - {}, SchemaCompilerTargetType::Instance)})}; + true, context, schema_context, relative_dynamic_context, + JSON::Type::Object, {}, SchemaCompilerTargetType::Instance)})}; } } // namespace internal diff --git a/vendor/jsontoolkit/src/jsonschema/default_compiler_draft7.h b/vendor/jsontoolkit/src/jsonschema/default_compiler_draft7.h index 190c023..2771a0d 100644 --- a/vendor/jsontoolkit/src/jsonschema/default_compiler_draft7.h +++ b/vendor/jsontoolkit/src/jsonschema/default_compiler_draft7.h @@ -17,11 +17,11 @@ auto compiler_draft7_applicator_if( SchemaCompilerTemplate children{compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer)}; - children.push_back(make( - context, schema_context, relative_dynamic_context, JSON{true}, {}, + children.push_back(make( + true, context, schema_context, relative_dynamic_context, JSON{true}, {}, SchemaCompilerTargetType::Instance)); return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), SchemaCompilerTemplate{})}; } @@ -40,11 +40,11 @@ auto compiler_draft7_applicator_then( SchemaCompilerTemplate children{compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer)}; - SchemaCompilerTemplate condition{make( - context, schema_context, relative_dynamic_context, JSON{true}, {}, + SchemaCompilerTemplate condition{make( + false, context, schema_context, relative_dynamic_context, JSON{true}, {}, SchemaCompilerTargetType::AdjacentAnnotations, Pointer{"if"})}; return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), std::move(condition))}; } @@ -64,11 +64,11 @@ auto compiler_draft7_applicator_else( relative_dynamic_context, empty_pointer, empty_pointer)}; SchemaCompilerTemplate condition{ - make( - context, schema_context, relative_dynamic_context, JSON{true}, {}, - SchemaCompilerTargetType::AdjacentAnnotations, Pointer{"if"})}; + make( + false, context, schema_context, relative_dynamic_context, JSON{true}, + {}, SchemaCompilerTargetType::AdjacentAnnotations, Pointer{"if"})}; return {make( - context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + true, context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), std::move(condition))}; } diff --git a/vendor/jsontoolkit/src/jsonschema/default_walker.cc b/vendor/jsontoolkit/src/jsonschema/default_walker.cc index 520af6f..516b369 100644 --- a/vendor/jsontoolkit/src/jsonschema/default_walker.cc +++ b/vendor/jsontoolkit/src/jsonschema/default_walker.cc @@ -21,7 +21,6 @@ auto sourcemeta::jsontoolkit::default_schema_walker( WALK(HTTPS_BASE "2020-12/vocab/core", "$defs", Members) WALK(HTTPS_BASE "2020-12/vocab/applicator", "prefixItems", Elements) WALK(HTTPS_BASE "2020-12/vocab/applicator", "dependentSchemas", Members) - WALK(HTTPS_BASE "2020-12/vocab/applicator", "properties", Members) WALK(HTTPS_BASE "2020-12/vocab/applicator", "patternProperties", Members) WALK(HTTPS_BASE "2020-12/vocab/applicator", "allOf", Elements) WALK(HTTPS_BASE "2020-12/vocab/applicator", "anyOf", Elements) @@ -49,11 +48,17 @@ auto sourcemeta::jsontoolkit::default_schema_walker( HTTPS_BASE "2020-12/vocab/unevaluated", "unevaluatedItems", Value, HTTPS_BASE "2020-12/vocab/applicator", "prefixItems", "items", "contains") + // For the purpose of compiler optimizations + WALK_MAYBE_DEPENDENT(HTTPS_BASE "2020-12/vocab/applicator", "properties", + Members, HTTPS_BASE "2020-12/vocab/validation", "type", + "required") + WALK(HTTPS_BASE "2020-12/vocab/validation", "maximum", None, "type") + WALK(HTTPS_BASE "2020-12/vocab/validation", "minimum", None, "type") + // 2019-09 WALK(HTTPS_BASE "2019-09/vocab/core", "$defs", Members) WALK(HTTPS_BASE "2019-09/vocab/applicator", "items", ValueOrElements) WALK(HTTPS_BASE "2019-09/vocab/applicator", "dependentSchemas", Members) - WALK(HTTPS_BASE "2019-09/vocab/applicator", "properties", Members) WALK(HTTPS_BASE "2019-09/vocab/applicator", "patternProperties", Members) WALK(HTTPS_BASE "2019-09/vocab/applicator", "allOf", Elements) WALK(HTTPS_BASE "2019-09/vocab/applicator", "anyOf", Elements) @@ -82,6 +87,13 @@ auto sourcemeta::jsontoolkit::default_schema_walker( WALK(HTTPS_BASE "2019-09/vocab/applicator", "unevaluatedItems", Value, "items", "additionalItems") + // For the purpose of compiler optimizations + WALK_MAYBE_DEPENDENT(HTTPS_BASE "2019-09/vocab/applicator", "properties", + Members, HTTPS_BASE "2019-09/vocab/validation", "type", + "required") + WALK(HTTPS_BASE "2019-09/vocab/validation", "maximum", None, "type") + WALK(HTTPS_BASE "2019-09/vocab/validation", "minimum", None, "type") + #undef HTTPS_BASE #define HTTP_BASE "http://json-schema.org/" @@ -89,7 +101,6 @@ auto sourcemeta::jsontoolkit::default_schema_walker( WALK(HTTP_BASE "draft-07/schema#", "definitions", Members, "$ref") WALK(HTTP_BASE "draft-07/schema#", "dependencies", Members, "$ref") WALK(HTTP_BASE "draft-07/schema#", "items", ValueOrElements, "$ref") - WALK(HTTP_BASE "draft-07/schema#", "properties", Members, "$ref") WALK(HTTP_BASE "draft-07/schema#", "patternProperties", Members, "$ref") WALK(HTTP_BASE "draft-07/schema#", "allOf", Elements, "$ref") WALK(HTTP_BASE "draft-07/schema#", "anyOf", Elements, "$ref") @@ -116,6 +127,12 @@ auto sourcemeta::jsontoolkit::default_schema_walker( WALK(HTTP_BASE "draft-07/schema#", "then", Value, "if") WALK(HTTP_BASE "draft-07/schema#", "else", Value, "if") + // For the purpose of compiler optimizations + WALK(HTTP_BASE "draft-07/schema#", "properties", Members, "$ref", "type", + "required") + WALK(HTTP_BASE "draft-07/schema#", "maximum", None, "$ref", "type") + WALK(HTTP_BASE "draft-07/schema#", "minimum", None, "$ref", "type") + // $ref also takes precedence over any unknown keyword if (vocabularies.contains(HTTP_BASE "draft-07/schema#") && keyword != "$ref") { @@ -126,7 +143,6 @@ auto sourcemeta::jsontoolkit::default_schema_walker( WALK(HTTP_BASE "draft-06/schema#", "definitions", Members, "$ref") WALK(HTTP_BASE "draft-06/schema#", "dependencies", Members, "$ref") WALK(HTTP_BASE "draft-06/schema#", "items", ValueOrElements, "$ref") - WALK(HTTP_BASE "draft-06/schema#", "properties", Members, "$ref") WALK(HTTP_BASE "draft-06/schema#", "patternProperties", Members, "$ref") WALK(HTTP_BASE "draft-06/schema#", "allOf", Elements, "$ref") WALK(HTTP_BASE "draft-06/schema#", "anyOf", Elements, "$ref") @@ -147,6 +163,12 @@ auto sourcemeta::jsontoolkit::default_schema_walker( WALK(HTTP_BASE "draft-06/schema#", "additionalProperties", Value, "properties", "patternProperties") + // For the purpose of compiler optimizations + WALK(HTTP_BASE "draft-06/schema#", "properties", Members, "$ref", "type", + "required") + WALK(HTTP_BASE "draft-06/schema#", "maximum", None, "$ref", "type") + WALK(HTTP_BASE "draft-06/schema#", "minimum", None, "$ref", "type") + // $ref also takes precedence over any unknown keyword if (vocabularies.contains(HTTP_BASE "draft-06/schema#") && keyword != "$ref") { @@ -172,9 +194,12 @@ auto sourcemeta::jsontoolkit::default_schema_walker( WALK(HTTP_BASE "draft-04/schema#", "minLength", None, "$ref", "type") WALK(HTTP_BASE "draft-04/schema#", "maxItems", None, "$ref", "type") WALK(HTTP_BASE "draft-04/schema#", "minItems", None, "$ref", "type") + WALK(HTTP_BASE "draft-04/schema#", "maximum", None, "$ref", "type") + WALK(HTTP_BASE "draft-04/schema#", "minimum", None, "$ref", "type") WALK(HTTP_BASE "draft-04/schema#", "maxProperties", None, "$ref", "type") WALK(HTTP_BASE "draft-04/schema#", "minProperties", None, "$ref", "type") - WALK(HTTP_BASE "draft-04/schema#", "properties", Members, "$ref", "type") + WALK(HTTP_BASE "draft-04/schema#", "properties", Members, "$ref", "type", + "required") WALK_MAYBE_DEPENDENT(HTTP_BASE "draft-04/hyper-schema#", "targetSchema", Value, HTTP_BASE "draft-04/schema#", "$ref") diff --git a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_compile.h b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_compile.h index 7af8008..2b4e80c 100644 --- a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_compile.h +++ b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_compile.h @@ -210,9 +210,30 @@ struct SchemaCompilerAssertionDivisible; /// certain type struct SchemaCompilerAssertionStringType; +/// @ingroup jsonschema +/// Represents a compiler assertion step that checks a given array, object, or +/// string has a certain number of items, properties, or characters, +/// respectively +struct SchemaCompilerAssertionSizeEqual; + /// @ingroup jsonschema /// Represents a compiler step that emits an annotation -struct SchemaCompilerAnnotationPublic; +struct SchemaCompilerAnnotationEmit; + +/// @ingroup jsonschema +/// Represents a compiler assertion step that checks a certain +/// annotation was produced +struct SchemaCompilerAssertionAnnotation; + +/// @ingroup jsonschema +/// Represents a compiler assertion step that checks a certain +/// annotation was not produced at an adjacent location +struct SchemaCompilerAssertionNoAdjacentAnnotation; + +/// @ingroup jsonschema +/// Represents a compiler assertion step that checks a certain +/// annotation was not produced independently of the schema location +struct SchemaCompilerAssertionNoAnnotation; /// @ingroup jsonschema /// Represents a compiler logical step that represents a disjunction @@ -235,36 +256,6 @@ struct SchemaCompilerLogicalTry; /// Represents a compiler logical step that represents a negation struct SchemaCompilerLogicalNot; -/// @ingroup jsonschema -/// Represents a compiler assertion step that checks a given array, object, or -/// string has a certain number of items, properties, or characters, -/// respectively -struct SchemaCompilerInternalSizeEqual; - -/// @ingroup jsonschema -/// Represents a hidden compiler assertion step that checks a certain -/// annotation was produced -struct SchemaCompilerInternalAnnotation; - -/// @ingroup jsonschema -/// Represents a hidden compiler assertion step that checks a certain -/// annotation was not produced at an adjacent location -struct SchemaCompilerInternalNoAdjacentAnnotation; - -/// @ingroup jsonschema -/// Represents a hidden compiler assertion step that checks a certain -/// annotation was not produced independently of the schema location -struct SchemaCompilerInternalNoAnnotation; - -/// @ingroup jsonschema -/// Represents a hidden conjunction compiler step -struct SchemaCompilerInternalContainer; - -/// @ingroup jsonschema -/// Represents a hidden compiler assertion step that checks if an object defines -/// a set of properties -struct SchemaCompilerInternalDefinesAll; - /// @ingroup jsonschema /// Represents a compiler step that loops over object properties struct SchemaCompilerLoopProperties; @@ -318,17 +309,17 @@ using SchemaCompilerTemplate = std::vector>; + SchemaCompilerLoopProperties, SchemaCompilerLoopKeys, + SchemaCompilerLoopItems, SchemaCompilerLoopItemsFromAnnotationIndex, + SchemaCompilerLoopContains, SchemaCompilerControlLabel, + SchemaCompilerControlMark, SchemaCompilerControlJump, + SchemaCompilerControlDynamicAnchorJump>>; #if !defined(DOXYGEN) #define DEFINE_STEP_WITH_VALUE(category, name, type) \ @@ -339,6 +330,7 @@ using SchemaCompilerTemplate = std::vector value; \ const SchemaCompilerTemplate condition; \ }; @@ -351,6 +343,7 @@ using SchemaCompilerTemplate = std::vector value; \ const SchemaCompilerTemplate condition; \ const data_type data; \ @@ -364,6 +357,7 @@ using SchemaCompilerTemplate = std::vector value; \ const SchemaCompilerTemplate children; \ const SchemaCompilerTemplate condition; \ @@ -376,6 +370,7 @@ using SchemaCompilerTemplate = std::vector &default_dialect = std::nullopt) -> SchemaCompilerTemplate; diff --git a/vendor/jsontoolkit/src/jsonschema/trace.h b/vendor/jsontoolkit/src/jsonschema/trace.h new file mode 100644 index 0000000..90ff006 --- /dev/null +++ b/vendor/jsontoolkit/src/jsonschema/trace.h @@ -0,0 +1,30 @@ +#ifndef SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_TRACE_H_ +#define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_TRACE_H_ + +// We only perform tracing on debugging builds, at least for now +#if !defined(NDEBUG) && defined(__APPLE__) + +#include +#include + +// See +// https://www.jviotti.com/2022/02/21/emitting-signposts-to-instruments-on-macos-using-cpp.html + +static os_log_t log_handle = os_log_create("com.sourcemeta.jsontoolkit", + OS_LOG_CATEGORY_POINTS_OF_INTEREST); + +#define SOURCEMETA_TRACE_REGISTER_ID(name) \ + const os_signpost_id_t name = os_signpost_id_generate(log_handle); \ + assert((name) != OS_SIGNPOST_ID_INVALID); +#define SOURCEMETA_TRACE_START(id, title) \ + os_signpost_interval_begin(log_handle, id, title); +#define SOURCEMETA_TRACE_END(id, title) \ + os_signpost_interval_end(log_handle, id, title); + +#else +#define SOURCEMETA_TRACE_REGISTER_ID(name) +#define SOURCEMETA_TRACE_START(id, title) +#define SOURCEMETA_TRACE_END(id, title) +#endif + +#endif