Filename | /Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern.pm |
Statements | Executed 18081506 statements in 15.0s |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
91595 | 3 | 2 | 11.8s | 99.6s | _eval_subschema (recurses: max depth 74, inclusive time 2294s) | JSON::Schema::Modern::
46881 | 5 | 3 | 2.44s | 21.5s | _fetch_from_uri | JSON::Schema::Modern::
2123434 | 4 | 1 | 389ms | 389ms | CORE:match (opcode) | JSON::Schema::Modern::
1 | 1 | 1 | 327ms | 99.9s | evaluate | JSON::Schema::Modern::
2863 | 5 | 2 | 204ms | 685ms | _traverse_subschema (recurses: max depth 6, inclusive time 702ms) | JSON::Schema::Modern::
39123 | 1 | 1 | 200ms | 7.07s | _get_or_load_resource | JSON::Schema::Modern::
87311 | 1 | 1 | 124ms | 124ms | CORE:qr (opcode) | JSON::Schema::Modern::
87311 | 1 | 1 | 116ms | 116ms | CORE:regcomp (opcode) | JSON::Schema::Modern::
128937 | 4 | 1 | 96.6ms | 96.6ms | CORE:sort (opcode) | JSON::Schema::Modern::
743 | 4 | 3 | 34.2ms | 1.47s | traverse (recurses: max depth 1, inclusive time 2.72ms) | JSON::Schema::Modern::
1 | 1 | 1 | 9.64ms | 85.9ms | BEGIN@31 | JSON::Schema::Modern::
1 | 1 | 1 | 8.37ms | 11.5ms | BEGIN@25 | JSON::Schema::Modern::
743 | 1 | 1 | 7.91ms | 129ms | _get_metaschema_info (recurses: max depth 1, inclusive time 53µs) | JSON::Schema::Modern::
1 | 1 | 1 | 4.74ms | 7.74ms | BEGIN@36 | JSON::Schema::Modern::
1 | 1 | 1 | 2.99ms | 55.2ms | BEGIN@35 | JSON::Schema::Modern::
1 | 1 | 1 | 2.59ms | 36.9ms | BEGIN@34 | JSON::Schema::Modern::
1 | 1 | 1 | 2.56ms | 2.77ms | BEGIN@28 | JSON::Schema::Modern::
1 | 1 | 1 | 2.51ms | 29.8ms | BEGIN@27 | JSON::Schema::Modern::
1 | 1 | 1 | 2.31ms | 166ms | BEGIN@23 | JSON::Schema::Modern::
1 | 1 | 1 | 1.68ms | 66.8ms | BEGIN@33 | JSON::Schema::Modern::
1 | 1 | 1 | 1.38ms | 10.1ms | BEGIN@18 | JSON::Schema::Modern::
9 | 3 | 2 | 1.16ms | 159ms | add_schema | JSON::Schema::Modern::
1 | 1 | 1 | 1.13ms | 1.37ms | BEGIN@30 | JSON::Schema::Modern::
1 | 1 | 1 | 636µs | 4.68ms | BEGIN@29 | JSON::Schema::Modern::
15 | 1 | 1 | 557µs | 12.0ms | __ANON__[:672] | JSON::Schema::Modern::
1 | 1 | 1 | 122µs | 66.5ms | add_vocabulary | JSON::Schema::Modern::
1 | 1 | 1 | 76µs | 60.3ms | __ANON__[:696] | JSON::Schema::Modern::
1 | 1 | 1 | 55µs | 104µs | BEGIN@32 | JSON::Schema::Modern::
1 | 1 | 1 | 46µs | 196µs | __ANON__[:738] | JSON::Schema::Modern::
126 | 3 | 1 | 32µs | 32µs | CORE:subst (opcode) | JSON::Schema::Modern::
1 | 1 | 1 | 24µs | 735µs | __ANON__[:124] | JSON::Schema::Modern::
1 | 1 | 1 | 21µs | 607µs | BEGIN@12 | JSON::Schema::Modern::
1 | 1 | 1 | 21µs | 93µs | BEGIN@20 | JSON::Schema::Modern::
1 | 1 | 1 | 20µs | 22µs | BEGIN@1 | OpenAPI::Modern::
1 | 1 | 1 | 19µs | 93µs | BEGIN@774 | JSON::Schema::Modern::
1 | 1 | 1 | 18µs | 51µs | BEGIN@780 | JSON::Schema::Modern::
1 | 1 | 1 | 17µs | 151µs | BEGIN@13 | JSON::Schema::Modern::
1 | 1 | 1 | 16µs | 48µs | BEGIN@21 | JSON::Schema::Modern::
26 | 2 | 1 | 16µs | 16µs | CACHED_METASCHEMAS (xsub) | JSON::Schema::Modern::
1 | 1 | 1 | 14µs | 47µs | BEGIN@14 | JSON::Schema::Modern::
1 | 1 | 1 | 12µs | 943µs | BEGIN@37 | JSON::Schema::Modern::
1 | 1 | 1 | 11µs | 98µs | BEGIN@46 | JSON::Schema::Modern::
15 | 1 | 1 | 11µs | 11µs | METASCHEMA_URIS (xsub) | JSON::Schema::Modern::
1 | 1 | 1 | 10µs | 31µs | BEGIN@15 | JSON::Schema::Modern::
1 | 1 | 1 | 10µs | 40µs | BEGIN@19 | JSON::Schema::Modern::
1 | 1 | 1 | 9µs | 24µs | BEGIN@22 | JSON::Schema::Modern::
1 | 1 | 1 | 9µs | 84µs | BEGIN@24 | JSON::Schema::Modern::
1 | 1 | 1 | 8µs | 8µs | BEGIN@10 | JSON::Schema::Modern::
1 | 1 | 1 | 8µs | 18µs | BEGIN@16 | JSON::Schema::Modern::
1 | 1 | 1 | 8µs | 54µs | BEGIN@26 | JSON::Schema::Modern::
1 | 1 | 1 | 8µs | 39µs | BEGIN@47 | JSON::Schema::Modern::
1 | 1 | 1 | 8µs | 12µs | __ANON__[:133] | JSON::Schema::Modern::
1 | 1 | 1 | 7µs | 10µs | __ANON__[:90] | JSON::Schema::Modern::
1 | 1 | 1 | 6µs | 8µs | __ANON__[:69] | JSON::Schema::Modern::
1 | 1 | 1 | 6µs | 45µs | __ANON__[:896] | JSON::Schema::Modern::
1 | 1 | 1 | 5µs | 185µs | BEGIN@11 | JSON::Schema::Modern::
1 | 1 | 1 | 5µs | 16µs | BEGIN@17 | JSON::Schema::Modern::
1 | 1 | 1 | 5µs | 24µs | BEGIN@2 | OpenAPI::Modern::
1 | 1 | 1 | 3µs | 3µs | __ANON__[:636] | JSON::Schema::Modern::
1 | 1 | 1 | 1µs | 1µs | SPECIFICATION_VERSION_DEFAULT (xsub) | JSON::Schema::Modern::
2 | 2 | 1 | 1µs | 1µs | __ANON__ (xsub) | JSON::Schema::Modern::
1 | 1 | 1 | 1µs | 1µs | __ANON__[:121] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | FREEZE | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | THAW | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | __ANON__[:177] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | __ANON__[:53] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | __ANON__[:55] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | __ANON__[:56] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | __ANON__[:692] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | __ANON__[:859] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | __ANON__[:913] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | __ANON__[:919] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | __ANON__[:922] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | __ANON__[:931] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | __ANON__[:933] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | __ANON__[:935] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | __ANON__[:948] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | __ANON__[:953] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | __ANON__[:955] | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | evaluate_json_string | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | get | JSON::Schema::Modern::
0 | 0 | 0 | 0s | 0s | validate_schema | JSON::Schema::Modern::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | 2 | 22µs | 2 | 24µs | # spent 22µs (20+2) within OpenAPI::Modern::BEGIN@1 which was called:
# once (20µs+2µs) by OpenAPI::Modern::BEGIN@26 at line 1 # spent 22µs making 1 call to OpenAPI::Modern::BEGIN@1
# spent 2µs making 1 call to strict::import |
2 | 2 | 40µs | 2 | 43µs | # spent 24µs (5+19) within OpenAPI::Modern::BEGIN@2 which was called:
# once (5µs+19µs) by OpenAPI::Modern::BEGIN@26 at line 2 # spent 24µs making 1 call to OpenAPI::Modern::BEGIN@2
# spent 19µs making 1 call to warnings::import |
3 | package JSON::Schema::Modern; # git description: v0.555-12-g5cd1dee9 | ||||
4 | # vim: set ts=8 sts=2 sw=2 tw=100 et : | ||||
5 | # ABSTRACT: Validate data against a schema | ||||
6 | # KEYWORDS: JSON Schema validator data validation structure specification | ||||
7 | |||||
8 | 1 | 1µs | our $VERSION = '0.556'; | ||
9 | |||||
10 | 2 | 27µs | 1 | 8µs | # spent 8µs within JSON::Schema::Modern::BEGIN@10 which was called:
# once (8µs+0s) by OpenAPI::Modern::BEGIN@26 at line 10 # spent 8µs making 1 call to JSON::Schema::Modern::BEGIN@10 |
11 | 2 | 25µs | 2 | 365µs | # spent 185µs (5+180) within JSON::Schema::Modern::BEGIN@11 which was called:
# once (5µs+180µs) by OpenAPI::Modern::BEGIN@26 at line 11 # spent 185µs making 1 call to JSON::Schema::Modern::BEGIN@11
# spent 180µs making 1 call to Moo::import |
12 | 3 | 41µs | 3 | 1.19ms | # spent 607µs (21+586) within JSON::Schema::Modern::BEGIN@12 which was called:
# once (21µs+586µs) by OpenAPI::Modern::BEGIN@26 at line 12 # spent 607µs making 1 call to JSON::Schema::Modern::BEGIN@12
# spent 572µs making 1 call to strictures::import
# spent 14µs making 1 call to strictures::VERSION |
13 | 2 | 60µs | 2 | 285µs | # spent 151µs (17+134) within JSON::Schema::Modern::BEGIN@13 which was called:
# once (17µs+134µs) by OpenAPI::Modern::BEGIN@26 at line 13 # spent 151µs making 1 call to JSON::Schema::Modern::BEGIN@13
# spent 134µs making 1 call to experimental::import |
14 | 2 | 30µs | 2 | 50µs | # spent 47µs (14+33) within JSON::Schema::Modern::BEGIN@14 which was called:
# once (14µs+33µs) by OpenAPI::Modern::BEGIN@26 at line 14 # spent 47µs making 1 call to JSON::Schema::Modern::BEGIN@14
# spent 3µs making 1 call to if::import |
15 | 2 | 20µs | 2 | 38µs | # spent 31µs (10+21) within JSON::Schema::Modern::BEGIN@15 which was called:
# once (10µs+21µs) by OpenAPI::Modern::BEGIN@26 at line 15 # spent 31µs making 1 call to JSON::Schema::Modern::BEGIN@15
# spent 7µs making 1 call to if::unimport |
16 | 2 | 16µs | 2 | 18µs | # spent 18µs (8+10) within JSON::Schema::Modern::BEGIN@16 which was called:
# once (8µs+10µs) by OpenAPI::Modern::BEGIN@26 at line 16 # spent 18µs making 1 call to JSON::Schema::Modern::BEGIN@16
# spent 0s making 1 call to if::unimport |
17 | 2 | 13µs | 2 | 18µs | # spent 16µs (5+11) within JSON::Schema::Modern::BEGIN@17 which was called:
# once (5µs+11µs) by OpenAPI::Modern::BEGIN@26 at line 17 # spent 16µs making 1 call to JSON::Schema::Modern::BEGIN@17
# spent 2µs making 1 call to if::unimport |
18 | 2 | 265µs | 2 | 10.2ms | # spent 10.1ms (1.38+8.75) within JSON::Schema::Modern::BEGIN@18 which was called:
# once (1.38ms+8.75ms) by OpenAPI::Modern::BEGIN@26 at line 18 # spent 10.1ms making 1 call to JSON::Schema::Modern::BEGIN@18
# spent 46µs making 1 call to Exporter::import |
19 | 2 | 77µs | 2 | 70µs | # spent 40µs (10+30) within JSON::Schema::Modern::BEGIN@19 which was called:
# once (10µs+30µs) by OpenAPI::Modern::BEGIN@26 at line 19 # spent 40µs making 1 call to JSON::Schema::Modern::BEGIN@19
# spent 30µs making 1 call to Exporter::import |
20 | 3 | 58µs | 3 | 119µs | # spent 93µs (21+72) within JSON::Schema::Modern::BEGIN@20 which was called:
# once (21µs+72µs) by OpenAPI::Modern::BEGIN@26 at line 20 # spent 93µs making 1 call to JSON::Schema::Modern::BEGIN@20
# spent 15µs making 1 call to List::Util::import
# spent 11µs making 1 call to UNIVERSAL::VERSION |
21 | 3 | 47µs | 3 | 80µs | # spent 48µs (16+32) within JSON::Schema::Modern::BEGIN@21 which was called:
# once (16µs+32µs) by OpenAPI::Modern::BEGIN@26 at line 21 # spent 48µs making 1 call to JSON::Schema::Modern::BEGIN@21
# spent 25µs making 1 call to Exporter::import
# spent 7µs making 1 call to UNIVERSAL::VERSION |
22 | 2 | 23µs | 2 | 39µs | # spent 24µs (9+15) within JSON::Schema::Modern::BEGIN@22 which was called:
# once (9µs+15µs) by OpenAPI::Modern::BEGIN@26 at line 22 # spent 24µs making 1 call to JSON::Schema::Modern::BEGIN@22
# spent 15µs making 1 call to Exporter::import |
23 | 2 | 658µs | 2 | 166ms | # spent 166ms (2.31+164) within JSON::Schema::Modern::BEGIN@23 which was called:
# once (2.31ms+164ms) by OpenAPI::Modern::BEGIN@26 at line 23 # spent 166ms making 1 call to JSON::Schema::Modern::BEGIN@23
# spent 4µs making 1 call to Mojo::Base::import |
24 | 2 | 21µs | 2 | 159µs | # spent 84µs (9+75) within JSON::Schema::Modern::BEGIN@24 which was called:
# once (9µs+75µs) by OpenAPI::Modern::BEGIN@26 at line 24 # spent 84µs making 1 call to JSON::Schema::Modern::BEGIN@24
# spent 75µs making 1 call to Exporter::import |
25 | 2 | 495µs | 2 | 11.5ms | # spent 11.5ms (8.37+3.12) within JSON::Schema::Modern::BEGIN@25 which was called:
# once (8.37ms+3.12ms) by OpenAPI::Modern::BEGIN@26 at line 25 # spent 11.5ms making 1 call to JSON::Schema::Modern::BEGIN@25
# spent 36µs making 1 call to Exporter::import |
26 | 2 | 19µs | 2 | 100µs | # spent 54µs (8+46) within JSON::Schema::Modern::BEGIN@26 which was called:
# once (8µs+46µs) by OpenAPI::Modern::BEGIN@26 at line 26 # spent 54µs making 1 call to JSON::Schema::Modern::BEGIN@26
# spent 46µs making 1 call to Exporter::import |
27 | 2 | 609µs | 2 | 29.9ms | # spent 29.8ms (2.51+27.3) within JSON::Schema::Modern::BEGIN@27 which was called:
# once (2.51ms+27.3ms) by OpenAPI::Modern::BEGIN@26 at line 27 # spent 29.8ms making 1 call to JSON::Schema::Modern::BEGIN@27
# spent 35µs making 1 call to Exporter::import |
28 | 2 | 777µs | 2 | 2.79ms | # spent 2.77ms (2.56+203µs) within JSON::Schema::Modern::BEGIN@28 which was called:
# once (2.56ms+203µs) by OpenAPI::Modern::BEGIN@26 at line 28 # spent 2.77ms making 1 call to JSON::Schema::Modern::BEGIN@28
# spent 20µs making 1 call to Module::Runtime::import |
29 | 3 | 493µs | 3 | 8.62ms | # spent 4.68ms (636µs+4.05) within JSON::Schema::Modern::BEGIN@29 which was called:
# once (636µs+4.05ms) by OpenAPI::Modern::BEGIN@26 at line 29 # spent 4.68ms making 1 call to JSON::Schema::Modern::BEGIN@29
# spent 3.93ms making 1 call to MooX::TypeTiny::import
# spent 9µs making 1 call to UNIVERSAL::VERSION |
30 | 2 | 558µs | 2 | 1.44ms | # spent 1.37ms (1.13+239µs) within JSON::Schema::Modern::BEGIN@30 which was called:
# once (1.13ms+239µs) by OpenAPI::Modern::BEGIN@26 at line 30 # spent 1.37ms making 1 call to JSON::Schema::Modern::BEGIN@30
# spent 74µs making 1 call to MooX::HandlesVia::import |
31 | 3 | 520µs | 3 | 88.9ms | # spent 85.9ms (9.64+76.3) within JSON::Schema::Modern::BEGIN@31 which was called:
# once (9.64ms+76.3ms) by OpenAPI::Modern::BEGIN@26 at line 31 # spent 85.9ms making 1 call to JSON::Schema::Modern::BEGIN@31
# spent 2.96ms making 1 call to Exporter::Tiny::import
# spent 20µs making 1 call to UNIVERSAL::VERSION |
32 | 2 | 51µs | 2 | 153µs | # spent 104µs (55+49) within JSON::Schema::Modern::BEGIN@32 which was called:
# once (55µs+49µs) by OpenAPI::Modern::BEGIN@26 at line 32 # spent 104µs making 1 call to JSON::Schema::Modern::BEGIN@32
# spent 49µs making 1 call to Feature::Compat::Try::import |
33 | 2 | 639µs | 2 | 66.8ms | # spent 66.8ms (1.68+65.1) within JSON::Schema::Modern::BEGIN@33 which was called:
# once (1.68ms+65.1ms) by OpenAPI::Modern::BEGIN@26 at line 33 # spent 66.8ms making 1 call to JSON::Schema::Modern::BEGIN@33
# spent 0s making 1 call to JSON::Schema::Modern::__ANON__ |
34 | 2 | 632µs | 2 | 36.9ms | # spent 36.9ms (2.59+34.3) within JSON::Schema::Modern::BEGIN@34 which was called:
# once (2.59ms+34.3ms) by OpenAPI::Modern::BEGIN@26 at line 34 # spent 36.9ms making 1 call to JSON::Schema::Modern::BEGIN@34
# spent 1µs making 1 call to JSON::Schema::Modern::__ANON__ |
35 | 2 | 704µs | 2 | 55.2ms | # spent 55.2ms (2.99+52.2) within JSON::Schema::Modern::BEGIN@35 which was called:
# once (2.99ms+52.2ms) by OpenAPI::Modern::BEGIN@26 at line 35 # spent 55.2ms making 1 call to JSON::Schema::Modern::BEGIN@35
# spent 6µs making 1 call to Mojo::Base::import |
36 | 2 | 748µs | 2 | 7.83ms | # spent 7.74ms (4.74+2.99) within JSON::Schema::Modern::BEGIN@36 which was called:
# once (4.74ms+2.99ms) by OpenAPI::Modern::BEGIN@26 at line 36 # spent 7.74ms making 1 call to JSON::Schema::Modern::BEGIN@36
# spent 96µs making 1 call to Exporter::import |
37 | 2 | 94µs | 2 | 1.87ms | # spent 943µs (12+931) within JSON::Schema::Modern::BEGIN@37 which was called:
# once (12µs+931µs) by OpenAPI::Modern::BEGIN@26 at line 37 # spent 943µs making 1 call to JSON::Schema::Modern::BEGIN@37
# spent 931µs making 1 call to namespace::clean::import |
38 | |||||
39 | 1 | 2µs | our @CARP_NOT = qw( | ||
40 | JSON::Schema::Modern::Document | ||||
41 | JSON::Schema::Modern::Vocabulary | ||||
42 | JSON::Schema::Modern::Vocabulary::Applicator | ||||
43 | OpenAPI::Modern | ||||
44 | ); | ||||
45 | |||||
46 | 2 | 71µs | 2 | 185µs | # spent 98µs (11+87) within JSON::Schema::Modern::BEGIN@46 which was called:
# once (11µs+87µs) by OpenAPI::Modern::BEGIN@26 at line 46 # spent 98µs making 1 call to JSON::Schema::Modern::BEGIN@46
# spent 87µs making 1 call to constant::import |
47 | 2 | 5.02ms | 2 | 70µs | # spent 39µs (8+31) within JSON::Schema::Modern::BEGIN@47 which was called:
# once (8µs+31µs) by OpenAPI::Modern::BEGIN@26 at line 47 # spent 39µs making 1 call to JSON::Schema::Modern::BEGIN@47
# spent 31µs making 1 call to constant::import |
48 | |||||
49 | has specification_version => ( | ||||
50 | is => 'ro', | ||||
51 | isa => Enum(SPECIFICATION_VERSIONS_SUPPORTED), | ||||
52 | coerce => sub { | ||||
53 | return $_[0] if any { $_[0] eq $_ } SPECIFICATION_VERSIONS_SUPPORTED->@*; | ||||
54 | my $real = 'draft'.($_[0]//''); | ||||
55 | (any { $real eq $_ } SPECIFICATION_VERSIONS_SUPPORTED->@*) ? $real : $_[0]; | ||||
56 | }, | ||||
57 | 1 | 14µs | 2 | 2.85ms | ); # spent 1.49ms making 1 call to Types::Standard::Enum
# spent 1.36ms making 1 call to MooX::HandlesVia::has |
58 | |||||
59 | 1 | 27µs | 3 | 741µs | has output_format => ( # spent 456µs making 1 call to MooX::HandlesVia::has
# spent 284µs making 1 call to Types::Standard::Enum
# spent 1µs making 1 call to JSON::Schema::Modern::Result::OUTPUT_FORMATS |
60 | is => 'ro', | ||||
61 | isa => Enum(JSON::Schema::Modern::Result->OUTPUT_FORMATS), | ||||
62 | default => 'basic', | ||||
63 | ); | ||||
64 | |||||
65 | has short_circuit => ( | ||||
66 | is => 'ro', | ||||
67 | isa => Bool, | ||||
68 | lazy => 1, | ||||
69 | 1 | 7µs | 1 | 2µs | # spent 8µs (6+2) within JSON::Schema::Modern::__ANON__[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern.pm:69] which was called:
# once (6µs+2µs) by JSON::Schema::Modern::short_circuit at line 23 of (eval 312)[Sub/Quote.pm:3] # spent 2µs making 1 call to JSON::Schema::Modern::output_format |
70 | 1 | 6µs | 2 | 735µs | ); # spent 733µs making 1 call to MooX::HandlesVia::has
# spent 2µs making 1 call to Types::Standard::Bool |
71 | |||||
72 | 1 | 4µs | 2 | 684µs | has max_traversal_depth => ( # spent 683µs making 1 call to MooX::HandlesVia::has
# spent 1µs making 1 call to Types::Standard::Int |
73 | is => 'ro', | ||||
74 | isa => Int, | ||||
75 | default => 50, | ||||
76 | ); | ||||
77 | |||||
78 | 1 | 15µs | 2 | 669µs | has validate_formats => ( # spent 657µs making 1 call to MooX::HandlesVia::has
# spent 12µs making 1 call to Types::Standard::Bool |
79 | is => 'ro', | ||||
80 | isa => Bool, | ||||
81 | default => 0, # as specified by https://json-schema.org/draft/<version>/schema#/$vocabulary | ||||
82 | ); | ||||
83 | |||||
84 | has validate_content_schemas => ( | ||||
85 | is => 'ro', | ||||
86 | isa => Bool, | ||||
87 | lazy => 1, | ||||
88 | # defaults to false in latest versions, as specified by | ||||
89 | # https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.8.2 | ||||
90 | 1 | 6µs | 1 | 3µs | # spent 10µs (7+3) within JSON::Schema::Modern::__ANON__[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern.pm:90] which was called:
# once (7µs+3µs) by JSON::Schema::Modern::validate_content_schemas at line 23 of (eval 315)[Sub/Quote.pm:3] # spent 3µs making 1 call to JSON::Schema::Modern::specification_version |
91 | 1 | 4µs | 2 | 704µs | ); # spent 702µs making 1 call to MooX::HandlesVia::has
# spent 2µs making 1 call to Types::Standard::Bool |
92 | |||||
93 | 1 | 2µs | 2 | 316µs | has collect_annotations => ( # spent 315µs making 1 call to MooX::HandlesVia::has
# spent 1µs making 1 call to Types::Standard::Bool |
94 | is => 'ro', | ||||
95 | isa => Bool, | ||||
96 | ); | ||||
97 | |||||
98 | 1 | 3µs | 2 | 298µs | has scalarref_booleans => ( # spent 297µs making 1 call to MooX::HandlesVia::has
# spent 1µs making 1 call to Types::Standard::Bool |
99 | is => 'ro', | ||||
100 | isa => Bool, | ||||
101 | ); | ||||
102 | |||||
103 | 1 | 2µs | 2 | 304µs | has strict => ( # spent 303µs making 1 call to MooX::HandlesVia::has
# spent 1µs making 1 call to Types::Standard::Bool |
104 | is => 'ro', | ||||
105 | isa => Bool, | ||||
106 | ); | ||||
107 | |||||
108 | has _format_validations => ( | ||||
109 | is => 'bare', | ||||
110 | isa => my $format_type = Dict[ | ||||
111 | (map +($_ => Optional[CodeRef]), qw(date-time date time duration email idn-email hostname idn-hostname ipv4 ipv6 uri uri-reference iri iri-reference uuid uri-template json-pointer relative-json-pointer regex)), | ||||
112 | Slurpy[HashRef[Dict[type => Enum[qw(null object array boolean string number integer)], sub => CodeRef]]], | ||||
113 | ], | ||||
114 | init_arg => 'format_validations', | ||||
115 | handles_via => 'Hash', | ||||
116 | handles => { | ||||
117 | _get_format_validation => 'get', | ||||
118 | add_format_validation => 'set', | ||||
119 | }, | ||||
120 | lazy => 1, | ||||
121 | 1 | 2µs | # spent 1µs within JSON::Schema::Modern::__ANON__[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern.pm:121] which was called:
# once (1µs+0s) by JSON::Schema::Modern::_assert__format_validations at line 23 of (eval 325)[Sub/Quote.pm:3] | ||
122 | 1 | 74µs | 45 | 31.2ms | ); # spent 15.1ms making 1 call to MooX::HandlesVia::has
# spent 8.88ms making 2 calls to Types::Standard::Dict, avg 4.44ms/call
# spent 3.42ms making 1 call to Types::Standard::Slurpy
# spent 2.14ms making 1 call to Types::Standard::HashRef
# spent 932µs making 19 calls to Types::Standard::Optional, avg 49µs/call
# spent 674µs making 1 call to Types::Standard::Enum
# spent 11µs making 20 calls to Types::Standard::CodeRef, avg 550ns/call |
123 | |||||
124 | 5 | 53µs | 17 | 985µs | # spent 735µs (24+711) within JSON::Schema::Modern::__ANON__[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern.pm:124] which was called:
# once (24µs+711µs) by JSON::Schema::Modern::add_format_validation at line 4 of (eval 327)[Class/Method/Modifiers.pm:148] # spent 668µs making 5 calls to Sub::Defer::__ANON__[Sub/Defer.pm:178], avg 134µs/call
# spent 293µs making 1 call to Moo::before
# spent 20µs making 10 calls to Type::Tiny::__ANON__[Type/Tiny.pm:89], avg 2µs/call
# spent 4µs making 1 call to List::Util::pairs |
125 | |||||
126 | 4 | 1µs | # spent 12µs (8+4) within JSON::Schema::Modern::__ANON__[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern.pm:133] which was called:
# once (8µs+4µs) by JSON::Schema::Modern::__ANON__[(eval 329)[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/Class/Method/Modifiers.pm:89]:1] at line 1 of (eval 329)[Class/Method/Modifiers.pm:89] | ||
127 | 1 | 2µs | 1 | 4µs | my $args = $class->$orig(@args); # spent 4µs making 1 call to Moo::Object::BUILDARGS |
128 | croak 'output_format: strict_basic can only be used with specification_version: draft2019-09' | ||||
129 | if ($args->{output_format}//'') eq 'strict_basic' | ||||
130 | 1 | 1µs | and ($args->{specification_version}//'') ne 'draft2019-09'; | ||
131 | |||||
132 | 1 | 5µs | return $args; | ||
133 | 1 | 5µs | 1 | 264µs | }; # spent 264µs making 1 call to Moo::around |
134 | |||||
135 | # spent 159ms (1.16+158) within JSON::Schema::Modern::add_schema which was called 9 times, avg 17.7ms/call:
# 6 times (867µs+147ms) by JSON::Schema::Modern::Document::OpenAPI::_add_vocab_and_default_schemas at line 204 of JSON/Schema/Modern/Document/OpenAPI.pm, avg 24.6ms/call
# 2 times (110µs+1.55ms) by JSON::Schema::Modern::Document::OpenAPI::_add_vocab_and_default_schemas at line 206 of JSON/Schema/Modern/Document/OpenAPI.pm, avg 832µs/call
# once (181µs+9.74ms) by OpenAPI::Modern::__ANON__[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/OpenAPI/Modern.pm:79] at line 75 of OpenAPI/Modern.pm | ||||
136 | 9 | 9µs | croak 'insufficient arguments' if @_ < 2; | ||
137 | 9 | 9µs | my $self = shift; | ||
138 | |||||
139 | # TODO: resolve $uri against $self->base_uri | ||||
140 | 9 | 62µs | 10 | 918µs | my $uri = !is_ref($_[0]) ? Mojo::URL->new(shift) # spent 892µs making 9 calls to Mojo::URL::new, avg 99µs/call
# spent 26µs making 1 call to Safe::Isa::__ANON__[Safe/Isa.pm:23] |
141 | : $_[0]->$_isa('Mojo::URL') ? shift : Mojo::URL->new; | ||||
142 | |||||
143 | 9 | 18µs | 9 | 15µs | croak 'cannot add a schema with a uri with a fragment' if defined $uri->fragment; # spent 15µs making 9 calls to Mojo::URL::fragment, avg 2µs/call |
144 | |||||
145 | 9 | 2µs | if (not @_) { | ||
146 | my $schema_info = $self->_fetch_from_uri($uri); | ||||
147 | return if not $schema_info or not defined wantarray; | ||||
148 | return $schema_info->{document}; | ||||
149 | } | ||||
150 | |||||
151 | # document BUILD will trigger $self->traverse($schema) | ||||
152 | 9 | 188µs | 21 | 117ms | my $document = $_[0]->$_isa('JSON::Schema::Modern::Document') ? shift # spent 117ms making 6 calls to JSON::Schema::Modern::Document::new, avg 19.5ms/call
# spent 139µs making 9 calls to Safe::Isa::__ANON__[Safe/Isa.pm:23], avg 15µs/call
# spent 14µs making 6 calls to Mojo::URL::__ANON__[Mojo/URL.pm:3], avg 2µs/call |
153 | : JSON::Schema::Modern::Document->new( | ||||
154 | schema => shift, | ||||
155 | $uri ? (canonical_uri => $uri) : (), | ||||
156 | evaluator => $self, # used mainly for traversal during document construction | ||||
157 | ); | ||||
158 | |||||
159 | 9 | 25µs | 9 | 253µs | if ($document->has_errors) { # spent 253µs making 9 calls to JSON::Schema::Modern::Document::has_errors, avg 28µs/call |
160 | my $result = JSON::Schema::Modern::Result->new( | ||||
161 | output_format => $self->output_format, | ||||
162 | valid => 0, | ||||
163 | errors => [ $document->errors ], | ||||
164 | exception => 1, | ||||
165 | ); | ||||
166 | die $result; | ||||
167 | } | ||||
168 | |||||
169 | 9 | 314µs | 189 | 251µs | if (not grep refaddr($_->{document}) == refaddr($document), $self->_canonical_resources) { # spent 213µs making 9 calls to JSON::Schema::Modern::_canonical_resources, avg 24µs/call
# spent 38µs making 180 calls to Scalar::Util::refaddr, avg 211ns/call |
170 | 7 | 8.87ms | 35 | 9.33ms | my $schema_content = $document->_serialized_schema # spent 8.76ms making 7 calls to Cpanel::JSON::XS::encode, avg 1.25ms/call
# spent 535µs making 14 calls to JSON::Schema::Modern::Document::_serialized_schema, avg 38µs/call
# spent 26µs making 7 calls to JSON::Schema::Modern::_json_decoder, avg 4µs/call
# spent 13µs making 7 calls to JSON::Schema::Modern::Document::schema, avg 2µs/call |
171 | // $document->_serialized_schema($self->_json_decoder->encode($document->schema)); | ||||
172 | |||||
173 | 7 | 159µs | 21 | 424µs | if (my $existing_doc = first { # spent 249µs making 7 calls to List::Util::first, avg 36µs/call
# spent 124µs making 7 calls to JSON::Schema::Modern::_canonical_resources, avg 18µs/call
# spent 51µs making 7 calls to List::Util::uniqint, avg 7µs/call |
174 | 29 | 155µs | 61 | 133µs | my $existing_content = $_->_serialized_schema # spent 94µs making 8 calls to Cpanel::JSON::XS::encode, avg 12µs/call
# spent 28µs making 37 calls to JSON::Schema::Modern::Document::_serialized_schema, avg 757ns/call
# spent 7µs making 8 calls to JSON::Schema::Modern::Document::schema, avg 875ns/call
# spent 4µs making 8 calls to JSON::Schema::Modern::_json_decoder, avg 500ns/call |
175 | // $_->_serialized_schema($self->_json_decoder->encode($_->schema)); | ||||
176 | 29 | 18µs | $existing_content eq $schema_content | ||
177 | } uniqint map $_->{document}, $self->_canonical_resources) { | ||||
178 | # we already have this schema content in another document object. | ||||
179 | $document = $existing_doc; | ||||
180 | } | ||||
181 | else { | ||||
182 | 7 | 83µs | 14 | 7.70ms | $self->_add_resources(map +($_->[0] => +{ $_->[1]->%*, document => $document }), # spent 7.56ms making 7 calls to JSON::Schema::Modern::_add_resources, avg 1.08ms/call
# spent 141µs making 7 calls to JSON::Schema::Modern::Document::resource_pairs, avg 20µs/call |
183 | $document->resource_pairs); | ||||
184 | } | ||||
185 | } | ||||
186 | |||||
187 | 9 | 42µs | 9 | 1.05ms | if ("$uri") { # spent 1.05ms making 9 calls to Mojo::URL::__ANON__[Mojo/URL.pm:3], avg 117µs/call |
188 | 8 | 29µs | 16 | 968µs | my $resource = $document->_get_resource($document->canonical_uri); # spent 950µs making 8 calls to JSON::Schema::Modern::Document::_get_resource, avg 119µs/call
# spent 18µs making 8 calls to JSON::Schema::Modern::Document::canonical_uri, avg 2µs/call |
189 | $self->_add_resources($uri => { | ||||
190 | path => '', | ||||
191 | canonical_uri => $document->canonical_uri, | ||||
192 | specification_version => $resource->{specification_version}, | ||||
193 | vocabularies => $resource->{vocabularies}, # reference, not copy | ||||
194 | document => $document, | ||||
195 | configs => $resource->{configs}, | ||||
196 | 8 | 52µs | 16 | 4.58ms | }); # spent 4.58ms making 8 calls to JSON::Schema::Modern::_add_resources, avg 572µs/call
# spent 5µs making 8 calls to JSON::Schema::Modern::Document::canonical_uri, avg 625ns/call |
197 | } | ||||
198 | |||||
199 | 9 | 39µs | return $document; | ||
200 | } | ||||
201 | |||||
202 | sub evaluate_json_string ($self, $json_data, $schema, $config_override = {}) { | ||||
203 | croak 'evaluate_json_string called in void context' if not defined wantarray; | ||||
204 | |||||
205 | my $data; | ||||
206 | try { | ||||
207 | $data = $self->_json_decoder->decode($json_data) | ||||
208 | } | ||||
209 | catch ($e) { | ||||
210 | return JSON::Schema::Modern::Result->new( | ||||
211 | output_format => $self->output_format, | ||||
212 | valid => 0, | ||||
213 | exception => 1, | ||||
214 | errors => [ | ||||
215 | JSON::Schema::Modern::Error->new( | ||||
216 | keyword => undef, | ||||
217 | instance_location => '', | ||||
218 | keyword_location => '', | ||||
219 | error => $e, | ||||
220 | ) | ||||
221 | ], | ||||
222 | ); | ||||
223 | } | ||||
224 | |||||
225 | return $self->evaluate($data, $schema, $config_override); | ||||
226 | } | ||||
227 | |||||
228 | # this is called whenever we need to walk a document for something. | ||||
229 | # for now it is just called when a ::Document object is created, to verify the integrity of the | ||||
230 | # schema structure, to identify the metaschema (via the $schema keyword), and to extract all | ||||
231 | # embedded resources via $id and $anchor keywords within. | ||||
232 | # Returns the internal $state object accumulated during the traversal. | ||||
233 | 2972 | 846µs | # spent 1.47s (34.2ms+1.43) within JSON::Schema::Modern::traverse which was called 743 times, avg 1.97ms/call:
# 727 times (33.2ms+1.28s) by JSON::Schema::Modern::Document::OpenAPI::_traverse_schema at line 218 of JSON/Schema/Modern/Document/OpenAPI.pm, avg 1.81ms/call
# 14 times (890µs+143ms) by JSON::Schema::Modern::Document::traverse at line 178 of JSON/Schema/Modern/Document.pm, avg 10.3ms/call
# once (52µs+4.03ms) by JSON::Schema::Modern::Document::OpenAPI::traverse at line 115 of JSON/Schema/Modern/Document/OpenAPI.pm
# once (56µs+-56µs) by JSON::Schema::Modern::_get_metaschema_info at line 750 | ||
234 | # Note: the starting position is not guaranteed to be at the root of the $document. | ||||
235 | 743 | 1.69ms | 743 | 309ms | my $initial_uri = Mojo::URL->new($config_override->{initial_schema_uri} // ''); # spent 309ms making 743 calls to Mojo::URL::new, avg 416µs/call |
236 | 743 | 801µs | my $initial_path = $config_override->{traversed_schema_path} // ''; | ||
237 | 743 | 2.67ms | 743 | 2.13ms | my $spec_version = $self->specification_version//SPECIFICATION_VERSION_DEFAULT; # spent 2.13ms making 743 calls to JSON::Schema::Modern::specification_version, avg 3µs/call |
238 | |||||
239 | my $state = { | ||||
240 | depth => 0, | ||||
241 | data_path => '', # this never changes since we don't have an instance yet | ||||
242 | initial_schema_uri => $initial_uri, # the canonical URI as of the start of this method, or last $id | ||||
243 | traversed_schema_path => $initial_path, # the accumulated traversal path as of the start, or last $id | ||||
244 | schema_path => '', # the rest of the path, since the start of this method, or last $id | ||||
245 | effective_base_uri => Mojo::URL->new(''), | ||||
246 | errors => [], | ||||
247 | identifiers => [], | ||||
248 | configs => {}, | ||||
249 | callbacks => $config_override->{callbacks} // {}, | ||||
250 | 743 | 7.46ms | 743 | 77.9ms | evaluator => $self, # spent 77.9ms making 743 calls to Mojo::URL::new, avg 105µs/call |
251 | }; | ||||
252 | |||||
253 | 743 | 470µs | try { | ||
254 | my $for_canonical_uri = Mojo::URL->new( | ||||
255 | (is_plain_hashref($schema_reference) && exists $schema_reference->{'$id'} | ||||
256 | ? Mojo::URL->new($schema_reference->{'$id'}) : undef) | ||||
257 | 743 | 2.27ms | 757 | 232ms | // $state->{initial_schema_uri}); # spent 232ms making 757 calls to Mojo::URL::new, avg 306µs/call |
258 | 743 | 945µs | 758 | 514µs | $for_canonical_uri->fragment(undef) if not length $for_canonical_uri->fragment; # spent 514µs making 758 calls to Mojo::URL::fragment, avg 678ns/call |
259 | |||||
260 | # a subsequent "$schema" keyword can still change these values | ||||
261 | $state->@{qw(spec_version vocabularies)} = $self->_get_metaschema_info( | ||||
262 | 743 | 6.60ms | 758 | 129ms | $config_override->{metaschema_uri} // $self->METASCHEMA_URIS->{$spec_version}, # spent 129ms making 743 calls to JSON::Schema::Modern::_get_metaschema_info, avg 173µs/call, recursion: max depth 1, sum of overlapping time 53µs
# spent 11µs making 15 calls to JSON::Schema::Modern::METASCHEMA_URIS, avg 733ns/call |
263 | $for_canonical_uri, | ||||
264 | ); | ||||
265 | } | ||||
266 | catch ($e) { | ||||
267 | if ($e->$_isa('JSON::Schema::Modern::Result')) { | ||||
268 | push $state->{errors}->@*, $e->errors; | ||||
269 | } | ||||
270 | elsif ($e->$_isa('JSON::Schema::Modern::Error')) { | ||||
271 | push $state->{errors}->@*, $e; | ||||
272 | } | ||||
273 | else { | ||||
274 | ()= E({ %$state, exception => 1 }, 'EXCEPTION: '.$e); | ||||
275 | } | ||||
276 | |||||
277 | return $state; | ||||
278 | } | ||||
279 | |||||
280 | 743 | 383µs | try { | ||
281 | 743 | 2.70ms | 743 | 685ms | $self->_traverse_subschema($schema_reference, $state); # spent 685ms making 743 calls to JSON::Schema::Modern::_traverse_subschema, avg 922µs/call |
282 | } | ||||
283 | catch ($e) { | ||||
284 | if ($e->$_isa('JSON::Schema::Modern::Error')) { | ||||
285 | # note: we should never be here, since traversal subs are no longer be fatal | ||||
286 | push $state->{errors}->@*, $e; | ||||
287 | } | ||||
288 | else { | ||||
289 | E({ %$state, exception => 1 }, 'EXCEPTION: '.$e); | ||||
290 | } | ||||
291 | } | ||||
292 | |||||
293 | 743 | 351µs | delete $state->{traverse}; | ||
294 | 743 | 3.02ms | return $state; | ||
295 | } | ||||
296 | |||||
297 | # the actual runtime evaluation of the schema against input data. | ||||
298 | 5 | 2µs | # spent 99.9s (327ms+99.6) within JSON::Schema::Modern::evaluate which was called:
# once (327ms+99.6s) by JSON::Schema::Modern::Document::OpenAPI::traverse at line 147 of JSON/Schema/Modern/Document/OpenAPI.pm | ||
299 | 1 | 0s | croak 'evaluate called in void context' if not defined wantarray; | ||
300 | |||||
301 | 1 | 1µs | my $initial_path = $config_override->{traversed_schema_path} // ''; | ||
302 | 1 | 2µs | 1 | 133µs | my $effective_base_uri = Mojo::URL->new($config_override->{effective_base_uri}//''); # spent 133µs making 1 call to Mojo::URL::new |
303 | |||||
304 | my $state = { | ||||
305 | 1 | 5µs | 1 | 3µs | data_path => $config_override->{data_path} // '', # spent 3µs making 1 call to Mojo::URL::new |
306 | traversed_schema_path => $initial_path, # the accumulated path as of the start of evaluation, or last $id or $ref | ||||
307 | initial_schema_uri => Mojo::URL->new, # the canonical URI as of the start of evaluation, or last $id or $ref | ||||
308 | schema_path => '', # the rest of the path, since the start of evaluation, or last $id or $ref | ||||
309 | effective_base_uri => $effective_base_uri, # resolve locations against this for errors and annotations | ||||
310 | errors => [], | ||||
311 | }; | ||||
312 | |||||
313 | 1 | 1µs | my $valid; | ||
314 | 1 | 0s | try { | ||
315 | 1 | 0s | my $schema_info; | ||
316 | |||||
317 | 1 | 3µs | 1 | 12µs | if (not is_ref($schema_reference) or $schema_reference->$_isa('Mojo::URL')) { # spent 12µs making 1 call to Safe::Isa::__ANON__[Safe/Isa.pm:23] |
318 | 1 | 2µs | 1 | 473µs | $schema_info = $self->_fetch_from_uri($schema_reference); # spent 473µs making 1 call to JSON::Schema::Modern::_fetch_from_uri |
319 | 1 | 2µs | 1 | 236µs | $state->{initial_schema_uri} = Mojo::URL->new($config_override->{initial_schema_uri} // ''); # spent 236µs making 1 call to Mojo::URL::new |
320 | } | ||||
321 | else { | ||||
322 | # traverse is called via add_schema -> ::Document->new -> ::Document->BUILD | ||||
323 | my $document = $self->add_schema('', $schema_reference); | ||||
324 | my $base_resource = $document->_get_resource($document->canonical_uri) | ||||
325 | || croak "couldn't get resource: document parse error"; | ||||
326 | |||||
327 | $schema_info = { | ||||
328 | schema => $document->schema, | ||||
329 | document => $document, | ||||
330 | document_path => '', | ||||
331 | $base_resource->%{qw(canonical_uri specification_version vocabularies configs)}, | ||||
332 | }; | ||||
333 | } | ||||
334 | |||||
335 | 1 | 1µs | abort($state, 'EXCEPTION: unable to find resource %s', $schema_reference) | ||
336 | if not $schema_info; | ||||
337 | |||||
338 | $state = +{ | ||||
339 | %$state, | ||||
340 | depth => 0, | ||||
341 | initial_schema_uri => $schema_info->{canonical_uri}, # the canonical URI as of the start of evaluation, or last $id or $ref | ||||
342 | document => $schema_info->{document}, # the ::Document object containing this schema | ||||
343 | document_path => $schema_info->{document_path}, # the path within the document of this schema, as of the start of evaluation, or last $id or $ref | ||||
344 | dynamic_scope => [ $schema_info->{canonical_uri} ], | ||||
345 | annotations => [], | ||||
346 | seen => {}, | ||||
347 | spec_version => $schema_info->{specification_version}, | ||||
348 | vocabularies => $schema_info->{vocabularies}, | ||||
349 | callbacks => $config_override->{callbacks} // {}, | ||||
350 | evaluator => $self, | ||||
351 | $schema_info->{configs}->%*, | ||||
352 | (map { | ||||
353 | 7 | 37µs | 6 | 44µs | my $val = $config_override->{$_} // $self->$_; # spent 19µs making 1 call to JSON::Schema::Modern::validate_content_schemas
# spent 15µs making 1 call to JSON::Schema::Modern::short_circuit
# spent 3µs making 1 call to JSON::Schema::Modern::collect_annotations
# spent 3µs making 1 call to JSON::Schema::Modern::validate_formats
# spent 2µs making 1 call to JSON::Schema::Modern::scalarref_booleans
# spent 2µs making 1 call to JSON::Schema::Modern::strict |
354 | 6 | 3µs | defined $val ? ( $_ => $val ) : () | ||
355 | } qw(validate_formats validate_content_schemas short_circuit collect_annotations scalarref_booleans strict)), | ||||
356 | }; | ||||
357 | |||||
358 | 1 | 0s | if ($state->{validate_formats}) { | ||
359 | $state->{vocabularies} = [ | ||||
360 | map s/^JSON::Schema::Modern::Vocabulary::Format\KAnnotation$/Assertion/r, $state->{vocabularies}->@* | ||||
361 | 1 | 14µs | 7 | 5µs | ]; # spent 5µs making 7 calls to JSON::Schema::Modern::CORE:subst, avg 714ns/call |
362 | 1 | 1µs | require JSON::Schema::Modern::Vocabulary::FormatAssertion; | ||
363 | } | ||||
364 | |||||
365 | 1 | 4µs | 1 | 99.6s | $valid = $self->_eval_subschema($data, $schema_info->{schema}, $state); # spent 99.6s making 1 call to JSON::Schema::Modern::_eval_subschema |
366 | 1 | 22µs | warn 'result is false but there are no errors' if not $valid and not $state->{errors}->@*; | ||
367 | } | ||||
368 | catch ($e) { | ||||
369 | if ($e->$_isa('JSON::Schema::Modern::Result')) { | ||||
370 | return $e; | ||||
371 | } | ||||
372 | elsif ($e->$_isa('JSON::Schema::Modern::Error')) { | ||||
373 | push $state->{errors}->@*, $e; | ||||
374 | } | ||||
375 | else { | ||||
376 | $valid = E({ %$state, exception => 1 }, 'EXCEPTION: '.$e); | ||||
377 | } | ||||
378 | } | ||||
379 | |||||
380 | 1 | 2µs | die 'evaluate validity inconstent with error count' if $valid xor !$state->{errors}->@*; | ||
381 | |||||
382 | return JSON::Schema::Modern::Result->new( | ||||
383 | output_format => $self->output_format, | ||||
384 | valid => $valid, | ||||
385 | $valid | ||||
386 | # strip annotations from result if user didn't explicitly ask for them | ||||
387 | ? ($config_override->{collect_annotations} // $self->collect_annotations | ||||
388 | ? (annotations => $state->{annotations}) : ()) | ||||
389 | 1 | 327ms | 3 | 2.65ms | : (errors => $state->{errors}), # spent 2.64ms making 1 call to JSON::Schema::Modern::Result::new
# spent 5µs making 1 call to JSON::Schema::Modern::output_format
# spent 2µs making 1 call to JSON::Schema::Modern::collect_annotations |
390 | ); | ||||
391 | } | ||||
392 | |||||
393 | sub validate_schema ($self, $schema, $config_override = {}) { | ||||
394 | croak 'validate_schema called in void context' if not defined wantarray; | ||||
395 | |||||
396 | my $metaschema_uri = is_plain_hashref($schema) && $schema->{'$schema'} ? $schema->{'$schema'} | ||||
397 | : $self->METASCHEMA_URIS->{$self->specification_version // $self->SPECIFICATION_VERSION_DEFAULT}; | ||||
398 | |||||
399 | return $self->evaluate($schema, $metaschema_uri, $config_override); | ||||
400 | } | ||||
401 | |||||
402 | sub get ($self, $uri) { | ||||
403 | my $schema_info = $self->_fetch_from_uri($uri); | ||||
404 | return if not $schema_info; | ||||
405 | my $subschema = is_ref($schema_info->{schema}) ? dclone($schema_info->{schema}) : $schema_info->{schema}; | ||||
406 | return wantarray ? ($subschema, $schema_info->{canonical_uri}) : $subschema; | ||||
407 | } | ||||
408 | |||||
409 | # defined lower down: | ||||
410 | # sub add_vocabulary { ... } | ||||
411 | # sub add_encoding { ... } | ||||
412 | # sub add_media_type { ... } | ||||
413 | |||||
414 | ######## NO PUBLIC INTERFACES FOLLOW THIS POINT ######## | ||||
415 | |||||
416 | # current spec version => { keyword => undef, or arrayref of alternatives } | ||||
417 | 1 | 14µs | my %removed_keywords = ( | ||
418 | 'draft7' => { | ||||
419 | id => [ '$id' ], | ||||
420 | }, | ||||
421 | 'draft2019-09' => { | ||||
422 | id => [ '$id' ], | ||||
423 | definitions => [ '$defs' ], | ||||
424 | dependencies => [ qw(dependentSchemas dependentRequired) ], | ||||
425 | }, | ||||
426 | 'draft2020-12' => { | ||||
427 | id => [ '$id' ], | ||||
428 | definitions => [ '$defs' ], | ||||
429 | dependencies => [ qw(dependentSchemas dependentRequired) ], | ||||
430 | '$recursiveAnchor' => [ '$dynamicAnchor' ], | ||||
431 | '$recursiveRef' => [ '$dynamicRef' ], | ||||
432 | additionalItems => [ 'items' ], | ||||
433 | }, | ||||
434 | ); | ||||
435 | |||||
436 | # { | ||||
437 | # $spec_version => { | ||||
438 | # $vocabulary_class => { | ||||
439 | # traverse => [ [ $keyword => $subref ], [ ... ] ], | ||||
440 | # evaluate => [ [ $keyword => $subref ], [ ... ] ], | ||||
441 | # } | ||||
442 | # } | ||||
443 | # } | ||||
444 | # If we could serialize coderefs, this could be an object attribute; | ||||
445 | # otherwise, we might as well persist this for the lifetime of the process. | ||||
446 | 1 | 1µs | our $vocabulary_cache = {}; | ||
447 | |||||
448 | 11452 | 2.20ms | # spent 685ms (204+481) within JSON::Schema::Modern::_traverse_subschema which was called 2863 times, avg 239µs/call:
# 1349 times (88.3ms+-88.3ms) by JSON::Schema::Modern::Vocabulary::traverse_object_schemas at line 55 of JSON/Schema/Modern/Vocabulary.pm, avg 0s/call
# 743 times (78.8ms+606ms) by JSON::Schema::Modern::traverse at line 281, avg 922µs/call
# 562 times (24.2ms+-24.2ms) by JSON::Schema::Modern::Vocabulary::traverse_subschema at line 34 of JSON/Schema/Modern/Vocabulary.pm, avg 0s/call
# 203 times (12.6ms+-12.6ms) by JSON::Schema::Modern::Vocabulary::traverse_array_schemas at line 44 of JSON/Schema/Modern/Vocabulary.pm, avg 0s/call
# 6 times (280µs+-280µs) by JSON::Schema::Modern::Vocabulary::traverse_property_schema at line 64 of JSON/Schema/Modern/Vocabulary.pm, avg 0s/call | ||
449 | 2863 | 1.25ms | delete $state->{keyword}; | ||
450 | |||||
451 | return E($state, 'EXCEPTION: maximum traversal depth exceeded') | ||||
452 | 2863 | 5.12ms | 2863 | 4.80ms | if $state->{depth}++ > $self->max_traversal_depth; # spent 4.80ms making 2863 calls to JSON::Schema::Modern::max_traversal_depth, avg 2µs/call |
453 | |||||
454 | 2863 | 4.17ms | 2863 | 10.5ms | my $schema_type = get_type($schema); # spent 10.5ms making 2863 calls to JSON::Schema::Modern::Utilities::get_type, avg 4µs/call |
455 | 2863 | 1.59ms | return 1 if $schema_type eq 'boolean'; | ||
456 | |||||
457 | 2491 | 710µs | return E($state, 'invalid schema type: %s', $schema_type) if $schema_type ne 'object'; | ||
458 | |||||
459 | 2491 | 1.36ms | return 1 if not keys %$schema; | ||
460 | |||||
461 | 2488 | 368µs | my $valid = 1; | ||
462 | 2488 | 9.87ms | my %unknown_keywords = map +($_ => undef), keys %$schema; | ||
463 | # we must check the array length on every iteration because some keywords can change it! | ||||
464 | 2488 | 11.8ms | for (my $idx = 0; $idx <= $state->{vocabularies}->$#*; ++$idx) { | ||
465 | 19434 | 6.98ms | my $vocabulary = $state->{vocabularies}[$idx]; | ||
466 | |||||
467 | # [ [ $keyword => $subref ], [ ... ] ] | ||||
468 | my $keyword_list = $vocabulary_cache->{$state->{spec_version}}{$vocabulary}{traverse} //= [ | ||||
469 | map [ $_ => $vocabulary->can('_traverse_keyword_'.($_ =~ s/^\$//r)) ], | ||||
470 | $vocabulary->keywords($state->{spec_version}) | ||||
471 | 19434 | 13.7ms | 130 | 195µs | ]; # spent 99µs making 61 calls to UNIVERSAL::can, avg 2µs/call
# spent 37µs making 1 call to JSON::Schema::Modern::Vocabulary::Applicator::keywords
# spent 15µs making 1 call to JSON::Schema::Modern::Vocabulary::Unevaluated::keywords
# spent 10µs making 61 calls to JSON::Schema::Modern::CORE:subst, avg 164ns/call
# spent 10µs making 1 call to JSON::Schema::Modern::Vocabulary::Content::keywords
# spent 8µs making 1 call to JSON::Schema::Modern::Vocabulary::Validation::keywords
# spent 5µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::keywords
# spent 5µs making 1 call to JSON::Schema::Modern::Vocabulary::MetaData::keywords
# spent 4µs making 1 call to JSON::Schema::Modern::Vocabulary::OpenAPI::keywords
# spent 2µs making 1 call to JSON::Schema::Modern::Vocabulary::FormatAnnotation::keywords |
472 | |||||
473 | 19434 | 28.0ms | foreach my $keyword_tuple ($keyword_list->@*) { | ||
474 | 149888 | 38.9ms | my ($keyword, $sub) = $keyword_tuple->@*; | ||
475 | 149888 | 28.5ms | next if not exists $schema->{$keyword}; | ||
476 | |||||
477 | # keywords adjacent to $ref are not evaluated before draft2019-09 | ||||
478 | 4388 | 2.08ms | next if $keyword ne '$ref' and exists $schema->{'$ref'} and $state->{spec_version} eq 'draft7'; | ||
479 | |||||
480 | 4388 | 1.53ms | delete $unknown_keywords{$keyword}; | ||
481 | 4388 | 1.68ms | $state->{keyword} = $keyword; | ||
482 | |||||
483 | 4388 | 8.21ms | 4388 | 1.10s | if (not $sub->($vocabulary, $schema, $state)) { # spent 384ms making 387 calls to JSON::Schema::Modern::Vocabulary::Applicator::_traverse_keyword_properties, avg 993µs/call, recursion: max depth 3, sum of overlapping time 40.9ms
# spent 204ms making 710 calls to JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_ref, avg 288µs/call
# spent 110ms making 98 calls to JSON::Schema::Modern::Vocabulary::Applicator::_traverse_keyword_items, avg 1.13ms/call, recursion: max depth 1, sum of overlapping time 5.42ms
# spent 96.3ms making 275 calls to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_required, avg 350µs/call
# spent 99.8ms making 12 calls to JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_defs, avg 8.32ms/call, recursion: max depth 1, sum of overlapping time 9.35ms
# spent 47.7ms making 1382 calls to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_type, avg 35µs/call
# spent 40.5ms making 130 calls to JSON::Schema::Modern::Vocabulary::Applicator::_traverse_keyword_additionalProperties, avg 311µs/call, recursion: max depth 1, sum of overlapping time 7.05ms
# spent 26.2ms making 23 calls to JSON::Schema::Modern::Vocabulary::Applicator::_traverse_keyword_allOf, avg 1.14ms/call
# spent 21.3ms making 65 calls to JSON::Schema::Modern::Vocabulary::Applicator::_traverse_keyword_then, avg 327µs/call
# spent 18.6ms making 21 calls to JSON::Schema::Modern::Vocabulary::Applicator::_traverse_keyword_oneOf, avg 887µs/call
# spent 15.3ms making 32 calls to JSON::Schema::Modern::Vocabulary::Applicator::_traverse_keyword_anyOf, avg 479µs/call, recursion: max depth 1, sum of overlapping time 192µs
# spent 13.3ms making 65 calls to JSON::Schema::Modern::Vocabulary::Applicator::_traverse_keyword_if, avg 205µs/call
# spent 7.75ms making 156 calls to JSON::Schema::Modern::Vocabulary::Unevaluated::_traverse_keyword_unevaluatedProperties, avg 50µs/call
# spent 7.47ms making 3 calls to JSON::Schema::Modern::Vocabulary::Applicator::_traverse_keyword_contains, avg 2.49ms/call
# spent 6.33ms making 11 calls to JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_vocabulary, avg 576µs/call
# spent 6.17ms making 14 calls to JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_id, avg 441µs/call
# spent 5.66ms making 15 calls to JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_schema, avg 378µs/call
# spent 5.34ms making 15 calls to JSON::Schema::Modern::Vocabulary::Applicator::_traverse_keyword_else, avg 356µs/call
# spent 5.18ms making 14 calls to JSON::Schema::Modern::Vocabulary::Applicator::_traverse_keyword_propertyNames, avg 370µs/call
# spent 5.04ms making 14 calls to JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_dynamicAnchor, avg 360µs/call
# spent 4.96ms making 69 calls to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_pattern, avg 72µs/call
# spent 4.81ms making 22 calls to JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_dynamicRef, avg 219µs/call
# spent 4.78ms making 3 calls to JSON::Schema::Modern::Vocabulary::Applicator::_traverse_keyword_dependentSchemas, avg 1.59ms/call
# spent 3.03ms making 135 calls to JSON::Schema::Modern::Vocabulary::MetaData::_traverse_keyword_readOnly, avg 22µs/call
# spent 2.36ms making 118 calls to JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_comment, avg 20µs/call
# spent 2.29ms making 97 calls to JSON::Schema::Modern::Vocabulary::MetaData::_traverse_keyword_description, avg 24µs/call
# spent 1.96ms making 6 calls to JSON::Schema::Modern::Vocabulary::Applicator::_traverse_keyword_patternProperties, avg 327µs/call
# spent 1.54ms making 15 calls to JSON::Schema::Modern::Vocabulary::Applicator::_traverse_keyword_not, avg 102µs/call
# spent 1.41ms making 50 calls to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_minProperties, avg 28µs/call
# spent 705µs making 50 calls to JSON::Schema::Modern::Vocabulary::FormatAnnotation::_traverse_keyword_format, avg 14µs/call
# spent 646µs making 34 calls to JSON::Schema::Modern::Vocabulary::MetaData::_traverse_keyword_title, avg 19µs/call
# spent 601µs making 60 calls to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_enum, avg 10µs/call
# spent 565µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_anchor
# spent 528µs making 21 calls to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_minimum, avg 25µs/call
# spent 308µs making 15 calls to JSON::Schema::Modern::Vocabulary::MetaData::_traverse_keyword_deprecated, avg 21µs/call
# spent 256µs making 14 calls to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_minItems, avg 18µs/call
# spent 183µs making 8 calls to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_minLength, avg 23µs/call
# spent 172µs making 8 calls to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_maxLength, avg 22µs/call
# spent 166µs making 8 calls to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_maximum, avg 21µs/call
# spent 147µs making 95 calls to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_const, avg 2µs/call
# spent 86µs making 1 call to JSON::Schema::Modern::Vocabulary::Content::_traverse_keyword_contentSchema
# spent 82µs making 98 calls to JSON::Schema::Modern::Vocabulary::MetaData::_traverse_keyword_default, avg 837ns/call
# spent 76µs making 1 call to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_dependentRequired
# spent 73µs making 3 calls to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_maxProperties, avg 24µs/call
# spent 62µs making 2 calls to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_maxContains, avg 31µs/call
# spent 61µs making 3 calls to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_uniqueItems, avg 20µs/call
# spent 41µs making 1 call to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_minContains
# spent 29µs making 1 call to JSON::Schema::Modern::Vocabulary::Content::_traverse_keyword_contentMediaType
# spent 19µs making 1 call to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_exclusiveMinimum
# spent 14µs making 1 call to JSON::Schema::Modern::Vocabulary::Validation::_traverse_keyword_maxItems
# spent 7µs making 5 calls to JSON::Schema::Modern::Vocabulary::OpenAPI::_traverse_keyword_externalDocs, avg 1µs/call |
484 | die 'traverse returned false but we have no errors' if not $state->{errors}->@*; | ||||
485 | $valid = 0; | ||||
486 | next; | ||||
487 | } | ||||
488 | |||||
489 | 4388 | 4.04ms | if (my $callback = $state->{callbacks}{$keyword}) { | ||
490 | $callback->($schema, $state); | ||||
491 | } | ||||
492 | } | ||||
493 | } | ||||
494 | |||||
495 | 2488 | 1.32ms | delete $state->{keyword}; | ||
496 | |||||
497 | 2488 | 4.82ms | 2488 | 4.76ms | if ($self->strict and keys %unknown_keywords) { # spent 4.76ms making 2488 calls to JSON::Schema::Modern::strict, avg 2µs/call |
498 | ()= E($state, 'unknown keyword%s found: %s', keys %unknown_keywords > 1 ? 's' : '', | ||||
499 | join(', ', sort keys %unknown_keywords)); | ||||
500 | } | ||||
501 | |||||
502 | # check for previously-supported but now removed keywords | ||||
503 | 2488 | 14.4ms | 2488 | 3.52ms | foreach my $keyword (sort keys $removed_keywords{$state->{spec_version}}->%*) { # spent 3.52ms making 2488 calls to JSON::Schema::Modern::CORE:sort, avg 1µs/call |
504 | 14928 | 1.88ms | next if not exists $schema->{$keyword}; | ||
505 | my $message ='no-longer-supported "'.$keyword.'" keyword present (at location "' | ||||
506 | .canonical_uri($state).'")'; | ||||
507 | if (my $alternates = $removed_keywords{$state->{spec_version}}->{$keyword}) { | ||||
508 | my @list = map '"'.$_.'"', @$alternates; | ||||
509 | @list = ((map $_.',', @list[0..$#list-1]), $list[-1]) if @list > 2; | ||||
510 | splice(@list, -1, 0, 'or') if @list > 1; | ||||
511 | $message .= ': this should be rewritten as '.join(' ', @list); | ||||
512 | } | ||||
513 | carp $message; | ||||
514 | } | ||||
515 | |||||
516 | 2488 | 6.98ms | return $valid; | ||
517 | } | ||||
518 | |||||
519 | 457975 | 83.2ms | # spent 99.6s (11.8+87.8) within JSON::Schema::Modern::_eval_subschema which was called 91595 times, avg 1.09ms/call:
# 49887 times (5.76s+-5.76s) by JSON::Schema::Modern::Vocabulary::eval at line 68 of JSON/Schema/Modern/Vocabulary.pm, avg 0s/call
# 41707 times (6.06s+-6.06s) by JSON::Schema::Modern::Vocabulary::eval_subschema_at_uri at line 89 of JSON/Schema/Modern/Vocabulary.pm, avg 0s/call
# once (18.9ms+99.6s) by JSON::Schema::Modern::evaluate at line 365 | ||
520 | 91595 | 36.3ms | croak '_eval_subschema called in void context' if not defined wantarray; | ||
521 | |||||
522 | # callers created a new $state for us, so we do not propagate upwards changes to depth, traversed | ||||
523 | # paths; but annotations, errors are arrayrefs so their contents will be shared | ||||
524 | 91595 | 115ms | $state->{dynamic_scope} = [ ($state->{dynamic_scope}//[])->@* ]; | ||
525 | 91595 | 2.45s | 2015087 | 264ms | delete $state->@{'keyword', grep /^_/, keys %$state}; # spent 264ms making 2015087 calls to JSON::Schema::Modern::CORE:match, avg 131ns/call |
526 | |||||
527 | abort($state, 'EXCEPTION: maximum evaluation depth exceeded') | ||||
528 | 91595 | 207ms | 91595 | 175ms | if $state->{depth}++ > $self->max_traversal_depth; # spent 175ms making 91595 calls to JSON::Schema::Modern::max_traversal_depth, avg 2µs/call |
529 | |||||
530 | 91595 | 131ms | 91595 | 164ms | my $schema_type = get_type($schema); # spent 164ms making 91595 calls to JSON::Schema::Modern::Utilities::get_type, avg 2µs/call |
531 | 91595 | 36.4ms | return $schema || E($state, 'subschema is false') if $schema_type eq 'boolean'; | ||
532 | |||||
533 | # this should never happen, due to checks in traverse | ||||
534 | 91595 | 27.7ms | abort($state, 'invalid schema type: %s', $schema_type) if $schema_type ne 'object'; | ||
535 | |||||
536 | 91595 | 36.3ms | return 1 if not keys %$schema; | ||
537 | |||||
538 | # find all schema locations in effect at this data path + canonical_uri combination | ||||
539 | # if any of them are absolute prefix of this schema location, we are in a loop. | ||||
540 | 91595 | 99.5ms | 91595 | 2.54s | my $canonical_uri = canonical_uri($state); # spent 2.54s making 91595 calls to JSON::Schema::Modern::Utilities::canonical_uri, avg 28µs/call |
541 | 91595 | 56.1ms | my $schema_location = $state->{traversed_schema_path}.$state->{schema_path}; | ||
542 | abort($state, 'EXCEPTION: infinite loop detected (same location evaluated twice)') | ||||
543 | if grep substr($schema_location, 0, length) eq $_, | ||||
544 | 91595 | 480ms | 91595 | 9.11s | keys $state->{seen}{$state->{data_path}}{$canonical_uri}->%*; # spent 9.11s making 91595 calls to Mojo::URL::__ANON__[Mojo/URL.pm:3], avg 99µs/call |
545 | 91595 | 336ms | 91595 | 7.85s | $state->{seen}{$state->{data_path}}{$canonical_uri}{$schema_location}++; # spent 7.85s making 91595 calls to Mojo::URL::__ANON__[Mojo/URL.pm:3], avg 86µs/call |
546 | |||||
547 | 91595 | 29.2ms | my $valid = 1; | ||
548 | 91595 | 348ms | my %unknown_keywords = map +($_ => undef), keys %$schema; | ||
549 | 91595 | 36.4ms | my $orig_annotations = $state->{annotations}; | ||
550 | 91595 | 49.0ms | $state->{annotations} = []; | ||
551 | 91595 | 20.3ms | my @new_annotations; | ||
552 | |||||
553 | ALL_KEYWORDS: | ||||
554 | 91595 | 84.0ms | foreach my $vocabulary ($state->{vocabularies}->@*) { | ||
555 | # [ [ $keyword => $subref|undef ], [ ... ] ] | ||||
556 | my $keyword_list = $vocabulary_cache->{$state->{spec_version}}{$vocabulary}{evaluate} //= [ | ||||
557 | map [ $_ => $vocabulary->can('_eval_keyword_'.($_ =~ s/^\$//r)) ], | ||||
558 | $vocabulary->keywords($state->{spec_version}) | ||||
559 | 641165 | 557ms | 124 | 211µs | ]; # spent 125µs making 58 calls to UNIVERSAL::can, avg 2µs/call
# spent 28µs making 1 call to JSON::Schema::Modern::Vocabulary::Applicator::keywords
# spent 17µs making 58 calls to JSON::Schema::Modern::CORE:subst, avg 293ns/call
# spent 10µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::keywords
# spent 9µs making 1 call to JSON::Schema::Modern::Vocabulary::Unevaluated::keywords
# spent 8µs making 1 call to JSON::Schema::Modern::Vocabulary::Validation::keywords
# spent 5µs making 1 call to JSON::Schema::Modern::Vocabulary::MetaData::keywords
# spent 4µs making 1 call to JSON::Schema::Modern::Vocabulary::Content::keywords
# spent 3µs making 1 call to JSON::Schema::Modern::Vocabulary::FormatAssertion::keywords
# spent 2µs making 1 call to JSON::Schema::Modern::Vocabulary::FormatAnnotation::keywords |
560 | |||||
561 | 641165 | 955ms | foreach my $keyword_tuple ($keyword_list->@*) { | ||
562 | 5220915 | 1.40s | my ($keyword, $sub) = $keyword_tuple->@*; | ||
563 | 5220915 | 1.00s | next if not exists $schema->{$keyword}; | ||
564 | |||||
565 | # keywords adjacent to $ref are not evaluated before draft2019-09 | ||||
566 | 308026 | 108ms | next if $keyword ne '$ref' and exists $schema->{'$ref'} and $state->{spec_version} eq 'draft7'; | ||
567 | |||||
568 | 308026 | 99.2ms | delete $unknown_keywords{$keyword}; | ||
569 | 308026 | 87.7ms | $state->{keyword} = $keyword; | ||
570 | |||||
571 | 308026 | 86.8ms | if ($sub) { | ||
572 | 202471 | 70.9ms | my $error_count = $state->{errors}->@*; | ||
573 | |||||
574 | 202471 | 433ms | 202471 | 620s | if (not $sub->($vocabulary, $data, $schema, $state)) { # spent 1085s making 39121 calls to JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_ref, avg 27.7ms/call, recursion: max depth 31, sum of overlapping time 986s
# spent 479s making 32492 calls to JSON::Schema::Modern::Vocabulary::Applicator::_eval_keyword_properties, avg 14.7ms/call, recursion: max depth 11, sum of overlapping time 379s
# spent 120s making 3369 calls to JSON::Schema::Modern::Vocabulary::Applicator::_eval_keyword_patternProperties, avg 35.8ms/call, recursion: max depth 2, sum of overlapping time 38.2s
# spent 79.3s making 3013 calls to JSON::Schema::Modern::Vocabulary::Applicator::_eval_keyword_if, avg 26.3ms/call, recursion: max depth 1, sum of overlapping time 4.49s
# spent 280s making 6002 calls to JSON::Schema::Modern::Vocabulary::Applicator::_eval_keyword_allOf, avg 46.7ms/call, recursion: max depth 13, sum of overlapping time 206s
# spent 151s making 2586 calls to JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_dynamicRef, avg 58.3ms/call, recursion: max depth 6, sum of overlapping time 79.9s
# spent 115s making 977 calls to JSON::Schema::Modern::Vocabulary::Applicator::_eval_keyword_additionalProperties, avg 118ms/call, recursion: max depth 4, sum of overlapping time 44.7s
# spent 26.1s making 1758 calls to JSON::Schema::Modern::Vocabulary::Applicator::_eval_keyword_items, avg 14.9ms/call, recursion: max depth 4, sum of overlapping time 2.52s
# spent 7.33s making 25860 calls to JSON::Schema::Modern::Vocabulary::MetaData::_eval_keyword_title, avg 284µs/call
# spent 4.19s making 305 calls to JSON::Schema::Modern::Vocabulary::Applicator::_eval_keyword_dependentSchemas, avg 13.7ms/call
# spent 3.25s making 1182 calls to JSON::Schema::Modern::Vocabulary::Applicator::_eval_keyword_anyOf, avg 2.75ms/call
# spent 2.58s making 25862 calls to JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_id, avg 100µs/call
# spent 2.07s making 3747 calls to JSON::Schema::Modern::Vocabulary::Unevaluated::_eval_keyword_unevaluatedProperties, avg 552µs/call
# spent 1.44s making 5174 calls to JSON::Schema::Modern::Vocabulary::MetaData::_eval_keyword_description, avg 277µs/call
# spent 974ms making 35648 calls to JSON::Schema::Modern::Vocabulary::Validation::_eval_keyword_type, avg 27µs/call
# spent 611ms making 2167 calls to JSON::Schema::Modern::Vocabulary::FormatAnnotation::_eval_keyword_format, avg 282µs/call
# spent 468ms making 4795 calls to JSON::Schema::Modern::Vocabulary::Validation::_eval_keyword_required, avg 98µs/call
# spent 449ms making 627 calls to JSON::Schema::Modern::Vocabulary::Applicator::_eval_keyword_propertyNames, avg 716µs/call
# spent 373ms making 1180 calls to JSON::Schema::Modern::Vocabulary::MetaData::_eval_keyword_default, avg 316µs/call
# spent 315ms making 305 calls to JSON::Schema::Modern::Vocabulary::Applicator::_eval_keyword_oneOf, avg 1.03ms/call
# spent 278ms making 1208 calls to JSON::Schema::Modern::Vocabulary::Validation::_eval_keyword_const, avg 230µs/call
# spent 248ms making 1538 calls to JSON::Schema::Modern::Vocabulary::Validation::_eval_keyword_enum, avg 161µs/call
# spent 86.1ms making 1403 calls to JSON::Schema::Modern::Vocabulary::Validation::_eval_keyword_uniqueItems, avg 61µs/call
# spent 25.7ms making 63 calls to JSON::Schema::Modern::Vocabulary::FormatAssertion::_eval_keyword_format, avg 408µs/call
# spent 11.1ms making 1244 calls to JSON::Schema::Modern::Vocabulary::Validation::_eval_keyword_minItems, avg 9µs/call
# spent 7.01ms making 262 calls to JSON::Schema::Modern::Vocabulary::Validation::_eval_keyword_pattern, avg 27µs/call
# spent 4.87ms making 485 calls to JSON::Schema::Modern::Vocabulary::Validation::_eval_keyword_minProperties, avg 10µs/call
# spent 2.98ms making 4 calls to JSON::Schema::Modern::Vocabulary::Applicator::_eval_keyword_not, avg 746µs/call
# spent 1.42ms making 79 calls to JSON::Schema::Modern::Vocabulary::Validation::_eval_keyword_minimum, avg 18µs/call
# spent 164µs making 15 calls to JSON::Schema::Modern::Vocabulary::Validation::_eval_keyword_maxProperties, avg 11µs/call |
575 | warn 'result is false but there are no errors (keyword: '.$keyword.')' | ||||
576 | 4283 | 2.55ms | if $error_count == $state->{errors}->@*; | ||
577 | 4283 | 1.08ms | $valid = 0; | ||
578 | |||||
579 | 4283 | 1.28ms | last ALL_KEYWORDS if $state->{short_circuit}; | ||
580 | 4283 | 3.56ms | next; | ||
581 | } | ||||
582 | } | ||||
583 | |||||
584 | 303743 | 242ms | 41641 | 133ms | if (my $callback = $state->{callbacks}{$keyword}) { # spent 122ms making 39055 calls to JSON::Schema::Modern::Document::OpenAPI::__ANON__[JSON/Schema/Modern/Document/OpenAPI.pm:144], avg 3µs/call
# spent 11.0ms making 2586 calls to JSON::Schema::Modern::Document::OpenAPI::__ANON__[JSON/Schema/Modern/Document/OpenAPI.pm:140], avg 4µs/call |
585 | $callback->($data, $schema, $state); | ||||
586 | } | ||||
587 | |||||
588 | 303743 | 489ms | push @new_annotations, $state->{annotations}->@[$#new_annotations+1 .. $state->{annotations}->$#*]; | ||
589 | } | ||||
590 | } | ||||
591 | |||||
592 | 91595 | 51.0ms | delete $state->{keyword}; | ||
593 | |||||
594 | 91595 | 23.9ms | if ($state->{strict} and keys %unknown_keywords) { | ||
595 | abort($state, 'unknown keyword%s found: %s', keys %unknown_keywords > 1 ? 's' : '', | ||||
596 | join(', ', sort keys %unknown_keywords)); | ||||
597 | } | ||||
598 | |||||
599 | 91595 | 103ms | $state->{annotations} = $orig_annotations; | ||
600 | |||||
601 | 91595 | 79.2ms | if ($valid) { | ||
602 | 87312 | 176ms | push $state->{annotations}->@*, @new_annotations; | ||
603 | 87312 | 962ms | 349244 | 375ms | if ($state->{collect_annotations} and $state->{spec_version} !~ qr/^draft(7|2019-09)$/) { # spent 124ms making 87311 calls to JSON::Schema::Modern::CORE:qr, avg 1µs/call
# spent 116ms making 87311 calls to JSON::Schema::Modern::CORE:regcomp, avg 1µs/call
# spent 107ms making 87311 calls to JSON::Schema::Modern::CORE:match, avg 1µs/call
# spent 28.4ms making 87311 calls to JSON::Schema::Modern::CORE:sort, avg 325ns/call |
604 | annotate_self(+{ %$state, keyword => $_, _unknown => 1 }, $schema) | ||||
605 | foreach sort keys %unknown_keywords; | ||||
606 | } | ||||
607 | } | ||||
608 | |||||
609 | 91595 | 419ms | return $valid; | ||
610 | } | ||||
611 | |||||
612 | has _resource_index => ( | ||||
613 | is => 'bare', | ||||
614 | isa => HashRef[my $resource_type = Dict[ | ||||
615 | canonical_uri => InstanceOf['Mojo::URL'], | ||||
616 | path => Str, | ||||
617 | specification_version => my $spec_version_type = Enum(SPECIFICATION_VERSIONS_SUPPORTED), | ||||
618 | document => InstanceOf['JSON::Schema::Modern::Document'], | ||||
619 | # the vocabularies used when evaluating instance data against schema | ||||
620 | vocabularies => ArrayRef[my $vocabulary_class_type = ClassName->where(q{$_->DOES('JSON::Schema::Modern::Vocabulary')})], | ||||
621 | configs => HashRef, | ||||
622 | Slurpy[HashRef[Undef]], # no other fields allowed | ||||
623 | ]], | ||||
624 | handles_via => 'Hash', | ||||
625 | handles => { | ||||
626 | _add_resources => 'set', | ||||
627 | _get_resource => 'get', | ||||
628 | _remove_resource => 'delete', | ||||
629 | _resource_index => 'elements', | ||||
630 | _resource_keys => 'keys', | ||||
631 | _add_resources_unsafe => 'set', | ||||
632 | _canonical_resources => 'values', | ||||
633 | _resource_exists => 'exists', | ||||
634 | }, | ||||
635 | lazy => 1, | ||||
636 | 1 | 10µs | # spent 3µs within JSON::Schema::Modern::__ANON__[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern.pm:636] which was called:
# once (3µs+0s) by JSON::Schema::Modern::_assert__resource_index at line 23 of (eval 344)[Sub/Quote.pm:3] | ||
637 | 1 | 52µs | 14 | 18.6ms | ); # spent 8.73ms making 1 call to MooX::HandlesVia::has
# spent 5.09ms making 1 call to Types::Standard::Dict
# spent 3.02ms making 3 calls to Types::Standard::HashRef, avg 1.01ms/call
# spent 754µs making 1 call to Types::Standard::ArrayRef
# spent 673µs making 2 calls to Types::Standard::InstanceOf, avg 336µs/call
# spent 195µs making 1 call to Type::Tiny::where
# spent 89µs making 1 call to Types::Standard::Enum
# spent 77µs making 1 call to Types::Standard::Slurpy
# spent 8µs making 1 call to Types::Standard::Undef
# spent 3µs making 1 call to Types::Standard::Str
# spent 2µs making 1 call to Types::Standard::ClassName |
638 | |||||
639 | # spent 12.0ms (557µs+11.4) within JSON::Schema::Modern::__ANON__[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern.pm:672] which was called 15 times, avg 799µs/call:
# 15 times (557µs+11.4ms) by JSON::Schema::Modern::__ANON__[(eval 346)[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/Class/Method/Modifiers.pm:89]:1] at line 1 of (eval 329)[Class/Method/Modifiers.pm:89], avg 799µs/call | ||||
640 | 15 | 8µs | my ($orig, $self) = (shift, shift); | ||
641 | |||||
642 | 15 | 5µs | my @resources; | ||
643 | 15 | 274µs | 30 | 102µs | foreach my $pair (sort { $a->[0] cmp $b->[0] } pairs @_) { # spent 54µs making 15 calls to List::Util::pairs, avg 4µs/call
# spent 48µs making 15 calls to JSON::Schema::Modern::CORE:sort, avg 3µs/call |
644 | 24 | 10µs | my ($key, $value) = @$pair; | ||
645 | |||||
646 | 24 | 119µs | 72 | 7.73ms | $resource_type->($value); # check type of hash value against Dict # spent 4.30ms making 24 calls to Sub::Defer::__ANON__[Sub/Defer.pm:178], avg 179µs/call
# spent 3.43ms making 48 calls to Type::Tiny::__ANON__[Type/Tiny.pm:89], avg 71µs/call |
647 | |||||
648 | 24 | 93µs | 44 | 1.48ms | if (my $existing = $self->_get_resource($key)) { # spent 1.37ms making 24 calls to JSON::Schema::Modern::_get_resource, avg 57µs/call
# spent 97µs making 2 calls to Mojo::URL::__ANON__[Mojo/URL.pm:3], avg 48µs/call
# spent 9µs making 18 calls to JSON::Schema::Modern::CACHED_METASCHEMAS, avg 500ns/call |
649 | # we allow overwriting canonical_uri = '' to allow for ad hoc evaluation of schemas that | ||||
650 | # lack all identifiers altogether, but preserve other resources from the original document | ||||
651 | 6 | 13µs | 6 | 486µs | if ($key ne '') { # spent 486µs making 6 calls to Mojo::URL::__ANON__[Mojo/URL.pm:3], avg 81µs/call |
652 | next if $existing->{path} eq $value->{path} | ||||
653 | and $existing->{canonical_uri} eq $value->{canonical_uri} | ||||
654 | and $existing->{specification_version} eq $value->{specification_version} | ||||
655 | 6 | 88µs | 24 | 875µs | and refaddr($existing->{document}) == refaddr($value->{document}); # spent 867µs making 12 calls to Mojo::URL::__ANON__[Mojo/URL.pm:3], avg 72µs/call
# spent 8µs making 12 calls to Scalar::Util::refaddr, avg 667ns/call |
656 | croak 'uri "'.$key.'" conflicts with an existing schema resource'; | ||||
657 | } | ||||
658 | } | ||||
659 | elsif ($self->CACHED_METASCHEMAS->{$key}) { | ||||
660 | croak 'uri "'.$key.'" conflicts with an existing meta-schema resource'; | ||||
661 | } | ||||
662 | |||||
663 | 18 | 47µs | 18 | 16µs | my $fragment = $value->{canonical_uri}->fragment; # spent 16µs making 18 calls to Mojo::URL::fragment, avg 889ns/call |
664 | croak sprintf('canonical_uri cannot contain an empty fragment (%s)', $value->{canonical_uri}) | ||||
665 | 18 | 8µs | if defined $fragment and $fragment eq ''; | ||
666 | |||||
667 | croak sprintf('canonical_uri cannot contain a plain-name fragment (%s)', $value->{canonical_uri}) | ||||
668 | 18 | 28µs | 18 | 10µs | if ($fragment // '') =~ m{^[^/]}; # spent 10µs making 18 calls to JSON::Schema::Modern::CORE:match, avg 556ns/call |
669 | |||||
670 | 18 | 27µs | 18 | 570µs | $self->$orig($key, $value); # spent 570µs making 18 calls to JSON::Schema::Modern::_add_resources, avg 32µs/call |
671 | } | ||||
672 | 1 | 5µs | 1 | 204µs | }; # spent 204µs making 1 call to Moo::around |
673 | |||||
674 | # $vocabulary uri (not its $id!) => [ spec_version, class ] | ||||
675 | has _vocabulary_classes => ( | ||||
676 | is => 'bare', | ||||
677 | isa => HashRef[ | ||||
678 | Tuple[ | ||||
679 | $spec_version_type, | ||||
680 | $vocabulary_class_type, | ||||
681 | ] | ||||
682 | ], | ||||
683 | handles_via => 'Hash', | ||||
684 | handles => { | ||||
685 | _get_vocabulary_class => 'get', | ||||
686 | _set_vocabulary_class => 'set', | ||||
687 | _get_vocabulary_values => 'values', | ||||
688 | }, | ||||
689 | lazy => 1, | ||||
690 | # spent 60.3ms (76µs+60.2) within JSON::Schema::Modern::__ANON__[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern.pm:696] which was called:
# once (76µs+60.2ms) by JSON::Schema::Modern::_assert__vocabulary_classes at line 23 of (eval 355)[Sub/Quote.pm:3] | ||||
691 | +{ | ||||
692 | 31 | 91µs | 24 | 60.2ms | map { my $class = $_; pairmap { $a => [ $b, $class ] } $class->vocabulary } # spent 60.2ms making 8 calls to Module::Runtime::use_module, avg 7.52ms/call
# spent 29µs making 8 calls to List::Util::pairmap, avg 4µs/call
# spent 2µs making 1 call to JSON::Schema::Modern::Vocabulary::Applicator::vocabulary
# spent 2µs making 1 call to JSON::Schema::Modern::Vocabulary::Content::vocabulary
# spent 2µs making 1 call to JSON::Schema::Modern::Vocabulary::Core::vocabulary
# spent 2µs making 1 call to JSON::Schema::Modern::Vocabulary::FormatAssertion::vocabulary
# spent 2µs making 1 call to JSON::Schema::Modern::Vocabulary::Unevaluated::vocabulary
# spent 1µs making 1 call to JSON::Schema::Modern::Vocabulary::FormatAnnotation::vocabulary
# spent 1µs making 1 call to JSON::Schema::Modern::Vocabulary::MetaData::vocabulary
# spent 1µs making 1 call to JSON::Schema::Modern::Vocabulary::Validation::vocabulary |
693 | map use_module('JSON::Schema::Modern::Vocabulary::'.$_), | ||||
694 | qw(Core Applicator Validation FormatAssertion FormatAnnotation Content MetaData Unevaluated) | ||||
695 | } | ||||
696 | }, | ||||
697 | 1 | 21µs | 3 | 9.20ms | ); # spent 4.48ms making 1 call to Types::Standard::Tuple
# spent 4.13ms making 1 call to MooX::HandlesVia::has
# spent 585µs making 1 call to Types::Standard::HashRef |
698 | |||||
699 | 3 | 2µs | # spent 66.5ms (122µs+66.4) within JSON::Schema::Modern::add_vocabulary which was called:
# once (122µs+66.4ms) by JSON::Schema::Modern::Document::OpenAPI::_add_vocab_and_default_schemas at line 182 of JSON/Schema/Modern/Document/OpenAPI.pm | ||
700 | 1 | 6µs | 1 | 60.5ms | return if grep $_->[1] eq $classname, $self->_get_vocabulary_values; # spent 60.5ms making 1 call to JSON::Schema::Modern::_get_vocabulary_values |
701 | |||||
702 | 1 | 8µs | 4 | 4.47ms | $vocabulary_class_type->(use_module($classname)); # spent 3.62ms making 1 call to Module::Runtime::use_module
# spent 617µs making 1 call to Sub::Defer::__ANON__[Sub/Defer.pm:178]
# spent 229µs making 2 calls to Type::Tiny::__ANON__[Type/Tiny.pm:89], avg 114µs/call |
703 | |||||
704 | # uri => version, uri => version | ||||
705 | 1 | 30µs | 2 | 19µs | foreach my $pair (pairs $classname->vocabulary) { # spent 18µs making 1 call to List::Util::pairs
# spent 1µs making 1 call to JSON::Schema::Modern::Vocabulary::OpenAPI::vocabulary |
706 | 1 | 9µs | my ($uri_string, $spec_version) = @$pair; | ||
707 | 1 | 11µs | 5 | 1.16ms | Str->where(q{my $uri = Mojo::URL->new($_); $uri->is_abs && !defined $uri->fragment})->($uri_string); # spent 522µs making 1 call to Sub::Defer::__ANON__[Sub/Defer.pm:178]
# spent 403µs making 1 call to Type::Tiny::where
# spent 232µs making 2 calls to Type::Tiny::__ANON__[Type/Tiny.pm:89], avg 116µs/call
# spent 2µs making 1 call to Types::Standard::Str |
708 | 1 | 39µs | 4 | 334µs | $spec_version_type->($spec_version); # spent 300µs making 1 call to Sub::Defer::__ANON__[Sub/Defer.pm:178]
# spent 27µs making 1 call to Type::Tiny::DESTROY
# spent 7µs making 2 calls to Type::Tiny::__ANON__[Type/Tiny.pm:89], avg 4µs/call |
709 | 1 | 5µs | 1 | 116µs | $self->_set_vocabulary_class($uri_string => [ $spec_version, $classname ]) # spent 116µs making 1 call to JSON::Schema::Modern::_set_vocabulary_class |
710 | } | ||||
711 | } | ||||
712 | |||||
713 | # $schema uri => [ spec_version, [ vocab classes ] ]. | ||||
714 | has _metaschema_vocabulary_classes => ( | ||||
715 | is => 'bare', | ||||
716 | isa => HashRef[ | ||||
717 | Tuple[ | ||||
718 | $spec_version_type, | ||||
719 | ArrayRef[$vocabulary_class_type], | ||||
720 | ] | ||||
721 | ], | ||||
722 | handles_via => 'Hash', | ||||
723 | handles => { | ||||
724 | _get_metaschema_vocabulary_classes => 'get', | ||||
725 | _set_metaschema_vocabulary_classes => 'set', | ||||
726 | __all_metaschema_vocabulary_classes => 'values', | ||||
727 | }, | ||||
728 | lazy => 1, | ||||
729 | # spent 196µs (46+150) within JSON::Schema::Modern::__ANON__[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern.pm:738] which was called:
# once (46µs+150µs) by JSON::Schema::Modern::_assert__metaschema_vocabulary_classes at line 23 of (eval 360)[Sub/Quote.pm:3] | ||||
730 | 1 | 15µs | 7 | 150µs | my @modules = map use_module('JSON::Schema::Modern::Vocabulary::'.$_), # spent 150µs making 7 calls to Module::Runtime::use_module, avg 21µs/call |
731 | qw(Core Applicator Validation FormatAnnotation Content MetaData Unevaluated); | ||||
732 | +{ | ||||
733 | 'https://json-schema.org/draft/2020-12/schema' => [ 'draft2020-12', [ @modules ] ], | ||||
734 | 3 | 26µs | do { pop @modules; () }, | ||
735 | 'https://json-schema.org/draft/2019-09/schema' => [ 'draft2019-09', \@modules ], | ||||
736 | 'http://json-schema.org/draft-07/schema#' => [ 'draft7', \@modules ], | ||||
737 | }, | ||||
738 | }, | ||||
739 | 1 | 23µs | 4 | 9.33ms | ); # spent 4.58ms making 1 call to MooX::HandlesVia::has
# spent 3.32ms making 1 call to Types::Standard::Tuple
# spent 1.23ms making 1 call to Types::Standard::HashRef
# spent 217µs making 1 call to Types::Standard::ArrayRef |
740 | |||||
741 | # retrieves metaschema info either from cache or by parsing the schema for vocabularies | ||||
742 | # throws a JSON::Schema::Modern::Result on error | ||||
743 | 2972 | 1.07ms | # spent 129ms (7.91+121) within JSON::Schema::Modern::_get_metaschema_info which was called 743 times, avg 173µs/call:
# 743 times (7.91ms+121ms) by JSON::Schema::Modern::traverse at line 262, avg 173µs/call | ||
744 | # check the cache | ||||
745 | 743 | 2.54ms | 743 | 118ms | my $metaschema_info = $self->_get_metaschema_vocabulary_classes($metaschema_uri); # spent 118ms making 743 calls to JSON::Schema::Modern::_get_metaschema_vocabulary_classes, avg 159µs/call |
746 | 743 | 2.30ms | return @$metaschema_info if $metaschema_info; | ||
747 | |||||
748 | # otherwise, fetch the metaschema and parse its $vocabulary keyword. | ||||
749 | # we do this by traversing a baby schema with just the $schema keyword. | ||||
750 | 1 | 10µs | 1 | 0s | my $state = $self->traverse({ '$schema' => $metaschema_uri.'' }); # spent 2.72ms making 1 call to JSON::Schema::Modern::traverse, recursion: max depth 1, sum of overlapping time 2.72ms |
751 | die JSON::Schema::Modern::Result->new( | ||||
752 | output_format => $self->output_format, | ||||
753 | valid => JSON::PP::false, | ||||
754 | errors => [ | ||||
755 | map { | ||||
756 | my $e = $_; | ||||
757 | # absolute location is undef iff the location = '/$schema' | ||||
758 | my $absolute_location = $e->absolute_keyword_location // $for_canonical_uri; | ||||
759 | JSON::Schema::Modern::Error->new( | ||||
760 | keyword => $e->keyword eq '$schema' ? '' : $e->keyword, | ||||
761 | instance_location => $e->instance_location, | ||||
762 | keyword_location => ($for_canonical_uri->fragment//'').($e->keyword_location =~ s{^/\$schema\b}{}r), | ||||
763 | length $absolute_location ? ( absolute_keyword_location => $absolute_location ) : (), | ||||
764 | error => $e->error, | ||||
765 | ) | ||||
766 | } | ||||
767 | $state->{errors}->@* ], | ||||
768 | exception => 1, | ||||
769 | 1 | 1µs | ) if $state->{errors}->@*; | ||
770 | 1 | 8µs | return ($state->{spec_version}, $state->{vocabularies}); | ||
771 | } | ||||
772 | |||||
773 | # used for determining a default '$schema' keyword where there is none | ||||
774 | 1 | 2µs | # spent 93µs (19+74) within JSON::Schema::Modern::BEGIN@774 which was called:
# once (19µs+74µs) by OpenAPI::Modern::BEGIN@26 at line 778 | ||
775 | 'draft2020-12' => 'https://json-schema.org/draft/2020-12/schema', | ||||
776 | 'draft2019-09' => 'https://json-schema.org/draft/2019-09/schema', | ||||
777 | 'draft7' => 'http://json-schema.org/draft-07/schema#', | ||||
778 | 1 | 120µs | 2 | 167µs | }; # spent 93µs making 1 call to JSON::Schema::Modern::BEGIN@774
# spent 74µs making 1 call to constant::import |
779 | |||||
780 | 1 | 1µs | # spent 51µs (18+33) within JSON::Schema::Modern::BEGIN@780 which was called:
# once (18µs+33µs) by OpenAPI::Modern::BEGIN@26 at line 803 | ||
781 | 'https://json-schema.org/draft/2020-12/meta/applicator' => 'draft2020-12/meta/applicator.json', | ||||
782 | 'https://json-schema.org/draft/2020-12/meta/content' => 'draft2020-12/meta/content.json', | ||||
783 | 'https://json-schema.org/draft/2020-12/meta/core' => 'draft2020-12/meta/core.json', | ||||
784 | 'https://json-schema.org/draft/2020-12/meta/format-annotation' => 'draft2020-12/meta/format-annotation.json', | ||||
785 | 'https://json-schema.org/draft/2020-12/meta/format-assertion' => 'draft2020-12/meta/format-assertion.json', | ||||
786 | 'https://json-schema.org/draft/2020-12/meta/meta-data' => 'draft2020-12/meta/meta-data.json', | ||||
787 | 'https://json-schema.org/draft/2020-12/meta/unevaluated' => 'draft2020-12/meta/unevaluated.json', | ||||
788 | 'https://json-schema.org/draft/2020-12/meta/validation' => 'draft2020-12/meta/validation.json', | ||||
789 | 'https://json-schema.org/draft/2020-12/output/schema' => 'draft2020-12/output/schema.json', | ||||
790 | 'https://json-schema.org/draft/2020-12/schema' => 'draft2020-12/schema.json', | ||||
791 | |||||
792 | 'https://json-schema.org/draft/2019-09/meta/applicator' => 'draft2019-09/meta/applicator.json', | ||||
793 | 'https://json-schema.org/draft/2019-09/meta/content' => 'draft2019-09/meta/content.json', | ||||
794 | 'https://json-schema.org/draft/2019-09/meta/core' => 'draft2019-09/meta/core.json', | ||||
795 | 'https://json-schema.org/draft/2019-09/meta/format' => 'draft2019-09/meta/format.json', | ||||
796 | 'https://json-schema.org/draft/2019-09/meta/meta-data' => 'draft2019-09/meta/meta-data.json', | ||||
797 | 'https://json-schema.org/draft/2019-09/meta/validation' => 'draft2019-09/meta/validation.json', | ||||
798 | 'https://json-schema.org/draft/2019-09/output/schema' => 'draft2019-09/output/schema.json', | ||||
799 | 'https://json-schema.org/draft/2019-09/schema' => 'draft2019-09/schema.json', | ||||
800 | |||||
801 | # trailing # is omitted because we always cache documents by its canonical (fragmentless) URI | ||||
802 | 'http://json-schema.org/draft-07/schema' => 'draft7/schema.json', | ||||
803 | 1 | 1.92ms | 2 | 84µs | }; # spent 51µs making 1 call to JSON::Schema::Modern::BEGIN@780
# spent 33µs making 1 call to constant::import |
804 | |||||
805 | # returns the same as _get_resource | ||||
806 | 117369 | 22.4ms | # spent 7.07s (200ms+6.87) within JSON::Schema::Modern::_get_or_load_resource which was called 39123 times, avg 181µs/call:
# 39123 times (200ms+6.87s) by JSON::Schema::Modern::_fetch_from_uri at line 853, avg 181µs/call | ||
807 | 39123 | 89.7ms | 39123 | 6.80s | my $resource = $self->_get_resource($uri); # spent 6.80s making 39123 calls to JSON::Schema::Modern::_get_resource, avg 174µs/call |
808 | 39123 | 76.3ms | return $resource if $resource; | ||
809 | |||||
810 | 8 | 60µs | 16 | 1.08ms | if (my $local_filename = $self->CACHED_METASCHEMAS->{$uri}) { # spent 1.07ms making 8 calls to Mojo::URL::__ANON__[Mojo/URL.pm:3], avg 134µs/call
# spent 7µs making 8 calls to JSON::Schema::Modern::CACHED_METASCHEMAS, avg 875ns/call |
811 | 8 | 36µs | 16 | 1.35ms | my $file = path(dist_dir('JSON-Schema-Modern'), $local_filename); # spent 1.27ms making 8 calls to File::ShareDir::dist_dir, avg 158µs/call
# spent 83µs making 8 calls to Path::Tiny::path, avg 10µs/call |
812 | 8 | 323µs | 24 | 299µs | my $schema = $self->_json_decoder->decode($file->slurp_raw); # spent 238µs making 8 calls to Cpanel::JSON::XS::decode, avg 30µs/call
# spent 32µs making 8 calls to JSON::Schema::Modern::_json_decoder, avg 4µs/call
# spent 29µs making 8 calls to Path::Tiny::slurp_raw, avg 4µs/call |
813 | 8 | 65µs | 8 | 58.2ms | my $document = JSON::Schema::Modern::Document->new(schema => $schema, evaluator => $self); # spent 58.2ms making 8 calls to JSON::Schema::Modern::Document::new, avg 7.28ms/call |
814 | |||||
815 | # this should be caught by the try/catch in evaluate() | ||||
816 | 8 | 24µs | 8 | 254µs | die JSON::Schema::Modern::Result->new( # spent 254µs making 8 calls to JSON::Schema::Modern::Document::has_errors, avg 32µs/call |
817 | output_format => $self->output_format, | ||||
818 | valid => 0, | ||||
819 | errors => [ $document->errors ], | ||||
820 | exception => 1, | ||||
821 | ) if $document->has_errors; | ||||
822 | |||||
823 | # we have already performed the appropriate collision checks, so we bypass them here | ||||
824 | 8 | 96µs | 16 | 372µs | $self->_add_resources_unsafe( # spent 240µs making 8 calls to JSON::Schema::Modern::_add_resources_unsafe, avg 30µs/call
# spent 132µs making 8 calls to JSON::Schema::Modern::Document::resource_pairs, avg 16µs/call |
825 | map +($_->[0] => +{ $_->[1]->%*, document => $document }), | ||||
826 | $document->resource_pairs | ||||
827 | ); | ||||
828 | |||||
829 | 8 | 66µs | 8 | 1.73ms | return $self->_get_resource($uri); # spent 1.73ms making 8 calls to JSON::Schema::Modern::_get_resource, avg 216µs/call |
830 | } | ||||
831 | |||||
832 | # TODO: | ||||
833 | # - load from network or disk | ||||
834 | |||||
835 | return; | ||||
836 | }; | ||||
837 | |||||
838 | # returns information necessary to use a schema found at a particular URI: | ||||
839 | # - a schema (which may not be at a document root) | ||||
840 | # - the canonical uri for that schema, | ||||
841 | # - the JSON::Schema::Modern::Document object that holds that schema | ||||
842 | # - the path relative to the document root for this schema | ||||
843 | # - the specification version that applies to this schema | ||||
844 | # - the vocabularies to use when considering schema keywords | ||||
845 | # - the config overrides to set when considering schema keywords | ||||
846 | # creates a Document and adds it to the resource index, if not already present. | ||||
847 | 140643 | 28.7ms | # spent 21.5s (2.44+19.1) within JSON::Schema::Modern::_fetch_from_uri which was called 46881 times, avg 459µs/call:
# 41707 times (2.34s+17.9s) by JSON::Schema::Modern::Vocabulary::eval_subschema_at_uri at line 72 of JSON/Schema/Modern/Vocabulary.pm, avg 485µs/call
# 2586 times (60.5ms+672ms) by JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_dynamicRef at line 232 of JSON/Schema/Modern/Vocabulary/Core.pm, avg 283µs/call
# 2586 times (45.2ms+527ms) by JSON::Schema::Modern::Vocabulary::Core::_eval_keyword_dynamicRef at line 244 of JSON/Schema/Modern/Vocabulary/Core.pm, avg 221µs/call
# once (111µs+565µs) by JSON::Schema::Modern::Vocabulary::Core::_traverse_keyword_schema at line 127 of JSON/Schema/Modern/Vocabulary/Core.pm
# once (73µs+400µs) by JSON::Schema::Modern::evaluate at line 318 | ||
848 | 46881 | 21.4ms | 1 | 62µs | $uri = Mojo::URL->new($uri) if not is_ref($uri); # spent 62µs making 1 call to Mojo::URL::new |
849 | 46881 | 57.4ms | 46881 | 40.8ms | my $fragment = $uri->fragment; # spent 40.8ms making 46881 calls to Mojo::URL::fragment, avg 870ns/call |
850 | |||||
851 | 46881 | 65.0ms | 21018 | 18.5ms | if (not length($fragment) or $fragment =~ m{^/}) { # spent 18.5ms making 21018 calls to JSON::Schema::Modern::CORE:match, avg 879ns/call |
852 | 39123 | 74.9ms | 78246 | 2.17s | my $base = $uri->clone->fragment(undef); # spent 2.13s making 39123 calls to Mojo::URL::clone, avg 55µs/call
# spent 40.0ms making 39123 calls to Mojo::URL::fragment, avg 1µs/call |
853 | 39123 | 74.3ms | 39123 | 7.07s | if (my $resource = $self->_get_or_load_resource($base)) { # spent 7.07s making 39123 calls to JSON::Schema::Modern::_get_or_load_resource, avg 181µs/call |
854 | 39123 | 133ms | 39123 | 803ms | my $subschema = $resource->{document}->get(my $document_path = $resource->{path}.($fragment//'')); # spent 803ms making 39123 calls to Mojo::JSON::Pointer::get, avg 21µs/call |
855 | 39123 | 14.6ms | return if not defined $subschema; | ||
856 | 39123 | 17.7ms | my $document = $resource->{document}; | ||
857 | my $closest_resource = first { !length($_->[1]{path}) # document root | ||||
858 | 39123 | 219ms | || length($document_path) | ||
859 | && $document_path =~ m{^\Q$_->[1]{path}\E(?:/|\z)} } # path is above present location | ||||
860 | sort { length($b->[1]{path}) <=> length($a->[1]{path}) } # sort by length, descending | ||||
861 | 39123 | 700ms | 295795 | 5.75s | grep { not length Mojo::URL->new($_->[0])->fragment } # omit anchors # spent 4.72s making 89213 calls to Mojo::URL::new, avg 53µs/call
# spent 795ms making 39123 calls to JSON::Schema::Modern::Document::resource_pairs, avg 20µs/call
# spent 109ms making 39123 calls to List::Util::first, avg 3µs/call
# spent 64.7ms making 39123 calls to JSON::Schema::Modern::CORE:sort, avg 2µs/call
# spent 58.1ms making 89213 calls to Mojo::URL::fragment, avg 651ns/call |
862 | $document->resource_pairs; | ||||
863 | |||||
864 | my $canonical_uri = $closest_resource->[1]{canonical_uri}->clone | ||||
865 | 39123 | 144ms | 78246 | 1.54s | ->fragment(substr($document_path, length($closest_resource->[1]{path}))); # spent 1.47s making 39123 calls to Mojo::URL::clone, avg 38µs/call
# spent 68.8ms making 39123 calls to Mojo::URL::fragment, avg 2µs/call |
866 | 39123 | 58.8ms | 64986 | 42.0ms | $canonical_uri->fragment(undef) if not length($canonical_uri->fragment); # spent 42.0ms making 64986 calls to Mojo::URL::fragment, avg 646ns/call |
867 | return { | ||||
868 | schema => $subschema, | ||||
869 | canonical_uri => $canonical_uri, | ||||
870 | document => $document, | ||||
871 | document_path => $document_path, | ||||
872 | 39123 | 497ms | $resource->%{qw(specification_version vocabularies configs)}, # reference, not copy | ||
873 | }; | ||||
874 | } | ||||
875 | } | ||||
876 | else { # we are following a URI with a plain-name fragment | ||||
877 | 7758 | 14.0ms | 7758 | 1.17s | if (my $resource = $self->_get_resource($uri)) { # spent 1.17s making 7758 calls to JSON::Schema::Modern::_get_resource, avg 150µs/call |
878 | 7758 | 18.4ms | 7758 | 169ms | my $subschema = $resource->{document}->get($resource->{path}); # spent 169ms making 7758 calls to Mojo::JSON::Pointer::get, avg 22µs/call |
879 | 7758 | 1.86ms | return if not defined $subschema; | ||
880 | return { | ||||
881 | schema => $subschema, | ||||
882 | canonical_uri => $resource->{canonical_uri}->clone, # this is *not* the anchor-containing URI | ||||
883 | document => $resource->{document}, | ||||
884 | document_path => $resource->{path}, | ||||
885 | 7758 | 62.1ms | 7758 | 298ms | $resource->%{qw(specification_version vocabularies configs)}, # reference, not copy # spent 298ms making 7758 calls to Mojo::URL::clone, avg 38µs/call |
886 | }; | ||||
887 | } | ||||
888 | } | ||||
889 | } | ||||
890 | |||||
891 | # used for internal encoding as well (when caching serialized schemas) | ||||
892 | has _json_decoder => ( | ||||
893 | is => 'ro', | ||||
894 | isa => HasMethods[qw(encode decode)], | ||||
895 | lazy => 1, | ||||
896 | 1 | 5µs | 1 | 39µs | # spent 45µs (6+39) within JSON::Schema::Modern::__ANON__[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern.pm:896] which was called:
# once (6µs+39µs) by JSON::Schema::Modern::_json_decoder at line 23 of (eval 362)[Sub/Quote.pm:3] # spent 39µs making 1 call to JSON::MaybeXS::new |
897 | 1 | 17µs | 2 | 5.03ms | ); # spent 3.07ms making 1 call to Types::Standard::HasMethods
# spent 1.96ms making 1 call to MooX::HandlesVia::has |
898 | |||||
899 | # since media types are case-insensitive, all type names must be foldcased on insertion. | ||||
900 | has _media_type => ( | ||||
901 | is => 'bare', | ||||
902 | isa => my $media_type_type = Map[Str->where(q{$_ eq CORE::fc($_)}), CodeRef], | ||||
903 | handles_via => 'Hash', | ||||
904 | handles => { | ||||
905 | get_media_type => 'get', | ||||
906 | add_media_type => 'set', | ||||
907 | _media_types => 'keys', | ||||
908 | }, | ||||
909 | lazy => 1, | ||||
910 | default => sub ($self) { | ||||
911 | my $_json_media_type = sub ($content_ref) { | ||||
912 | \ JSON::MaybeXS->new(allow_nonref => 1, utf8 => 0)->decode($content_ref->$*); | ||||
913 | }; | ||||
914 | +{ | ||||
915 | # note: utf-8 decoding is NOT done, as we can't be sure that's the correct charset! | ||||
916 | 'application/json' => $_json_media_type, | ||||
917 | 'application/schema+json' => $_json_media_type, | ||||
918 | 'application/schema-instance+json' => $_json_media_type, | ||||
919 | map +($_ => sub ($content_ref) { $content_ref }), | ||||
920 | qw(text/plain application/octet-stream), | ||||
921 | }; | ||||
922 | }, | ||||
923 | 1 | 25µs | 5 | 8.46ms | ); # spent 4.13ms making 1 call to Types::Standard::Map
# spent 4.02ms making 1 call to MooX::HandlesVia::has
# spent 304µs making 1 call to Type::Tiny::where
# spent 4µs making 1 call to Types::Standard::Str
# spent 2µs making 1 call to Types::Standard::CodeRef |
924 | |||||
925 | # get_media_type('TExT/bloop') will match an entry for 'text/*' or '*/*' | ||||
926 | # TODO: support queries for application/schema+json to match entry of application/json | ||||
927 | around get_media_type => sub ($orig, $self, $type) { | ||||
928 | my $mt = $self->$orig(fc $type); | ||||
929 | return $mt if $mt; | ||||
930 | |||||
931 | return $self->$orig((first { m{([^/]+)/\*$} && fc($type) =~ m{^\Q$1\E/[^/]+$} } $self->_media_types) | ||||
932 | // '*/*'); | ||||
933 | 1 | 6µs | 1 | 249µs | }; # spent 249µs making 1 call to Moo::around |
934 | |||||
935 | 1 | 3µs | 1 | 120µs | before add_media_type => sub ($self, $type, $sub) { $media_type_type->({ $type => $sub }) }; # spent 120µs making 1 call to Moo::before |
936 | |||||
937 | has _encoding => ( | ||||
938 | is => 'bare', | ||||
939 | isa => HashRef[CodeRef], | ||||
940 | handles_via => 'Hash', | ||||
941 | handles => { | ||||
942 | get_encoding => 'get', | ||||
943 | add_encoding => 'set', | ||||
944 | }, | ||||
945 | lazy => 1, | ||||
946 | default => sub ($self) { | ||||
947 | +{ | ||||
948 | identity => sub ($content_ref) { $content_ref }, | ||||
949 | base64 => sub ($content_ref) { | ||||
950 | die "invalid characters in base64 string" | ||||
951 | if $content_ref->$* =~ m{[^A-Za-z0-9+/=]} or $content_ref->$* =~ m{=(?=[^=])}; | ||||
952 | require MIME::Base64; \ MIME::Base64::decode($content_ref->$*); | ||||
953 | }, | ||||
954 | }; | ||||
955 | }, | ||||
956 | 1 | 12µs | 3 | 1.90ms | ); # spent 1.63ms making 1 call to MooX::HandlesVia::has
# spent 263µs making 1 call to Types::Standard::HashRef
# spent 3µs making 1 call to Types::Standard::CodeRef |
957 | |||||
958 | sub FREEZE ($self, $serializer) { | ||||
959 | my $data = +{ %$self }; | ||||
960 | # Cpanel::JSON::XS doesn't serialize: https://github.com/Sereal/Sereal/issues/266 | ||||
961 | # coderefs can't serialize cleanly and must be re-added by the user. | ||||
962 | delete $data->@{qw(_json_decoder _format_validations _media_type _encoding)}; | ||||
963 | return $data; | ||||
964 | } | ||||
965 | |||||
966 | sub THAW ($class, $serializer, $data) { | ||||
967 | my $self = bless($data, $class); | ||||
968 | |||||
969 | # load all vocabulary classes | ||||
970 | require_module($_) foreach uniq map $_->{vocabularies}->@*, $self->_canonical_resources; | ||||
971 | |||||
972 | return $self; | ||||
973 | } | ||||
974 | |||||
975 | 1 | 71µs | 1; | ||
976 | |||||
977 | 1 | 336µs | 1 | 1.83ms | __END__ # spent 1.83ms making 1 call to B::Hooks::EndOfScope::XS::__ANON__[B/Hooks/EndOfScope/XS.pm:26] |
# spent 16µs within JSON::Schema::Modern::CACHED_METASCHEMAS which was called 26 times, avg 615ns/call:
# 18 times (9µs+0s) by JSON::Schema::Modern::__ANON__[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern.pm:672] at line 648, avg 500ns/call
# 8 times (7µs+0s) by JSON::Schema::Modern::_get_or_load_resource at line 810, avg 875ns/call | |||||
# spent 389ms within JSON::Schema::Modern::CORE:match which was called 2123434 times, avg 183ns/call:
# 2015087 times (264ms+0s) by JSON::Schema::Modern::_eval_subschema at line 525, avg 131ns/call
# 87311 times (107ms+0s) by JSON::Schema::Modern::_eval_subschema at line 603, avg 1µs/call
# 21018 times (18.5ms+0s) by JSON::Schema::Modern::_fetch_from_uri at line 851, avg 879ns/call
# 18 times (10µs+0s) by JSON::Schema::Modern::__ANON__[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern.pm:672] at line 668, avg 556ns/call | |||||
# spent 124ms within JSON::Schema::Modern::CORE:qr which was called 87311 times, avg 1µs/call:
# 87311 times (124ms+0s) by JSON::Schema::Modern::_eval_subschema at line 603, avg 1µs/call | |||||
# spent 116ms within JSON::Schema::Modern::CORE:regcomp which was called 87311 times, avg 1µs/call:
# 87311 times (116ms+0s) by JSON::Schema::Modern::_eval_subschema at line 603, avg 1µs/call | |||||
# spent 96.6ms within JSON::Schema::Modern::CORE:sort which was called 128937 times, avg 750ns/call:
# 87311 times (28.4ms+0s) by JSON::Schema::Modern::_eval_subschema at line 603, avg 325ns/call
# 39123 times (64.7ms+0s) by JSON::Schema::Modern::_fetch_from_uri at line 861, avg 2µs/call
# 2488 times (3.52ms+0s) by JSON::Schema::Modern::_traverse_subschema at line 503, avg 1µs/call
# 15 times (48µs+0s) by JSON::Schema::Modern::__ANON__[/Users/ether/.perlbrew/libs/36.0@std/lib/perl5/JSON/Schema/Modern.pm:672] at line 643, avg 3µs/call | |||||
# spent 32µs within JSON::Schema::Modern::CORE:subst which was called 126 times, avg 254ns/call:
# 61 times (10µs+0s) by JSON::Schema::Modern::_traverse_subschema at line 471, avg 164ns/call
# 58 times (17µs+0s) by JSON::Schema::Modern::_eval_subschema at line 559, avg 293ns/call
# 7 times (5µs+0s) by JSON::Schema::Modern::evaluate at line 361, avg 714ns/call | |||||
# spent 11µs within JSON::Schema::Modern::METASCHEMA_URIS which was called 15 times, avg 733ns/call:
# 15 times (11µs+0s) by JSON::Schema::Modern::traverse at line 262, avg 733ns/call | |||||
# spent 1µs within JSON::Schema::Modern::SPECIFICATION_VERSION_DEFAULT which was called:
# once (1µs+0s) by JSON::Schema::Modern::Document::OpenAPI::traverse at line 76 of JSON/Schema/Modern/Document/OpenAPI.pm | |||||
sub JSON::Schema::Modern::__ANON__; # xsub |