diff --git a/DEPENDENCIES b/DEPENDENCIES index 47a1192bc..ff32fd886 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,3 +1,3 @@ vendorpull https://github.com/sourcemeta/vendorpull dea311b5bfb53b6926a4140267959ae334d3ecf4 -core https://github.com/sourcemeta/core 89c1394849a488667f03ec5ee1844326570f486d +core https://github.com/sourcemeta/core 78c8548ce280f2bf88a1f21eb509046ac54f1532 jsonschema-test-suite https://github.com/json-schema-org/JSON-Schema-Test-Suite 4ba013d58e747ecaf48c8bb7cf248cb0d564afbc diff --git a/src/compiler/compile.cc b/src/compiler/compile.cc index 2ed0e71c9..245ec8f38 100644 --- a/src/compiler/compile.cc +++ b/src/compiler/compile.cc @@ -103,20 +103,11 @@ namespace sourcemeta::blaze { auto compile(const sourcemeta::core::JSON &schema, const sourcemeta::core::SchemaWalker &walker, const sourcemeta::core::SchemaResolver &resolver, - const Compiler &compiler, const Mode mode, + const Compiler &compiler, + const sourcemeta::core::SchemaFrame &frame, const Mode mode, const std::optional &default_dialect) -> Template { assert(is_schema(schema)); - // Make sure the input schema is bundled, otherwise we won't be able to - // resolve remote references here - const sourcemeta::core::JSON result{ - sourcemeta::core::bundle(schema, walker, resolver, default_dialect)}; - - // Perform framing to resolve references later on - sourcemeta::core::SchemaFrame frame{ - sourcemeta::core::SchemaFrame::Mode::References}; - frame.analyse(result, walker, resolver, default_dialect); - const std::string base{sourcemeta::core::URI{ sourcemeta::core::identify( schema, resolver, @@ -144,7 +135,7 @@ auto compile(const sourcemeta::core::JSON &schema, SchemaContext schema_context{ sourcemeta::core::empty_pointer, - result, + schema, vocabularies(schema, resolver, root_frame_entry.dialect), sourcemeta::core::URI{root_frame_entry.base}.canonicalize().recompose(), {}, @@ -207,10 +198,10 @@ auto compile(const sourcemeta::core::JSON &schema, } auto unevaluated{ - sourcemeta::core::unevaluated(result, frame, walker, resolver)}; + sourcemeta::core::unevaluated(schema, frame, walker, resolver)}; - const Context context{result, - std::move(frame), + const Context context{schema, + frame, std::move(resources), walker, resolver, @@ -275,6 +266,27 @@ auto compile(const sourcemeta::core::JSON &schema, } } +auto compile(const sourcemeta::core::JSON &schema, + const sourcemeta::core::SchemaWalker &walker, + const sourcemeta::core::SchemaResolver &resolver, + const Compiler &compiler, const Mode mode, + const std::optional &default_dialect) -> Template { + assert(is_schema(schema)); + + // Make sure the input schema is bundled, otherwise we won't be able to + // resolve remote references here + const sourcemeta::core::JSON result{ + sourcemeta::core::bundle(schema, walker, resolver, default_dialect)}; + + // Perform framing to resolve references later on + sourcemeta::core::SchemaFrame frame{ + sourcemeta::core::SchemaFrame::Mode::References}; + frame.analyse(result, walker, resolver, default_dialect); + + return compile(result, walker, resolver, compiler, frame, mode, + default_dialect); +} + auto compile(const Context &context, const SchemaContext &schema_context, const DynamicContext &dynamic_context, const sourcemeta::core::Pointer &schema_suffix, diff --git a/src/compiler/include/sourcemeta/blaze/compiler.h b/src/compiler/include/sourcemeta/blaze/compiler.h index 368408b2e..433397285 100644 --- a/src/compiler/include/sourcemeta/blaze/compiler.h +++ b/src/compiler/include/sourcemeta/blaze/compiler.h @@ -148,6 +148,24 @@ compile(const sourcemeta::core::JSON &schema, const std::optional &default_dialect = std::nullopt) -> Template; +/// @ingroup compiler +/// +/// This function compiles an input JSON Schema into a template that can be +/// later evaluated, but given an existing schema frame. The schema frame must +/// contain reference information for the given schema and the input schema must +/// be bundled. If those pre-conditions are not met, you will hit undefined +/// behavior. +/// +/// Don't use this function unless you know what you are doing. +auto SOURCEMETA_BLAZE_COMPILER_EXPORT +compile(const sourcemeta::core::JSON &schema, + const sourcemeta::core::SchemaWalker &walker, + const sourcemeta::core::SchemaResolver &resolver, + const Compiler &compiler, const sourcemeta::core::SchemaFrame &frame, + const Mode mode = Mode::FastValidation, + const std::optional &default_dialect = std::nullopt) + -> Template; + /// @ingroup compiler /// /// This function compiles a single subschema into a compiler template as diff --git a/test/evaluator/evaluator_test.cc b/test/evaluator/evaluator_test.cc index 1c15d62c1..934d28bf8 100644 --- a/test/evaluator/evaluator_test.cc +++ b/test/evaluator/evaluator_test.cc @@ -126,6 +126,51 @@ TEST(Evaluator, cross_2012_12_ref_2019_09_without_id) { EXPECT_TRUE(evaluator.validate(compiled_schema, instance)); } +TEST(Evaluator, explicit_frame) { + const sourcemeta::core::JSON schema{sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "allOf": [ { "$ref": "https://json-schema.org/draft/2020-12/schema" } ] + })JSON")}; + + const sourcemeta::core::JSON result{ + sourcemeta::core::bundle(schema, sourcemeta::core::schema_official_walker, + sourcemeta::core::schema_official_resolver)}; + sourcemeta::core::SchemaFrame frame{ + sourcemeta::core::SchemaFrame::Mode::References}; + frame.analyse(result, sourcemeta::core::schema_official_walker, + sourcemeta::core::schema_official_resolver); + + const auto compiled_schema{sourcemeta::blaze::compile( + result, sourcemeta::core::schema_official_walker, + sourcemeta::core::schema_official_resolver, + sourcemeta::blaze::default_schema_compiler, frame)}; + + sourcemeta::blaze::Evaluator evaluator; + const sourcemeta::core::JSON instance{true}; + EXPECT_TRUE(evaluator.validate(compiled_schema, instance)); +} + +TEST(Evaluator, explicit_frame_locations_only) { + const sourcemeta::core::JSON schema{sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "allOf": [ { "$ref": "https://json-schema.org/draft/2020-12/schema" } ] + })JSON")}; + + const sourcemeta::core::JSON result{ + sourcemeta::core::bundle(schema, sourcemeta::core::schema_official_walker, + sourcemeta::core::schema_official_resolver)}; + sourcemeta::core::SchemaFrame frame{ + sourcemeta::core::SchemaFrame::Mode::Locations}; + frame.analyse(result, sourcemeta::core::schema_official_walker, + sourcemeta::core::schema_official_resolver); + + EXPECT_THROW(sourcemeta::blaze::compile( + result, sourcemeta::core::schema_official_walker, + sourcemeta::core::schema_official_resolver, + sourcemeta::blaze::default_schema_compiler, frame), + sourcemeta::core::SchemaReferenceError); +} + TEST(Evaluator, is_annotation) { EXPECT_TRUE(sourcemeta::blaze::is_annotation( sourcemeta::blaze::InstructionIndex::AnnotationBasenameToParent)); diff --git a/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_frame.h b/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_frame.h index 1a78270f1..ffde19ccb 100644 --- a/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_frame.h +++ b/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_frame.h @@ -110,6 +110,9 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame { SchemaFrame(const Mode mode) : mode_{mode} {} + // Query the current mode that the schema frame was configured with + auto mode() const noexcept -> Mode { return this->mode_; } + /// A single entry in a JSON Schema reference map struct ReferencesEntry { std::string destination;