| Filename | /Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern/Vocabulary/Core.pm |
| Statements | Executed 812582 statements in 1.74s |
| Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
|---|---|---|---|---|---|
| 39121 | 1 | 1 | 887ms | 87.2s | JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_ref (recurses: max depth 31, inclusive time 854s) |
| 25862 | 1 | 1 | 726ms | 2.64s | JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_id |
| 2586 | 1 | 1 | 155ms | 61.5s | JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_dynamicRef (recurses: max depth 6, inclusive time 67.1s) |
| 181034 | 1 | 1 | 98.3ms | 98.3ms | JSON::Schema::Modern::Vocabulary::Core::CORE:subst (opcode) |
| 732 | 2 | 2 | 4.73ms | 175ms | JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_ref |
| 118 | 1 | 1 | 851µs | 2.38ms | JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_comment |
| 11 | 1 | 1 | 511µs | 5.26ms | JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_vocabulary |
| 14 | 1 | 1 | 472µs | 5.89ms | JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_id |
| 15 | 2 | 2 | 381µs | 4.07ms | JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_anchor |
| 15 | 1 | 1 | 230µs | 4.39ms | JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_schema |
| 12 | 1 | 1 | 115µs | 69.6ms | JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_defs (recurses: max depth 1, inclusive time 9.78ms) |
| 13 | 3 | 1 | 90µs | 104µs | JSON::Schema::Modern::Vocabulary::Core::CORE:sort (opcode) |
| 14 | 1 | 1 | 86µs | 3.91ms | JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_dynamicAnchor |
| 22 | 1 | 1 | 81µs | 6.15ms | JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_dynamicRef |
| 30 | 1 | 1 | 66µs | 66µs | JSON::Schema::Modern::Vocabulary::Core::CORE:match (opcode) |
| 1 | 1 | 1 | 63µs | 390µs | JSON::Schema::Modern::Vocabulary::Core::__fetch_vocabulary_data |
| 1 | 1 | 1 | 29µs | 31µs | Module::Runtime::BEGIN@1 |
| 1 | 1 | 1 | 17µs | 570µs | JSON::Schema::Modern::Vocabulary::Core::BEGIN@11 |
| 1 | 1 | 1 | 10µs | 32µs | JSON::Schema::Modern::Vocabulary::Core::BEGIN@13 |
| 1 | 1 | 1 | 10µs | 10µs | JSON::Schema::Modern::Vocabulary::Core::BEGIN@9 |
| 2 | 2 | 1 | 10µs | 10µs | JSON::Schema::Modern::Vocabulary::Core::keywords |
| 1 | 1 | 1 | 8µs | 200µs | JSON::Schema::Modern::Vocabulary::Core::BEGIN@10 |
| 1 | 1 | 1 | 8µs | 52µs | JSON::Schema::Modern::Vocabulary::Core::BEGIN@17 |
| 1 | 1 | 1 | 8µs | 162µs | JSON::Schema::Modern::Vocabulary::Core::BEGIN@18 |
| 1 | 1 | 1 | 7µs | 20µs | JSON::Schema::Modern::Vocabulary::Core::BEGIN@14 |
| 1 | 1 | 1 | 7µs | 15µs | JSON::Schema::Modern::Vocabulary::Core::BEGIN@15 |
| 1 | 1 | 1 | 6µs | 16µs | JSON::Schema::Modern::Vocabulary::Core::BEGIN@16 |
| 1 | 1 | 1 | 6µs | 28µs | Module::Runtime::BEGIN@2 |
| 1 | 1 | 1 | 5µs | 95µs | JSON::Schema::Modern::Vocabulary::Core::BEGIN@12 |
| 3 | 1 | 1 | 2µs | 2µs | JSON::Schema::Modern::Vocabulary::Core::evaluation_order |
| 1 | 1 | 1 | 2µs | 2µs | JSON::Schema::Modern::Vocabulary::Core::vocabulary |
| 0 | 0 | 0 | 0s | 0s | JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_recursiveAnchor |
| 0 | 0 | 0 | 0s | 0s | JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_recursiveRef |
| 0 | 0 | 0 | 0s | 0s | JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_definitions |
| 0 | 0 | 0 | 0s | 0s | JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_recursiveAnchor |
| 0 | 0 | 0 | 0s | 0s | JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_recursiveRef |
| Line | State ments |
Time on line |
Calls | Time in subs |
Code |
|---|---|---|---|---|---|
| 1 | 2 | 28µs | 2 | 33µs | # spent 31µs (29+2) within Module::Runtime::BEGIN@1 which was called:
# once (29µs+2µs) by Module::Runtime::require_module at line 1 # spent 31µs making 1 call to Module::Runtime::BEGIN@1
# spent 2µs making 1 call to strict::import |
| 2 | 2 | 35µs | 2 | 50µs | # spent 28µs (6+22) within Module::Runtime::BEGIN@2 which was called:
# once (6µs+22µs) by Module::Runtime::require_module at line 2 # spent 28µs making 1 call to Module::Runtime::BEGIN@2
# spent 22µ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.558'; | ||
| 8 | |||||
| 9 | 2 | 27µs | 1 | 10µs | # spent 10µs within JSON::Schema::Modern::Vocabulary::Core::BEGIN@9 which was called:
# once (10µs+0s) by Module::Runtime::require_module at line 9 # spent 10µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@9 |
| 10 | 2 | 23µs | 2 | 392µs | # spent 200µs (8+192) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@10 which was called:
# once (8µs+192µs) by Module::Runtime::require_module at line 10 # spent 200µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@10
# spent 192µs making 1 call to Moo::import |
| 11 | 3 | 26µs | 3 | 1.12ms | # spent 570µs (17+553) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@11 which was called:
# once (17µs+553µs) by Module::Runtime::require_module at line 11 # spent 570µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@11
# spent 538µs making 1 call to strictures::import
# spent 15µs making 1 call to strictures::VERSION |
| 12 | 2 | 34µs | 2 | 185µs | # spent 95µs (5+90) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@12 which was called:
# once (5µs+90µs) by Module::Runtime::require_module at line 12 # spent 95µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@12
# spent 90µs making 1 call to experimental::import |
| 13 | 2 | 21µs | 2 | 35µs | # spent 32µs (10+22) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@13 which was called:
# once (10µs+22µs) by Module::Runtime::require_module at line 13 # spent 32µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@13
# spent 3µs making 1 call to if::import |
| 14 | 2 | 17µs | 2 | 22µs | # spent 20µs (7+13) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@14 which was called:
# once (7µs+13µs) by Module::Runtime::require_module at line 14 # spent 20µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@14
# spent 2µs making 1 call to if::unimport |
| 15 | 2 | 16µs | 2 | 16µs | # spent 15µs (7+8) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@15 which was called:
# once (7µs+8µs) by Module::Runtime::require_module at line 15 # spent 15µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@15
# spent 1µs making 1 call to if::unimport |
| 16 | 2 | 17µs | 2 | 16µs | # spent 16µs (6+10) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@16 which was called:
# once (6µs+10µs) by Module::Runtime::require_module at line 16 # spent 16µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@16
# spent 0s making 1 call to if::unimport |
| 17 | 2 | 19µs | 2 | 96µs | # spent 52µs (8+44) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@17 which was called:
# once (8µs+44µs) by Module::Runtime::require_module at line 17 # spent 52µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@17
# spent 44µs making 1 call to Exporter::import |
| 18 | 2 | 2.23ms | 2 | 316µs | # spent 162µs (8+154) within JSON::Schema::Modern::Vocabulary::Core::BEGIN@18 which was called:
# once (8µs+154µs) by Module::Runtime::require_module at line 18 # spent 162µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::BEGIN@18
# spent 154µs making 1 call to namespace::clean::import |
| 19 | |||||
| 20 | 1 | 1µs | 1 | 5.47ms | with 'JSON::Schema::Modern::Vocabulary'; # spent 5.47ms 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:700] at line 696 of JSON/Schema/Modern.pm | ||||
| 23 | 1 | 3µ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 | 3µ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 | 2µs | # spent 10µs within JSON::Schema::Modern::Vocabulary::Core::keywords which was called 2 times, avg 5µs/call:
# once (5µs+0s) by JSON::Schema::Modern::_traverse_subschema at line 475 of JSON/Schema/Modern.pm
# once (5µs+0s) by JSON::Schema::Modern::_eval_subschema at line 563 of JSON/Schema/Modern.pm | ||
| 30 | return ( | ||||
| 31 | 2 | 11µ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 | 20µs | # spent 5.89ms (472µs+5.42) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_id which was called 14 times, avg 421µs/call:
# 14 times (472µs+5.42ms) by JSON::Schema::Modern::_traverse_subschema at line 487 of JSON/Schema/Modern.pm, avg 421µs/call | ||
| 48 | 14 | 57µs | 28 | 2.96ms | return if not assert_keyword_type($state, $schema, 'string') # spent 2.58ms making 14 calls to JSON::Schema::Modern::Utilities::assert_uri_reference, avg 184µs/call
# spent 377µs making 14 calls to JSON::Schema::Modern::Utilities::assert_keyword_type, avg 27µs/call |
| 49 | or not assert_uri_reference($state, $schema); | ||||
| 50 | |||||
| 51 | 14 | 28µs | 14 | 628µs | my $uri = Mojo::URL->new($schema->{'$id'}); # spent 628µs making 14 calls to Mojo::URL::new, avg 45µs/call |
| 52 | |||||
| 53 | 14 | 17µ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 | 13µs | 14 | 9µs | return E($state, '$id value "%s" cannot have a non-empty fragment', $schema->{'$id'}) # spent 9µs making 14 calls to Mojo::URL::fragment, avg 643ns/call |
| 63 | if length $uri->fragment; | ||||
| 64 | } | ||||
| 65 | |||||
| 66 | 14 | 35µs | 14 | 10µs | $uri->fragment(undef); # spent 10µs making 14 calls to Mojo::URL::fragment, avg 714ns/call |
| 67 | 14 | 29µs | 14 | 1.22ms | return E($state, '$id cannot be empty') if not length $uri; # spent 1.22ms making 14 calls to Mojo::URL::__ANON__[Mojo/URL.pm:3], avg 87µs/call |
| 68 | |||||
| 69 | 14 | 26µs | 14 | 44µs | $state->{initial_schema_uri} = $uri->is_abs ? $uri : $uri->to_abs($state->{initial_schema_uri}); # spent 44µs making 14 calls to Mojo::URL::is_abs, avg 3µs/call |
| 70 | 14 | 13µ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 | 2µ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 | 65µs | 14 | 555µs | }; # spent 555µs making 14 calls to Mojo::URL::clone, avg 40µs/call |
| 82 | 14 | 33µs | return 1; | ||
| 83 | } | ||||
| 84 | |||||
| 85 | 129310 | 43.3ms | # spent 2.64s (726ms+1.91) within JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_id which was called 25862 times, avg 102µs/call:
# 25862 times (726ms+1.91s) by JSON::Schema::Modern::_eval_subschema at line 578 of JSON/Schema/Modern.pm, avg 102µs/call | ||
| 86 | 25862 | 89.1ms | 25862 | 792ms | my $schema_info = $state->{document}->path_to_resource($state->{document_path}.$state->{schema_path}); # spent 792ms making 25862 calls to JSON::Schema::Modern::Document::path_to_resource, avg 31µs/call |
| 87 | # this should never happen, if the pre-evaluation traversal was performed correctly | ||||
| 88 | 25862 | 8.52ms | abort($state, 'failed to resolve %s to canonical uri', $state->{keyword}) if not $schema_info; | ||
| 89 | |||||
| 90 | 25862 | 44.1ms | 25862 | 1.02s | $state->{initial_schema_uri} = $schema_info->{canonical_uri}->clone; # spent 1.02s making 25862 calls to Mojo::URL::clone, avg 40µs/call |
| 91 | 25862 | 34.5ms | $state->{traversed_schema_path} = $state->{traversed_schema_path}.$state->{schema_path}; | ||
| 92 | 25862 | 12.2ms | $state->{document_path} = $state->{document_path}.$state->{schema_path}; | ||
| 93 | 25862 | 14.0ms | $state->{schema_path} = ''; | ||
| 94 | 25862 | 14.7ms | $state->{spec_version} = $schema_info->{specification_version}; | ||
| 95 | 25862 | 10.6ms | $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 | 11.9ms | if ($state->{validate_formats}) { | ||
| 99 | $state->{vocabularies} = [ | ||||
| 100 | map s/^JSON::Schema::Modern::Vocabulary::Format\KAnnotation$/Assertion/r, $state->{vocabularies}->@* | ||||
| 101 | 25862 | 380ms | 181034 | 98.3ms | ]; # spent 98.3ms making 181034 calls to JSON::Schema::Modern::Vocabulary::Core::CORE:subst, avg 543ns/call |
| 102 | 25862 | 28.5ms | require JSON::Schema::Modern::Vocabulary::FormatAssertion; | ||
| 103 | } | ||||
| 104 | |||||
| 105 | 25862 | 30.4ms | $state->@{keys $state->{configs}->%*} = values $state->{configs}->%*; | ||
| 106 | 25862 | 27.2ms | push $state->{dynamic_scope}->@*, $state->{initial_schema_uri}; | ||
| 107 | |||||
| 108 | 25862 | 70.4ms | return 1; | ||
| 109 | } | ||||
| 110 | |||||
| 111 | 60 | 13µs | # spent 4.39ms (230µs+4.16) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_schema which was called 15 times, avg 292µs/call:
# 15 times (230µs+4.16ms) by JSON::Schema::Modern::_traverse_subschema at line 487 of JSON/Schema/Modern.pm, avg 292µs/call | ||
| 112 | 15 | 48µs | 30 | 2.78ms | return if not assert_keyword_type($state, $schema, 'string') or not assert_uri($state, $schema); # spent 2.47ms making 15 calls to JSON::Schema::Modern::Utilities::assert_uri, avg 164µs/call
# spent 313µs making 15 calls to JSON::Schema::Modern::Utilities::assert_keyword_type, avg 21µ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 | 7µs | if length($state->{schema_path}); | ||
| 120 | |||||
| 121 | 15 | 5µs | my ($spec_version, $vocabularies); | ||
| 122 | |||||
| 123 | 15 | 46µs | 15 | 439µs | if (my $metaschema_info = $state->{evaluator}->_get_metaschema_vocabulary_classes($schema->{'$schema'})) { # spent 439µs making 15 calls to JSON::Schema::Modern::_get_metaschema_vocabulary_classes, avg 29µs/call |
| 124 | ($spec_version, $vocabularies) = @$metaschema_info; | ||||
| 125 | } | ||||
| 126 | else { | ||||
| 127 | 1 | 3µs | 1 | 526µs | my $schema_info = $state->{evaluator}->_fetch_from_uri($schema->{'$schema'}); # spent 526µ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 | 17µs | 3 | 414µs | traversed_schema_path => jsonp($state->{schema_path}, '$schema'), # spent 390µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::__fetch_vocabulary_data
# spent 18µs making 1 call to Mojo::URL::new
# spent 6µs making 1 call to JSON::Schema::Modern::Utilities::jsonp |
| 133 | }, $schema_info); | ||||
| 134 | } | ||||
| 135 | |||||
| 136 | 15 | 8µ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 | 5µs | if exists $schema->{'$ref'} and $spec_version eq 'draft7'; | ||
| 141 | |||||
| 142 | 15 | 15µ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 | 21µs | if ($state->{identifiers}->@*) { | ||
| 146 | $state->{identifiers}[-1]->@{qw(specification_version vocabularies)} = $state->@{qw(spec_version vocabularies)}; | ||||
| 147 | } | ||||
| 148 | |||||
| 149 | 15 | 29µs | return 1; | ||
| 150 | } | ||||
| 151 | |||||
| 152 | 60 | 20µs | # spent 4.07ms (381µs+3.69) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_anchor which was called 15 times, avg 271µs/call:
# 14 times (326µs+3.49ms) by JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_dynamicAnchor at line 197, avg 273µs/call
# once (55µs+194µs) by JSON::Schema::Modern::_traverse_subschema at line 487 of JSON/Schema/Modern.pm | ||
| 153 | 15 | 19µs | 15 | 243µs | return if not assert_keyword_type($state, $schema, 'string'); # spent 243µs making 15 calls to JSON::Schema::Modern::Utilities::assert_keyword_type, avg 16µ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 | 146µs | 30 | 66µs | and $schema->{$state->{keyword}} !~ /^[A-Za-z_][A-Za-z0-9._-]*$/; # spent 66µs making 30 calls to JSON::Schema::Modern::Vocabulary::Core::CORE:match, avg 2µs/call |
| 161 | |||||
| 162 | 15 | 36µs | 15 | 267µs | my $canonical_uri = canonical_uri($state); # spent 267µs making 15 calls to JSON::Schema::Modern::Utilities::canonical_uri, avg 18µ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 | 126µs | 45 | 3.11ms | }; # spent 3.05ms making 15 calls to Mojo::URL::to_abs, avg 203µs/call
# spent 41µs making 15 calls to Mojo::URL::new, avg 3µs/call
# spent 19µs making 15 calls to Mojo::URL::fragment, avg 1µs/call |
| 172 | 15 | 58µ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 | 71µs | 14 | 3.82ms | # spent 3.91ms (86µs+3.82) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_dynamicAnchor which was called 14 times, avg 279µs/call:
# 14 times (86µs+3.82ms) by JSON::Schema::Modern::_traverse_subschema at line 487 of JSON/Schema/Modern.pm, avg 279µs/call # spent 3.82ms making 14 calls to JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_anchor, avg 273µ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 | 509µs | # spent 175ms (4.73+170) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_ref which was called 732 times, avg 238µs/call:
# 710 times (4.60ms+164ms) by JSON::Schema::Modern::_traverse_subschema at line 487 of JSON/Schema/Modern.pm, avg 237µs/call
# 22 times (128µs+5.94ms) by JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_dynamicRef at line 228, avg 276µs/call | ||
| 203 | 732 | 1.66ms | 1464 | 170ms | return if not assert_keyword_type($state, $schema, 'string') # spent 160ms making 732 calls to JSON::Schema::Modern::Utilities::assert_uri_reference, avg 218µs/call
# spent 10.1ms making 732 calls to JSON::Schema::Modern::Utilities::assert_keyword_type, avg 14µs/call |
| 204 | or not assert_uri_reference($state, $schema); | ||||
| 205 | 732 | 1.31ms | return 1; | ||
| 206 | } | ||||
| 207 | |||||
| 208 | 195605 | 47.9ms | # spent 87.2s (887ms+86.3) within JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_ref which was called 39121 times, avg 2.23ms/call:
# 39121 times (887ms+86.3s) by JSON::Schema::Modern::_eval_subschema at line 578 of JSON/Schema/Modern.pm, avg 2.23ms/call | ||
| 209 | 39121 | 184ms | 78242 | 13.6s | my $uri = Mojo::URL->new($schema->{'$ref'})->to_abs($state->{initial_schema_uri}); # spent 9.13s making 39121 calls to Mojo::URL::to_abs, avg 233µs/call
# spent 4.51s making 39121 calls to Mojo::URL::new, avg 115µs/call |
| 210 | 39121 | 563ms | 39121 | 87.2s | $self->eval_subschema_at_uri($data, $schema, $state, $uri); # spent 927s making 39121 calls to JSON::Schema::Modern::Vocabulary::eval_subschema_at_uri, avg 23.7ms/call, recursion: max depth 38, sum of overlapping time 840s |
| 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 | 82µs | 22 | 6.07ms | # spent 6.15ms (81µs+6.07) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_dynamicRef which was called 22 times, avg 280µs/call:
# 22 times (81µs+6.07ms) by JSON::Schema::Modern::_traverse_subschema at line 487 of JSON/Schema/Modern.pm, avg 280µs/call # spent 6.07ms making 22 calls to JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_ref, avg 276µs/call |
| 229 | |||||
| 230 | 12930 | 4.19ms | # spent 61.5s (155ms+61.3) within JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_dynamicRef which was called 2586 times, avg 23.8ms/call:
# 2586 times (155ms+61.3s) by JSON::Schema::Modern::_eval_subschema at line 578 of JSON/Schema/Modern.pm, avg 23.8ms/call | ||
| 231 | 2586 | 12.5ms | 5172 | 861ms | my $uri = Mojo::URL->new($schema->{'$dynamicRef'})->to_abs($state->{initial_schema_uri}); # spent 496ms making 2586 calls to Mojo::URL::to_abs, avg 192µs/call
# spent 365ms making 2586 calls to Mojo::URL::new, avg 141µs/call |
| 232 | 2586 | 8.46ms | 2586 | 818ms | my $schema_info = $state->{evaluator}->_fetch_from_uri($uri); # spent 818ms making 2586 calls to JSON::Schema::Modern::_fetch_from_uri, avg 316µs/call |
| 233 | 2586 | 1.26ms | 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 | 10.1ms | 5172 | 4.22ms | if (length $uri->fragment and exists $schema_info->{schema}{'$dynamicAnchor'} # spent 4.22ms making 5172 calls to Mojo::URL::fragment, avg 816ns/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.10ms | foreach my $base_scope ($state->{dynamic_scope}->@*) { | ||
| 243 | 2586 | 4.51ms | 5172 | 362ms | my $test_uri = Mojo::URL->new($base_scope)->fragment($anchor); # spent 359ms making 2586 calls to Mojo::URL::new, avg 139µs/call
# spent 2.49ms making 2586 calls to Mojo::URL::fragment, avg 962ns/call |
| 244 | 2586 | 5.22ms | 2586 | 557ms | my $dynamic_anchor_subschema_info = $state->{evaluator}->_fetch_from_uri($test_uri); # spent 557ms making 2586 calls to JSON::Schema::Modern::_fetch_from_uri, avg 215µs/call |
| 245 | 2586 | 2.51ms | if (($dynamic_anchor_subschema_info->{schema}->{'$dynamicAnchor'}//'') eq $anchor) { | ||
| 246 | 2586 | 10.6ms | $uri = $test_uri; | ||
| 247 | 2586 | 7.08ms | last; | ||
| 248 | } | ||||
| 249 | } | ||||
| 250 | } | ||||
| 251 | |||||
| 252 | 2586 | 46.2ms | 2586 | 0s | return $self->eval_subschema_at_uri($data, $schema, $state, $uri); # spent 126s making 2586 calls to JSON::Schema::Modern::Vocabulary::eval_subschema_at_uri, avg 48.6ms/call, recursion: max depth 34, sum of overlapping time 126s |
| 253 | } | ||||
| 254 | |||||
| 255 | 44 | 7µs | # spent 5.26ms (511µs+4.75) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_vocabulary which was called 11 times, avg 478µs/call:
# 11 times (511µs+4.75ms) by JSON::Schema::Modern::_traverse_subschema at line 487 of JSON/Schema/Modern.pm, avg 478µs/call | ||
| 256 | 11 | 18µs | 11 | 76µs | return if not assert_keyword_type($state, $schema, 'object'); # spent 76µs making 11 calls to JSON::Schema::Modern::Utilities::assert_keyword_type, avg 7µs/call |
| 257 | |||||
| 258 | return E($state, '$vocabulary can only appear at the schema resource root') | ||||
| 259 | 11 | 4µs | if length($state->{schema_path}); | ||
| 260 | |||||
| 261 | 11 | 86µs | my $valid = 1; | ||
| 262 | |||||
| 263 | 11 | 4µs | my @vocabulary_classes; | ||
| 264 | 11 | 58µs | 11 | 15µs | foreach my $uri (sort keys $schema->{'$vocabulary'}->%*) { # spent 15µs making 11 calls to JSON::Schema::Modern::Vocabulary::Core::CORE:sort, avg 1µs/call |
| 265 | 31 | 185µs | 31 | 490µs | $valid = 0, next if not assert_keyword_type({ %$state, _schema_path_suffix => $uri }, $schema, 'boolean'); # spent 490µs making 31 calls to JSON::Schema::Modern::Utilities::assert_keyword_type, avg 16µs/call |
| 266 | 31 | 115µs | 31 | 4.17ms | $valid = 0, next if not assert_uri({ %$state, _schema_path_suffix => $uri }, undef, $uri); # spent 4.17ms making 31 calls to JSON::Schema::Modern::Utilities::assert_uri, avg 134µ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 | 38µ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 | 99µs | # spent 2.38ms (851µs+1.53) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_comment which was called 118 times, avg 20µs/call:
# 118 times (851µs+1.53ms) by JSON::Schema::Modern::_traverse_subschema at line 487 of JSON/Schema/Modern.pm, avg 20µs/call | ||
| 285 | 118 | 163µs | 118 | 1.53ms | return if not assert_keyword_type($state, $schema, 'string'); # spent 1.53ms making 118 calls to JSON::Schema::Modern::Utilities::assert_keyword_type, avg 13µs/call |
| 286 | 118 | 302µ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 | 113µs | 12 | 69.5ms | # spent 69.6ms (115µs+69.5) within JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_defs which was called 12 times, avg 5.80ms/call:
# 12 times (115µs+69.5ms) by JSON::Schema::Modern::_traverse_subschema at line 487 of JSON/Schema/Modern.pm, avg 5.80ms/call # spent 79.2ms making 12 calls to JSON::Schema::Modern::Vocabulary::traverse_object_schemas, avg 6.60ms/call, recursion: max depth 2, sum of overlapping time 9.76ms |
| 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 | 0s | # spent 390µs (63+327) within JSON::Schema::Modern::Vocabulary::Core::__fetch_vocabulary_data which was called:
# once (63µs+327µ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 | 0s | $valid = E($state, '$vocabulary can only appear at the document root') if length $schema_info->{document_path}; | ||
| 309 | 1 | 0s | $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 | 9µs | 1 | 2µs | foreach my $uri (sort keys $schema_info->{schema}{'$vocabulary'}->%*) { # spent 2µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::CORE:sort |
| 316 | 8 | 7µs | 8 | 89µs | my $class_info = $state->{evaluator}->_get_vocabulary_class($uri); # spent 89µs making 8 calls to JSON::Schema::Modern::_get_vocabulary_class, avg 11µs/call |
| 317 | $valid = E({ %$state, _schema_path_suffix => $uri }, '"%s" is not a known vocabulary', $uri), next | ||||
| 318 | 8 | 17µs | 8 | 4µs | if $schema_info->{schema}{'$vocabulary'}{$uri} and not $class_info; # spent 4µs making 8 calls to JSON::PP::Boolean::__ANON__[JSON/PP/Boolean.pm:7], avg 500ns/call |
| 319 | |||||
| 320 | 8 | 0s | next if not $class_info; # vocabulary is not known, but marked as false in the metaschema | ||
| 321 | |||||
| 322 | 8 | 7µ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 | 1µs | if $spec_version ne $schema_info->{specification_version}; | ||
| 326 | |||||
| 327 | 8 | 4µs | push @vocabulary_classes, $class; | ||
| 328 | } | ||||
| 329 | |||||
| 330 | @vocabulary_classes = sort { | ||||
| 331 | 16 | 38µs | 31 | 101µs | $a->evaluation_order <=> $b->evaluation_order # spent 87µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::CORE:sort
# 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 5 calls to JSON::Schema::Modern::Vocabulary::FormatAnnotation::evaluation_order, avg 400ns/call
# spent 2µs making 5 calls to JSON::Schema::Modern::Vocabulary::MetaData::evaluation_order, avg 400ns/call
# spent 1µs making 3 calls to JSON::Schema::Modern::Vocabulary::Content::evaluation_order, avg 333ns/call
# spent 1µs making 2 calls to JSON::Schema::Modern::Vocabulary::Unevaluated::evaluation_order, avg 500ns/call
# spent 1µs making 3 calls to JSON::Schema::Modern::Vocabulary::evaluation_order, avg 333ns/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 | 5µs | 1 | 145µs | [ $schema_info->{specification_version}, \@vocabulary_classes ]) if $valid; # spent 145µs making 1 call to JSON::Schema::Modern::_set_metaschema_vocabulary_classes |
| 341 | |||||
| 342 | 1 | 2µs | return ($schema_info->{specification_version}, $valid ? \@vocabulary_classes : []); | ||
| 343 | } | ||||
| 344 | |||||
| 345 | 1 | 5µs | 1; | ||
| 346 | |||||
| 347 | 1 | 25µs | 1 | 303µs | __END__ # spent 303µs making 1 call to B::Hooks::EndOfScope::XS::__ANON__[B/Hooks/EndOfScope/XS.pm:26] |
# spent 66µs within JSON::Schema::Modern::Vocabulary::Core::CORE:match which was called 30 times, avg 2µs/call:
# 30 times (66µs+0s) by JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_anchor at line 160, avg 2µs/call | |||||
# spent 104µs (90+14) within JSON::Schema::Modern::Vocabulary::Core::CORE:sort which was called 13 times, avg 8µs/call:
# 11 times (15µs+0s) by JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_vocabulary at line 264, avg 1µs/call
# once (73µs+14µs) by JSON::Schema::Modern::Vocabulary::Core::__fetch_vocabulary_data at line 331
# once (2µs+0s) by JSON::Schema::Modern::Vocabulary::Core::__fetch_vocabulary_data at line 315 | |||||
# spent 98.3ms within JSON::Schema::Modern::Vocabulary::Core::CORE:subst which was called 181034 times, avg 543ns/call:
# 181034 times (98.3ms+0s) by JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_id at line 101, avg 543ns/call |