schemurai 2.3.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +67 -4
- data/ext/schemurai_native/Cargo.lock +8 -1
- data/ext/schemurai_native/Cargo.toml +2 -1
- data/ext/schemurai_native/glue.c +155 -14
- data/ext/schemurai_native/src/budget.rs +101 -0
- data/ext/schemurai_native/src/content.rs +18 -0
- data/ext/schemurai_native/src/diagnostics.rs +395 -225
- data/ext/schemurai_native/src/enumeration.rs +154 -0
- data/ext/schemurai_native/src/evaluation.rs +219 -0
- data/ext/schemurai_native/src/format.rs +118 -8
- data/ext/schemurai_native/src/graph.rs +50 -0
- data/ext/schemurai_native/src/instance_graph.rs +84 -0
- data/ext/schemurai_native/src/integer_scan.rs +132 -0
- data/ext/schemurai_native/src/lib.rs +954 -185
- data/ext/schemurai_native/src/memo.rs +1149 -0
- data/ext/schemurai_native/src/memo_scope.rs +240 -0
- data/ext/schemurai_native/src/number.rs +7 -8
- data/ext/schemurai_native/src/pattern.rs +191 -22
- data/ext/schemurai_native/src/scope.rs +243 -33
- data/ext/schemurai_native/src/structural.rs +86 -0
- data/ext/schemurai_native/src/tracking.rs +245 -90
- data/lib/schemurai/backend.rb +1 -0
- data/lib/schemurai/evaluator.rb +45 -3
- data/lib/schemurai/limit_budget.rb +78 -0
- data/lib/schemurai/limit_exceeded.rb +18 -0
- data/lib/schemurai/limited_evaluator.rb +100 -0
- data/lib/schemurai/native/evaluator.rb +32 -5
- data/lib/schemurai/schema_domain.rb +12 -7
- data/lib/schemurai/schema_graph.rb +10 -1
- data/lib/schemurai/unique_items.rb +2 -0
- data/lib/schemurai/version.rb +1 -1
- data/lib/schemurai/vm/compiler.rb +7 -3
- data/lib/schemurai/vm/evaluator.rb +50 -3
- data/lib/schemurai/vm/limited_evaluator.rb +97 -0
- data/lib/schemurai.rb +74 -27
- data/sig/schemurai.rbs +20 -12
- metadata +17 -4
|
@@ -24,18 +24,139 @@ impl Hash for JsonKey {
|
|
|
24
24
|
}
|
|
25
25
|
pub(super) type KeySet = HashSet<JsonKey, BuildHasherDefault<BucketHasher>>;
|
|
26
26
|
|
|
27
|
+
// Store only nonempty words. The common single-word case needs no allocation,
|
|
28
|
+
// even when its property IDs are near the end of a large compiled engine.
|
|
29
|
+
#[derive(Clone, Default)]
|
|
30
|
+
enum PropertyWords {
|
|
31
|
+
#[default]
|
|
32
|
+
Empty,
|
|
33
|
+
Single(usize, u64),
|
|
34
|
+
Dense(usize, Vec<u64>),
|
|
35
|
+
Many(HashMap<usize, u64>),
|
|
36
|
+
}
|
|
37
|
+
#[derive(Clone, Default)]
|
|
38
|
+
struct PropertyBits {
|
|
39
|
+
words: PropertyWords,
|
|
40
|
+
count: usize,
|
|
41
|
+
}
|
|
42
|
+
impl PropertyBits {
|
|
43
|
+
fn word(&self, index: usize) -> u64 {
|
|
44
|
+
match &self.words {
|
|
45
|
+
PropertyWords::Empty => 0,
|
|
46
|
+
PropertyWords::Single(at, bits) => {
|
|
47
|
+
if *at == index {
|
|
48
|
+
*bits
|
|
49
|
+
} else {
|
|
50
|
+
0
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
PropertyWords::Dense(first, words) => index
|
|
54
|
+
.checked_sub(*first)
|
|
55
|
+
.and_then(|offset| words.get(offset))
|
|
56
|
+
.copied()
|
|
57
|
+
.unwrap_or(0),
|
|
58
|
+
PropertyWords::Many(words) => words.get(&index).copied().unwrap_or(0),
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
fn insert_word(&mut self, index: usize, bits: u64) {
|
|
62
|
+
let previous = self.word(index);
|
|
63
|
+
self.count += (bits & !previous).count_ones() as usize;
|
|
64
|
+
match &mut self.words {
|
|
65
|
+
PropertyWords::Empty => self.words = PropertyWords::Single(index, bits),
|
|
66
|
+
PropertyWords::Single(at, existing) if *at == index => *existing |= bits,
|
|
67
|
+
PropertyWords::Single(at, existing) if at.abs_diff(index) < 128 => {
|
|
68
|
+
let first = (*at).min(index);
|
|
69
|
+
let mut words = vec![0; at.abs_diff(index) + 1];
|
|
70
|
+
words[*at - first] = *existing;
|
|
71
|
+
words[index - first] = bits;
|
|
72
|
+
self.words = PropertyWords::Dense(first, words);
|
|
73
|
+
}
|
|
74
|
+
PropertyWords::Single(at, existing) => {
|
|
75
|
+
self.words = PropertyWords::Many(HashMap::from([(*at, *existing), (index, bits)]));
|
|
76
|
+
}
|
|
77
|
+
PropertyWords::Dense(first, words)
|
|
78
|
+
if index >= *first && index - *first < words.len() =>
|
|
79
|
+
{
|
|
80
|
+
words[index - *first] |= bits;
|
|
81
|
+
}
|
|
82
|
+
PropertyWords::Dense(first, words)
|
|
83
|
+
if index.max(*first + words.len() - 1) - index.min(*first) < 128 =>
|
|
84
|
+
{
|
|
85
|
+
if index < *first {
|
|
86
|
+
let offset = *first - index;
|
|
87
|
+
words.splice(0..0, std::iter::repeat_n(0, offset));
|
|
88
|
+
*first = index;
|
|
89
|
+
} else {
|
|
90
|
+
words.resize(index - *first + 1, 0);
|
|
91
|
+
}
|
|
92
|
+
words[index - *first] |= bits;
|
|
93
|
+
}
|
|
94
|
+
PropertyWords::Dense(first, words) => {
|
|
95
|
+
let mut sparse: HashMap<_, _> = words
|
|
96
|
+
.iter()
|
|
97
|
+
.enumerate()
|
|
98
|
+
.filter(|&(_, &word)| word != 0)
|
|
99
|
+
.map(|(offset, &word)| (*first + offset, word))
|
|
100
|
+
.collect();
|
|
101
|
+
sparse.insert(index, bits);
|
|
102
|
+
self.words = PropertyWords::Many(sparse);
|
|
103
|
+
}
|
|
104
|
+
PropertyWords::Many(words) => *words.entry(index).or_default() |= bits,
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
fn insert(&mut self, index: usize) {
|
|
108
|
+
self.insert_word(index / 64, 1_u64 << (index % 64));
|
|
109
|
+
}
|
|
110
|
+
fn contains(&self, index: usize) -> bool {
|
|
111
|
+
self.word(index / 64) & (1_u64 << (index % 64)) != 0
|
|
112
|
+
}
|
|
113
|
+
fn merge(&mut self, other: Self) {
|
|
114
|
+
if matches!(self.words, PropertyWords::Empty) {
|
|
115
|
+
*self = other;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
match other.words {
|
|
119
|
+
PropertyWords::Empty => {}
|
|
120
|
+
PropertyWords::Single(index, bits) => self.insert_word(index, bits),
|
|
121
|
+
PropertyWords::Dense(first, words) => {
|
|
122
|
+
for (offset, bits) in words.into_iter().enumerate() {
|
|
123
|
+
if bits != 0 {
|
|
124
|
+
self.insert_word(first + offset, bits);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
PropertyWords::Many(words) => {
|
|
129
|
+
for (index, bits) in words {
|
|
130
|
+
self.insert_word(index, bits);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#[derive(Clone)]
|
|
27
138
|
pub(super) struct Tracked {
|
|
28
139
|
pub status: Verdict,
|
|
29
140
|
properties: KeySet,
|
|
30
|
-
known_properties:
|
|
141
|
+
known_properties: PropertyBits,
|
|
31
142
|
items: Vec<u64>,
|
|
32
143
|
}
|
|
33
144
|
impl Tracked {
|
|
145
|
+
// Bound retained annotation slots independently of the number of results.
|
|
146
|
+
pub(super) fn memo_slots(&self) -> usize {
|
|
147
|
+
self.properties.capacity()
|
|
148
|
+
+ self.items.capacity()
|
|
149
|
+
+ match &self.known_properties.words {
|
|
150
|
+
PropertyWords::Empty | PropertyWords::Single(..) => 0,
|
|
151
|
+
PropertyWords::Dense(_, words) => words.capacity(),
|
|
152
|
+
PropertyWords::Many(words) => words.capacity(),
|
|
153
|
+
}
|
|
154
|
+
}
|
|
34
155
|
pub(super) fn new(status: Verdict) -> Self {
|
|
35
156
|
Self {
|
|
36
157
|
status,
|
|
37
158
|
properties: KeySet::default(),
|
|
38
|
-
known_properties:
|
|
159
|
+
known_properties: PropertyBits::default(),
|
|
39
160
|
items: vec![],
|
|
40
161
|
}
|
|
41
162
|
}
|
|
@@ -52,24 +173,13 @@ impl Tracked {
|
|
|
52
173
|
.is_some_and(|word| word & (1_u64 << (index % 64)) != 0)
|
|
53
174
|
}
|
|
54
175
|
fn known_property(&mut self, index: usize) {
|
|
55
|
-
|
|
56
|
-
if self.known_properties.len() <= word {
|
|
57
|
-
self.known_properties.resize(word + 1, 0);
|
|
58
|
-
}
|
|
59
|
-
self.known_properties[word] |= 1_u64 << (index % 64);
|
|
176
|
+
self.known_properties.insert(index);
|
|
60
177
|
}
|
|
61
178
|
fn contains_known_property(&self, index: usize) -> bool {
|
|
62
|
-
self.known_properties
|
|
63
|
-
.get(index / 64)
|
|
64
|
-
.is_some_and(|word| word & (1_u64 << (index % 64)) != 0)
|
|
179
|
+
self.known_properties.contains(index)
|
|
65
180
|
}
|
|
66
181
|
pub(super) fn property_count(&self) -> usize {
|
|
67
|
-
self.properties.len()
|
|
68
|
-
+ self
|
|
69
|
-
.known_properties
|
|
70
|
-
.iter()
|
|
71
|
-
.map(|word| word.count_ones() as usize)
|
|
72
|
-
.sum::<usize>()
|
|
182
|
+
self.properties.len() + self.known_properties.count
|
|
73
183
|
}
|
|
74
184
|
pub(super) fn item_count(&self) -> usize {
|
|
75
185
|
self.items
|
|
@@ -86,7 +196,7 @@ impl Tracked {
|
|
|
86
196
|
} else {
|
|
87
197
|
self.properties.extend(other.properties);
|
|
88
198
|
}
|
|
89
|
-
|
|
199
|
+
self.known_properties.merge(other.known_properties);
|
|
90
200
|
merge_bits(&mut self.items, other.items);
|
|
91
201
|
Valid
|
|
92
202
|
}
|
|
@@ -132,7 +242,7 @@ fn merge_bits(target: &mut Vec<u64>, source: Vec<u64>) {
|
|
|
132
242
|
}
|
|
133
243
|
}
|
|
134
244
|
}
|
|
135
|
-
impl
|
|
245
|
+
impl Execution<'_> {
|
|
136
246
|
pub(super) fn eval_tracked(
|
|
137
247
|
&self,
|
|
138
248
|
node: usize,
|
|
@@ -143,6 +253,9 @@ impl Engine {
|
|
|
143
253
|
if depth > MAX_EVALUATION_DEPTH {
|
|
144
254
|
return Tracked::new(Unsupported);
|
|
145
255
|
}
|
|
256
|
+
if !self.budget.checkpoint() {
|
|
257
|
+
return Tracked::new(Unsupported);
|
|
258
|
+
}
|
|
146
259
|
let kind = if value & self.fixnum_flag != 0 {
|
|
147
260
|
TYPE_INTEGRAL_NUMBER
|
|
148
261
|
} else {
|
|
@@ -160,6 +273,36 @@ impl Engine {
|
|
|
160
273
|
depth: usize,
|
|
161
274
|
active: scope::Context<'_>,
|
|
162
275
|
kind: i32,
|
|
276
|
+
) -> Tracked {
|
|
277
|
+
if !self.budget.checkpoint() {
|
|
278
|
+
return Tracked::new(Unsupported);
|
|
279
|
+
}
|
|
280
|
+
let Some(key) = self
|
|
281
|
+
.memo
|
|
282
|
+
.key(self, node, value, depth, active, &self.budget)
|
|
283
|
+
else {
|
|
284
|
+
if self.budget.hit.get().0 != 0 {
|
|
285
|
+
return Tracked::new(Unsupported);
|
|
286
|
+
}
|
|
287
|
+
return self.eval_tracked_uncached(node, value, depth, active, kind);
|
|
288
|
+
};
|
|
289
|
+
if self.budget.hit.get().0 != 0 || !self.budget.checkpoint() {
|
|
290
|
+
return Tracked::new(Unsupported);
|
|
291
|
+
}
|
|
292
|
+
if let Some(result) = self.memo.tracked(key) {
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
let result = self.eval_tracked_uncached(node, value, depth, active, kind);
|
|
296
|
+
self.memo.record_tracked(key, &result);
|
|
297
|
+
result
|
|
298
|
+
}
|
|
299
|
+
fn eval_tracked_uncached(
|
|
300
|
+
&self,
|
|
301
|
+
node: usize,
|
|
302
|
+
value: Value,
|
|
303
|
+
depth: usize,
|
|
304
|
+
active: scope::Context<'_>,
|
|
305
|
+
kind: i32,
|
|
163
306
|
) -> Tracked {
|
|
164
307
|
let frame = scope::ScopeFrame {
|
|
165
308
|
resource: self.metadata[node].resource,
|
|
@@ -178,6 +321,9 @@ impl Engine {
|
|
|
178
321
|
}
|
|
179
322
|
let mut evaluation = Tracked::new(Valid);
|
|
180
323
|
for op in &self.nodes[node] {
|
|
324
|
+
if !self.budget.checkpoint() {
|
|
325
|
+
return Tracked::new(Unsupported);
|
|
326
|
+
}
|
|
181
327
|
let result = match op {
|
|
182
328
|
Op::Ref(target) => {
|
|
183
329
|
if RefFrame::contains(active.active, node, value) {
|
|
@@ -203,6 +349,9 @@ impl Engine {
|
|
|
203
349
|
Op::All(nodes) => {
|
|
204
350
|
let mut result = Valid;
|
|
205
351
|
for &child in nodes {
|
|
352
|
+
if !self.budget.checkpoint() {
|
|
353
|
+
return Tracked::new(Unsupported);
|
|
354
|
+
}
|
|
206
355
|
result = evaluation.merge(self.eval_tracked_kind(
|
|
207
356
|
child,
|
|
208
357
|
value,
|
|
@@ -217,26 +366,19 @@ impl Engine {
|
|
|
217
366
|
result
|
|
218
367
|
}
|
|
219
368
|
Op::Any(nodes) | Op::One(nodes) => {
|
|
220
|
-
let
|
|
221
|
-
|
|
222
|
-
let mut result = Valid;
|
|
223
|
-
for &(child, multiplicity) in nodes {
|
|
224
|
-
let branch = self.eval_tracked_kind(child, value, depth + 1, active, kind);
|
|
225
|
-
if branch.status == Unsupported {
|
|
226
|
-
result = Unsupported;
|
|
227
|
-
break;
|
|
228
|
-
}
|
|
229
|
-
if branch.status == Valid {
|
|
230
|
-
count += multiplicity;
|
|
231
|
-
matched.merge(branch);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
if result != Valid {
|
|
235
|
-
result
|
|
236
|
-
} else if count == 0 || (matches!(op, Op::One(_)) && count != 1) {
|
|
237
|
-
Invalid
|
|
369
|
+
let branch_kind = if matches!(op, Op::Any(_)) {
|
|
370
|
+
evaluation::BranchKind::Any
|
|
238
371
|
} else {
|
|
239
|
-
evaluation
|
|
372
|
+
evaluation::BranchKind::One
|
|
373
|
+
};
|
|
374
|
+
match evaluation::scan_branches(
|
|
375
|
+
nodes,
|
|
376
|
+
evaluation::BranchPolicy::Complete,
|
|
377
|
+
|child| self.eval_tracked_kind(child, value, depth + 1, active, kind),
|
|
378
|
+
) {
|
|
379
|
+
Err(status) => status,
|
|
380
|
+
Ok(result) if !branch_kind.accepts(result.matches) => Invalid,
|
|
381
|
+
Ok(result) => evaluation.merge(result.coverage),
|
|
240
382
|
}
|
|
241
383
|
}
|
|
242
384
|
Op::Not(child) => match self.eval(*child, value, depth + 1, active) {
|
|
@@ -304,17 +446,23 @@ impl Engine {
|
|
|
304
446
|
active: scope::Context<'_>,
|
|
305
447
|
evaluation: &mut Tracked,
|
|
306
448
|
) -> Verdict {
|
|
449
|
+
if !self.budget.checkpoint() {
|
|
450
|
+
return Unsupported;
|
|
451
|
+
}
|
|
307
452
|
let len = unsafe { sn_len(value) };
|
|
308
453
|
if bounds(len, r.min, r.max) == Invalid {
|
|
309
454
|
return Invalid;
|
|
310
455
|
}
|
|
311
456
|
if r.unique {
|
|
312
|
-
let result = unique(value, depth, self.fixnum_flag);
|
|
457
|
+
let result = unique(value, depth, self.fixnum_flag, &self.budget);
|
|
313
458
|
if result != Valid {
|
|
314
459
|
return result;
|
|
315
460
|
}
|
|
316
461
|
}
|
|
317
462
|
for (index, &child) in r.prefix.iter().take(len).enumerate() {
|
|
463
|
+
if !self.budget.checkpoint() {
|
|
464
|
+
return Unsupported;
|
|
465
|
+
}
|
|
318
466
|
let result = self.eval(child, unsafe { sn_entry(value, index) }, depth, active);
|
|
319
467
|
if result != Valid {
|
|
320
468
|
return result;
|
|
@@ -322,6 +470,9 @@ impl Engine {
|
|
|
322
470
|
evaluation.item(index);
|
|
323
471
|
}
|
|
324
472
|
for index in 0..len {
|
|
473
|
+
if !self.budget.checkpoint() {
|
|
474
|
+
return Unsupported;
|
|
475
|
+
}
|
|
325
476
|
let child = if r.list {
|
|
326
477
|
r.items.get(index).copied().or(r.additional)
|
|
327
478
|
} else if index >= r.prefix.len() {
|
|
@@ -338,17 +489,14 @@ impl Engine {
|
|
|
338
489
|
}
|
|
339
490
|
}
|
|
340
491
|
if let Some(child) = r.contains {
|
|
341
|
-
let
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
Unsupported => return Unsupported,
|
|
350
|
-
}
|
|
351
|
-
}
|
|
492
|
+
let count = match evaluation::scan_contains(
|
|
493
|
+
len,
|
|
494
|
+
|index| self.eval(child, unsafe { sn_entry(value, index) }, depth, active),
|
|
495
|
+
|index| evaluation.item(index),
|
|
496
|
+
) {
|
|
497
|
+
Ok(count) => count,
|
|
498
|
+
Err(status) => return status,
|
|
499
|
+
};
|
|
352
500
|
if bounds(count, Some(r.min_contains), r.max_contains) == Invalid {
|
|
353
501
|
return Invalid;
|
|
354
502
|
}
|
|
@@ -358,6 +506,9 @@ impl Engine {
|
|
|
358
506
|
return Invalid;
|
|
359
507
|
}
|
|
360
508
|
for index in 0..len {
|
|
509
|
+
if !self.budget.checkpoint() {
|
|
510
|
+
return Unsupported;
|
|
511
|
+
}
|
|
361
512
|
if evaluation.contains_item(index) {
|
|
362
513
|
continue;
|
|
363
514
|
}
|
|
@@ -378,47 +529,47 @@ impl Engine {
|
|
|
378
529
|
active: scope::Context<'_>,
|
|
379
530
|
evaluation: &mut Tracked,
|
|
380
531
|
) -> Verdict {
|
|
532
|
+
if !self.budget.checkpoint() {
|
|
533
|
+
return Unsupported;
|
|
534
|
+
}
|
|
381
535
|
let len = unsafe { sn_len(value) };
|
|
382
536
|
if bounds(len, r.min, r.max) == Invalid {
|
|
383
537
|
return Invalid;
|
|
384
538
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}
|
|
539
|
+
let status = r.check_required(value, self.undefined, &self.budget);
|
|
540
|
+
if status != Valid {
|
|
541
|
+
return status;
|
|
389
542
|
}
|
|
390
|
-
let
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
543
|
+
let found = match r.visit_properties(
|
|
544
|
+
value,
|
|
545
|
+
len,
|
|
546
|
+
self.undefined,
|
|
547
|
+
&self.budget,
|
|
548
|
+
|index, child, item| {
|
|
549
|
+
let result = self.record_value(child, item, depth, active);
|
|
550
|
+
if result == Valid {
|
|
551
|
+
evaluation.known_property(r.property_indices[index]);
|
|
396
552
|
}
|
|
397
|
-
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
evaluation.known_property(r.property_indices[index]);
|
|
405
|
-
}
|
|
553
|
+
result
|
|
554
|
+
},
|
|
555
|
+
) {
|
|
556
|
+
Ok(found) => found,
|
|
557
|
+
Err(status) => return status,
|
|
558
|
+
};
|
|
406
559
|
if r.names.is_some() || !r.patterns.is_empty() || (r.additional.is_some() && found < len) {
|
|
407
560
|
let result = self.tracked_object_walk(r, value, depth, active, evaluation, false);
|
|
408
561
|
if result != Valid {
|
|
409
562
|
return result;
|
|
410
563
|
}
|
|
411
564
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
if missing(value, key, self.undefined) {
|
|
416
|
-
return Invalid;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
565
|
+
let status = r.check_dependent_required(value, self.undefined, &self.budget);
|
|
566
|
+
if status != Valid {
|
|
567
|
+
return status;
|
|
420
568
|
}
|
|
421
569
|
for &(key, child) in &r.dependent_schemas {
|
|
570
|
+
if !self.budget.checkpoint() {
|
|
571
|
+
return Unsupported;
|
|
572
|
+
}
|
|
422
573
|
if !missing(value, key, self.undefined) {
|
|
423
574
|
let result = evaluation.merge(self.eval_tracked(child, value, depth, active));
|
|
424
575
|
if result != Valid {
|
|
@@ -464,7 +615,7 @@ impl Engine {
|
|
|
464
615
|
}
|
|
465
616
|
}
|
|
466
617
|
struct TrackingContext<'a> {
|
|
467
|
-
engine: &'a
|
|
618
|
+
engine: &'a Execution<'a>,
|
|
468
619
|
rules: &'a Object,
|
|
469
620
|
depth: usize,
|
|
470
621
|
active: scope::Context<'a>,
|
|
@@ -474,6 +625,9 @@ struct TrackingContext<'a> {
|
|
|
474
625
|
unsafe extern "C" fn tracking_entry(context: *mut c_void, key: Value, value: Value) -> i32 {
|
|
475
626
|
catch_unwind(AssertUnwindSafe(|| {
|
|
476
627
|
let c = unsafe { &mut *context.cast::<TrackingContext>() };
|
|
628
|
+
if !c.engine.budget.checkpoint() {
|
|
629
|
+
return Unsupported as i32;
|
|
630
|
+
}
|
|
477
631
|
if unsafe { sn_kind(key) } != TYPE_STRING {
|
|
478
632
|
return Unsupported as i32;
|
|
479
633
|
}
|
|
@@ -494,26 +648,27 @@ unsafe extern "C" fn tracking_entry(context: *mut c_void, key: Value, value: Val
|
|
|
494
648
|
return result as i32;
|
|
495
649
|
}
|
|
496
650
|
}
|
|
497
|
-
let
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
651
|
+
let result = c.rules.visit_value_rules::<false>(
|
|
652
|
+
c.engine.caches,
|
|
653
|
+
&c.engine.budget,
|
|
654
|
+
key,
|
|
655
|
+
Some(key_location),
|
|
656
|
+
|_, _, child| match c.engine.eval(child, value, c.depth, c.active) {
|
|
502
657
|
Valid => {
|
|
503
|
-
matched = true;
|
|
504
|
-
let result = c.engine.eval(*child, value, c.depth, c.active);
|
|
505
|
-
if result != Valid {
|
|
506
|
-
return result as i32;
|
|
507
|
-
}
|
|
508
658
|
if let Some(index) = known {
|
|
509
659
|
c.evaluation.known_property(index);
|
|
510
660
|
} else {
|
|
511
661
|
c.evaluation.properties.insert(key_location);
|
|
512
662
|
}
|
|
663
|
+
Ok(())
|
|
513
664
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
665
|
+
status => Err(status),
|
|
666
|
+
},
|
|
667
|
+
);
|
|
668
|
+
return match result {
|
|
669
|
+
Ok(_) => Valid as i32,
|
|
670
|
+
Err(status) => status as i32,
|
|
671
|
+
};
|
|
517
672
|
};
|
|
518
673
|
if let Some(child) = child {
|
|
519
674
|
let result = c.engine.eval(child, value, c.depth, c.active);
|