sync 0.32.0 - #7
Merged
Merged
Conversation
nativeTypeProvider.NativeToValue routed every struct-kind value to newNativeObject, regardless of whether the type was registered via ext.NativeTypes. Every other method on the provider checks the nativeTypes registry first and falls back to the composed base adapter when a type is not registered; NativeToValue was the exception, so a custom adapter added with cel.CustomTypeAdapter never saw unregistered structs it wanted to convert itself. Mirror the registry check from NewValue: only wrap registered struct types as native objects, and delegate the rest to the base adapter. Fixes cel-expr#1343
* Regex program plan size controls This implementation mirrors the logic used in RE2 within cel-java and cel-cpp. Regex implementations across platforms do not guarantee equivalent program plan sizes, but this at least provides a means to control the plan size per-platform. * Minor refactor for maintenance of limit extraction * Minor updates to test setup
* Simplify support for native object types Shift most of the logic from the NativeTypeProvider over to the NativeObject wrappers. This change is a stepping stone to consolidating type providers into the common/types package and making it less painful to manage type adaptation. * Fix for embedded type field traversal * Robustness checks for field retrieval with nil embedded pointers to structs Since promoted fields were previously not discovered, field setting also needed to be updated to ensure nested pointers to structs with promoted fields were initialized properly. * Note for future work
* Support self-describing, self-adapting struct types * Safer duplicate type detection
* Optimize NativeToValue to minimize allocs - **Unified `NativeToValue` Dispatch**: Consolidated private `nativeToValue` and `Registry.NativeToValue` into a single authoritative receiver method. `DefaultTypeAdapter` delegates directly to `emptyRegistry.NativeToValue`. - **Reflection & Allocation Reduction**: - Replaced dynamic `.Convert().Interface().(T)` reflection conversions for type aliases with direct zero-allocation primitive getters (`.Int()`, `.Uint()`, `.Float()`, `.Bool()`, `.String()`). - Integrated custom struct descriptor lookups directly into the reflection branch using `reflect.TypeOf(value)` for instant map matching. - Eliminated duplicate `reflect.ValueOf()` instantiations and redundant nil pointer checks across conversion paths. --- | Benchmark Target | `master` Baseline | Refactored Branch | Delta | Allocs / Op | | :--- | :---: | :---: | :---: | :---: | | `proto/TestAllTypes` | 177.0 ns/op | **87.91 ns/op** | 🚀 **+50.3% faster** | 1 alloc | | `nativeStruct/pointer` | 20.59 ns/op | **13.18 ns/op** | 🚀 **+36.0% faster** | 0 allocs | | `nativeStruct/value` | 17.25 ns/op | **13.52 ns/op** | 🚀 **+21.6% faster** | 0 allocs | | `ref.Val/String` | 4.81 ns/op | **2.62 ns/op** | 🚀 **+45.5% faster** | 0 allocs | | `ref.Val/Int` | 3.73 ns/op | **2.69 ns/op** | 🚀 **+27.9% faster** | 0 allocs | | `int/1` | 3.86 ns/op | **2.55 ns/op** | 🚀 **+33.9% faster** | 0 allocs | | `bool/true` | 2.91 ns/op | **2.11 ns/op** | 🚀 **+27.5% faster** | 0 allocs | | Benchmark Target | `master` Baseline | Refactored Branch | Delta | | :--- | :---: | :---: | :---: | | `nested_proto_field` | 141.3 ns/op | **124.7 ns/op** | 🚀 **+11.7% faster** | | `nested_proto_field_with_index` | 577.5 ns/op | **540.6 ns/op** | 🚀 **+6.4% faster** | | `index` | 122.3 ns/op | **108.1 ns/op** | 🚀 **+11.6% faster** | | `index_list_int_uint_type_index` | 42.87 ns/op | **38.22 ns/op** | 🚀 **+10.8% faster** | | `index_cross_type_float_uint` | 264.4 ns/op | **240.0 ns/op** | 🚀 **+9.2% faster** | | `select_subsumed_field` | 14.27 ns/op | **13.05 ns/op** | 🚀 **+8.5% faster** | | `select_custom_pb3_optional_field` | 43.96 ns/op | **41.71 ns/op** | 🚀 **+5.1% faster** | | `complex_qual_vars` | 338.4 ns/op | **323.1 ns/op** | 🚀 **+4.5% faster** | * Consistency fix for safe field getter * Remove test added by mistake
* Program plan optimizations | Benchmark Case | Before (ns/op) | After (ns/op) | Δ Time | Before (B/op) | After (B/op) | Δ Memory | Before (allocs) | After (allocs) | Δ Allocs | | :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | | BenchmarkProgramPlan/Default | 8,153 | 930 | **-88.6%** | 8,320 | 1,416 | **-83.0%** | 36 | 26 | **-27.8%** | | BenchmarkProgramPlan/OptimizeUnneeded | 7,344 | 1,150 | **-84.3%** | 8,784 | 1,512 | **-82.8%** | 50 | 32 | **-36.0%** | | BenchmarkProgramPlan/OptimizeNeeded | 8,370 | 2,164 | **-74.1%** | 10,224 | 2,976 | **-70.9%** | 67 | 52 | **-22.4%** | * Minor refactor of the initialization logic to reduce program size * Shift the dispatcher-reuse to the environment rather than the program
* Fold list concat expressions together * Test cases for optional tracking
* Program plan optimizations | Benchmark Case | Before (ns/op) | After (ns/op) | Δ Time | Before (B/op) | After (B/op) | Δ Memory | Before (allocs) | After (allocs) | Δ Allocs | | :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | | BenchmarkProgramPlan/Default | 8,153 | 930 | **-88.6%** | 8,320 | 1,416 | **-83.0%** | 36 | 26 | **-27.8%** | | BenchmarkProgramPlan/OptimizeUnneeded | 7,344 | 1,150 | **-84.3%** | 8,784 | 1,512 | **-82.8%** | 50 | 32 | **-36.0%** | | BenchmarkProgramPlan/OptimizeNeeded | 8,370 | 2,164 | **-74.1%** | 10,224 | 2,976 | **-70.9%** | 67 | 52 | **-22.4%** | * Minor refactor of the initialization logic to reduce program size * Copy-on-write semantics for types.Registry and cel.Env internals * Ensure shared declarations aren't copied unless necessary within the checker * Capture NewEnv setup benchmarks as well * Fix race-related issue with copy-on-write mutability check * Eliminate dead-code from former Copy() approach. More tests * Bug fix to support disabling declarations when using inherited declarations
Following the repository relocation and adoption of the cel.dev domain, update the module path in go.mod files from github.com/google/cel-go to cel.dev/cel-go. Update all internal package imports and Bazel BUILD definitions accordingly.
…amp (cel-expr#1414) * Parsing helper for working with different types and formats of timestamp * Add overflow checks and negative tests
* Add Go-native JSON type support into NativeToValue * JWT object rep and helpers for inspecting claims * JWT data types, parse, and claim helpers
…-expr#1404) * Support aggregate size computations over list, maps, and structs * Depth and traversal limits for aggregate size calculations * Enhancements to use Foldable and reduce duplication in native size computations
Cost tracking silently reported a cost of zero whenever state tracking was also enabled: cel.CostTracking(nil) -> 5 cel.CostTracking(nil) + cel.EvalOptions(OptTrackState) -> 0 cel.CostTracking(nil) + cel.EvalOptions(OptExhaustiveEval) -> 0 Each observer installed its own decorator, and decObserveEval returns a node which is already wrapped in a watcher untouched. Since the planner applies decorators in order, the state observer's decorator wrapped each node first and the cost observer's decorator then found a watcher and left it alone, so the cost observer's per-node callback was never installed. ObservableInterpretable still ran the tracker's InitState and GetState, so evaluation produced a CostTracker reporting a cost of zero rather than an error or a nil result -- including for programs configured with a cost limit, which then could not be exceeded. Observers now register only as observers, and the planner installs a single decorator which reports each observation to all of them.
String and bytes values now count as one element per ten bytes, rounding up with a minimum of one element, configurable via SizeCalculatorStringUnitLength. Sizes are measured in bytes rather than characters so that sizing large values is O(1) rather than a full UTF-8 scan per observation; byte length is never smaller than the character count, so byte-based sizing is conservative for limit enforcement.
* Aggregate policy evaluation semantics support * Additional aggregation tests showcasing multiple aggregation rules with nested match behavior * Replace 'emit' with 'output' to conform to spec update * Updates to the composition base step and aggregation
…y conformance test (cel-expr#1424)
The same saturating arithmetic existed in four places under three names: `safeAdd`/`safeMul` in `interpreter`, byte-identical copies in `ext`, and `addUint64NoOverflow`/`multiplyUint64NoOverflow`/`multiplyByCostFactor` in `checker`. Costs and sizes are uint64 values where math.MaxUint64 doubles as "unbounded", so every operation on them has to saturate rather than wrap; having one implementation of that per package made it easy for the domains to drift. `common/cost` now owns the single exported set: SafeAdd, SafeMultiply, SafeMultiplyByFactor, and SafeCeil. The package depends on nothing but `math`, so `checker`, `interpreter`, and `ext` can all reach it without cycles. Behavioral notes, all in extreme-value territory: - `CostTracker.costCall` computed the `matches` and `contains` products with an unguarded `*` while `safeMul` sat unused in the same file. Those products now saturate instead of wrapping, which matters as soon as an operand size can itself be saturated. - Inline `uint64(math.Ceil(float64(x)*factor))` conversions became SafeMultiplyByFactor, which is identical in range and returns MaxUint64 rather than an implementation-defined value out of range. - Truncating conversions (`uint64(float64(size)*costFactor)`) were left alone so that no in-range cost changes value. Locals named `cost` in the touched functions were renamed to `total`/`estimate` so the package identifier is not shadowed.
The aggregate size memoization on immutable lists and maps is now accessed atomically since values may be shared across concurrent evaluations, and sizes from computations aborted at the calculator's depth or traversal limits are no longer memoized, as they depend on where in the traversal the value was encountered. EstimateAggregateSize additionally distinguishes genuine uint32 saturation from computations aborted at the limits.
Add parser benchmarks
This reverts commit aadbbf9.
Switch module and import paths to cel.dev/cel-go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bring over https://github.com/cel-expr/cel-go/releases/tag/v0.32.0