Skip to content

Commit 376f397

Browse files
authored
Use WeakPointer to index frame references (#2183)
Signed-off-by: Juan Cruz Viotti <[email protected]>
1 parent 5b226d2 commit 376f397

File tree

9 files changed

+110
-81
lines changed

9 files changed

+110
-81
lines changed

src/core/jsonschema/bundle.cc

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010

1111
namespace {
1212

13-
auto is_official_metaschema_reference(const sourcemeta::core::Pointer &pointer,
14-
const std::string &destination) -> bool {
13+
auto is_official_metaschema_reference(
14+
const sourcemeta::core::WeakPointer &pointer,
15+
const std::string &destination) -> bool {
1516
assert(!pointer.empty());
1617
assert(pointer.back().is_property());
1718
return pointer.back().to_property() == "$schema" &&
@@ -47,7 +48,8 @@ auto dependencies_internal(const sourcemeta::core::JSON &schema,
4748

4849
if (reference.base.empty()) {
4950
throw sourcemeta::core::SchemaReferenceError(
50-
reference.destination, pointer, "Could not resolve schema reference");
51+
reference.destination, sourcemeta::core::to_pointer(pointer),
52+
"Could not resolve schema reference");
5153
}
5254

5355
// To not infinitely loop on circular references
@@ -59,7 +61,8 @@ auto dependencies_internal(const sourcemeta::core::JSON &schema,
5961
// find the base, then we are facing an unresolved fragment
6062
if (frame.traverse(reference.base).has_value()) {
6163
throw sourcemeta::core::SchemaReferenceError(
62-
reference.destination, pointer, "Could not resolve schema reference");
64+
reference.destination, sourcemeta::core::to_pointer(pointer),
65+
"Could not resolve schema reference");
6366
}
6467

6568
assert(!reference.base.empty());
@@ -72,14 +75,16 @@ auto dependencies_internal(const sourcemeta::core::JSON &schema,
7275

7376
if (!sourcemeta::core::is_schema(remote.value())) {
7477
throw sourcemeta::core::SchemaReferenceError(
75-
identifier, pointer, "The JSON document is not a valid JSON Schema");
78+
identifier, sourcemeta::core::to_pointer(pointer),
79+
"The JSON document is not a valid JSON Schema");
7680
}
7781

7882
const auto remote_base_dialect{sourcemeta::core::base_dialect(
7983
remote.value(), resolver, default_dialect)};
8084
if (!remote_base_dialect.has_value()) {
8185
throw sourcemeta::core::SchemaReferenceError(
82-
identifier, pointer, "The JSON document is not a valid JSON Schema");
86+
identifier, sourcemeta::core::to_pointer(pointer),
87+
"The JSON document is not a valid JSON Schema");
8388
}
8489

8590
callback(origin, pointer, identifier, remote.value());
@@ -161,12 +166,14 @@ auto bundle_schema(sourcemeta::core::JSON &root,
161166
// find base, then we are facing an unresolved fragment
162167
if (!reference.base.empty() && frame.traverse(reference.base).has_value()) {
163168
throw sourcemeta::core::SchemaReferenceError(
164-
reference.destination, pointer, "Could not resolve schema reference");
169+
reference.destination, sourcemeta::core::to_pointer(pointer),
170+
"Could not resolve schema reference");
165171
}
166172

167173
if (reference.base.empty()) {
168174
throw sourcemeta::core::SchemaReferenceError(
169-
reference.destination, pointer, "Could not resolve schema reference");
175+
reference.destination, sourcemeta::core::to_pointer(pointer),
176+
"Could not resolve schema reference");
170177
}
171178

172179
assert(!reference.base.empty());
@@ -182,7 +189,7 @@ auto bundle_schema(sourcemeta::core::JSON &root,
182189
if (!remote.has_value()) {
183190
if (frame.traverse(identifier).has_value()) {
184191
throw sourcemeta::core::SchemaReferenceError(
185-
reference.destination, pointer,
192+
reference.destination, sourcemeta::core::to_pointer(pointer),
186193
"Could not resolve schema reference");
187194
}
188195

@@ -192,14 +199,16 @@ auto bundle_schema(sourcemeta::core::JSON &root,
192199

193200
if (!sourcemeta::core::is_schema(remote.value())) {
194201
throw sourcemeta::core::SchemaReferenceError(
195-
identifier, pointer, "The JSON document is not a valid JSON Schema");
202+
identifier, sourcemeta::core::to_pointer(pointer),
203+
"The JSON document is not a valid JSON Schema");
196204
}
197205

198206
const auto remote_base_dialect{sourcemeta::core::base_dialect(
199207
remote.value(), resolver, default_dialect)};
200208
if (!remote_base_dialect.has_value()) {
201209
throw sourcemeta::core::SchemaReferenceError(
202-
identifier, pointer, "The JSON document is not a valid JSON Schema");
210+
identifier, sourcemeta::core::to_pointer(pointer),
211+
"The JSON document is not a valid JSON Schema");
203212
}
204213

205214
// If the reference has a fragment, verify it exists in the remote
@@ -213,7 +222,7 @@ auto bundle_schema(sourcemeta::core::JSON &root,
213222
identifier);
214223
if (!remote_frame.traverse(reference.destination).has_value()) {
215224
throw sourcemeta::core::SchemaReferenceError(
216-
reference.destination, pointer,
225+
reference.destination, sourcemeta::core::to_pointer(pointer),
217226
"Could not resolve schema reference");
218227
}
219228
}

src/core/jsonschema/frame.cc

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313

1414
enum class AnchorType : std::uint8_t { Static, Dynamic, All };
1515

16+
// Static keyword strings for reference pointers
17+
static const std::string KEYWORD_SCHEMA{"$schema"};
18+
static const std::string KEYWORD_REF{"$ref"};
19+
static const std::string KEYWORD_RECURSIVE_REF{"$recursiveRef"};
20+
static const std::string KEYWORD_DYNAMIC_REF{"$dynamicRef"};
21+
1622
namespace {
1723

1824
auto find_anchors(const sourcemeta::core::JSON &schema,
@@ -400,8 +406,8 @@ auto SchemaFrame::to_json(
400406

401407
if (tracker.has_value()) {
402408
entry.assign_assume_new("position",
403-
sourcemeta::core::to_json(
404-
tracker.value().get(reference.first.second)));
409+
sourcemeta::core::to_json(tracker.value().get(
410+
to_pointer(reference.first.second))));
405411
} else {
406412
entry.assign_assume_new("position", sourcemeta::core::to_json(nullptr));
407413
}
@@ -527,7 +533,6 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker,
527533
for (const auto &entry_index : current_subschema_entries) {
528534
const auto &entry{subschema_entries[entry_index]};
529535
const auto &common_pointer_weak{entry.common.pointer};
530-
const auto common_pointer{to_pointer(common_pointer_weak)};
531536
const auto &common_parent{entry.common.parent};
532537
if (entry.id.has_value()) {
533538
assert(entry.common.base_dialect.has_value());
@@ -618,8 +623,10 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker,
618623

619624
metaschema.canonicalize();
620625
assert(entry.common.subschema.get().defines("$schema"));
626+
auto schema_pointer{common_pointer_weak};
627+
schema_pointer.push_back(std::cref(KEYWORD_SCHEMA));
621628
const auto [it, inserted] = this->references_.insert_or_assign(
622-
{SchemaReferenceType::Static, common_pointer.concat({"$schema"})},
629+
{SchemaReferenceType::Static, std::move(schema_pointer)},
623630
SchemaFrame::ReferencesEntry{.original = maybe_metaschema,
624631
.destination =
625632
metaschema.recompose(),
@@ -809,7 +816,6 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker,
809816
// Resolve references after all framing was performed
810817
for (const auto &entry : subschema_entries) {
811818
const auto &common_pointer_weak{entry.common.pointer};
812-
const auto common_pointer{to_pointer(common_pointer_weak)};
813819
if (entry.common.subschema.get().is_object()) {
814820
const auto nearest_bases{find_nearest_bases(
815821
base_uris, common_pointer_weak,
@@ -825,8 +831,10 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker,
825831
}
826832

827833
ref.canonicalize();
834+
auto ref_pointer{common_pointer_weak};
835+
ref_pointer.push_back(std::cref(KEYWORD_REF));
828836
const auto [it, inserted] = this->references_.insert_or_assign(
829-
{SchemaReferenceType::Static, common_pointer.concat({"$ref"})},
837+
{SchemaReferenceType::Static, std::move(ref_pointer)},
830838
SchemaFrame::ReferencesEntry{.original = original,
831839
.destination = ref.recompose(),
832840
.base = std::string_view{},
@@ -848,7 +856,8 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker,
848856
// https://json-schema.org/draft/2019-09/draft-handrews-json-schema-02#rfc.section.8.2.4.2.1
849857
if (ref != "#") {
850858
throw sourcemeta::core::SchemaReferenceError(
851-
entry.id.value_or(""), common_pointer.concat({"$recursiveRef"}),
859+
entry.id.value_or(""),
860+
to_pointer(common_pointer_weak).concat({"$recursiveRef"}),
852861
"Invalid recursive reference");
853862
}
854863

@@ -860,8 +869,10 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker,
860869
? SchemaReferenceType::Static
861870
: SchemaReferenceType::Dynamic};
862871
const sourcemeta::core::URI anchor_uri{anchor_uri_string};
872+
auto recursive_ref_pointer{common_pointer_weak};
873+
recursive_ref_pointer.push_back(std::cref(KEYWORD_RECURSIVE_REF));
863874
const auto [it, inserted] = this->references_.insert_or_assign(
864-
{reference_type, common_pointer.concat({"$recursiveRef"})},
875+
{reference_type, std::move(recursive_ref_pointer)},
865876
SchemaFrame::ReferencesEntry{.original = ref,
866877
.destination = anchor_uri.recompose(),
867878
.base = std::string_view{},
@@ -896,10 +907,12 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker,
896907
!has_fragment ||
897908
(has_fragment && maybe_static_frame != this->locations_.end() &&
898909
maybe_dynamic_frame == this->locations_.end())};
910+
auto dynamic_ref_pointer{common_pointer_weak};
911+
dynamic_ref_pointer.push_back(std::cref(KEYWORD_DYNAMIC_REF));
899912
const auto [it, inserted] = this->references_.insert_or_assign(
900913
{behaves_as_static ? SchemaReferenceType::Static
901914
: SchemaReferenceType::Dynamic,
902-
common_pointer.concat({"$dynamicRef"})},
915+
std::move(dynamic_ref_pointer)},
903916
SchemaFrame::ReferencesEntry{.original = original,
904917
.destination = std::move(ref_string),
905918
.base = std::string_view{},
@@ -981,7 +994,7 @@ auto SchemaFrame::references() const noexcept -> const References & {
981994
auto SchemaFrame::reference(const SchemaReferenceType type,
982995
const WeakPointer &pointer) const
983996
-> std::optional<std::reference_wrapper<const ReferencesEntry>> {
984-
const auto result{this->references_.find({type, to_pointer(pointer)})};
997+
const auto result{this->references_.find({type, pointer})};
985998
if (result != this->references_.cend()) {
986999
return result->second;
9871000
}
@@ -1086,7 +1099,7 @@ auto SchemaFrame::dereference(const Location &location,
10861099
-> std::pair<SchemaReferenceType,
10871100
std::optional<std::reference_wrapper<const Location>>> {
10881101
const auto effective_location{
1089-
to_pointer(location.pointer.concat(relative_schema_location))};
1102+
location.pointer.concat(relative_schema_location)};
10901103
const auto maybe_reference_entry{this->references_.find(
10911104
{SchemaReferenceType::Static, effective_location})};
10921105
if (maybe_reference_entry == this->references_.cend()) {
@@ -1118,7 +1131,7 @@ auto SchemaFrame::for_each_resource_uri(
11181131
}
11191132

11201133
auto SchemaFrame::for_each_unresolved_reference(
1121-
const std::function<void(const Pointer &, const ReferencesEntry &)>
1134+
const std::function<void(const WeakPointer &, const ReferencesEntry &)>
11221135
&callback) const -> void {
11231136
for (const auto &[key, reference] : this->references_) {
11241137
if (!this->traverse(reference.destination).has_value()) {

src/core/jsonschema/include/sourcemeta/core/jsonschema_bundle.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ namespace sourcemeta::core {
2424
/// - Pointer (reference keyword from the origin)
2525
/// - Target URI
2626
/// - Target schema
27-
using DependencyCallback = std::function<void(std::string_view, const Pointer &,
28-
std::string_view, const JSON &)>;
27+
using DependencyCallback = std::function<void(
28+
std::string_view, const WeakPointer &, std::string_view, const JSON &)>;
2929

3030
/// @ingroup jsonschema
3131
///

src/core/jsonschema/include/sourcemeta/core/jsonschema_frame.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,8 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame {
8282
/// The reference type is part of the key as it is possible to
8383
/// have a static and a dynamic reference to the same location
8484
/// on the same schema object.
85-
using References = std::map<std::pair<SchemaReferenceType,
86-
// TODO: Turn this into a weak pointer
87-
// or reference to the location pointer?
88-
Pointer>,
89-
ReferencesEntry>;
85+
using References =
86+
std::map<std::pair<SchemaReferenceType, WeakPointer>, ReferencesEntry>;
9087

9188
#if defined(__GNUC__)
9289
#pragma GCC diagnostic push
@@ -204,7 +201,7 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame {
204201
/// Iterate over all unresolved references (where destination cannot be
205202
/// traversed)
206203
auto for_each_unresolved_reference(
207-
const std::function<void(const Pointer &, const ReferencesEntry &)>
204+
const std::function<void(const WeakPointer &, const ReferencesEntry &)>
208205
&callback) const -> void;
209206

210207
/// Check if there are any references to a given location pointer

src/core/jsonschema/transformer.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,8 @@ auto SchemaTransformer::apply(JSON &schema, const SchemaWalker &walker,
220220

221221
const auto &target{destination.value().get()};
222222
potentially_broken_references.push_back(
223-
{reference.first.second, JSON::String{reference.second.original},
223+
{to_pointer(reference.first.second),
224+
JSON::String{reference.second.original},
224225
reference.second.destination, to_pointer(target.pointer),
225226
target.relative_pointer});
226227
}

src/extension/alterschema/common/orphan_definitions.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,15 @@ class OrphanDefinitions final : public SchemaTransformRule {
3737
const auto destination_location{frame.traverse(reference.destination)};
3838
if (destination_location.has_value()) {
3939
const auto &destination_pointer{destination_location->get().pointer};
40-
const auto source_pointer{to_weak_pointer(key.second)};
4140
if (has_defs) {
42-
process_reference(source_pointer, destination_pointer,
43-
location.pointer, "$defs", has_external_to_defs,
41+
process_reference(key.second, destination_pointer, location.pointer,
42+
"$defs", has_external_to_defs,
4443
outside_referenced_defs);
4544
}
4645

4746
if (has_definitions) {
48-
process_reference(source_pointer, destination_pointer,
49-
location.pointer, "definitions",
50-
has_external_to_definitions,
47+
process_reference(key.second, destination_pointer, location.pointer,
48+
"definitions", has_external_to_definitions,
5149
outside_referenced_definitions);
5250
}
5351
}

src/extension/editorschema/editorschema.cc

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,26 +94,27 @@ auto for_editor(JSON &schema, const SchemaWalker &walker,
9494
if (key.first == SchemaReferenceType::Dynamic) {
9595
if (reference.fragment.has_value()) {
9696
const auto destination{top_dynamic_anchor_location(
97-
frame, to_weak_pointer(key.second), reference.fragment.value(),
97+
frame, key.second, reference.fragment.value(),
9898
reference.destination)};
9999
if (!destination.has_value()) {
100100
continue;
101101
}
102102

103103
reference_changes.push_back(
104-
{key.second, to_uri(destination.value().get()).recompose(),
105-
keyword, true});
104+
{to_pointer(key.second),
105+
to_uri(destination.value().get()).recompose(), keyword, true});
106106
} else {
107-
reference_changes.push_back({key.second, "", keyword, true});
107+
reference_changes.push_back(
108+
{to_pointer(key.second), "", keyword, true});
108109
}
109110
} else {
110111
if (keyword == "$schema") {
111-
const auto uri{frame.uri(to_weak_pointer(key.second))};
112+
const auto uri{frame.uri(key.second)};
112113
assert(uri.has_value());
113114
const auto origin{frame.traverse(uri.value().get())};
114115
assert(origin.has_value());
115116
reference_changes.push_back(
116-
{key.second,
117+
{to_pointer(key.second),
117118
JSON::String{to_string(origin.value().get().base_dialect)},
118119
keyword, false});
119120
continue;
@@ -124,11 +125,12 @@ auto for_editor(JSON &schema, const SchemaWalker &walker,
124125
const bool should_rename =
125126
keyword == "$dynamicRef" || keyword == "$recursiveRef";
126127
reference_changes.push_back(
127-
{key.second, to_uri(result.value().get().pointer).recompose(),
128-
keyword, should_rename});
128+
{to_pointer(key.second),
129+
to_uri(result.value().get().pointer).recompose(), keyword,
130+
should_rename});
129131
} else {
130132
reference_changes.push_back(
131-
{key.second, reference.destination, keyword, false});
133+
{to_pointer(key.second), reference.destination, keyword, false});
132134
}
133135
}
134136
}

0 commit comments

Comments
 (0)