Filename | /Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern/Vocabulary/Core.pm |
Statements | Executed 812582 statements in 1.70s |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
39121 | 1 | 1 | 799ms | 99.6s | _eval_keyword_ref (recurses: max depth 31, inclusive time 986s) | JSON::Schema::Modern::Vocabulary::Core::
25862 | 1 | 1 | 777ms | 2.58s | _eval_keyword_id | JSON::Schema::Modern::Vocabulary::Core::
2586 | 1 | 1 | 138ms | 70.9s | _eval_keyword_dynamicRef (recurses: max depth 6, inclusive time 79.9s) | JSON::Schema::Modern::Vocabulary::Core::
181034 | 1 | 1 | 89.6ms | 89.6ms | CORE:subst (opcode) | JSON::Schema::Modern::Vocabulary::Core::
732 | 2 | 2 | 5.20ms | 209ms | _traverse_keyword_ref | JSON::Schema::Modern::Vocabulary::Core::
11 | 1 | 1 | 668µs | 6.33ms | _traverse_keyword_vocabulary | JSON::Schema::Modern::Vocabulary::Core::
118 | 1 | 1 | 583µs | 2.36ms | _traverse_keyword_comment | JSON::Schema::Modern::Vocabulary::Core::
14 | 1 | 1 | 486µs | 6.17ms | _traverse_keyword_id | JSON::Schema::Modern::Vocabulary::Core::
15 | 2 | 2 | 474µs | 5.40ms | _traverse_keyword_anchor | JSON::Schema::Modern::Vocabulary::Core::
15 | 1 | 1 | 334µs | 5.66ms | _traverse_keyword_schema | JSON::Schema::Modern::Vocabulary::Core::
14 | 1 | 1 | 205µs | 5.04ms | _traverse_keyword_dynamicAnchor | JSON::Schema::Modern::Vocabulary::Core::
13 | 3 | 1 | 124µs | 141µs | CORE:sort (opcode) | JSON::Schema::Modern::Vocabulary::Core::
12 | 1 | 1 | 113µs | 90.4ms | _traverse_keyword_defs (recurses: max depth 1, inclusive time 9.35ms) | JSON::Schema::Modern::Vocabulary::Core::
22 | 1 | 1 | 104µs | 4.81ms | _traverse_keyword_dynamicRef | JSON::Schema::Modern::Vocabulary::Core::
1 | 1 | 1 | 100µs | 588µs | __fetch_vocabulary_data | JSON::Schema::Modern::Vocabulary::Core::
30 | 1 | 1 | 98µs | 98µs | CORE:match (opcode) | JSON::Schema::Modern::Vocabulary::Core::
1 | 1 | 1 | 81µs | 92µs | BEGIN@1 | Module::Runtime::
1 | 1 | 1 | 21µs | 1.42ms | BEGIN@11 | JSON::Schema::Modern::Vocabulary::Core::
1 | 1 | 1 | 21µs | 50µs | BEGIN@13 | JSON::Schema::Modern::Vocabulary::Core::
1 | 1 | 1 | 21µs | 21µs | BEGIN@9 | JSON::Schema::Modern::Vocabulary::Core::
1 | 1 | 1 | 18µs | 33µs | BEGIN@15 | JSON::Schema::Modern::Vocabulary::Core::
2 | 2 | 1 | 15µs | 15µs | keywords | JSON::Schema::Modern::Vocabulary::Core::
1 | 1 | 1 | 14µs | 178µs | BEGIN@12 | JSON::Schema::Modern::Vocabulary::Core::
1 | 1 | 1 | 13µs | 29µs | BEGIN@14 | JSON::Schema::Modern::Vocabulary::Core::
1 | 1 | 1 | 12µs | 75µs | BEGIN@17 | JSON::Schema::Modern::Vocabulary::Core::
1 | 1 | 1 | 10µs | 353µs | BEGIN@10 | JSON::Schema::Modern::Vocabulary::Core::
1 | 1 | 1 | 9µs | 26µs | BEGIN@16 | JSON::Schema::Modern::Vocabulary::Core::
1 | 1 | 1 | 8µs | 294µs | BEGIN@18 | JSON::Schema::Modern::Vocabulary::Core::
1 | 1 | 1 | 8µs | 95µs | BEGIN@2 | Module::Runtime::
3 | 1 | 1 | 2µs | 2µs | evaluation_order | JSON::Schema::Modern::Vocabulary::Core::
1 | 1 | 1 | 2µs | 2µs | vocabulary | JSON::Schema::Modern::Vocabulary::Core::
0 | 0 | 0 | 0s | 0s | _eval_keyword_recursiveAnchor | JSON::Schema::Modern::Vocabulary::Core::
0 | 0 | 0 | 0s | 0s | _eval_keyword_recursiveRef | JSON::Schema::Modern::Vocabulary::Core::
0 | 0 | 0 | 0s | 0s | _traverse_keyword_definitions | JSON::Schema::Modern::Vocabulary::Core::
0 | 0 | 0 | 0s | 0s | _traverse_keyword_recursiveAnchor | JSON::Schema::Modern::Vocabulary::Core::
0 | 0 | 0 | 0s | 0s | _traverse_keyword_recursiveRef | JSON::Schema::Modern::Vocabulary::Core::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | 2 | 90µs | 2 | 103µs | # spent 92µs (81+11) within Module::Runtime::BEGIN@1 which was called:
# once (81µs+11µs) by Module::Runtime::require_module at line 1 # spent 92µs making 1 call to Module::Runtime::BEGIN@1
# spent 11µs making 1 call to strict::import |
2 | 2 | 71µs | 2 | 182µs | # spent 95µs (8+87) within Module::Runtime::BEGIN@2 which was called:
# once (8µs+87µs) by Module::Runtime::require_module at line 2 # spent 95µs making 1 call to Module::Runtime::BEGIN@2
# spent 87µs making 1 call to warnings::import |
3 | package JSON::Schema::Modern::Vocabulary::Core; | ||||
4 | # vim: set ts=8 sts=2 sw=2 tw=100 et : | ||||
5 | # ABSTRACT: Implementation of the JSON Schema Core vocabulary | ||||
6 | |||||
7 | 1 | 1µs | our $VERSION = '0.556'; | ||
8 | |||||
9 | 2 | 67µs | 1 | 21µs | # spent 21µs within JSON::Schema::Modern::Vocabulary::Core::BEGIN@9 which was called:
# once (21µs+0s) by Module::Runtime::require_module at line 9 # spent 21µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@9 |
10 | 2 | 36µs | 2 | 696µs | # spent 353µs (10+343) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@10 which was called:
# once (10µs+343µs) by Module::Runtime::require_module at line 10 # spent 353µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@10
# spent 343µs making 1 call to Moo::import |
11 | 3 | 89µs | 3 | 2.82ms | # spent 1.42ms (21µs+1.40) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@11 which was called:
# once (21µs+1.40ms) by Module::Runtime::require_module at line 11 # spent 1.42ms making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@11
# spent 1.37ms making 1 call to strictures::import
# spent 27µs making 1 call to strictures::VERSION |
12 | 2 | 57µs | 2 | 342µs | # spent 178µs (14+164) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@12 which was called:
# once (14µs+164µs) by Module::Runtime::require_module at line 12 # spent 178µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@12
# spent 164µs making 1 call to experimental::import |
13 | 2 | 49µs | 2 | 53µs | # spent 50µs (21+29) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@13 which was called:
# once (21µs+29µs) by Module::Runtime::require_module at line 13 # spent 50µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@13
# spent 3µs making 1 call to if::import |
14 | 2 | 32µs | 2 | 30µs | # spent 29µs (13+16) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@14 which was called:
# once (13µs+16µs) by Module::Runtime::require_module at line 14 # spent 29µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@14
# spent 1µs making 1 call to if::unimport |
15 | 2 | 28µs | 2 | 35µs | # spent 33µs (18+15) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@15 which was called:
# once (18µs+15µs) by Module::Runtime::require_module at line 15 # spent 33µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@15
# spent 2µs making 1 call to if::unimport |
16 | 2 | 26µs | 2 | 26µs | # spent 26µs (9+17) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@16 which was called:
# once (9µs+17µs) by Module::Runtime::require_module at line 16 # spent 26µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@16
# spent 0s making 1 call to if::unimport |
17 | 2 | 31µs | 2 | 138µs | # spent 75µs (12+63) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@17 which was called:
# once (12µs+63µs) by Module::Runtime::require_module at line 17 # spent 75µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@17
# spent 63µs making 1 call to Exporter::import |
18 | 2 | 2.19ms | 2 | 580µs | # spent 294µs (8+286) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@18 which was called:
# once (8µs+286µs) by Module::Runtime::require_module at line 18 # spent 294µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@18
# spent 286µs making 1 call to namespace::clean::import |
19 | |||||
20 | 1 | 16µs | 1 | 4.57ms | with 'JSON::Schema::Modern::Vocabulary'; # spent 4.57ms making 1 call to Moo::with |
21 | |||||
22 | # spent 2µs within JSON::Schema::Modern::Vocabulary::Core::vocabulary which was called:
# once (2µs+0s) by JSON::Schema::Modern::__ANON__[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern.pm:696] at line 692 of JSON/Schema/Modern.pm | ||||
23 | 1 | 4µs | 'https://json-schema.org/draft/2019-09/vocab/core' => 'draft2019-09', | ||
24 | 'https://json-schema.org/draft/2020-12/vocab/core' => 'draft2020-12'; | ||||
25 | } | ||||
26 | |||||
27 | 3 | 7µs | # spent 2µs within JSON::Schema::Modern::Vocabulary::Core::evaluation_order which was called 3 times, avg 667ns/call:
# 3 times (2µs+0s) by JSON::Schema::Modern::Vocabulary::Core::CORE:sort at line 331, avg 667ns/call | ||
28 | |||||
29 | 6 | 4µs | # spent 15µs within JSON::Schema::Modern::Vocabulary::Core::keywords which was called 2 times, avg 8µs/call:
# once (10µs+0s) by JSON::Schema::Modern::_eval_subschema at line 559 of JSON/Schema/Modern.pm
# once (5µs+0s) by JSON::Schema::Modern::_traverse_subschema at line 471 of JSON/Schema/Modern.pm | ||
30 | return ( | ||||
31 | 2 | 19µs | qw($id $schema), | ||
32 | $spec_version ne 'draft7' ? '$anchor' : (), | ||||
33 | $spec_version eq 'draft2019-09' ? '$recursiveAnchor' : (), | ||||
34 | $spec_version eq 'draft2020-12' ? '$dynamicAnchor' : (), | ||||
35 | '$ref', | ||||
36 | $spec_version eq 'draft2019-09' ? '$recursiveRef' : (), | ||||
37 | $spec_version eq 'draft2020-12' ? '$dynamicRef' : (), | ||||
38 | $spec_version eq 'draft7' ? 'definitions' : qw($vocabulary $comment $defs), | ||||
39 | ); | ||||
40 | } | ||||
41 | |||||
42 | # adds the following keys to $state during traversal: | ||||
43 | # - identifiers: an arrayref of tuples: | ||||
44 | # $uri => { path => $path_to_identifier, canonical_uri => Mojo::URL (absolute when possible) } | ||||
45 | # this is used by the Document constructor to build its resource_index. | ||||
46 | |||||
47 | 56 | 12µs | # spent 6.17ms (486µs+5.68) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_id which was called 14 times, avg 441µs/call:
# 14 times (486µs+5.68ms) by JSON::Schema::Modern::_traverse_subschema at line 483 of JSON/Schema/Modern.pm, avg 441µs/call | ||
48 | 14 | 59µs | 28 | 2.91ms | return if not assert_keyword_type($state, $schema, 'string') # spent 2.52ms making 14 calls to JSON::Schema::Modern::Utilities::assert_uri_reference, avg 180µs/call
# spent 394µs making 14 calls to JSON::Schema::Modern::Utilities::assert_keyword_type, avg 28µs/call |
49 | or not assert_uri_reference($state, $schema); | ||||
50 | |||||
51 | 14 | 27µs | 14 | 622µs | my $uri = Mojo::URL->new($schema->{'$id'}); # spent 622µs making 14 calls to Mojo::URL::new, avg 44µs/call |
52 | |||||
53 | 14 | 12µs | if ($state->{spec_version} eq 'draft7') { | ||
54 | if (length($uri->fragment)) { | ||||
55 | return E($state, '$id cannot change the base uri at the same time as declaring an anchor') | ||||
56 | if length($uri->clone->fragment(undef)); | ||||
57 | |||||
58 | return $self->_traverse_keyword_anchor({ %$schema, $state->{keyword} => $uri->fragment }, $state); | ||||
59 | } | ||||
60 | } | ||||
61 | else { | ||||
62 | 14 | 14µs | 14 | 12µs | return E($state, '$id value "%s" cannot have a non-empty fragment', $schema->{'$id'}) # spent 12µs making 14 calls to Mojo::URL::fragment, avg 857ns/call |
63 | if length $uri->fragment; | ||||
64 | } | ||||
65 | |||||
66 | 14 | 8µs | 14 | 11µs | $uri->fragment(undef); # spent 11µs making 14 calls to Mojo::URL::fragment, avg 786ns/call |
67 | 14 | 35µs | 14 | 1.32ms | return E($state, '$id cannot be empty') if not length $uri; # spent 1.32ms making 14 calls to Mojo::URL::__ANON__[Mojo/URL.pm:3], avg 95µs/call |
68 | |||||
69 | 14 | 26µs | 14 | 76µs | $state->{initial_schema_uri} = $uri->is_abs ? $uri : $uri->to_abs($state->{initial_schema_uri}); # spent 76µs making 14 calls to Mojo::URL::is_abs, avg 5µs/call |
70 | 14 | 18µs | $state->{traversed_schema_path} = $state->{traversed_schema_path}.$state->{schema_path}; | ||
71 | # we don't set or update document_path because it is identical to traversed_schema_path | ||||
72 | 14 | 9µs | $state->{schema_path} = ''; | ||
73 | |||||
74 | push $state->{identifiers}->@*, | ||||
75 | $state->{initial_schema_uri} => { | ||||
76 | path => $state->{traversed_schema_path}, | ||||
77 | canonical_uri => $state->{initial_schema_uri}->clone, | ||||
78 | specification_version => $state->{spec_version}, # note! $schema keyword can change this | ||||
79 | vocabularies => $state->{vocabularies}, # reference, not copy | ||||
80 | configs => $state->{configs}, | ||||
81 | 14 | 83µs | 14 | 726µs | }; # spent 726µs making 14 calls to Mojo::URL::clone, avg 52µs/call |
82 | 14 | 32µs | return 1; | ||
83 | } | ||||
84 | |||||
85 | 129310 | 28.1ms | # spent 2.58s (777ms+1.80) within JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_id which was called 25862 times, avg 100µs/call:
# 25862 times (777ms+1.80s) by JSON::Schema::Modern::_eval_subschema at line 574 of JSON/Schema/Modern.pm, avg 100µs/call | ||
86 | 25862 | 88.9ms | 25862 | 771ms | my $schema_info = $state->{document}->path_to_resource($state->{document_path}.$state->{schema_path}); # spent 771ms making 25862 calls to JSON::Schema::Modern::Document::path_to_resource, avg 30µs/call |
87 | # this should never happen, if the pre-evaluation traversal was performed correctly | ||||
88 | 25862 | 8.58ms | abort($state, 'failed to resolve %s to canonical uri', $state->{keyword}) if not $schema_info; | ||
89 | |||||
90 | 25862 | 45.1ms | 25862 | 943ms | $state->{initial_schema_uri} = $schema_info->{canonical_uri}->clone; # spent 943ms making 25862 calls to Mojo::URL::clone, avg 36µs/call |
91 | 25862 | 31.9ms | $state->{traversed_schema_path} = $state->{traversed_schema_path}.$state->{schema_path}; | ||
92 | 25862 | 9.70ms | $state->{document_path} = $state->{document_path}.$state->{schema_path}; | ||
93 | 25862 | 9.33ms | $state->{schema_path} = ''; | ||
94 | 25862 | 10.8ms | $state->{spec_version} = $schema_info->{specification_version}; | ||
95 | 25862 | 9.80ms | $state->{vocabularies} = $schema_info->{vocabularies}; | ||
96 | # it's possible that a different schema resource has its own set of configs, different from the | ||||
97 | # JSM attribute value | ||||
98 | 25862 | 12.4ms | if ($state->{validate_formats}) { | ||
99 | $state->{vocabularies} = [ | ||||
100 | map s/^JSON::Schema::Modern::Vocabulary::Format\KAnnotation$/Assertion/r, $state->{vocabularies}->@* | ||||
101 | 25862 | 454ms | 181034 | 89.6ms | ]; # spent 89.6ms making 181034 calls to JSON::Schema::Modern::Vocabulary::Core::CORE:subst, avg 495ns/call |
102 | 25862 | 26.7ms | require JSON::Schema::Modern::Vocabulary::FormatAssertion; | ||
103 | } | ||||
104 | |||||
105 | 25862 | 25.1ms | $state->@{keys $state->{configs}->%*} = values $state->{configs}->%*; | ||
106 | 25862 | 24.7ms | push $state->{dynamic_scope}->@*, $state->{initial_schema_uri}; | ||
107 | |||||
108 | 25862 | 53.7ms | return 1; | ||
109 | } | ||||
110 | |||||
111 | 60 | 15µs | # spent 5.66ms (334µs+5.33) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_schema which was called 15 times, avg 378µs/call:
# 15 times (334µs+5.33ms) by JSON::Schema::Modern::_traverse_subschema at line 483 of JSON/Schema/Modern.pm, avg 378µs/call | ||
112 | 15 | 62µs | 30 | 3.32ms | return if not assert_keyword_type($state, $schema, 'string') or not assert_uri($state, $schema); # spent 2.97ms making 15 calls to JSON::Schema::Modern::Utilities::assert_uri, avg 198µs/call
# spent 350µs making 15 calls to JSON::Schema::Modern::Utilities::assert_keyword_type, avg 23µs/call |
113 | |||||
114 | # "A JSON Schema resource is a schema which is canonically identified by an absolute URI." | ||||
115 | # "A resource's root schema is its top-level schema object." | ||||
116 | # note: we need not be at the document root, but simply adjacent to an $id (or be the at the | ||||
117 | # document root) | ||||
118 | return E($state, '$schema can only appear at the schema resource root') | ||||
119 | 15 | 11µs | if length($state->{schema_path}); | ||
120 | |||||
121 | 15 | 4µs | my ($spec_version, $vocabularies); | ||
122 | |||||
123 | 15 | 56µs | 15 | 685µs | if (my $metaschema_info = $state->{evaluator}->_get_metaschema_vocabulary_classes($schema->{'$schema'})) { # spent 685µs making 15 calls to JSON::Schema::Modern::_get_metaschema_vocabulary_classes, avg 46µs/call |
124 | ($spec_version, $vocabularies) = @$metaschema_info; | ||||
125 | } | ||||
126 | else { | ||||
127 | 1 | 7µs | 1 | 676µs | my $schema_info = $state->{evaluator}->_fetch_from_uri($schema->{'$schema'}); # spent 676µs making 1 call to JSON::Schema::Modern::_fetch_from_uri |
128 | 1 | 0s | return E($state, 'EXCEPTION: unable to find resource %s', $schema->{'$schema'}) if not $schema_info; | ||
129 | |||||
130 | ($spec_version, $vocabularies) = $self->__fetch_vocabulary_data({ %$state, | ||||
131 | keyword => '$vocabulary', initial_schema_uri => Mojo::URL->new($schema->{'$schema'}), | ||||
132 | 1 | 20µs | 3 | 647µs | traversed_schema_path => jsonp($state->{schema_path}, '$schema'), # spent 588µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::__fetch_vocabulary_data
# spent 49µs making 1 call to Mojo::URL::new
# spent 10µs making 1 call to JSON::Schema::Modern::Utilities::jsonp |
133 | }, $schema_info); | ||||
134 | } | ||||
135 | |||||
136 | 15 | 6µs | return E($state, '"%s" is not a valid metaschema', $schema->{'$schema'}) if not @$vocabularies; | ||
137 | |||||
138 | # we special-case this because the check in _eval_subschema for older drafts + $ref has already happened | ||||
139 | return E($state, '$schema and $ref cannot be used together in older drafts') | ||||
140 | 15 | 9µs | if exists $schema->{'$ref'} and $spec_version eq 'draft7'; | ||
141 | |||||
142 | 15 | 18µs | $state->@{qw(spec_version vocabularies)} = ($spec_version, $vocabularies); | ||
143 | |||||
144 | # remember, if we don't have a sibling $id, we must be at the document root with no identifiers | ||||
145 | 15 | 24µs | if ($state->{identifiers}->@*) { | ||
146 | $state->{identifiers}[-1]->@{qw(specification_version vocabularies)} = $state->@{qw(spec_version vocabularies)}; | ||||
147 | } | ||||
148 | |||||
149 | 15 | 35µs | return 1; | ||
150 | } | ||||
151 | |||||
152 | 60 | 10µs | # spent 5.40ms (474µs+4.93) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_anchor which was called 15 times, avg 360µs/call:
# 14 times (391µs+4.45ms) by JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_dynamicAnchor at line 197, avg 346µs/call
# once (83µs+482µs) by JSON::Schema::Modern::_traverse_subschema at line 483 of JSON/Schema/Modern.pm | ||
153 | 15 | 40µs | 15 | 302µs | return if not assert_keyword_type($state, $schema, 'string'); # spent 302µs making 15 calls to JSON::Schema::Modern::Utilities::assert_keyword_type, avg 20µs/call |
154 | |||||
155 | return E($state, '%s value "%s" does not match required syntax', | ||||
156 | $state->{keyword}, ($state->{keyword} eq '$id' ? '#' : '').$schema->{$state->{keyword}}) | ||||
157 | if $state->{spec_version} =~ /^draft(?:7|2019-09)$/ | ||||
158 | and $schema->{$state->{keyword}} !~ /^[A-Za-z][A-Za-z0-9_:.-]*$/ | ||||
159 | or $state->{spec_version} eq 'draft2020-12' | ||||
160 | 15 | 193µs | 30 | 98µs | and $schema->{$state->{keyword}} !~ /^[A-Za-z_][A-Za-z0-9._-]*$/; # spent 98µs making 30 calls to JSON::Schema::Modern::Vocabulary::Core::CORE:match, avg 3µs/call |
161 | |||||
162 | 15 | 57µs | 15 | 490µs | my $canonical_uri = canonical_uri($state); # spent 490µs making 15 calls to JSON::Schema::Modern::Utilities::canonical_uri, avg 33µs/call |
163 | |||||
164 | push $state->{identifiers}->@*, | ||||
165 | Mojo::URL->new->to_abs($canonical_uri)->fragment($schema->{$state->{keyword}}) => { | ||||
166 | path => $state->{traversed_schema_path}.$state->{schema_path}, | ||||
167 | canonical_uri => $canonical_uri, | ||||
168 | specification_version => $state->{spec_version}, | ||||
169 | vocabularies => $state->{vocabularies}, # reference, not copy | ||||
170 | configs => $state->{configs}, | ||||
171 | 15 | 150µs | 45 | 4.04ms | }; # spent 3.89ms making 15 calls to Mojo::URL::to_abs, avg 259µs/call
# spent 130µs making 15 calls to Mojo::URL::new, avg 9µs/call
# spent 19µs making 15 calls to Mojo::URL::fragment, avg 1µs/call |
172 | 15 | 82µs | return 1; | ||
173 | } | ||||
174 | |||||
175 | # we already indexed the $anchor uri, so there is nothing more to do at evaluation time. | ||||
176 | # we explicitly do NOT set $state->{initial_schema_uri}. | ||||
177 | |||||
178 | sub _traverse_keyword_recursiveAnchor ($self, $schema, $state) { | ||||
179 | return if not assert_keyword_type($state, $schema, 'boolean'); | ||||
180 | |||||
181 | # this is required because the location is used as the base URI for future resolution | ||||
182 | # of $recursiveRef, and the fragment would be disregarded in the base | ||||
183 | return E($state, '"$recursiveAnchor" keyword used without "$id"') | ||||
184 | if length($state->{schema_path}); | ||||
185 | return 1; | ||||
186 | } | ||||
187 | |||||
188 | sub _eval_keyword_recursiveAnchor ($self, $data, $schema, $state) { | ||||
189 | return 1 if not $schema->{'$recursiveAnchor'} or exists $state->{recursive_anchor_uri}; | ||||
190 | |||||
191 | # record the canonical location of the current position, to be used against future resolution | ||||
192 | # of a $recursiveRef uri -- as if it was the current location when we encounter a $ref. | ||||
193 | $state->{recursive_anchor_uri} = canonical_uri($state); | ||||
194 | return 1; | ||||
195 | } | ||||
196 | |||||
197 | 14 | 135µs | 14 | 4.84ms | # spent 5.04ms (205µs+4.84) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_dynamicAnchor which was called 14 times, avg 360µs/call:
# 14 times (205µs+4.84ms) by JSON::Schema::Modern::_traverse_subschema at line 483 of JSON/Schema/Modern.pm, avg 360µs/call # spent 4.84ms making 14 calls to JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_anchor, avg 346µs/call |
198 | |||||
199 | # we already indexed the $dynamicAnchor uri, so there is nothing more to do at evaluation time. | ||||
200 | # we explicitly do NOT set $state->{initial_schema_uri}. | ||||
201 | |||||
202 | 2928 | 523µs | # spent 209ms (5.20+204) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_ref which was called 732 times, avg 285µs/call:
# 710 times (5.11ms+199ms) by JSON::Schema::Modern::_traverse_subschema at line 483 of JSON/Schema/Modern.pm, avg 288µs/call
# 22 times (90µs+4.62ms) by JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_dynamicRef at line 228, avg 214µs/call | ||
203 | 732 | 1.95ms | 1464 | 204ms | return if not assert_keyword_type($state, $schema, 'string') # spent 192ms making 732 calls to JSON::Schema::Modern::Utilities::assert_uri_reference, avg 263µs/call
# spent 11.6ms making 732 calls to JSON::Schema::Modern::Utilities::assert_keyword_type, avg 16µs/call |
204 | or not assert_uri_reference($state, $schema); | ||||
205 | 732 | 1.71ms | return 1; | ||
206 | } | ||||
207 | |||||
208 | 195605 | 40.5ms | # spent 99.6s (799ms+98.8) within JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_ref which was called 39121 times, avg 2.55ms/call:
# 39121 times (799ms+98.8s) by JSON::Schema::Modern::_eval_subschema at line 574 of JSON/Schema/Modern.pm, avg 2.55ms/call | ||
209 | 39121 | 164ms | 78242 | 13.2s | my $uri = Mojo::URL->new($schema->{'$ref'})->to_abs($state->{initial_schema_uri}); # spent 8.76s making 39121 calls to Mojo::URL::to_abs, avg 224µs/call
# spent 4.43s making 39121 calls to Mojo::URL::new, avg 113µs/call |
210 | 39121 | 534ms | 39121 | 99.6s | $self->eval_subschema_at_uri($data, $schema, $state, $uri); # spent 1071s making 39121 calls to JSON::Schema::Modern::Vocabulary::eval_subschema_at_uri, avg 27.4ms/call, recursion: max depth 38, sum of overlapping time 972s |
211 | } | ||||
212 | |||||
213 | sub _traverse_keyword_recursiveRef { shift->_traverse_keyword_ref(@_) } | ||||
214 | |||||
215 | sub _eval_keyword_recursiveRef ($self, $data, $schema, $state) { | ||||
216 | my $uri = Mojo::URL->new($schema->{'$recursiveRef'})->to_abs($state->{initial_schema_uri}); | ||||
217 | my $schema_info = $state->{evaluator}->_fetch_from_uri($uri); | ||||
218 | abort($state, 'EXCEPTION: unable to find resource %s', $uri) if not $schema_info; | ||||
219 | |||||
220 | if (is_type('boolean', $schema_info->{schema}{'$recursiveAnchor'}) and $schema_info->{schema}{'$recursiveAnchor'}) { | ||||
221 | $uri = Mojo::URL->new($schema->{'$recursiveRef'}) | ||||
222 | ->to_abs($state->{recursive_anchor_uri} // $state->{initial_schema_uri}); | ||||
223 | } | ||||
224 | |||||
225 | return $self->eval_subschema_at_uri($data, $schema, $state, $uri); | ||||
226 | } | ||||
227 | |||||
228 | 22 | 133µs | 22 | 4.71ms | # spent 4.81ms (104µs+4.71) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_dynamicRef which was called 22 times, avg 219µs/call:
# 22 times (104µs+4.71ms) by JSON::Schema::Modern::_traverse_subschema at line 483 of JSON/Schema/Modern.pm, avg 219µs/call # spent 4.71ms making 22 calls to JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_ref, avg 214µs/call |
229 | |||||
230 | 12930 | 3.45ms | # spent 70.9s (138ms+70.8) within JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_dynamicRef which was called 2586 times, avg 27.4ms/call:
# 2586 times (138ms+70.8s) by JSON::Schema::Modern::_eval_subschema at line 574 of JSON/Schema/Modern.pm, avg 27.4ms/call | ||
231 | 2586 | 11.7ms | 5172 | 874ms | my $uri = Mojo::URL->new($schema->{'$dynamicRef'})->to_abs($state->{initial_schema_uri}); # spent 506ms making 2586 calls to Mojo::URL::to_abs, avg 196µs/call
# spent 368ms making 2586 calls to Mojo::URL::new, avg 142µs/call |
232 | 2586 | 7.58ms | 2586 | 732ms | my $schema_info = $state->{evaluator}->_fetch_from_uri($uri); # spent 732ms making 2586 calls to JSON::Schema::Modern::_fetch_from_uri, avg 283µs/call |
233 | 2586 | 1.25ms | abort($state, 'EXCEPTION: unable to find resource %s', $uri) if not $schema_info; | ||
234 | |||||
235 | # If the initially resolved starting point URI includes a fragment that was created by the | ||||
236 | # "$dynamicAnchor" keyword, ... | ||||
237 | 2586 | 11.1ms | 5172 | 4.14ms | if (length $uri->fragment and exists $schema_info->{schema}{'$dynamicAnchor'} # spent 4.14ms making 5172 calls to Mojo::URL::fragment, avg 801ns/call |
238 | and $uri->fragment eq (my $anchor = $schema_info->{schema}{'$dynamicAnchor'})) { | ||||
239 | # ...the initial URI MUST be replaced by the URI (including the fragment) for the outermost | ||||
240 | # schema resource in the dynamic scope that defines an identically named fragment with | ||||
241 | # "$dynamicAnchor". | ||||
242 | 2586 | 2.86ms | foreach my $base_scope ($state->{dynamic_scope}->@*) { | ||
243 | 2586 | 5.52ms | 5172 | 356ms | my $test_uri = Mojo::URL->new($base_scope)->fragment($anchor); # spent 354ms making 2586 calls to Mojo::URL::new, avg 137µs/call
# spent 2.46ms making 2586 calls to Mojo::URL::fragment, avg 952ns/call |
244 | 2586 | 4.89ms | 2586 | 572ms | my $dynamic_anchor_subschema_info = $state->{evaluator}->_fetch_from_uri($test_uri); # spent 572ms making 2586 calls to JSON::Schema::Modern::_fetch_from_uri, avg 221µs/call |
245 | 2586 | 2.53ms | if (($dynamic_anchor_subschema_info->{schema}->{'$dynamicAnchor'}//'') eq $anchor) { | ||
246 | 2586 | 9.95ms | $uri = $test_uri; | ||
247 | 2586 | 6.74ms | last; | ||
248 | } | ||||
249 | } | ||||
250 | } | ||||
251 | |||||
252 | 2586 | 46.1ms | 2586 | 0s | return $self->eval_subschema_at_uri($data, $schema, $state, $uri); # spent 148s making 2586 calls to JSON::Schema::Modern::Vocabulary::eval_subschema_at_uri, avg 57.3ms/call, recursion: max depth 34, sum of overlapping time 148s |
253 | } | ||||
254 | |||||
255 | 44 | 15µs | # spent 6.33ms (668µs+5.67) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_vocabulary which was called 11 times, avg 576µs/call:
# 11 times (668µs+5.67ms) by JSON::Schema::Modern::_traverse_subschema at line 483 of JSON/Schema/Modern.pm, avg 576µs/call | ||
256 | 11 | 22µs | 11 | 138µs | return if not assert_keyword_type($state, $schema, 'object'); # spent 138µs making 11 calls to JSON::Schema::Modern::Utilities::assert_keyword_type, avg 13µs/call |
257 | |||||
258 | return E($state, '$vocabulary can only appear at the schema resource root') | ||||
259 | 11 | 8µs | if length($state->{schema_path}); | ||
260 | |||||
261 | 11 | 3µs | my $valid = 1; | ||
262 | |||||
263 | 11 | 4µs | my @vocabulary_classes; | ||
264 | 11 | 88µs | 11 | 37µs | foreach my $uri (sort keys $schema->{'$vocabulary'}->%*) { # spent 37µs making 11 calls to JSON::Schema::Modern::Vocabulary::Core::CORE:sort, avg 3µs/call |
265 | 31 | 280µs | 31 | 487µs | $valid = 0, next if not assert_keyword_type({ %$state, _schema_path_suffix => $uri }, $schema, 'boolean'); # spent 487µs making 31 calls to JSON::Schema::Modern::Utilities::assert_keyword_type, avg 16µs/call |
266 | 31 | 170µs | 31 | 5.00ms | $valid = 0, next if not assert_uri({ %$state, _schema_path_suffix => $uri }, undef, $uri); # spent 5.00ms making 31 calls to JSON::Schema::Modern::Utilities::assert_uri, avg 161µs/call |
267 | } | ||||
268 | |||||
269 | # we cannot return an error here for invalid or incomplete vocabulary lists, because | ||||
270 | # - the specification vocabulary schemas themselves don't list Core, | ||||
271 | # - it is possible for a metaschema to $ref to another metaschema that uses an unrecognized | ||||
272 | # vocabulary uri while still validating those vocabulary keywords (e.g. | ||||
273 | # https://spec.openapis.org/oas/3.1/schema-base/2021-05-20) | ||||
274 | # Instead, we will verify these constraints when we actually use the metaschema, in | ||||
275 | # _traverse_keyword_schema -> __fetch_vocabulary_data | ||||
276 | |||||
277 | 11 | 58µs | return $valid; | ||
278 | } | ||||
279 | |||||
280 | # we do nothing with $vocabulary yet at evaluation time. When we know we are in a metaschema, | ||||
281 | # we can scan the URIs included here and either abort if a vocabulary is enabled that we do not | ||||
282 | # understand, or turn on and off certain keyword behaviours based on the boolean values seen. | ||||
283 | |||||
284 | 472 | 109µs | # spent 2.36ms (583µs+1.77) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_comment which was called 118 times, avg 20µs/call:
# 118 times (583µs+1.77ms) by JSON::Schema::Modern::_traverse_subschema at line 483 of JSON/Schema/Modern.pm, avg 20µs/call | ||
285 | 118 | 169µs | 118 | 1.78ms | return if not assert_keyword_type($state, $schema, 'string'); # spent 1.78ms making 118 calls to JSON::Schema::Modern::Utilities::assert_keyword_type, avg 15µs/call |
286 | 118 | 232µs | return 1; | ||
287 | } | ||||
288 | |||||
289 | # we do nothing with $comment at evaluation time, including not collecting its value for annotations. | ||||
290 | |||||
291 | sub _traverse_keyword_definitions { shift->traverse_object_schemas(@_) } | ||||
292 | 12 | 71µs | 12 | 90.4ms | # spent 90.4ms (113µs+90.3) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_defs which was called 12 times, avg 7.54ms/call:
# 12 times (113µs+90.3ms) by JSON::Schema::Modern::_traverse_subschema at line 483 of JSON/Schema/Modern.pm, avg 7.54ms/call # spent 99.7ms making 12 calls to JSON::Schema::Modern::Vocabulary::traverse_object_schemas, avg 8.31ms/call, recursion: max depth 2, sum of overlapping time 9.29ms |
293 | |||||
294 | # we do nothing directly with $defs at evaluation time, including not collecting its value for | ||||
295 | # annotations. | ||||
296 | |||||
297 | |||||
298 | # translate vocabulary URIs into classes, caching the results (if any) | ||||
299 | 4 | 2µs | # spent 588µs (100+488) within JSON::Schema::Modern::Vocabulary::Core::__fetch_vocabulary_data which was called:
# once (100µs+488µs) by JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_schema at line 132 | ||
300 | 1 | 1µs | if (not exists $schema_info->{schema}{'$vocabulary'}) { | ||
301 | # "If "$vocabulary" is absent, an implementation MAY determine behavior based on the meta-schema | ||||
302 | # if it is recognized from the URI value of the referring schema's "$schema" keyword." | ||||
303 | my $metaschema_uri = $state->{evaluator}->METASCHEMA_URIS->{$schema_info->{specification_version}}; | ||||
304 | return $state->{evaluator}->_get_metaschema_vocabulary_classes($metaschema_uri)->@*; | ||||
305 | } | ||||
306 | |||||
307 | 1 | 1µs | my $valid = 1; | ||
308 | 1 | 1µs | $valid = E($state, '$vocabulary can only appear at the document root') if length $schema_info->{document_path}; | ||
309 | 1 | 1µs | $valid = E($state, 'metaschemas must have an $id') if not exists $schema_info->{schema}{'$id'}; | ||
310 | |||||
311 | 1 | 1µs | return (undef, []) if not $valid; | ||
312 | |||||
313 | 1 | 0s | my @vocabulary_classes; | ||
314 | |||||
315 | 1 | 11µs | 1 | 4µs | foreach my $uri (sort keys $schema_info->{schema}{'$vocabulary'}->%*) { # spent 4µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::CORE:sort |
316 | 8 | 6µs | 8 | 145µs | my $class_info = $state->{evaluator}->_get_vocabulary_class($uri); # spent 145µs making 8 calls to JSON::Schema::Modern::_get_vocabulary_class, avg 18µs/call |
317 | $valid = E({ %$state, _schema_path_suffix => $uri }, '"%s" is not a known vocabulary', $uri), next | ||||
318 | 8 | 29µs | 8 | 6µs | if $schema_info->{schema}{'$vocabulary'}{$uri} and not $class_info; # spent 6µs making 8 calls to JSON::PP::Boolean::__ANON__[JSON/PP/Boolean.pm:7], avg 750ns/call |
319 | |||||
320 | 8 | 0s | next if not $class_info; # vocabulary is not known, but marked as false in the metaschema | ||
321 | |||||
322 | 8 | 6µs | my ($spec_version, $class) = @$class_info; | ||
323 | $valid = E({ %$state, _schema_path_suffix => $uri }, '"%s" uses %s, but the metaschema itself uses %s', | ||||
324 | $uri, $spec_version, $schema_info->{specification_version}), next | ||||
325 | 8 | 0s | if $spec_version ne $schema_info->{specification_version}; | ||
326 | |||||
327 | 8 | 5µs | push @vocabulary_classes, $class; | ||
328 | } | ||||
329 | |||||
330 | @vocabulary_classes = sort { | ||||
331 | 16 | 55µs | 31 | 117µs | $a->evaluation_order <=> $b->evaluation_order # spent 100µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::CORE:sort
# spent 3µs making 3 calls to JSON::Schema::Modern::Vocabulary::Content::evaluation_order, avg 1µs/call
# spent 3µs making 5 calls to JSON::Schema::Modern::Vocabulary::FormatAnnotation::evaluation_order, avg 600ns/call
# spent 3µs making 5 calls to JSON::Schema::Modern::Vocabulary::Validation::evaluation_order, avg 600ns/call
# spent 2µs making 4 calls to JSON::Schema::Modern::Vocabulary::Applicator::evaluation_order, avg 500ns/call
# spent 2µs making 3 calls to JSON::Schema::Modern::Vocabulary::Core::evaluation_order, avg 667ns/call
# spent 2µs making 3 calls to JSON::Schema::Modern::Vocabulary::evaluation_order, avg 667ns/call
# spent 1µs making 5 calls to JSON::Schema::Modern::Vocabulary::MetaData::evaluation_order, avg 200ns/call
# spent 1µs making 2 calls to JSON::Schema::Modern::Vocabulary::Unevaluated::evaluation_order, avg 500ns/call |
332 | || ($a->evaluation_order == 999 ? 0 | ||||
333 | : ($valid = E($state, '%s and %s have a conflicting evaluation_order', sort $a, $b))) | ||||
334 | } @vocabulary_classes; | ||||
335 | |||||
336 | 1 | 1µs | $valid = E($state, 'the first vocabulary (by evaluation_order) must be Core') | ||
337 | if ($vocabulary_classes[0]//'') ne 'JSON::Schema::Modern::Vocabulary::Core'; | ||||
338 | |||||
339 | $state->{evaluator}->_set_metaschema_vocabulary_classes($schema_info->{canonical_uri}, | ||||
340 | 1 | 4µs | 1 | 233µs | [ $schema_info->{specification_version}, \@vocabulary_classes ]) if $valid; # spent 233µs making 1 call to JSON::Schema::Modern::_set_metaschema_vocabulary_classes |
341 | |||||
342 | 1 | 4µs | return ($schema_info->{specification_version}, $valid ? \@vocabulary_classes : []); | ||
343 | } | ||||
344 | |||||
345 | 1 | 7µs | 1; | ||
346 | |||||
347 | 1 | 36µs | 1 | 605µs | __END__ # spent 605µs making 1 call to B::Hooks::EndOfScope::XS::__ANON__[B/Hooks/EndOfScope/XS.pm:26] |
# spent 98µs within JSON::Schema::Modern::Vocabulary::Core::CORE:match which was called 30 times, avg 3µs/call:
# 30 times (98µs+0s) by JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_anchor at line 160, avg 3µs/call | |||||
# spent 141µs (124+17) within JSON::Schema::Modern::Vocabulary::Core::CORE:sort which was called 13 times, avg 11µs/call:
# 11 times (37µs+0s) by JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_vocabulary at line 264, avg 3µs/call
# once (83µs+17µs) by JSON::Schema::Modern::Vocabulary::Core::__fetch_vocabulary_data at line 331
# once (4µs+0s) by JSON::Schema::Modern::Vocabulary::Core::__fetch_vocabulary_data at line 315 | |||||
# spent 89.6ms within JSON::Schema::Modern::Vocabulary::Core::CORE:subst which was called 181034 times, avg 495ns/call:
# 181034 times (89.6ms+0s) by JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_id at line 101, avg 495ns/call |