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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -4
  3. data/ext/schemurai_native/Cargo.lock +55 -1
  4. data/ext/schemurai_native/Cargo.toml +4 -1
  5. data/ext/schemurai_native/extconf.rb +1 -1
  6. data/ext/schemurai_native/glue.c +1679 -62
  7. data/ext/schemurai_native/protocol.def +45 -0
  8. data/ext/schemurai_native/src/budget.rs +49 -0
  9. data/ext/schemurai_native/src/container.rs +460 -0
  10. data/ext/schemurai_native/src/content.rs +74 -27
  11. data/ext/schemurai_native/src/diagnostics/array.rs +392 -0
  12. data/ext/schemurai_native/src/diagnostics/object.rs +218 -0
  13. data/ext/schemurai_native/src/diagnostics.rs +1087 -307
  14. data/ext/schemurai_native/src/enumeration.rs +6 -1
  15. data/ext/schemurai_native/src/evaluation.rs +49 -20
  16. data/ext/schemurai_native/src/format.rs +126 -14
  17. data/ext/schemurai_native/src/graph.rs +37 -0
  18. data/ext/schemurai_native/src/instance_graph.rs +90 -0
  19. data/ext/schemurai_native/src/integer_scan.rs +132 -0
  20. data/ext/schemurai_native/src/lib.rs +917 -238
  21. data/ext/schemurai_native/src/memo.rs +11 -38
  22. data/ext/schemurai_native/src/number/arbitrary.rs +154 -0
  23. data/ext/schemurai_native/src/number.rs +250 -59
  24. data/ext/schemurai_native/src/pattern.rs +147 -109
  25. data/ext/schemurai_native/src/pointer.rs +97 -0
  26. data/ext/schemurai_native/src/runtime/properties.rs +162 -0
  27. data/ext/schemurai_native/src/runtime/reports.rs +266 -0
  28. data/ext/schemurai_native/src/runtime/scopes.rs +430 -0
  29. data/ext/schemurai_native/src/runtime.rs +1020 -0
  30. data/ext/schemurai_native/src/scope.rs +60 -16
  31. data/ext/schemurai_native/src/string.rs +68 -0
  32. data/ext/schemurai_native/src/tracking.rs +244 -174
  33. data/lib/schemurai/container_checks/array_diagnostics.rb +119 -0
  34. data/lib/schemurai/container_checks/object_diagnostics.rb +152 -0
  35. data/lib/schemurai/container_checks.rb +534 -0
  36. data/lib/schemurai/decimal.rb +11 -0
  37. data/lib/schemurai/dialect.rb +0 -12
  38. data/lib/schemurai/ecma_regexp.rb +46 -0
  39. data/lib/schemurai/evaluation_pool.rb +66 -0
  40. data/lib/schemurai/evaluator.rb +9 -48
  41. data/lib/schemurai/json_pointer.rb +11 -0
  42. data/lib/schemurai/native/diagnostics.rb +4 -3
  43. data/lib/schemurai/native/evaluator.rb +38 -10
  44. data/lib/schemurai/native/null_numeric.rb +23 -0
  45. data/lib/schemurai/number_checks.rb +128 -0
  46. data/lib/schemurai/string_checks.rb +166 -0
  47. data/lib/schemurai/type_checks.rb +59 -0
  48. data/lib/schemurai/value_equality.rb +72 -0
  49. data/lib/schemurai/version.rb +1 -1
  50. data/lib/schemurai/vm/compiler.rb +18 -26
  51. data/lib/schemurai/vm/evaluator.rb +87 -545
  52. data/lib/schemurai/vm/limited_evaluator.rb +7 -22
  53. metadata +25 -2
@@ -1,5 +1,6 @@
1
- //! Evaluated locations stay in native buffers, scoped to one instance location.
2
- //! Keys remain borrowed VALUEs; no Ruby objects or instance trees are allocated.
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.properties.capacity()
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
- self.properties.len() + self.known_properties.count
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(&mut self, other: Self) -> Result<(), Verdict> {
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.merge(other);
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 = if value & self.fixnum_flag != 0 {
260
- TYPE_INTEGRAL_NUMBER
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
- kind: i32,
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, kind);
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, kind);
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
- kind: i32,
487
+ kind_state: &InstanceKind,
306
488
  ) -> Tracked {
307
- let frame = scope::ScopeFrame {
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 = unique(value, depth, self.fixnum_flag, &self.budget);
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
- |index| self.eval(child, unsafe { sn_entry(value, index) }, depth, active),
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 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]);
552
- }
553
- result
554
- },
555
- ) {
556
- Ok(found) => found,
557
- Err(status) => return status,
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 r.names.is_some() || !r.patterns.is_empty() || (r.additional.is_some() && found < len) {
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
- let status = r.check_dependent_required(value, self.undefined, &self.budget);
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 = evaluation.merge(self.eval_tracked(child, value, depth, active));
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 let Some(child) = c.rules.names {
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::<false>(
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) {