schemurai 3.0.0 → 3.0.2
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 +6 -4
- data/ext/schemurai_native/Cargo.lock +55 -1
- data/ext/schemurai_native/Cargo.toml +4 -1
- data/ext/schemurai_native/extconf.rb +1 -1
- data/ext/schemurai_native/glue.c +1679 -62
- data/ext/schemurai_native/protocol.def +45 -0
- data/ext/schemurai_native/src/budget.rs +49 -0
- data/ext/schemurai_native/src/container.rs +460 -0
- data/ext/schemurai_native/src/content.rs +74 -27
- data/ext/schemurai_native/src/diagnostics/array.rs +392 -0
- data/ext/schemurai_native/src/diagnostics/object.rs +218 -0
- data/ext/schemurai_native/src/diagnostics.rs +1087 -307
- data/ext/schemurai_native/src/enumeration.rs +6 -1
- data/ext/schemurai_native/src/evaluation.rs +49 -20
- data/ext/schemurai_native/src/format.rs +126 -14
- data/ext/schemurai_native/src/graph.rs +37 -0
- data/ext/schemurai_native/src/instance_graph.rs +90 -0
- data/ext/schemurai_native/src/integer_scan.rs +132 -0
- data/ext/schemurai_native/src/lib.rs +917 -238
- data/ext/schemurai_native/src/memo.rs +11 -38
- data/ext/schemurai_native/src/number/arbitrary.rs +154 -0
- data/ext/schemurai_native/src/number.rs +250 -59
- data/ext/schemurai_native/src/pattern.rs +147 -109
- data/ext/schemurai_native/src/pointer.rs +97 -0
- data/ext/schemurai_native/src/runtime/properties.rs +162 -0
- data/ext/schemurai_native/src/runtime/reports.rs +266 -0
- data/ext/schemurai_native/src/runtime/scopes.rs +430 -0
- data/ext/schemurai_native/src/runtime.rs +1020 -0
- data/ext/schemurai_native/src/scope.rs +60 -16
- data/ext/schemurai_native/src/string.rs +68 -0
- data/ext/schemurai_native/src/tracking.rs +244 -174
- data/lib/schemurai/container_checks/array_diagnostics.rb +119 -0
- data/lib/schemurai/container_checks/object_diagnostics.rb +152 -0
- data/lib/schemurai/container_checks.rb +534 -0
- data/lib/schemurai/decimal.rb +11 -0
- data/lib/schemurai/dialect.rb +0 -12
- data/lib/schemurai/ecma_regexp.rb +46 -0
- data/lib/schemurai/evaluation_pool.rb +66 -0
- data/lib/schemurai/evaluator.rb +9 -48
- data/lib/schemurai/json_pointer.rb +11 -0
- data/lib/schemurai/native/diagnostics.rb +4 -3
- data/lib/schemurai/native/evaluator.rb +38 -10
- data/lib/schemurai/native/null_numeric.rb +23 -0
- data/lib/schemurai/number_checks.rb +128 -0
- data/lib/schemurai/string_checks.rb +166 -0
- data/lib/schemurai/type_checks.rb +59 -0
- data/lib/schemurai/value_equality.rb +72 -0
- data/lib/schemurai/version.rb +1 -1
- data/lib/schemurai/vm/compiler.rb +18 -26
- data/lib/schemurai/vm/evaluator.rb +87 -545
- data/lib/schemurai/vm/limited_evaluator.rb +7 -22
- metadata +25 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
//!
|
|
2
|
-
//!
|
|
1
|
+
//! Ordinary locations use native buffers; runtime locations retain opaque Ruby
|
|
2
|
+
//! buffers without borrowing their sets. This module never allocates Ruby objects
|
|
3
|
+
//! or decodes instance trees.
|
|
3
4
|
use super::*;
|
|
4
5
|
use std::collections::HashSet;
|
|
5
6
|
use std::hash::Hash;
|
|
@@ -24,6 +25,47 @@ impl Hash for JsonKey {
|
|
|
24
25
|
}
|
|
25
26
|
pub(super) type KeySet = HashSet<JsonKey, BuildHasherDefault<BucketHasher>>;
|
|
26
27
|
|
|
28
|
+
#[repr(C)]
|
|
29
|
+
#[derive(Clone, Copy)]
|
|
30
|
+
pub struct RuntimeLocations {
|
|
31
|
+
pub(crate) buffer: Value,
|
|
32
|
+
pub(crate) properties: Value,
|
|
33
|
+
pub(crate) items: Value,
|
|
34
|
+
}
|
|
35
|
+
impl RuntimeLocations {
|
|
36
|
+
pub(crate) fn mark(&self) {
|
|
37
|
+
for value in [self.buffer, self.properties, self.items] {
|
|
38
|
+
unsafe {
|
|
39
|
+
sn_pin(value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// An owned export snapshot can be traversed by C across Ruby allocations.
|
|
46
|
+
pub struct PropertyLocations {
|
|
47
|
+
pub(crate) set: Option<Value>,
|
|
48
|
+
pub(crate) keys: Vec<Value>,
|
|
49
|
+
}
|
|
50
|
+
impl PropertyLocations {
|
|
51
|
+
pub(crate) fn new(engine: &Engine, tracked: &Tracked) -> Self {
|
|
52
|
+
Self {
|
|
53
|
+
set: tracked.properties_set(),
|
|
54
|
+
keys: tracked.native_properties(engine),
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
pub(crate) fn mark(&self) {
|
|
58
|
+
for &value in self.set.iter().chain(&self.keys) {
|
|
59
|
+
unsafe {
|
|
60
|
+
sn_pin(value);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
pub(crate) fn memory_usage(&self) -> usize {
|
|
65
|
+
self.keys.capacity() * std::mem::size_of::<Value>()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
27
69
|
// Store only nonempty words. The common single-word case needs no allocation,
|
|
28
70
|
// even when its property IDs are near the end of a large compiled engine.
|
|
29
71
|
#[derive(Clone, Default)]
|
|
@@ -138,13 +180,36 @@ impl PropertyBits {
|
|
|
138
180
|
pub(super) struct Tracked {
|
|
139
181
|
pub status: Verdict,
|
|
140
182
|
properties: KeySet,
|
|
183
|
+
runtime: Option<RuntimeLocations>,
|
|
141
184
|
known_properties: PropertyBits,
|
|
142
185
|
items: Vec<u64>,
|
|
143
186
|
}
|
|
144
187
|
impl Tracked {
|
|
188
|
+
pub(super) fn memory_usage(&self) -> usize {
|
|
189
|
+
self.properties.capacity() * std::mem::size_of::<JsonKey>()
|
|
190
|
+
+ self.items.capacity() * std::mem::size_of::<u64>()
|
|
191
|
+
+ match &self.known_properties.words {
|
|
192
|
+
PropertyWords::Empty | PropertyWords::Single(..) => 0,
|
|
193
|
+
PropertyWords::Dense(_, words) => words.capacity() * std::mem::size_of::<u64>(),
|
|
194
|
+
PropertyWords::Many(words) => {
|
|
195
|
+
words.capacity() * std::mem::size_of::<(usize, u64)>()
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
pub(super) fn mark(&self) {
|
|
200
|
+
if let Some(runtime) = self.runtime {
|
|
201
|
+
runtime.mark();
|
|
202
|
+
}
|
|
203
|
+
for key in &self.properties {
|
|
204
|
+
unsafe {
|
|
205
|
+
sn_pin(key.0);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
145
209
|
// Bound retained annotation slots independently of the number of results.
|
|
146
210
|
pub(super) fn memo_slots(&self) -> usize {
|
|
147
|
-
self.
|
|
211
|
+
self.runtime.map_or(0, |_| 3)
|
|
212
|
+
+ self.properties.capacity()
|
|
148
213
|
+ self.items.capacity()
|
|
149
214
|
+ match &self.known_properties.words {
|
|
150
215
|
PropertyWords::Empty | PropertyWords::Single(..) => 0,
|
|
@@ -156,6 +221,7 @@ impl Tracked {
|
|
|
156
221
|
Self {
|
|
157
222
|
status,
|
|
158
223
|
properties: KeySet::default(),
|
|
224
|
+
runtime: None,
|
|
159
225
|
known_properties: PropertyBits::default(),
|
|
160
226
|
items: vec![],
|
|
161
227
|
}
|
|
@@ -179,7 +245,13 @@ impl Tracked {
|
|
|
179
245
|
self.known_properties.contains(index)
|
|
180
246
|
}
|
|
181
247
|
pub(super) fn property_count(&self) -> usize {
|
|
182
|
-
|
|
248
|
+
// Runtime sets can be reset or changed by retained blocks. Returning
|
|
249
|
+
// zero disables native count-based scan elision; Ruby reads live sets.
|
|
250
|
+
if self.runtime.is_some() {
|
|
251
|
+
0
|
|
252
|
+
} else {
|
|
253
|
+
self.properties.len() + self.known_properties.count
|
|
254
|
+
}
|
|
183
255
|
}
|
|
184
256
|
pub(super) fn item_count(&self) -> usize {
|
|
185
257
|
self.items
|
|
@@ -188,6 +260,7 @@ impl Tracked {
|
|
|
188
260
|
.sum()
|
|
189
261
|
}
|
|
190
262
|
pub(super) fn merge(&mut self, other: Self) -> Verdict {
|
|
263
|
+
debug_assert!(self.runtime.is_none() && other.runtime.is_none());
|
|
191
264
|
if other.status != Valid {
|
|
192
265
|
return other.status;
|
|
193
266
|
}
|
|
@@ -200,17 +273,86 @@ impl Tracked {
|
|
|
200
273
|
merge_bits(&mut self.items, other.items);
|
|
201
274
|
Valid
|
|
202
275
|
}
|
|
203
|
-
pub(super) fn merge_diagnostics(
|
|
276
|
+
pub(super) fn merge_diagnostics(
|
|
277
|
+
&mut self,
|
|
278
|
+
execution: &Execution<'_>,
|
|
279
|
+
other: Self,
|
|
280
|
+
) -> Result<(), Verdict> {
|
|
204
281
|
if other.status == Unsupported {
|
|
205
282
|
return Err(Unsupported);
|
|
206
283
|
}
|
|
207
284
|
if self.status != Valid || other.status != Valid {
|
|
208
285
|
*self = Self::new(Invalid);
|
|
209
286
|
} else {
|
|
210
|
-
self.
|
|
287
|
+
if self.merge_runtime(execution, other) == Unsupported {
|
|
288
|
+
return Err(Unsupported);
|
|
289
|
+
}
|
|
211
290
|
}
|
|
212
291
|
Ok(())
|
|
213
292
|
}
|
|
293
|
+
pub(super) fn has_runtime_properties(&self) -> bool {
|
|
294
|
+
self.runtime.is_some()
|
|
295
|
+
}
|
|
296
|
+
pub(super) fn runtime_locations(&self) -> Option<RuntimeLocations> {
|
|
297
|
+
self.runtime
|
|
298
|
+
}
|
|
299
|
+
pub(super) fn properties_set(&self) -> Option<Value> {
|
|
300
|
+
self.runtime.map(|runtime| runtime.properties)
|
|
301
|
+
}
|
|
302
|
+
pub(super) fn items_set(&self) -> Option<Value> {
|
|
303
|
+
self.runtime.map(|runtime| runtime.items)
|
|
304
|
+
}
|
|
305
|
+
pub(super) fn set_runtime(&mut self, runtime: RuntimeLocations) {
|
|
306
|
+
debug_assert!(
|
|
307
|
+
self.properties.is_empty() && self.known_properties.count == 0 && self.items.is_empty()
|
|
308
|
+
);
|
|
309
|
+
self.runtime = Some(runtime);
|
|
310
|
+
}
|
|
311
|
+
pub(super) fn native_items(&self) -> Vec<usize> {
|
|
312
|
+
let mut result = vec![];
|
|
313
|
+
for (word, &bits) in self.items.iter().enumerate() {
|
|
314
|
+
let mut bits = bits;
|
|
315
|
+
while bits != 0 {
|
|
316
|
+
let bit = bits.trailing_zeros() as usize;
|
|
317
|
+
result.push(word * 64 + bit);
|
|
318
|
+
bits &= bits - 1;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
result
|
|
322
|
+
}
|
|
323
|
+
pub(super) fn native_properties(&self, engine: &Engine) -> Vec<Value> {
|
|
324
|
+
let mut known: Vec<_> = engine
|
|
325
|
+
.property_ids
|
|
326
|
+
.iter()
|
|
327
|
+
.filter(|(_, index)| self.contains_known_property(**index))
|
|
328
|
+
.map(|(key, &index)| (index, key.0))
|
|
329
|
+
.collect();
|
|
330
|
+
known.sort_unstable_by_key(|&(index, _)| index);
|
|
331
|
+
known
|
|
332
|
+
.into_iter()
|
|
333
|
+
.map(|(_, key)| key)
|
|
334
|
+
.chain(self.properties.iter().map(|key| key.0))
|
|
335
|
+
.collect()
|
|
336
|
+
}
|
|
337
|
+
pub(super) fn merge_runtime(&mut self, execution: &Execution<'_>, other: Self) -> Verdict {
|
|
338
|
+
if other.status != Valid {
|
|
339
|
+
return other.status;
|
|
340
|
+
}
|
|
341
|
+
if !self.has_runtime_properties() && !other.has_runtime_properties() {
|
|
342
|
+
return self.merge(other);
|
|
343
|
+
}
|
|
344
|
+
let Ok(runtime) = execution
|
|
345
|
+
.caches
|
|
346
|
+
.merge_properties(execution.engine, self, &other)
|
|
347
|
+
else {
|
|
348
|
+
return Unsupported;
|
|
349
|
+
};
|
|
350
|
+
self.properties.clear();
|
|
351
|
+
self.known_properties = PropertyBits::default();
|
|
352
|
+
self.items.clear();
|
|
353
|
+
self.runtime = Some(runtime);
|
|
354
|
+
Valid
|
|
355
|
+
}
|
|
214
356
|
pub(super) fn property(&mut self, engine: &Engine, value: Value) {
|
|
215
357
|
let key = JsonKey::new(value);
|
|
216
358
|
if let Some(&index) = engine.property_ids.get(&key) {
|
|
@@ -243,6 +385,31 @@ fn merge_bits(target: &mut Vec<u64>, source: Vec<u64>) {
|
|
|
243
385
|
}
|
|
244
386
|
}
|
|
245
387
|
impl Execution<'_> {
|
|
388
|
+
// contains needs the trial outcome, not the child's exported annotations.
|
|
389
|
+
pub(super) fn trial_status(
|
|
390
|
+
&self,
|
|
391
|
+
node: usize,
|
|
392
|
+
value: Value,
|
|
393
|
+
depth: usize,
|
|
394
|
+
active: scope::Context<'_>,
|
|
395
|
+
) -> Verdict {
|
|
396
|
+
if self.should_continue_report(depth) {
|
|
397
|
+
return self.eval_tracked(node, value, depth, active).status;
|
|
398
|
+
}
|
|
399
|
+
if depth > MAX_EVALUATION_DEPTH || !self.budget.checkpoint() {
|
|
400
|
+
return Unsupported;
|
|
401
|
+
}
|
|
402
|
+
let kind_state = InstanceKind::new(TYPE_UNCHECKED);
|
|
403
|
+
let kind = self.diagnostic_kind(node, value, &kind_state);
|
|
404
|
+
if self.unsupported_kind(node, kind) {
|
|
405
|
+
return Unsupported;
|
|
406
|
+
}
|
|
407
|
+
self.trial_scalar(node, value, depth, kind)
|
|
408
|
+
.unwrap_or_else(|| {
|
|
409
|
+
self.eval_tracked_kind(node, value, depth, active, &kind_state)
|
|
410
|
+
.status
|
|
411
|
+
})
|
|
412
|
+
}
|
|
246
413
|
pub(super) fn eval_tracked(
|
|
247
414
|
&self,
|
|
248
415
|
node: usize,
|
|
@@ -250,18 +417,23 @@ impl Execution<'_> {
|
|
|
250
417
|
depth: usize,
|
|
251
418
|
active: scope::Context<'_>,
|
|
252
419
|
) -> Tracked {
|
|
420
|
+
if self.should_continue_report(depth) {
|
|
421
|
+
return self.collect_trial(
|
|
422
|
+
node,
|
|
423
|
+
value,
|
|
424
|
+
depth,
|
|
425
|
+
active,
|
|
426
|
+
&InstanceKind::new(TYPE_UNCHECKED),
|
|
427
|
+
);
|
|
428
|
+
}
|
|
253
429
|
if depth > MAX_EVALUATION_DEPTH {
|
|
254
430
|
return Tracked::new(Unsupported);
|
|
255
431
|
}
|
|
256
432
|
if !self.budget.checkpoint() {
|
|
257
433
|
return Tracked::new(Unsupported);
|
|
258
434
|
}
|
|
259
|
-
let kind =
|
|
260
|
-
|
|
261
|
-
} else {
|
|
262
|
-
unsafe { sn_kind(value) }
|
|
263
|
-
};
|
|
264
|
-
self.eval_tracked_kind(node, value, depth, active, kind)
|
|
435
|
+
let kind = InstanceKind::new(TYPE_UNCHECKED);
|
|
436
|
+
self.eval_tracked_kind(node, value, depth, active, &kind)
|
|
265
437
|
}
|
|
266
438
|
// Applicator branches evaluate the same borrowed instance under the GVL.
|
|
267
439
|
// No Ruby callback can mutate it, so its guarded kind can be reused while
|
|
@@ -272,11 +444,21 @@ impl Execution<'_> {
|
|
|
272
444
|
value: Value,
|
|
273
445
|
depth: usize,
|
|
274
446
|
active: scope::Context<'_>,
|
|
275
|
-
|
|
447
|
+
kind_state: &InstanceKind,
|
|
276
448
|
) -> Tracked {
|
|
449
|
+
if self.should_continue_report(depth) {
|
|
450
|
+
return self.collect_trial(node, value, depth, active, kind_state);
|
|
451
|
+
}
|
|
452
|
+
let kind = self.diagnostic_kind(node, value, kind_state);
|
|
277
453
|
if !self.budget.checkpoint() {
|
|
278
454
|
return Tracked::new(Unsupported);
|
|
279
455
|
}
|
|
456
|
+
if depth <= MAX_EVALUATION_DEPTH
|
|
457
|
+
&& !self.unsupported_kind(node, kind)
|
|
458
|
+
&& let Some(status) = self.trial_scalar(node, value, depth, kind)
|
|
459
|
+
{
|
|
460
|
+
return Tracked::new(status);
|
|
461
|
+
}
|
|
280
462
|
let Some(key) = self
|
|
281
463
|
.memo
|
|
282
464
|
.key(self, node, value, depth, active, &self.budget)
|
|
@@ -284,7 +466,7 @@ impl Execution<'_> {
|
|
|
284
466
|
if self.budget.hit.get().0 != 0 {
|
|
285
467
|
return Tracked::new(Unsupported);
|
|
286
468
|
}
|
|
287
|
-
return self.eval_tracked_uncached(node, value, depth, active,
|
|
469
|
+
return self.eval_tracked_uncached(node, value, depth, active, kind_state);
|
|
288
470
|
};
|
|
289
471
|
if self.budget.hit.get().0 != 0 || !self.budget.checkpoint() {
|
|
290
472
|
return Tracked::new(Unsupported);
|
|
@@ -292,7 +474,7 @@ impl Execution<'_> {
|
|
|
292
474
|
if let Some(result) = self.memo.tracked(key) {
|
|
293
475
|
return result;
|
|
294
476
|
}
|
|
295
|
-
let result = self.eval_tracked_uncached(node, value, depth, active,
|
|
477
|
+
let result = self.eval_tracked_uncached(node, value, depth, active, kind_state);
|
|
296
478
|
self.memo.record_tracked(key, &result);
|
|
297
479
|
result
|
|
298
480
|
}
|
|
@@ -302,143 +484,11 @@ impl Execution<'_> {
|
|
|
302
484
|
value: Value,
|
|
303
485
|
depth: usize,
|
|
304
486
|
active: scope::Context<'_>,
|
|
305
|
-
|
|
487
|
+
kind_state: &InstanceKind,
|
|
306
488
|
) -> Tracked {
|
|
307
|
-
|
|
308
|
-
resource: self.metadata[node].resource,
|
|
309
|
-
parent: active.scope,
|
|
310
|
-
primary_binding: active
|
|
311
|
-
.scope
|
|
312
|
-
.and_then(|frame| frame.primary_binding)
|
|
313
|
-
.or(self.metadata[node].primary_binding),
|
|
314
|
-
};
|
|
315
|
-
let active = self.enter_scope(node, active, &frame);
|
|
316
|
-
if depth > MAX_EVALUATION_DEPTH {
|
|
317
|
-
return Tracked::new(Unsupported);
|
|
318
|
-
}
|
|
319
|
-
if kind == TYPE_UNSUPPORTED {
|
|
320
|
-
return Tracked::new(Unsupported);
|
|
321
|
-
}
|
|
322
|
-
let mut evaluation = Tracked::new(Valid);
|
|
323
|
-
for op in &self.nodes[node] {
|
|
324
|
-
if !self.budget.checkpoint() {
|
|
325
|
-
return Tracked::new(Unsupported);
|
|
326
|
-
}
|
|
327
|
-
let result = match op {
|
|
328
|
-
Op::Ref(target) => {
|
|
329
|
-
if RefFrame::contains(active.active, node, value) {
|
|
330
|
-
Valid
|
|
331
|
-
} else {
|
|
332
|
-
let frame = RefFrame {
|
|
333
|
-
source: node,
|
|
334
|
-
value,
|
|
335
|
-
parent: active.active,
|
|
336
|
-
};
|
|
337
|
-
evaluation.merge(self.eval_tracked_kind(
|
|
338
|
-
self.reference_target(*target, active),
|
|
339
|
-
value,
|
|
340
|
-
depth + 1,
|
|
341
|
-
scope::Context {
|
|
342
|
-
active: Some(&frame),
|
|
343
|
-
..active
|
|
344
|
-
},
|
|
345
|
-
kind,
|
|
346
|
-
))
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
Op::All(nodes) => {
|
|
350
|
-
let mut result = Valid;
|
|
351
|
-
for &child in nodes {
|
|
352
|
-
if !self.budget.checkpoint() {
|
|
353
|
-
return Tracked::new(Unsupported);
|
|
354
|
-
}
|
|
355
|
-
result = evaluation.merge(self.eval_tracked_kind(
|
|
356
|
-
child,
|
|
357
|
-
value,
|
|
358
|
-
depth + 1,
|
|
359
|
-
active,
|
|
360
|
-
kind,
|
|
361
|
-
));
|
|
362
|
-
if result != Valid {
|
|
363
|
-
break;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
result
|
|
367
|
-
}
|
|
368
|
-
Op::Any(nodes) | Op::One(nodes) => {
|
|
369
|
-
let branch_kind = if matches!(op, Op::Any(_)) {
|
|
370
|
-
evaluation::BranchKind::Any
|
|
371
|
-
} else {
|
|
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),
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
Op::Not(child) => match self.eval(*child, value, depth + 1, active) {
|
|
385
|
-
Valid => Invalid,
|
|
386
|
-
Invalid => Valid,
|
|
387
|
-
Unsupported => Unsupported,
|
|
388
|
-
},
|
|
389
|
-
Op::Conditional(condition, yes, no) => {
|
|
390
|
-
let condition =
|
|
391
|
-
self.eval_tracked_kind(*condition, value, depth + 1, active, kind);
|
|
392
|
-
let status = condition.status;
|
|
393
|
-
if status == Unsupported {
|
|
394
|
-
Unsupported
|
|
395
|
-
} else {
|
|
396
|
-
let branch = if status == Valid {
|
|
397
|
-
evaluation.merge(condition);
|
|
398
|
-
yes
|
|
399
|
-
} else {
|
|
400
|
-
no
|
|
401
|
-
};
|
|
402
|
-
if let Some(child) = branch {
|
|
403
|
-
evaluation.merge(self.eval_tracked_kind(
|
|
404
|
-
*child,
|
|
405
|
-
value,
|
|
406
|
-
depth + 1,
|
|
407
|
-
active,
|
|
408
|
-
kind,
|
|
409
|
-
))
|
|
410
|
-
} else {
|
|
411
|
-
Valid
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
Op::Array(required, rules) => {
|
|
416
|
-
if *required != 0 && kind != TYPE_ARRAY {
|
|
417
|
-
Invalid
|
|
418
|
-
} else if kind != TYPE_ARRAY {
|
|
419
|
-
Valid
|
|
420
|
-
} else {
|
|
421
|
-
self.array_tracked(rules, value, depth + 1, active, &mut evaluation)
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
Op::Object(required, rules) => {
|
|
425
|
-
if *required != 0 && kind != TYPE_OBJECT {
|
|
426
|
-
Invalid
|
|
427
|
-
} else if kind != TYPE_OBJECT {
|
|
428
|
-
Valid
|
|
429
|
-
} else {
|
|
430
|
-
self.object_tracked(rules, value, depth + 1, active, &mut evaluation)
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
_ => self.leaf(op, kind, value, depth),
|
|
434
|
-
};
|
|
435
|
-
if result != Valid {
|
|
436
|
-
return Tracked::new(result);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
evaluation
|
|
489
|
+
self.collect_trial(node, value, depth, active, kind_state)
|
|
440
490
|
}
|
|
441
|
-
fn array_tracked(
|
|
491
|
+
pub(super) fn array_tracked(
|
|
442
492
|
&self,
|
|
443
493
|
r: &Array,
|
|
444
494
|
value: Value,
|
|
@@ -454,7 +504,7 @@ impl Execution<'_> {
|
|
|
454
504
|
return Invalid;
|
|
455
505
|
}
|
|
456
506
|
if r.unique {
|
|
457
|
-
let result =
|
|
507
|
+
let result = self.unique_values(value, depth);
|
|
458
508
|
if result != Valid {
|
|
459
509
|
return result;
|
|
460
510
|
}
|
|
@@ -491,7 +541,8 @@ impl Execution<'_> {
|
|
|
491
541
|
if let Some(child) = r.contains {
|
|
492
542
|
let count = match evaluation::scan_contains(
|
|
493
543
|
len,
|
|
494
|
-
|
|
544
|
+
evaluation::ContainsPolicy::AllMatches,
|
|
545
|
+
|index| self.trial_status(child, unsafe { sn_entry(value, index) }, depth, active),
|
|
495
546
|
|index| evaluation.item(index),
|
|
496
547
|
) {
|
|
497
548
|
Ok(count) => count,
|
|
@@ -521,7 +572,7 @@ impl Execution<'_> {
|
|
|
521
572
|
}
|
|
522
573
|
Valid
|
|
523
574
|
}
|
|
524
|
-
fn object_tracked(
|
|
575
|
+
pub(super) fn object_tracked(
|
|
525
576
|
&self,
|
|
526
577
|
r: &Object,
|
|
527
578
|
value: Value,
|
|
@@ -536,33 +587,48 @@ impl Execution<'_> {
|
|
|
536
587
|
if bounds(len, r.min, r.max) == Invalid {
|
|
537
588
|
return Invalid;
|
|
538
589
|
}
|
|
539
|
-
let status = r.check_required(value, self.undefined, &self.budget);
|
|
590
|
+
let status = r.check_required(self.caches, value, self.undefined, &self.budget);
|
|
540
591
|
if status != Valid {
|
|
541
592
|
return status;
|
|
542
593
|
}
|
|
543
|
-
let
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
594
|
+
let ordered = self.caches.runtime_order();
|
|
595
|
+
let found = if ordered {
|
|
596
|
+
0
|
|
597
|
+
} else {
|
|
598
|
+
match r.visit_properties(
|
|
599
|
+
value,
|
|
600
|
+
len,
|
|
601
|
+
self.undefined,
|
|
602
|
+
&self.budget,
|
|
603
|
+
|index, child, item| {
|
|
604
|
+
let result = self.record_value(child, item, depth, active);
|
|
605
|
+
if result == Valid {
|
|
606
|
+
evaluation.known_property(r.property_indices[index]);
|
|
607
|
+
}
|
|
608
|
+
result
|
|
609
|
+
},
|
|
610
|
+
) {
|
|
611
|
+
Ok(found) => found,
|
|
612
|
+
Err(status) => return status,
|
|
613
|
+
}
|
|
558
614
|
};
|
|
559
|
-
if
|
|
615
|
+
if ordered
|
|
616
|
+
|| r.names.is_some()
|
|
617
|
+
|| !r.patterns.is_empty()
|
|
618
|
+
|| (r.additional.is_some() && found < len)
|
|
619
|
+
{
|
|
560
620
|
let result = self.tracked_object_walk(r, value, depth, active, evaluation, false);
|
|
561
621
|
if result != Valid {
|
|
562
622
|
return result;
|
|
563
623
|
}
|
|
564
624
|
}
|
|
565
|
-
|
|
625
|
+
if ordered && r.names.is_some() {
|
|
626
|
+
let status = self.object_names(r, value, depth, active);
|
|
627
|
+
if status != Valid {
|
|
628
|
+
return status;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
let status = r.check_dependent_required(self.caches, value, self.undefined, &self.budget);
|
|
566
632
|
if status != Valid {
|
|
567
633
|
return status;
|
|
568
634
|
}
|
|
@@ -571,7 +637,8 @@ impl Execution<'_> {
|
|
|
571
637
|
return Unsupported;
|
|
572
638
|
}
|
|
573
639
|
if !missing(value, key, self.undefined) {
|
|
574
|
-
let result =
|
|
640
|
+
let result =
|
|
641
|
+
evaluation.merge_runtime(self, self.eval_tracked(child, value, depth, active));
|
|
575
642
|
if result != Valid {
|
|
576
643
|
return result;
|
|
577
644
|
}
|
|
@@ -642,15 +709,18 @@ unsafe extern "C" fn tracking_entry(context: *mut c_void, key: Value, value: Val
|
|
|
642
709
|
}
|
|
643
710
|
c.rules.unevaluated
|
|
644
711
|
} else {
|
|
645
|
-
if
|
|
712
|
+
if !c.engine.caches.runtime_order()
|
|
713
|
+
&& let Some(child) = c.rules.names
|
|
714
|
+
{
|
|
646
715
|
let result = c.engine.eval(child, key, c.depth, c.active);
|
|
647
716
|
if result != Valid {
|
|
648
717
|
return result as i32;
|
|
649
718
|
}
|
|
650
719
|
}
|
|
651
|
-
let result = c.rules.visit_value_rules
|
|
720
|
+
let result = c.rules.visit_value_rules(
|
|
652
721
|
c.engine.caches,
|
|
653
722
|
&c.engine.budget,
|
|
723
|
+
c.engine.caches.runtime_order(),
|
|
654
724
|
key,
|
|
655
725
|
Some(key_location),
|
|
656
726
|
|_, _, child| match c.engine.eval(child, value, c.depth, c.active) {
|