Update cel-go to v0.21.0

This commit is contained in:
Cici Huang 2024-10-02 17:15:58 +00:00
parent a35bca903e
commit 80c0c2c32e
59 changed files with 1366 additions and 8897 deletions

2
go.mod
View File

@ -35,7 +35,7 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
github.com/golang/protobuf v1.5.4 github.com/golang/protobuf v1.5.4
github.com/google/cadvisor v0.50.0 github.com/google/cadvisor v0.50.0
github.com/google/cel-go v0.20.1 github.com/google/cel-go v0.21.0
github.com/google/gnostic-models v0.6.8 github.com/google/gnostic-models v0.6.8
github.com/google/go-cmp v0.6.0 github.com/google/go-cmp v0.6.0
github.com/google/gofuzz v1.2.0 github.com/google/gofuzz v1.2.0

4
go.sum
View File

@ -335,8 +335,8 @@ github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cadvisor v0.50.0 h1:7w/hKIbJKBWqQsRTy+Hpj2vj+fnxrLXcEXFy+LW0Bsg= github.com/google/cadvisor v0.50.0 h1:7w/hKIbJKBWqQsRTy+Hpj2vj+fnxrLXcEXFy+LW0Bsg=
github.com/google/cadvisor v0.50.0/go.mod h1:VxCDwZalpFyENvmfabFqaIGsqNKLtDzE62a19rfVTB8= github.com/google/cadvisor v0.50.0/go.mod h1:VxCDwZalpFyENvmfabFqaIGsqNKLtDzE62a19rfVTB8=
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=

View File

@ -9,7 +9,7 @@ godebug default=go1.23
require ( require (
github.com/emicklei/go-restful/v3 v3.11.0 github.com/emicklei/go-restful/v3 v3.11.0
github.com/gogo/protobuf v1.3.2 github.com/gogo/protobuf v1.3.2
github.com/google/cel-go v0.20.1 github.com/google/cel-go v0.21.0
github.com/google/gnostic-models v0.6.8 github.com/google/gnostic-models v0.6.8
github.com/google/go-cmp v0.6.0 github.com/google/go-cmp v0.6.0
github.com/google/gofuzz v1.2.0 github.com/google/gofuzz v1.2.0

View File

@ -214,8 +214,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=

View File

@ -15,7 +15,7 @@ require (
github.com/fsnotify/fsnotify v1.7.0 github.com/fsnotify/fsnotify v1.7.0
github.com/go-logr/logr v1.4.2 github.com/go-logr/logr v1.4.2
github.com/gogo/protobuf v1.3.2 github.com/gogo/protobuf v1.3.2
github.com/google/cel-go v0.20.1 github.com/google/cel-go v0.21.0
github.com/google/gnostic-models v0.6.8 github.com/google/gnostic-models v0.6.8
github.com/google/go-cmp v0.6.0 github.com/google/go-cmp v0.6.0
github.com/google/gofuzz v1.2.0 github.com/google/gofuzz v1.2.0

View File

@ -215,8 +215,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=

View File

@ -47,7 +47,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/cel-go v0.20.1 // indirect github.com/google/cel-go v0.21.0 // indirect
github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/gofuzz v1.2.0 // indirect github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect

View File

@ -78,8 +78,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@ -42,7 +42,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/cel-go v0.20.1 // indirect github.com/google/cel-go v0.21.0 // indirect
github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect github.com/google/gofuzz v1.2.0 // indirect

View File

@ -75,8 +75,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@ -9,7 +9,7 @@ godebug default=go1.23
require ( require (
github.com/blang/semver/v4 v4.0.0 github.com/blang/semver/v4 v4.0.0
github.com/go-logr/logr v1.4.2 github.com/go-logr/logr v1.4.2
github.com/google/cel-go v0.20.1 github.com/google/cel-go v0.21.0
github.com/google/go-cmp v0.6.0 github.com/google/go-cmp v0.6.0
github.com/onsi/gomega v1.33.1 github.com/onsi/gomega v1.33.1
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0

View File

@ -61,8 +61,8 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4er
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@ -51,7 +51,7 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/cel-go v0.20.1 // indirect github.com/google/cel-go v0.21.0 // indirect
github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect

View File

@ -76,8 +76,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@ -31,7 +31,7 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=

View File

@ -57,7 +57,7 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4er
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@ -45,7 +45,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/cel-go v0.20.1 // indirect github.com/google/cel-go v0.21.0 // indirect
github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/gofuzz v1.2.0 // indirect github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect

View File

@ -75,8 +75,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@ -43,7 +43,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/cel-go v0.20.1 // indirect github.com/google/cel-go v0.21.0 // indirect
github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-cmp v0.6.0 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect

View File

@ -75,8 +75,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@ -44,6 +44,9 @@ type Ast struct {
// NativeRep converts the AST to a Go-native representation. // NativeRep converts the AST to a Go-native representation.
func (ast *Ast) NativeRep() *celast.AST { func (ast *Ast) NativeRep() *celast.AST {
if ast == nil {
return nil
}
return ast.impl return ast.impl
} }
@ -55,16 +58,13 @@ func (ast *Ast) Expr() *exprpb.Expr {
if ast == nil { if ast == nil {
return nil return nil
} }
pbExpr, _ := celast.ExprToProto(ast.impl.Expr()) pbExpr, _ := celast.ExprToProto(ast.NativeRep().Expr())
return pbExpr return pbExpr
} }
// IsChecked returns whether the Ast value has been successfully type-checked. // IsChecked returns whether the Ast value has been successfully type-checked.
func (ast *Ast) IsChecked() bool { func (ast *Ast) IsChecked() bool {
if ast == nil { return ast.NativeRep().IsChecked()
return false
}
return ast.impl.IsChecked()
} }
// SourceInfo returns character offset and newline position information about expression elements. // SourceInfo returns character offset and newline position information about expression elements.
@ -72,7 +72,7 @@ func (ast *Ast) SourceInfo() *exprpb.SourceInfo {
if ast == nil { if ast == nil {
return nil return nil
} }
pbInfo, _ := celast.SourceInfoToProto(ast.impl.SourceInfo()) pbInfo, _ := celast.SourceInfoToProto(ast.NativeRep().SourceInfo())
return pbInfo return pbInfo
} }
@ -95,7 +95,7 @@ func (ast *Ast) OutputType() *Type {
if ast == nil { if ast == nil {
return types.ErrorType return types.ErrorType
} }
return ast.impl.GetType(ast.impl.Expr().ID()) return ast.NativeRep().GetType(ast.NativeRep().Expr().ID())
} }
// Source returns a view of the input used to create the Ast. This source may be complete or // Source returns a view of the input used to create the Ast. This source may be complete or
@ -218,12 +218,12 @@ func (e *Env) Check(ast *Ast) (*Ast, *Issues) {
if err != nil { if err != nil {
errs := common.NewErrors(ast.Source()) errs := common.NewErrors(ast.Source())
errs.ReportError(common.NoLocation, err.Error()) errs.ReportError(common.NoLocation, err.Error())
return nil, NewIssuesWithSourceInfo(errs, ast.impl.SourceInfo()) return nil, NewIssuesWithSourceInfo(errs, ast.NativeRep().SourceInfo())
} }
checked, errs := checker.Check(ast.impl, ast.Source(), chk) checked, errs := checker.Check(ast.NativeRep(), ast.Source(), chk)
if len(errs.GetErrors()) > 0 { if len(errs.GetErrors()) > 0 {
return nil, NewIssuesWithSourceInfo(errs, ast.impl.SourceInfo()) return nil, NewIssuesWithSourceInfo(errs, ast.NativeRep().SourceInfo())
} }
// Manually create the Ast to ensure that the Ast source information (which may be more // Manually create the Ast to ensure that the Ast source information (which may be more
// detailed than the information provided by Check), is returned to the caller. // detailed than the information provided by Check), is returned to the caller.
@ -244,7 +244,7 @@ func (e *Env) Check(ast *Ast) (*Ast, *Issues) {
} }
} }
// Apply additional validators on the type-checked result. // Apply additional validators on the type-checked result.
iss := NewIssuesWithSourceInfo(errs, ast.impl.SourceInfo()) iss := NewIssuesWithSourceInfo(errs, ast.NativeRep().SourceInfo())
for _, v := range e.validators { for _, v := range e.validators {
v.Validate(e, vConfig, checked, iss) v.Validate(e, vConfig, checked, iss)
} }
@ -309,17 +309,13 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
copy(chkOptsCopy, e.chkOpts) copy(chkOptsCopy, e.chkOpts)
// Copy the declarations if needed. // Copy the declarations if needed.
varsCopy := []*decls.VariableDecl{}
if chk != nil { if chk != nil {
// If the type-checker has already been instantiated, then the e.declarations have been // If the type-checker has already been instantiated, then the e.declarations have been
// validated within the chk instance. // validated within the chk instance.
chkOptsCopy = append(chkOptsCopy, checker.ValidatedDeclarations(chk)) chkOptsCopy = append(chkOptsCopy, checker.ValidatedDeclarations(chk))
} else {
// If the type-checker has not been instantiated, ensure the unvalidated declarations are
// provided to the extended Env instance.
varsCopy = make([]*decls.VariableDecl, len(e.variables))
copy(varsCopy, e.variables)
} }
varsCopy := make([]*decls.VariableDecl, len(e.variables))
copy(varsCopy, e.variables)
// Copy macros and program options // Copy macros and program options
macsCopy := make([]parser.Macro, len(e.macros)) macsCopy := make([]parser.Macro, len(e.macros))
@ -416,6 +412,17 @@ func (e *Env) Libraries() []string {
return libraries return libraries
} }
// HasFunction returns whether a specific function has been configured in the environment
func (e *Env) HasFunction(functionName string) bool {
_, ok := e.functions[functionName]
return ok
}
// Functions returns map of Functions, keyed by function name, that have been configured in the environment.
func (e *Env) Functions() map[string]*decls.FunctionDecl {
return e.functions
}
// HasValidator returns whether a specific ASTValidator has been configured in the environment. // HasValidator returns whether a specific ASTValidator has been configured in the environment.
func (e *Env) HasValidator(name string) bool { func (e *Env) HasValidator(name string) bool {
for _, v := range e.validators { for _, v := range e.validators {
@ -753,10 +760,10 @@ func (i *Issues) Append(other *Issues) *Issues {
if i == nil { if i == nil {
return other return other
} }
if other == nil { if other == nil || i == other {
return i return i
} }
return NewIssues(i.errs.Append(other.errs.GetErrors())) return NewIssuesWithSourceInfo(i.errs.Append(other.errs.GetErrors()), i.info)
} }
// String converts the issues to a suitable display string. // String converts the issues to a suitable display string.
@ -790,7 +797,7 @@ type interopCELTypeProvider struct {
// FindStructType returns a types.Type instance for the given fully-qualified typeName if one exists. // FindStructType returns a types.Type instance for the given fully-qualified typeName if one exists.
// //
// This method proxies to the underyling ref.TypeProvider's FindType method and converts protobuf type // This method proxies to the underlying ref.TypeProvider's FindType method and converts protobuf type
// into a native type representation. If the conversion fails, the type is listed as not found. // into a native type representation. If the conversion fails, the type is listed as not found.
func (p *interopCELTypeProvider) FindStructType(typeName string) (*types.Type, bool) { func (p *interopCELTypeProvider) FindStructType(typeName string) (*types.Type, bool) {
if et, found := p.FindType(typeName); found { if et, found := p.FindType(typeName); found {
@ -813,7 +820,7 @@ func (p *interopCELTypeProvider) FindStructFieldNames(typeName string) ([]string
// FindStructFieldType returns a types.FieldType instance for the given fully-qualified typeName and field // FindStructFieldType returns a types.FieldType instance for the given fully-qualified typeName and field
// name, if one exists. // name, if one exists.
// //
// This method proxies to the underyling ref.TypeProvider's FindFieldType method and converts protobuf type // This method proxies to the underlying ref.TypeProvider's FindFieldType method and converts protobuf type
// into a native type representation. If the conversion fails, the type is listed as not found. // into a native type representation. If the conversion fails, the type is listed as not found.
func (p *interopCELTypeProvider) FindStructFieldType(structType, fieldName string) (*types.FieldType, bool) { func (p *interopCELTypeProvider) FindStructFieldType(structType, fieldName string) (*types.FieldType, bool) {
if ft, found := p.FindFieldType(structType, fieldName); found { if ft, found := p.FindFieldType(structType, fieldName); found {

View File

@ -403,7 +403,7 @@ func optMap(meh MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *
meh.NewList(), meh.NewList(),
unusedIterVar, unusedIterVar,
varName, varName,
meh.NewMemberCall(valueFunc, target), meh.NewMemberCall(valueFunc, meh.Copy(target)),
meh.NewLiteral(types.False), meh.NewLiteral(types.False),
meh.NewIdent(varName), meh.NewIdent(varName),
mapExpr, mapExpr,
@ -430,7 +430,7 @@ func optFlatMap(meh MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Exp
meh.NewList(), meh.NewList(),
unusedIterVar, unusedIterVar,
varName, varName,
meh.NewMemberCall(valueFunc, target), meh.NewMemberCall(valueFunc, meh.Copy(target)),
meh.NewLiteral(types.False), meh.NewLiteral(types.False),
meh.NewIdent(varName), meh.NewIdent(varName),
mapExpr, mapExpr,
@ -446,6 +446,12 @@ func enableOptionalSyntax() EnvOption {
} }
} }
// EnableErrorOnBadPresenceTest enables error generation when a presence test or optional field
// selection is performed on a primitive type.
func EnableErrorOnBadPresenceTest(value bool) EnvOption {
return features(featureEnableErrorOnBadPresenceTest, value)
}
func decorateOptionalOr(i interpreter.Interpretable) (interpreter.Interpretable, error) { func decorateOptionalOr(i interpreter.Interpretable) (interpreter.Interpretable, error) {
call, ok := i.(interpreter.InterpretableCall) call, ok := i.(interpreter.InterpretableCall)
if !ok { if !ok {

View File

@ -15,6 +15,8 @@
package cel package cel
import ( import (
"sort"
"github.com/google/cel-go/common" "github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast" "github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types"
@ -98,14 +100,21 @@ func (opt *StaticOptimizer) Optimize(env *Env, a *Ast) (*Ast, *Issues) {
// that the ids within the expression correspond to the ids within macros. // that the ids within the expression correspond to the ids within macros.
func normalizeIDs(idGen ast.IDGenerator, optimized ast.Expr, info *ast.SourceInfo) { func normalizeIDs(idGen ast.IDGenerator, optimized ast.Expr, info *ast.SourceInfo) {
optimized.RenumberIDs(idGen) optimized.RenumberIDs(idGen)
if len(info.MacroCalls()) == 0 { if len(info.MacroCalls()) == 0 {
return return
} }
// Sort the macro ids to make sure that the renumbering of macro-specific variables
// is stable across normalization calls.
sortedMacroIDs := []int64{}
for id := range info.MacroCalls() {
sortedMacroIDs = append(sortedMacroIDs, id)
}
sort.Slice(sortedMacroIDs, func(i, j int) bool { return sortedMacroIDs[i] < sortedMacroIDs[j] })
// First, update the macro call ids themselves. // First, update the macro call ids themselves.
callIDMap := map[int64]int64{} callIDMap := map[int64]int64{}
for id := range info.MacroCalls() { for _, id := range sortedMacroIDs {
callIDMap[id] = idGen(id) callIDMap[id] = idGen(id)
} }
// Then update the macro call definitions which refer to these ids, but // Then update the macro call definitions which refer to these ids, but
@ -116,7 +125,8 @@ func normalizeIDs(idGen ast.IDGenerator, optimized ast.Expr, info *ast.SourceInf
call ast.Expr call ast.Expr
} }
macroUpdates := []macroUpdate{} macroUpdates := []macroUpdate{}
for oldID, newID := range callIDMap { for _, oldID := range sortedMacroIDs {
newID := callIDMap[oldID]
call, found := info.GetMacroCall(oldID) call, found := info.GetMacroCall(oldID)
if !found { if !found {
continue continue
@ -134,6 +144,7 @@ func cleanupMacroRefs(expr ast.Expr, info *ast.SourceInfo) {
if len(info.MacroCalls()) == 0 { if len(info.MacroCalls()) == 0 {
return return
} }
// Sanitize the macro call references once the optimized expression has been computed // Sanitize the macro call references once the optimized expression has been computed
// and the ids normalized between the expression and the macros. // and the ids normalized between the expression and the macros.
exprRefMap := make(map[int64]struct{}) exprRefMap := make(map[int64]struct{})
@ -253,6 +264,11 @@ func (opt *optimizerExprFactory) SetMacroCall(id int64, expr ast.Expr) {
opt.sourceInfo.SetMacroCall(id, expr) opt.sourceInfo.SetMacroCall(id, expr)
} }
// MacroCalls returns the map of macro calls currently in the context.
func (opt *optimizerExprFactory) MacroCalls() map[int64]ast.Expr {
return opt.sourceInfo.MacroCalls()
}
// NewBindMacro creates an AST expression representing the expanded bind() macro, and a macro expression // NewBindMacro creates an AST expression representing the expanded bind() macro, and a macro expression
// representing the unexpanded call signature to be inserted into the source info macro call metadata. // representing the unexpanded call signature to be inserted into the source info macro call metadata.
func (opt *optimizerExprFactory) NewBindMacro(macroID int64, varName string, varInit, remaining ast.Expr) (astExpr, macroExpr ast.Expr) { func (opt *optimizerExprFactory) NewBindMacro(macroID int64, varName string, varInit, remaining ast.Expr) (astExpr, macroExpr ast.Expr) {

View File

@ -61,6 +61,10 @@ const (
// compressing the logic graph to a single call when multiple like-operator // compressing the logic graph to a single call when multiple like-operator
// expressions occur: e.g. a && b && c && d -> call(_&&_, [a, b, c, d]) // expressions occur: e.g. a && b && c && d -> call(_&&_, [a, b, c, d])
featureVariadicLogicalASTs featureVariadicLogicalASTs
// Enable error generation when a presence test or optional field selection is
// performed on a primitive type.
featureEnableErrorOnBadPresenceTest
) )
// EnvOption is a functional interface for configuring the environment. // EnvOption is a functional interface for configuring the environment.
@ -243,6 +247,13 @@ func Abbrevs(qualifiedNames ...string) EnvOption {
} }
} }
// customTypeRegistry is an internal-only interface containing the minimum methods required to support
// custom types. It is a subset of methods from ref.TypeRegistry.
type customTypeRegistry interface {
RegisterDescriptor(protoreflect.FileDescriptor) error
RegisterType(...ref.Type) error
}
// Types adds one or more type declarations to the environment, allowing for construction of // Types adds one or more type declarations to the environment, allowing for construction of
// type-literals whose definitions are included in the common expression built-in set. // type-literals whose definitions are included in the common expression built-in set.
// //
@ -255,12 +266,7 @@ func Abbrevs(qualifiedNames ...string) EnvOption {
// Note: This option must be specified after the CustomTypeProvider option when used together. // Note: This option must be specified after the CustomTypeProvider option when used together.
func Types(addTypes ...any) EnvOption { func Types(addTypes ...any) EnvOption {
return func(e *Env) (*Env, error) { return func(e *Env) (*Env, error) {
var reg ref.TypeRegistry reg, isReg := e.provider.(customTypeRegistry)
var isReg bool
reg, isReg = e.provider.(*types.Registry)
if !isReg {
reg, isReg = e.provider.(ref.TypeRegistry)
}
if !isReg { if !isReg {
return nil, fmt.Errorf("custom types not supported by provider: %T", e.provider) return nil, fmt.Errorf("custom types not supported by provider: %T", e.provider)
} }
@ -297,7 +303,7 @@ func Types(addTypes ...any) EnvOption {
// extension or by re-using the same EnvOption with another NewEnv() call. // extension or by re-using the same EnvOption with another NewEnv() call.
func TypeDescs(descs ...any) EnvOption { func TypeDescs(descs ...any) EnvOption {
return func(e *Env) (*Env, error) { return func(e *Env) (*Env, error) {
reg, isReg := e.provider.(ref.TypeRegistry) reg, isReg := e.provider.(customTypeRegistry)
if !isReg { if !isReg {
return nil, fmt.Errorf("custom types not supported by provider: %T", e.provider) return nil, fmt.Errorf("custom types not supported by provider: %T", e.provider)
} }
@ -345,7 +351,7 @@ func TypeDescs(descs ...any) EnvOption {
} }
} }
func registerFileSet(reg ref.TypeRegistry, fileSet *descpb.FileDescriptorSet) error { func registerFileSet(reg customTypeRegistry, fileSet *descpb.FileDescriptorSet) error {
files, err := protodesc.NewFiles(fileSet) files, err := protodesc.NewFiles(fileSet)
if err != nil { if err != nil {
return fmt.Errorf("protodesc.NewFiles(%v) failed: %v", fileSet, err) return fmt.Errorf("protodesc.NewFiles(%v) failed: %v", fileSet, err)
@ -353,7 +359,7 @@ func registerFileSet(reg ref.TypeRegistry, fileSet *descpb.FileDescriptorSet) er
return registerFiles(reg, files) return registerFiles(reg, files)
} }
func registerFiles(reg ref.TypeRegistry, files *protoregistry.Files) error { func registerFiles(reg customTypeRegistry, files *protoregistry.Files) error {
var err error var err error
files.RangeFiles(func(fd protoreflect.FileDescriptor) bool { files.RangeFiles(func(fd protoreflect.FileDescriptor) bool {
err = reg.RegisterDescriptor(fd) err = reg.RegisterDescriptor(fd)

View File

@ -187,10 +187,13 @@ func newProgram(e *Env, a *Ast, opts []ProgramOption) (Program, error) {
// Set the attribute factory after the options have been set. // Set the attribute factory after the options have been set.
var attrFactory interpreter.AttributeFactory var attrFactory interpreter.AttributeFactory
attrFactorOpts := []interpreter.AttrFactoryOption{
interpreter.EnableErrorOnBadPresenceTest(p.HasFeature(featureEnableErrorOnBadPresenceTest)),
}
if p.evalOpts&OptPartialEval == OptPartialEval { if p.evalOpts&OptPartialEval == OptPartialEval {
attrFactory = interpreter.NewPartialAttributeFactory(e.Container, e.adapter, e.provider) attrFactory = interpreter.NewPartialAttributeFactory(e.Container, e.adapter, e.provider, attrFactorOpts...)
} else { } else {
attrFactory = interpreter.NewAttributeFactory(e.Container, e.adapter, e.provider) attrFactory = interpreter.NewAttributeFactory(e.Container, e.adapter, e.provider, attrFactorOpts...)
} }
interp := interpreter.NewInterpreter(disp, e.Container, e.provider, e.adapter, attrFactory) interp := interpreter.NewInterpreter(disp, e.Container, e.provider, e.adapter, attrFactory)
p.interpreter = interp p.interpreter = interp

View File

@ -16,7 +16,6 @@ go_library(
"options.go", "options.go",
"printer.go", "printer.go",
"scopes.go", "scopes.go",
"standard.go",
"types.go", "types.go",
], ],
importpath = "github.com/google/cel-go/checker", importpath = "github.com/google/cel-go/checker",

View File

@ -1,35 +0,0 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package checker
import (
"github.com/google/cel-go/common/stdlib"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
// StandardFunctions returns the Decls for all functions in the evaluator.
//
// Deprecated: prefer stdlib.FunctionExprDecls()
func StandardFunctions() []*exprpb.Decl {
return stdlib.FunctionExprDecls()
}
// StandardTypes returns the set of type identifiers for standard library types.
//
// Deprecated: prefer stdlib.TypeExprDecls()
func StandardTypes() []*exprpb.Decl {
return stdlib.TypeExprDecls()
}

View File

@ -18,7 +18,6 @@ go_library(
deps = [ deps = [
"//common/runes:go_default_library", "//common/runes:go_default_library",
"@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library",
"@org_golang_x_text//width:go_default_library",
], ],
) )

View File

@ -310,21 +310,18 @@ func (s *SourceInfo) SetOffsetRange(id int64, o OffsetRange) {
s.offsetRanges[id] = o s.offsetRanges[id] = o
} }
// ClearOffsetRange removes the OffsetRange for the given expression id.
func (s *SourceInfo) ClearOffsetRange(id int64) {
if s != nil {
delete(s.offsetRanges, id)
}
}
// GetStartLocation calculates the human-readable 1-based line and 0-based column of the first character // GetStartLocation calculates the human-readable 1-based line and 0-based column of the first character
// of the expression node at the id. // of the expression node at the id.
func (s *SourceInfo) GetStartLocation(id int64) common.Location { func (s *SourceInfo) GetStartLocation(id int64) common.Location {
if o, found := s.GetOffsetRange(id); found { if o, found := s.GetOffsetRange(id); found {
line := 1 return s.GetLocationByOffset(o.Start)
col := int(o.Start)
for _, lineOffset := range s.LineOffsets() {
if lineOffset < o.Start {
line++
col = int(o.Start - lineOffset)
} else {
break
}
}
return common.NewLocation(line, col)
} }
return common.NoLocation return common.NoLocation
} }
@ -336,21 +333,25 @@ func (s *SourceInfo) GetStartLocation(id int64) common.Location {
// be identical to the start location for the expression. // be identical to the start location for the expression.
func (s *SourceInfo) GetStopLocation(id int64) common.Location { func (s *SourceInfo) GetStopLocation(id int64) common.Location {
if o, found := s.GetOffsetRange(id); found { if o, found := s.GetOffsetRange(id); found {
line := 1 return s.GetLocationByOffset(o.Stop)
col := int(o.Stop)
for _, lineOffset := range s.LineOffsets() {
if lineOffset < o.Stop {
line++
col = int(o.Stop - lineOffset)
} else {
break
}
}
return common.NewLocation(line, col)
} }
return common.NoLocation return common.NoLocation
} }
// GetLocationByOffset returns the line and column information for a given character offset.
func (s *SourceInfo) GetLocationByOffset(offset int32) common.Location {
line := 1
col := int(offset)
for _, lineOffset := range s.LineOffsets() {
if lineOffset > offset {
break
}
line++
col = int(offset - lineOffset)
}
return common.NewLocation(line, col)
}
// ComputeOffset calculates the 0-based character offset from a 1-based line and 0-based column. // ComputeOffset calculates the 0-based character offset from a 1-based line and 0-based column.
func (s *SourceInfo) ComputeOffset(line, col int32) int32 { func (s *SourceInfo) ComputeOffset(line, col int32) int32 {
if s != nil { if s != nil {

View File

@ -158,7 +158,7 @@ type EntryExpr interface {
// IDGenerator produces unique ids suitable for tagging expression nodes // IDGenerator produces unique ids suitable for tagging expression nodes
type IDGenerator func(originalID int64) int64 type IDGenerator func(originalID int64) int64
// CallExpr defines an interface for inspecting a function call and its arugments. // CallExpr defines an interface for inspecting a function call and its arguments.
type CallExpr interface { type CallExpr interface {
// FunctionName returns the name of the function. // FunctionName returns the name of the function.
FunctionName() string FunctionName() string

View File

@ -162,7 +162,9 @@ func (f *FunctionDecl) AddOverload(overload *OverloadDecl) error {
if oID == overload.ID() { if oID == overload.ID() {
if o.SignatureEquals(overload) && o.IsNonStrict() == overload.IsNonStrict() { if o.SignatureEquals(overload) && o.IsNonStrict() == overload.IsNonStrict() {
// Allow redefinition of an overload implementation so long as the signatures match. // Allow redefinition of an overload implementation so long as the signatures match.
f.overloads[oID] = overload if overload.hasBinding() {
f.overloads[oID] = overload
}
return nil return nil
} }
return fmt.Errorf("overload redefinition in function. %s: %s has multiple definitions", f.Name(), oID) return fmt.Errorf("overload redefinition in function. %s: %s has multiple definitions", f.Name(), oID)

View File

@ -18,8 +18,6 @@ import (
"fmt" "fmt"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
"golang.org/x/text/width"
) )
// NewError creates an error associated with an expression id with the given message at the given location. // NewError creates an error associated with an expression id with the given message at the given location.
@ -35,18 +33,15 @@ type Error struct {
} }
const ( const (
dot = "." dot = "."
ind = "^" ind = "^"
wideDot = "\uff0e"
wideInd = "\uff3e"
// maxSnippetLength is the largest number of characters which can be rendered in an error message snippet. // maxSnippetLength is the largest number of characters which can be rendered in an error message snippet.
maxSnippetLength = 16384 maxSnippetLength = 16384
) )
var (
wideDot = width.Widen.String(dot)
wideInd = width.Widen.String(ind)
)
// ToDisplayString decorates the error message with the source location. // ToDisplayString decorates the error message with the source location.
func (e *Error) ToDisplayString(source Source) string { func (e *Error) ToDisplayString(source Source) string {
var result = fmt.Sprintf("ERROR: %s:%d:%d: %s", var result = fmt.Sprintf("ERROR: %s:%d:%d: %s",

View File

@ -127,20 +127,48 @@ var nilBuffer = &emptyBuffer{}
// elements of the byte or uint16 array, and continue. The underlying storage is an rune array // elements of the byte or uint16 array, and continue. The underlying storage is an rune array
// containing any Unicode character. // containing any Unicode character.
func NewBuffer(data string) Buffer { func NewBuffer(data string) Buffer {
buf, _ := newBuffer(data, false)
return buf
}
// NewBufferAndLineOffsets returns an efficient implementation of Buffer for the given text based on
// the ranges of the encoded code points contained within, as well as returning the line offsets.
//
// Code points are represented as an array of byte, uint16, or rune. This approach ensures that
// each index represents a code point by itself without needing to use an array of rune. At first
// we assume all code points are less than or equal to '\u007f'. If this holds true, the
// underlying storage is a byte array containing only ASCII characters. If we encountered a code
// point above this range but less than or equal to '\uffff' we allocate a uint16 array, copy the
// elements of previous byte array to the uint16 array, and continue. If this holds true, the
// underlying storage is a uint16 array containing only Unicode characters in the Basic Multilingual
// Plane. If we encounter a code point above '\uffff' we allocate an rune array, copy the previous
// elements of the byte or uint16 array, and continue. The underlying storage is an rune array
// containing any Unicode character.
func NewBufferAndLineOffsets(data string) (Buffer, []int32) {
return newBuffer(data, true)
}
func newBuffer(data string, lines bool) (Buffer, []int32) {
if len(data) == 0 { if len(data) == 0 {
return nilBuffer return nilBuffer, []int32{0}
} }
var ( var (
idx = 0 idx = 0
buf8 = make([]byte, 0, len(data)) off int32 = 0
buf8 = make([]byte, 0, len(data))
buf16 []uint16 buf16 []uint16
buf32 []rune buf32 []rune
offs []int32
) )
for idx < len(data) { for idx < len(data) {
r, s := utf8.DecodeRuneInString(data[idx:]) r, s := utf8.DecodeRuneInString(data[idx:])
idx += s idx += s
if lines && r == '\n' {
offs = append(offs, off+1)
}
if r < utf8.RuneSelf { if r < utf8.RuneSelf {
buf8 = append(buf8, byte(r)) buf8 = append(buf8, byte(r))
off++
continue continue
} }
if r <= 0xffff { if r <= 0xffff {
@ -150,6 +178,7 @@ func NewBuffer(data string) Buffer {
} }
buf8 = nil buf8 = nil
buf16 = append(buf16, uint16(r)) buf16 = append(buf16, uint16(r))
off++
goto copy16 goto copy16
} }
buf32 = make([]rune, len(buf8), len(data)) buf32 = make([]rune, len(buf8), len(data))
@ -158,17 +187,25 @@ func NewBuffer(data string) Buffer {
} }
buf8 = nil buf8 = nil
buf32 = append(buf32, r) buf32 = append(buf32, r)
off++
goto copy32 goto copy32
} }
if lines {
offs = append(offs, off+1)
}
return &asciiBuffer{ return &asciiBuffer{
arr: buf8, arr: buf8,
} }, offs
copy16: copy16:
for idx < len(data) { for idx < len(data) {
r, s := utf8.DecodeRuneInString(data[idx:]) r, s := utf8.DecodeRuneInString(data[idx:])
idx += s idx += s
if lines && r == '\n' {
offs = append(offs, off+1)
}
if r <= 0xffff { if r <= 0xffff {
buf16 = append(buf16, uint16(r)) buf16 = append(buf16, uint16(r))
off++
continue continue
} }
buf32 = make([]rune, len(buf16), len(data)) buf32 = make([]rune, len(buf16), len(data))
@ -177,18 +214,29 @@ copy16:
} }
buf16 = nil buf16 = nil
buf32 = append(buf32, r) buf32 = append(buf32, r)
off++
goto copy32 goto copy32
} }
if lines {
offs = append(offs, off+1)
}
return &basicBuffer{ return &basicBuffer{
arr: buf16, arr: buf16,
} }, offs
copy32: copy32:
for idx < len(data) { for idx < len(data) {
r, s := utf8.DecodeRuneInString(data[idx:]) r, s := utf8.DecodeRuneInString(data[idx:])
idx += s idx += s
if lines && r == '\n' {
offs = append(offs, off+1)
}
buf32 = append(buf32, r) buf32 = append(buf32, r)
off++
}
if lines {
offs = append(offs, off+1)
} }
return &supplementalBuffer{ return &supplementalBuffer{
arr: buf32, arr: buf32,
} }, offs
} }

View File

@ -15,9 +15,6 @@
package common package common
import ( import (
"strings"
"unicode/utf8"
"github.com/google/cel-go/common/runes" "github.com/google/cel-go/common/runes"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
@ -80,17 +77,11 @@ func NewTextSource(text string) Source {
// NewStringSource creates a new Source from the given contents and description. // NewStringSource creates a new Source from the given contents and description.
func NewStringSource(contents string, description string) Source { func NewStringSource(contents string, description string) Source {
// Compute line offsets up front as they are referred to frequently. // Compute line offsets up front as they are referred to frequently.
lines := strings.Split(contents, "\n") buf, offs := runes.NewBufferAndLineOffsets(contents)
offsets := make([]int32, len(lines))
var offset int32
for i, line := range lines {
offset = offset + int32(utf8.RuneCountInString(line)) + 1
offsets[int32(i)] = offset
}
return &sourceImpl{ return &sourceImpl{
Buffer: runes.NewBuffer(contents), Buffer: buf,
description: description, description: description,
lineOffsets: offsets, lineOffsets: offs,
} }
} }
@ -172,9 +163,8 @@ func (s *sourceImpl) findLine(characterOffset int32) (int32, int32) {
for _, lineOffset := range s.lineOffsets { for _, lineOffset := range s.lineOffsets {
if lineOffset > characterOffset { if lineOffset > characterOffset {
break break
} else {
line++
} }
line++
} }
if line == 1 { if line == 1 {
return line, 0 return line, 0

View File

@ -12,7 +12,6 @@ go_library(
], ],
importpath = "github.com/google/cel-go/common/stdlib", importpath = "github.com/google/cel-go/common/stdlib",
deps = [ deps = [
"//checker/decls:go_default_library",
"//common/decls:go_default_library", "//common/decls:go_default_library",
"//common/functions:go_default_library", "//common/functions:go_default_library",
"//common/operators:go_default_library", "//common/operators:go_default_library",
@ -20,6 +19,5 @@ go_library(
"//common/types:go_default_library", "//common/types:go_default_library",
"//common/types/ref:go_default_library", "//common/types/ref:go_default_library",
"//common/types/traits:go_default_library", "//common/types/traits:go_default_library",
"@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library",
], ],
) )

View File

@ -23,15 +23,11 @@ import (
"github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits" "github.com/google/cel-go/common/types/traits"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
) )
var ( var (
stdFunctions []*decls.FunctionDecl stdFunctions []*decls.FunctionDecl
stdFnDecls []*exprpb.Decl
stdTypes []*decls.VariableDecl stdTypes []*decls.VariableDecl
stdTypeDecls []*exprpb.Decl
) )
func init() { func init() {
@ -55,15 +51,6 @@ func init() {
decls.TypeVariable(types.UintType), decls.TypeVariable(types.UintType),
} }
stdTypeDecls = make([]*exprpb.Decl, 0, len(stdTypes))
for _, stdType := range stdTypes {
typeVar, err := decls.VariableDeclToExprDecl(stdType)
if err != nil {
panic(err)
}
stdTypeDecls = append(stdTypeDecls, typeVar)
}
stdFunctions = []*decls.FunctionDecl{ stdFunctions = []*decls.FunctionDecl{
// Logical operators. Special-cased within the interpreter. // Logical operators. Special-cased within the interpreter.
// Note, the singleton binding prevents extensions from overriding the operator behavior. // Note, the singleton binding prevents extensions from overriding the operator behavior.
@ -576,18 +563,6 @@ func init() {
decls.MemberOverload(overloads.DurationToMilliseconds, decls.MemberOverload(overloads.DurationToMilliseconds,
argTypes(types.DurationType), types.IntType)), argTypes(types.DurationType), types.IntType)),
} }
stdFnDecls = make([]*exprpb.Decl, 0, len(stdFunctions))
for _, fn := range stdFunctions {
if fn.IsDeclarationDisabled() {
continue
}
ed, err := decls.FunctionDeclToExprDecl(fn)
if err != nil {
panic(err)
}
stdFnDecls = append(stdFnDecls, ed)
}
} }
// Functions returns the set of standard library function declarations and definitions for CEL. // Functions returns the set of standard library function declarations and definitions for CEL.
@ -595,27 +570,11 @@ func Functions() []*decls.FunctionDecl {
return stdFunctions return stdFunctions
} }
// FunctionExprDecls returns the legacy style protobuf-typed declarations for all functions and overloads
// in the CEL standard environment.
//
// Deprecated: use Functions
func FunctionExprDecls() []*exprpb.Decl {
return stdFnDecls
}
// Types returns the set of standard library types for CEL. // Types returns the set of standard library types for CEL.
func Types() []*decls.VariableDecl { func Types() []*decls.VariableDecl {
return stdTypes return stdTypes
} }
// TypeExprDecls returns the legacy style protobuf-typed declarations for all types in the CEL
// standard environment.
//
// Deprecated: use Types
func TypeExprDecls() []*exprpb.Decl {
return stdTypeDecls
}
func notStrictlyFalse(value ref.Val) ref.Val { func notStrictlyFalse(value ref.Val) ref.Val {
if types.IsBool(value) { if types.IsBool(value) {
return value return value

View File

@ -58,7 +58,17 @@ func (b Bytes) Compare(other ref.Val) ref.Val {
// ConvertToNative implements the ref.Val interface method. // ConvertToNative implements the ref.Val interface method.
func (b Bytes) ConvertToNative(typeDesc reflect.Type) (any, error) { func (b Bytes) ConvertToNative(typeDesc reflect.Type) (any, error) {
switch typeDesc.Kind() { switch typeDesc.Kind() {
case reflect.Array, reflect.Slice: case reflect.Array:
if len(b) != typeDesc.Len() {
return nil, fmt.Errorf("[%d]byte not assignable to [%d]byte array", len(b), typeDesc.Len())
}
refArrPtr := reflect.New(reflect.ArrayOf(len(b), typeDesc.Elem()))
refArr := refArrPtr.Elem()
for i, byt := range b {
refArr.Index(i).Set(reflect.ValueOf(byt).Convert(typeDesc.Elem()))
}
return refArr.Interface(), nil
case reflect.Slice:
return reflect.ValueOf(b).Convert(typeDesc).Interface(), nil return reflect.ValueOf(b).Convert(typeDesc).Interface(), nil
case reflect.Ptr: case reflect.Ptr:
switch typeDesc { switch typeDesc {

View File

@ -427,22 +427,49 @@ func unwrap(desc description, msg proto.Message) (any, bool, error) {
return structpb.NullValue_NULL_VALUE, true, nil return structpb.NullValue_NULL_VALUE, true, nil
} }
case *wrapperspb.BoolValue: case *wrapperspb.BoolValue:
if v == nil {
return nil, true, nil
}
return v.GetValue(), true, nil return v.GetValue(), true, nil
case *wrapperspb.BytesValue: case *wrapperspb.BytesValue:
if v == nil {
return nil, true, nil
}
return v.GetValue(), true, nil return v.GetValue(), true, nil
case *wrapperspb.DoubleValue: case *wrapperspb.DoubleValue:
if v == nil {
return nil, true, nil
}
return v.GetValue(), true, nil return v.GetValue(), true, nil
case *wrapperspb.FloatValue: case *wrapperspb.FloatValue:
if v == nil {
return nil, true, nil
}
return float64(v.GetValue()), true, nil return float64(v.GetValue()), true, nil
case *wrapperspb.Int32Value: case *wrapperspb.Int32Value:
if v == nil {
return nil, true, nil
}
return int64(v.GetValue()), true, nil return int64(v.GetValue()), true, nil
case *wrapperspb.Int64Value: case *wrapperspb.Int64Value:
if v == nil {
return nil, true, nil
}
return v.GetValue(), true, nil return v.GetValue(), true, nil
case *wrapperspb.StringValue: case *wrapperspb.StringValue:
if v == nil {
return nil, true, nil
}
return v.GetValue(), true, nil return v.GetValue(), true, nil
case *wrapperspb.UInt32Value: case *wrapperspb.UInt32Value:
if v == nil {
return nil, true, nil
}
return uint64(v.GetValue()), true, nil return uint64(v.GetValue()), true, nil
case *wrapperspb.UInt64Value: case *wrapperspb.UInt64Value:
if v == nil {
return nil, true, nil
}
return v.GetValue(), true, nil return v.GetValue(), true, nil
} }
return msg, false, nil return msg, false, nil

View File

@ -585,6 +585,14 @@ func nativeToValue(a Adapter, value any) (ref.Val, bool) {
refKind := refValue.Kind() refKind := refValue.Kind()
switch refKind { switch refKind {
case reflect.Array, reflect.Slice: case reflect.Array, reflect.Slice:
if refValue.Type().Elem() == reflect.TypeOf(byte(0)) {
if refValue.CanAddr() {
return Bytes(refValue.Bytes()), true
}
tmp := reflect.New(refValue.Type())
tmp.Elem().Set(refValue)
return Bytes(tmp.Elem().Bytes()), true
}
return NewDynamicList(a, v), true return NewDynamicList(a, v), true
case reflect.Map: case reflect.Map:
return NewDynamicMap(a, v), true return NewDynamicMap(a, v), true

View File

@ -100,7 +100,8 @@ argument. Simple numeric and list literals are supported as valid argument
types; however, other literals will be flagged as errors during macro types; however, other literals will be flagged as errors during macro
expansion. If the argument expression does not resolve to a numeric or expansion. If the argument expression does not resolve to a numeric or
list(numeric) type during type-checking, or during runtime then an error list(numeric) type during type-checking, or during runtime then an error
will be produced. If a list argument is empty, this too will produce an error. will be produced. If a list argument is empty, this too will produce an
error.
math.least(<arg>, ...) -> <double|int|uint> math.least(<arg>, ...) -> <double|int|uint>
@ -117,6 +118,244 @@ Examples:
math.least(a, b) // check-time error if a or b is non-numeric math.least(a, b) // check-time error if a or b is non-numeric
math.least(dyn('string')) // runtime error math.least(dyn('string')) // runtime error
### Math.BitOr
Introduced at version: 1
Performs a bitwise-OR operation over two int or uint values.
math.bitOr(<int>, <int>) -> <int>
math.bitOr(<uint>, <uint>) -> <uint>
Examples:
math.bitOr(1u, 2u) // returns 3u
math.bitOr(-2, -4) // returns -2
### Math.BitAnd
Introduced at version: 1
Performs a bitwise-AND operation over two int or uint values.
math.bitAnd(<int>, <int>) -> <int>
math.bitAnd(<uint>, <uint>) -> <uint>
Examples:
math.bitAnd(3u, 2u) // return 2u
math.bitAnd(3, 5) // returns 3
math.bitAnd(-3, -5) // returns -7
### Math.BitXor
Introduced at version: 1
math.bitXor(<int>, <int>) -> <int>
math.bitXor(<uint>, <uint>) -> <uint>
Performs a bitwise-XOR operation over two int or uint values.
Examples:
math.bitXor(3u, 5u) // returns 6u
math.bitXor(1, 3) // returns 2
### Math.BitNot
Introduced at version: 1
Function which accepts a single int or uint and performs a bitwise-NOT
ones-complement of the given binary value.
math.bitNot(<int>) -> <int>
math.bitNot(<uint>) -> <uint>
Examples
math.bitNot(1) // returns -1
math.bitNot(-1) // return 0
math.bitNot(0u) // returns 18446744073709551615u
### Math.BitShiftLeft
Introduced at version: 1
Perform a left shift of bits on the first parameter, by the amount of bits
specified in the second parameter. The first parameter is either a uint or
an int. The second parameter must be an int.
When the second parameter is 64 or greater, 0 will be always be returned
since the number of bits shifted is greater than or equal to the total bit
length of the number being shifted. Negative valued bit shifts will result
in a runtime error.
math.bitShiftLeft(<int>, <int>) -> <int>
math.bitShiftLeft(<uint>, <int>) -> <uint>
Examples
math.bitShiftLeft(1, 2) // returns 4
math.bitShiftLeft(-1, 2) // returns -4
math.bitShiftLeft(1u, 2) // return 4u
math.bitShiftLeft(1u, 200) // returns 0u
### Math.BitShiftRight
Introduced at version: 1
Perform a right shift of bits on the first parameter, by the amount of bits
specified in the second parameter. The first parameter is either a uint or
an int. The second parameter must be an int.
When the second parameter is 64 or greater, 0 will always be returned since
the number of bits shifted is greater than or equal to the total bit length
of the number being shifted. Negative valued bit shifts will result in a
runtime error.
The sign bit extension will not be preserved for this operation: vacant bits
on the left are filled with 0.
math.bitShiftRight(<int>, <int>) -> <int>
math.bitShiftRight(<uint>, <int>) -> <uint>
Examples
math.bitShiftRight(1024, 2) // returns 256
math.bitShiftRight(1024u, 2) // returns 256u
math.bitShiftRight(1024u, 64) // returns 0u
### Math.Ceil
Introduced at version: 1
Compute the ceiling of a double value.
math.ceil(<double>) -> <double>
Examples:
math.ceil(1.2) // returns 2.0
math.ceil(-1.2) // returns -1.0
### Math.Floor
Introduced at version: 1
Compute the floor of a double value.
math.floor(<double>) -> <double>
Examples:
math.floor(1.2) // returns 1.0
math.floor(-1.2) // returns -2.0
### Math.Round
Introduced at version: 1
Rounds the double value to the nearest whole number with ties rounding away
from zero, e.g. 1.5 -> 2.0, -1.5 -> -2.0.
math.round(<double>) -> <double>
Examples:
math.round(1.2) // returns 1.0
math.round(1.5) // returns 2.0
math.round(-1.5) // returns -2.0
### Math.Trunc
Introduced at version: 1
Truncates the fractional portion of the double value.
math.trunc(<double>) -> <double>
Examples:
math.trunc(-1.3) // returns -1.0
math.trunc(1.3) // returns 1.0
### Math.Abs
Introduced at version: 1
Returns the absolute value of the numeric type provided as input. If the
value is NaN, the output is NaN. If the input is int64 min, the function
will result in an overflow error.
math.abs(<double>) -> <double>
math.abs(<int>) -> <int>
math.abs(<uint>) -> <uint>
Examples:
math.abs(-1) // returns 1
math.abs(1) // returns 1
math.abs(-9223372036854775808) // overlflow error
### Math.Sign
Introduced at version: 1
Returns the sign of the numeric type, either -1, 0, 1 as an int, double, or
uint depending on the overload. For floating point values, if NaN is
provided as input, the output is also NaN. The implementation does not
differentiate between positive and negative zero.
math.sign(<double>) -> <double>
math.sign(<int>) -> <int>
math.sign(<uint>) -> <uint>
Examples:
math.sign(-42) // returns -1
math.sign(0) // returns 0
math.sign(42) // returns 1
### Math.IsInf
Introduced at version: 1
Returns true if the input double value is -Inf or +Inf.
math.isInf(<double>) -> <bool>
Examples:
math.isInf(1.0/0.0) // returns true
math.isInf(1.2) // returns false
### Math.IsNaN
Introduced at version: 1
Returns true if the input double value is NaN, false otherwise.
math.isNaN(<double>) -> <bool>
Examples:
math.isNaN(0.0/0.0) // returns true
math.isNaN(1.2) // returns false
### Math.IsFinite
Introduced at version: 1
Returns true if the value is a finite number. Equivalent in behavior to:
!math.isNaN(double) && !math.isInf(double)
math.isFinite(<double>) -> <bool>
Examples:
math.isFinite(0.0/0.0) // returns false
math.isFinite(1.2) // returns true
## Protos ## Protos
Protos configure extended macros and functions for proto manipulation. Protos configure extended macros and functions for proto manipulation.
@ -273,10 +512,10 @@ elements in the resulting string.
Examples: Examples:
['hello', 'mellow'].join() // returns 'hellomellow' ['hello', 'mellow'].join() // returns 'hellomellow'
['hello', 'mellow'].join(' ') // returns 'hello mellow' ['hello', 'mellow'].join(' ') // returns 'hello mellow'
[].join() // returns '' [].join() // returns ''
[].join('/') // returns '' [].join('/') // returns ''
### LastIndexOf ### LastIndexOf

View File

@ -484,7 +484,7 @@ func matchConstantFormatStringWithListLiteralArgs(a *ast.AST) ast.ExprMatcher {
} }
} }
formatString := call.Target() formatString := call.Target()
if formatString.Kind() != ast.LiteralKind && formatString.AsLiteral().Type() != cel.StringType { if formatString.Kind() != ast.LiteralKind || formatString.AsLiteral().Type() != cel.StringType {
return false return false
} }
args := call.Args() args := call.Args()

View File

@ -16,6 +16,7 @@ package ext
import ( import (
"fmt" "fmt"
"math"
"strings" "strings"
"github.com/google/cel-go/cel" "github.com/google/cel-go/cel"
@ -86,28 +87,306 @@ import (
// math.least('string') // parse error // math.least('string') // parse error
// math.least(a, b) // check-time error if a or b is non-numeric // math.least(a, b) // check-time error if a or b is non-numeric
// math.least(dyn('string')) // runtime error // math.least(dyn('string')) // runtime error
//
// # Math.BitOr
//
// Introduced at version: 1
//
// Performs a bitwise-OR operation over two int or uint values.
//
// math.bitOr(<int>, <int>) -> <int>
// math.bitOr(<uint>, <uint>) -> <uint>
//
// Examples:
//
// math.bitOr(1u, 2u) // returns 3u
// math.bitOr(-2, -4) // returns -2
//
// # Math.BitAnd
//
// Introduced at version: 1
//
// Performs a bitwise-AND operation over two int or uint values.
//
// math.bitAnd(<int>, <int>) -> <int>
// math.bitAnd(<uint>, <uint>) -> <uint>
//
// Examples:
//
// math.bitAnd(3u, 2u) // return 2u
// math.bitAnd(3, 5) // returns 3
// math.bitAnd(-3, -5) // returns -7
//
// # Math.BitXor
//
// Introduced at version: 1
//
// math.bitXor(<int>, <int>) -> <int>
// math.bitXor(<uint>, <uint>) -> <uint>
//
// Performs a bitwise-XOR operation over two int or uint values.
//
// Examples:
//
// math.bitXor(3u, 5u) // returns 6u
// math.bitXor(1, 3) // returns 2
//
// # Math.BitNot
//
// Introduced at version: 1
//
// Function which accepts a single int or uint and performs a bitwise-NOT
// ones-complement of the given binary value.
//
// math.bitNot(<int>) -> <int>
// math.bitNot(<uint>) -> <uint>
//
// Examples
//
// math.bitNot(1) // returns -1
// math.bitNot(-1) // return 0
// math.bitNot(0u) // returns 18446744073709551615u
//
// # Math.BitShiftLeft
//
// Introduced at version: 1
//
// Perform a left shift of bits on the first parameter, by the amount of bits
// specified in the second parameter. The first parameter is either a uint or
// an int. The second parameter must be an int.
//
// When the second parameter is 64 or greater, 0 will be always be returned
// since the number of bits shifted is greater than or equal to the total bit
// length of the number being shifted. Negative valued bit shifts will result
// in a runtime error.
//
// math.bitShiftLeft(<int>, <int>) -> <int>
// math.bitShiftLeft(<uint>, <int>) -> <uint>
//
// Examples
//
// math.bitShiftLeft(1, 2) // returns 4
// math.bitShiftLeft(-1, 2) // returns -4
// math.bitShiftLeft(1u, 2) // return 4u
// math.bitShiftLeft(1u, 200) // returns 0u
//
// # Math.BitShiftRight
//
// Introduced at version: 1
//
// Perform a right shift of bits on the first parameter, by the amount of bits
// specified in the second parameter. The first parameter is either a uint or
// an int. The second parameter must be an int.
//
// When the second parameter is 64 or greater, 0 will always be returned since
// the number of bits shifted is greater than or equal to the total bit length
// of the number being shifted. Negative valued bit shifts will result in a
// runtime error.
//
// The sign bit extension will not be preserved for this operation: vacant bits
// on the left are filled with 0.
//
// math.bitShiftRight(<int>, <int>) -> <int>
// math.bitShiftRight(<uint>, <int>) -> <uint>
//
// Examples
//
// math.bitShiftRight(1024, 2) // returns 256
// math.bitShiftRight(1024u, 2) // returns 256u
// math.bitShiftRight(1024u, 64) // returns 0u
//
// # Math.Ceil
//
// Introduced at version: 1
//
// Compute the ceiling of a double value.
//
// math.ceil(<double>) -> <double>
//
// Examples:
//
// math.ceil(1.2) // returns 2.0
// math.ceil(-1.2) // returns -1.0
//
// # Math.Floor
//
// Introduced at version: 1
//
// Compute the floor of a double value.
//
// math.floor(<double>) -> <double>
//
// Examples:
//
// math.floor(1.2) // returns 1.0
// math.floor(-1.2) // returns -2.0
//
// # Math.Round
//
// Introduced at version: 1
//
// Rounds the double value to the nearest whole number with ties rounding away
// from zero, e.g. 1.5 -> 2.0, -1.5 -> -2.0.
//
// math.round(<double>) -> <double>
//
// Examples:
//
// math.round(1.2) // returns 1.0
// math.round(1.5) // returns 2.0
// math.round(-1.5) // returns -2.0
//
// # Math.Trunc
//
// Introduced at version: 1
//
// Truncates the fractional portion of the double value.
//
// math.trunc(<double>) -> <double>
//
// Examples:
//
// math.trunc(-1.3) // returns -1.0
// math.trunc(1.3) // returns 1.0
//
// # Math.Abs
//
// Introduced at version: 1
//
// Returns the absolute value of the numeric type provided as input. If the
// value is NaN, the output is NaN. If the input is int64 min, the function
// will result in an overflow error.
//
// math.abs(<double>) -> <double>
// math.abs(<int>) -> <int>
// math.abs(<uint>) -> <uint>
//
// Examples:
//
// math.abs(-1) // returns 1
// math.abs(1) // returns 1
// math.abs(-9223372036854775808) // overflow error
//
// # Math.Sign
//
// Introduced at version: 1
//
// Returns the sign of the numeric type, either -1, 0, 1 as an int, double, or
// uint depending on the overload. For floating point values, if NaN is
// provided as input, the output is also NaN. The implementation does not
// differentiate between positive and negative zero.
//
// math.sign(<double>) -> <double>
// math.sign(<int>) -> <int>
// math.sign(<uint>) -> <uint>
//
// Examples:
//
// math.sign(-42) // returns -1
// math.sign(0) // returns 0
// math.sign(42) // returns 1
//
// # Math.IsInf
//
// Introduced at version: 1
//
// Returns true if the input double value is -Inf or +Inf.
//
// math.isInf(<double>) -> <bool>
//
// Examples:
//
// math.isInf(1.0/0.0) // returns true
// math.isInf(1.2) // returns false
//
// # Math.IsNaN
//
// Introduced at version: 1
//
// Returns true if the input double value is NaN, false otherwise.
//
// math.isNaN(<double>) -> <bool>
//
// Examples:
//
// math.isNaN(0.0/0.0) // returns true
// math.isNaN(1.2) // returns false
//
// # Math.IsFinite
//
// Introduced at version: 1
//
// Returns true if the value is a finite number. Equivalent in behavior to:
// !math.isNaN(double) && !math.isInf(double)
//
// math.isFinite(<double>) -> <bool>
//
// Examples:
//
// math.isFinite(0.0/0.0) // returns false
// math.isFinite(1.2) // returns true
func Math() cel.EnvOption { func Math() cel.EnvOption {
return cel.Lib(mathLib{}) return cel.Lib(&mathLib{version: math.MaxUint32})
} }
const ( const (
mathNamespace = "math" mathNamespace = "math"
leastMacro = "least" leastMacro = "least"
greatestMacro = "greatest" greatestMacro = "greatest"
minFunc = "math.@min"
maxFunc = "math.@max" // Min-max functions
minFunc = "math.@min"
maxFunc = "math.@max"
// Rounding functions
ceilFunc = "math.ceil"
floorFunc = "math.floor"
roundFunc = "math.round"
truncFunc = "math.trunc"
// Floating point helper functions
isInfFunc = "math.isInf"
isNanFunc = "math.isNaN"
isFiniteFunc = "math.isFinite"
// Signedness functions
absFunc = "math.abs"
signFunc = "math.sign"
// Bitwise functions
bitAndFunc = "math.bitAnd"
bitOrFunc = "math.bitOr"
bitXorFunc = "math.bitXor"
bitNotFunc = "math.bitNot"
bitShiftLeftFunc = "math.bitShiftLeft"
bitShiftRightFunc = "math.bitShiftRight"
) )
type mathLib struct{} var (
errIntOverflow = types.NewErr("integer overflow")
)
type MathOption func(*mathLib) *mathLib
func MathVersion(version uint32) MathOption {
return func(lib *mathLib) *mathLib {
lib.version = version
return lib
}
}
type mathLib struct {
version uint32
}
// LibraryName implements the SingletonLibrary interface method. // LibraryName implements the SingletonLibrary interface method.
func (mathLib) LibraryName() string { func (*mathLib) LibraryName() string {
return "cel.lib.ext.math" return "cel.lib.ext.math"
} }
// CompileOptions implements the Library interface method. // CompileOptions implements the Library interface method.
func (mathLib) CompileOptions() []cel.EnvOption { func (lib *mathLib) CompileOptions() []cel.EnvOption {
return []cel.EnvOption{ opts := []cel.EnvOption{
cel.Macros( cel.Macros(
// math.least(num, ...) // math.least(num, ...)
cel.ReceiverVarArgMacro(leastMacro, mathLeast), cel.ReceiverVarArgMacro(leastMacro, mathLeast),
@ -179,10 +458,95 @@ func (mathLib) CompileOptions() []cel.EnvOption {
cel.UnaryBinding(maxList)), cel.UnaryBinding(maxList)),
), ),
} }
if lib.version >= 1 {
opts = append(opts,
// Rounding function declarations
cel.Function(ceilFunc,
cel.Overload("math_ceil_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
cel.UnaryBinding(ceil))),
cel.Function(floorFunc,
cel.Overload("math_floor_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
cel.UnaryBinding(floor))),
cel.Function(roundFunc,
cel.Overload("math_round_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
cel.UnaryBinding(round))),
cel.Function(truncFunc,
cel.Overload("math_trunc_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
cel.UnaryBinding(trunc))),
// Floating point helpers
cel.Function(isInfFunc,
cel.Overload("math_isInf_double", []*cel.Type{cel.DoubleType}, cel.BoolType,
cel.UnaryBinding(isInf))),
cel.Function(isNanFunc,
cel.Overload("math_isNaN_double", []*cel.Type{cel.DoubleType}, cel.BoolType,
cel.UnaryBinding(isNaN))),
cel.Function(isFiniteFunc,
cel.Overload("math_isFinite_double", []*cel.Type{cel.DoubleType}, cel.BoolType,
cel.UnaryBinding(isFinite))),
// Signedness functions
cel.Function(absFunc,
cel.Overload("math_abs_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
cel.UnaryBinding(absDouble)),
cel.Overload("math_abs_int", []*cel.Type{cel.IntType}, cel.IntType,
cel.UnaryBinding(absInt)),
cel.Overload("math_abs_uint", []*cel.Type{cel.UintType}, cel.UintType,
cel.UnaryBinding(identity)),
),
cel.Function(signFunc,
cel.Overload("math_sign_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
cel.UnaryBinding(sign)),
cel.Overload("math_sign_int", []*cel.Type{cel.IntType}, cel.IntType,
cel.UnaryBinding(sign)),
cel.Overload("math_sign_uint", []*cel.Type{cel.UintType}, cel.UintType,
cel.UnaryBinding(sign)),
),
// Bitwise operator declarations
cel.Function(bitAndFunc,
cel.Overload("math_bitAnd_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
cel.BinaryBinding(bitAndPairInt)),
cel.Overload("math_bitAnd_uint_uint", []*cel.Type{cel.UintType, cel.UintType}, cel.UintType,
cel.BinaryBinding(bitAndPairUint)),
),
cel.Function(bitOrFunc,
cel.Overload("math_bitOr_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
cel.BinaryBinding(bitOrPairInt)),
cel.Overload("math_bitOr_uint_uint", []*cel.Type{cel.UintType, cel.UintType}, cel.UintType,
cel.BinaryBinding(bitOrPairUint)),
),
cel.Function(bitXorFunc,
cel.Overload("math_bitXor_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
cel.BinaryBinding(bitXorPairInt)),
cel.Overload("math_bitXor_uint_uint", []*cel.Type{cel.UintType, cel.UintType}, cel.UintType,
cel.BinaryBinding(bitXorPairUint)),
),
cel.Function(bitNotFunc,
cel.Overload("math_bitNot_int_int", []*cel.Type{cel.IntType}, cel.IntType,
cel.UnaryBinding(bitNotInt)),
cel.Overload("math_bitNot_uint_uint", []*cel.Type{cel.UintType}, cel.UintType,
cel.UnaryBinding(bitNotUint)),
),
cel.Function(bitShiftLeftFunc,
cel.Overload("math_bitShiftLeft_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
cel.BinaryBinding(bitShiftLeftIntInt)),
cel.Overload("math_bitShiftLeft_uint_int", []*cel.Type{cel.UintType, cel.IntType}, cel.UintType,
cel.BinaryBinding(bitShiftLeftUintInt)),
),
cel.Function(bitShiftRightFunc,
cel.Overload("math_bitShiftRight_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
cel.BinaryBinding(bitShiftRightIntInt)),
cel.Overload("math_bitShiftRight_uint_int", []*cel.Type{cel.UintType, cel.IntType}, cel.UintType,
cel.BinaryBinding(bitShiftRightUintInt)),
),
)
}
return opts
} }
// ProgramOptions implements the Library interface method. // ProgramOptions implements the Library interface method.
func (mathLib) ProgramOptions() []cel.ProgramOption { func (*mathLib) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{} return []cel.ProgramOption{}
} }
@ -194,7 +558,7 @@ func mathLeast(meh cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.
case 0: case 0:
return nil, meh.NewError(target.ID(), "math.least() requires at least one argument") return nil, meh.NewError(target.ID(), "math.least() requires at least one argument")
case 1: case 1:
if isListLiteralWithValidArgs(args[0]) || isValidArgType(args[0]) { if isListLiteralWithNumericArgs(args[0]) || isNumericArgType(args[0]) {
return meh.NewCall(minFunc, args[0]), nil return meh.NewCall(minFunc, args[0]), nil
} }
return nil, meh.NewError(args[0].ID(), "math.least() invalid single argument value") return nil, meh.NewError(args[0].ID(), "math.least() invalid single argument value")
@ -221,7 +585,7 @@ func mathGreatest(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (a
case 0: case 0:
return nil, mef.NewError(target.ID(), "math.greatest() requires at least one argument") return nil, mef.NewError(target.ID(), "math.greatest() requires at least one argument")
case 1: case 1:
if isListLiteralWithValidArgs(args[0]) || isValidArgType(args[0]) { if isListLiteralWithNumericArgs(args[0]) || isNumericArgType(args[0]) {
return mef.NewCall(maxFunc, args[0]), nil return mef.NewCall(maxFunc, args[0]), nil
} }
return nil, mef.NewError(args[0].ID(), "math.greatest() invalid single argument value") return nil, mef.NewError(args[0].ID(), "math.greatest() invalid single argument value")
@ -244,6 +608,165 @@ func identity(val ref.Val) ref.Val {
return val return val
} }
func ceil(val ref.Val) ref.Val {
v := val.(types.Double)
return types.Double(math.Ceil(float64(v)))
}
func floor(val ref.Val) ref.Val {
v := val.(types.Double)
return types.Double(math.Floor(float64(v)))
}
func round(val ref.Val) ref.Val {
v := val.(types.Double)
return types.Double(math.Round(float64(v)))
}
func trunc(val ref.Val) ref.Val {
v := val.(types.Double)
return types.Double(math.Trunc(float64(v)))
}
func isInf(val ref.Val) ref.Val {
v := val.(types.Double)
return types.Bool(math.IsInf(float64(v), 0))
}
func isFinite(val ref.Val) ref.Val {
v := float64(val.(types.Double))
return types.Bool(!math.IsInf(v, 0) && !math.IsNaN(v))
}
func isNaN(val ref.Val) ref.Val {
v := val.(types.Double)
return types.Bool(math.IsNaN(float64(v)))
}
func absDouble(val ref.Val) ref.Val {
v := float64(val.(types.Double))
return types.Double(math.Abs(v))
}
func absInt(val ref.Val) ref.Val {
v := int64(val.(types.Int))
if v == math.MinInt64 {
return errIntOverflow
}
if v >= 0 {
return val
}
return -types.Int(v)
}
func sign(val ref.Val) ref.Val {
switch v := val.(type) {
case types.Double:
if isNaN(v) == types.True {
return v
}
zero := types.Double(0)
if v > zero {
return types.Double(1)
}
if v < zero {
return types.Double(-1)
}
return zero
case types.Int:
return v.Compare(types.IntZero)
case types.Uint:
if v == types.Uint(0) {
return types.Uint(0)
}
return types.Uint(1)
default:
return maybeSuffixError(val, "math.sign")
}
}
func bitAndPairInt(first, second ref.Val) ref.Val {
l := first.(types.Int)
r := second.(types.Int)
return l & r
}
func bitAndPairUint(first, second ref.Val) ref.Val {
l := first.(types.Uint)
r := second.(types.Uint)
return l & r
}
func bitOrPairInt(first, second ref.Val) ref.Val {
l := first.(types.Int)
r := second.(types.Int)
return l | r
}
func bitOrPairUint(first, second ref.Val) ref.Val {
l := first.(types.Uint)
r := second.(types.Uint)
return l | r
}
func bitXorPairInt(first, second ref.Val) ref.Val {
l := first.(types.Int)
r := second.(types.Int)
return l ^ r
}
func bitXorPairUint(first, second ref.Val) ref.Val {
l := first.(types.Uint)
r := second.(types.Uint)
return l ^ r
}
func bitNotInt(value ref.Val) ref.Val {
v := value.(types.Int)
return ^v
}
func bitNotUint(value ref.Val) ref.Val {
v := value.(types.Uint)
return ^v
}
func bitShiftLeftIntInt(value, bits ref.Val) ref.Val {
v := value.(types.Int)
bs := bits.(types.Int)
if bs < types.IntZero {
return types.NewErr("math.bitShiftLeft() negative offset: %d", bs)
}
return v << bs
}
func bitShiftLeftUintInt(value, bits ref.Val) ref.Val {
v := value.(types.Uint)
bs := bits.(types.Int)
if bs < types.IntZero {
return types.NewErr("math.bitShiftLeft() negative offset: %d", bs)
}
return v << bs
}
func bitShiftRightIntInt(value, bits ref.Val) ref.Val {
v := value.(types.Int)
bs := bits.(types.Int)
if bs < types.IntZero {
return types.NewErr("math.bitShiftRight() negative offset: %d", bs)
}
return types.Int(types.Uint(v) >> bs)
}
func bitShiftRightUintInt(value, bits ref.Val) ref.Val {
v := value.(types.Uint)
bs := bits.(types.Int)
if bs < types.IntZero {
return types.NewErr("math.bitShiftRight() negative offset: %d", bs)
}
return v >> bs
}
func minPair(first, second ref.Val) ref.Val { func minPair(first, second ref.Val) ref.Val {
cmp, ok := first.(traits.Comparer) cmp, ok := first.(traits.Comparer)
if !ok { if !ok {
@ -321,13 +844,13 @@ func checkInvalidArgs(meh cel.MacroExprFactory, funcName string, args []ast.Expr
} }
func checkInvalidArgLiteral(funcName string, arg ast.Expr) error { func checkInvalidArgLiteral(funcName string, arg ast.Expr) error {
if !isValidArgType(arg) { if !isNumericArgType(arg) {
return fmt.Errorf("%s simple literal arguments must be numeric", funcName) return fmt.Errorf("%s simple literal arguments must be numeric", funcName)
} }
return nil return nil
} }
func isValidArgType(arg ast.Expr) bool { func isNumericArgType(arg ast.Expr) bool {
switch arg.Kind() { switch arg.Kind() {
case ast.LiteralKind: case ast.LiteralKind:
c := ref.Val(arg.AsLiteral()) c := ref.Val(arg.AsLiteral())
@ -344,7 +867,7 @@ func isValidArgType(arg ast.Expr) bool {
} }
} }
func isListLiteralWithValidArgs(arg ast.Expr) bool { func isListLiteralWithNumericArgs(arg ast.Expr) bool {
switch arg.Kind() { switch arg.Kind() {
case ast.ListKind: case ast.ListKind:
list := arg.AsList() list := arg.AsList()
@ -352,7 +875,7 @@ func isListLiteralWithValidArgs(arg ast.Expr) bool {
return false return false
} }
for _, e := range list.Elements() { for _, e := range list.Elements() {
if !isValidArgType(e) { if !isNumericArgType(e) {
return false return false
} }
} }

View File

@ -15,6 +15,7 @@
package ext package ext
import ( import (
"errors"
"fmt" "fmt"
"reflect" "reflect"
"strings" "strings"
@ -77,12 +78,45 @@ var (
// same advice holds if you are using custom type adapters and type providers. The native type // same advice holds if you are using custom type adapters and type providers. The native type
// provider composes over whichever type adapter and provider is configured in the cel.Env at // provider composes over whichever type adapter and provider is configured in the cel.Env at
// the time that it is invoked. // the time that it is invoked.
func NativeTypes(refTypes ...any) cel.EnvOption { //
// There is also the possibility to rename the fields of native structs by setting the `cel` tag
// for fields you want to override. In order to enable this feature, pass in the `EnableStructTag`
// option. Here is an example to see it in action:
//
// ```go
// package identity
//
// type Account struct {
// ID int
// OwnerName string `cel:"owner"`
// }
//
// ```
//
// The `OwnerName` field is now accessible in CEL via `owner`, e.g. `identity.Account{owner: 'bob'}`.
// In case there are duplicated field names in the struct, an error will be returned.
func NativeTypes(args ...any) cel.EnvOption {
return func(env *cel.Env) (*cel.Env, error) { return func(env *cel.Env) (*cel.Env, error) {
tp, err := newNativeTypeProvider(env.CELTypeAdapter(), env.CELTypeProvider(), refTypes...) nativeTypes := make([]any, 0, len(args))
tpOptions := nativeTypeOptions{}
for _, v := range args {
switch v := v.(type) {
case NativeTypesOption:
err := v(&tpOptions)
if err != nil {
return nil, err
}
default:
nativeTypes = append(nativeTypes, v)
}
}
tp, err := newNativeTypeProvider(tpOptions, env.CELTypeAdapter(), env.CELTypeProvider(), nativeTypes...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
env, err = cel.CustomTypeAdapter(tp)(env) env, err = cel.CustomTypeAdapter(tp)(env)
if err != nil { if err != nil {
return nil, err return nil, err
@ -91,12 +125,29 @@ func NativeTypes(refTypes ...any) cel.EnvOption {
} }
} }
func newNativeTypeProvider(adapter types.Adapter, provider types.Provider, refTypes ...any) (*nativeTypeProvider, error) { // NativeTypesOption is a functional interface for configuring handling of native types.
type NativeTypesOption func(*nativeTypeOptions) error
type nativeTypeOptions struct {
// parseStructTags controls if CEL should support struct field renames, by parsing
// struct field tags.
parseStructTags bool
}
// ParseStructTags configures if native types field names should be overridable by CEL struct tags.
func ParseStructTags(enabled bool) NativeTypesOption {
return func(ntp *nativeTypeOptions) error {
ntp.parseStructTags = true
return nil
}
}
func newNativeTypeProvider(tpOptions nativeTypeOptions, adapter types.Adapter, provider types.Provider, refTypes ...any) (*nativeTypeProvider, error) {
nativeTypes := make(map[string]*nativeType, len(refTypes)) nativeTypes := make(map[string]*nativeType, len(refTypes))
for _, refType := range refTypes { for _, refType := range refTypes {
switch rt := refType.(type) { switch rt := refType.(type) {
case reflect.Type: case reflect.Type:
result, err := newNativeTypes(rt) result, err := newNativeTypes(tpOptions.parseStructTags, rt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -104,7 +155,7 @@ func newNativeTypeProvider(adapter types.Adapter, provider types.Provider, refTy
nativeTypes[result[idx].TypeName()] = result[idx] nativeTypes[result[idx].TypeName()] = result[idx]
} }
case reflect.Value: case reflect.Value:
result, err := newNativeTypes(rt.Type()) result, err := newNativeTypes(tpOptions.parseStructTags, rt.Type())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -119,6 +170,7 @@ func newNativeTypeProvider(adapter types.Adapter, provider types.Provider, refTy
nativeTypes: nativeTypes, nativeTypes: nativeTypes,
baseAdapter: adapter, baseAdapter: adapter,
baseProvider: provider, baseProvider: provider,
options: tpOptions,
}, nil }, nil
} }
@ -126,6 +178,7 @@ type nativeTypeProvider struct {
nativeTypes map[string]*nativeType nativeTypes map[string]*nativeType
baseAdapter types.Adapter baseAdapter types.Adapter
baseProvider types.Provider baseProvider types.Provider
options nativeTypeOptions
} }
// EnumValue proxies to the types.Provider configured at the times the NativeTypes // EnumValue proxies to the types.Provider configured at the times the NativeTypes
@ -155,6 +208,18 @@ func (tp *nativeTypeProvider) FindStructType(typeName string) (*types.Type, bool
return tp.baseProvider.FindStructType(typeName) return tp.baseProvider.FindStructType(typeName)
} }
func toFieldName(parseStructTag bool, f reflect.StructField) string {
if !parseStructTag {
return f.Name
}
if name, found := f.Tag.Lookup("cel"); found {
return name
}
return f.Name
}
// FindStructFieldNames looks up the type definition first from the native types, then from // FindStructFieldNames looks up the type definition first from the native types, then from
// the backing provider type set. If found, a set of field names corresponding to the type // the backing provider type set. If found, a set of field names corresponding to the type
// will be returned. // will be returned.
@ -163,7 +228,7 @@ func (tp *nativeTypeProvider) FindStructFieldNames(typeName string) ([]string, b
fieldCount := t.refType.NumField() fieldCount := t.refType.NumField()
fields := make([]string, fieldCount) fields := make([]string, fieldCount)
for i := 0; i < fieldCount; i++ { for i := 0; i < fieldCount; i++ {
fields[i] = t.refType.Field(i).Name fields[i] = toFieldName(tp.options.parseStructTags, t.refType.Field(i))
} }
return fields, true return fields, true
} }
@ -173,6 +238,22 @@ func (tp *nativeTypeProvider) FindStructFieldNames(typeName string) ([]string, b
return tp.baseProvider.FindStructFieldNames(typeName) return tp.baseProvider.FindStructFieldNames(typeName)
} }
// valueFieldByName retrieves the corresponding reflect.Value field for the given field name, by
// searching for a matching field tag value or field name.
func valueFieldByName(parseStructTags bool, target reflect.Value, fieldName string) reflect.Value {
if !parseStructTags {
return target.FieldByName(fieldName)
}
for i := 0; i < target.Type().NumField(); i++ {
f := target.Type().Field(i)
if toFieldName(parseStructTags, f) == fieldName {
return target.FieldByIndex(f.Index)
}
}
return reflect.Value{}
}
// FindStructFieldType looks up a native type's field definition, and if the type name is not a native // FindStructFieldType looks up a native type's field definition, and if the type name is not a native
// type then proxies to the composed types.Provider // type then proxies to the composed types.Provider
func (tp *nativeTypeProvider) FindStructFieldType(typeName, fieldName string) (*types.FieldType, bool) { func (tp *nativeTypeProvider) FindStructFieldType(typeName, fieldName string) (*types.FieldType, bool) {
@ -192,13 +273,13 @@ func (tp *nativeTypeProvider) FindStructFieldType(typeName, fieldName string) (*
Type: celType, Type: celType,
IsSet: func(obj any) bool { IsSet: func(obj any) bool {
refVal := reflect.Indirect(reflect.ValueOf(obj)) refVal := reflect.Indirect(reflect.ValueOf(obj))
refField := refVal.FieldByName(fieldName) refField := valueFieldByName(tp.options.parseStructTags, refVal, fieldName)
return !refField.IsZero() return !refField.IsZero()
}, },
GetFrom: func(obj any) (any, error) { GetFrom: func(obj any) (any, error) {
refVal := reflect.Indirect(reflect.ValueOf(obj)) refVal := reflect.Indirect(reflect.ValueOf(obj))
refField := refVal.FieldByName(fieldName) refField := valueFieldByName(tp.options.parseStructTags, refVal, fieldName)
return getFieldValue(tp, refField), nil return getFieldValue(refField), nil
}, },
}, true }, true
} }
@ -249,6 +330,9 @@ func (tp *nativeTypeProvider) NativeToValue(val any) ref.Val {
case []byte: case []byte:
return tp.baseAdapter.NativeToValue(val) return tp.baseAdapter.NativeToValue(val)
default: default:
if refVal.Type().Elem() == reflect.TypeOf(byte(0)) {
return tp.baseAdapter.NativeToValue(val)
}
return types.NewDynamicList(tp, val) return types.NewDynamicList(tp, val)
} }
case reflect.Map: case reflect.Map:
@ -259,7 +343,7 @@ func (tp *nativeTypeProvider) NativeToValue(val any) ref.Val {
time.Time: time.Time:
return tp.baseAdapter.NativeToValue(val) return tp.baseAdapter.NativeToValue(val)
default: default:
return newNativeObject(tp, val, rawVal) return tp.newNativeObject(val, rawVal)
} }
default: default:
return tp.baseAdapter.NativeToValue(val) return tp.baseAdapter.NativeToValue(val)
@ -319,13 +403,13 @@ func convertToCelType(refType reflect.Type) (*cel.Type, bool) {
return nil, false return nil, false
} }
func newNativeObject(adapter types.Adapter, val any, refValue reflect.Value) ref.Val { func (tp *nativeTypeProvider) newNativeObject(val any, refValue reflect.Value) ref.Val {
valType, err := newNativeType(refValue.Type()) valType, err := newNativeType(tp.options.parseStructTags, refValue.Type())
if err != nil { if err != nil {
return types.NewErr(err.Error()) return types.NewErr(err.Error())
} }
return &nativeObj{ return &nativeObj{
Adapter: adapter, Adapter: tp,
val: val, val: val,
valType: valType, valType: valType,
refValue: refValue, refValue: refValue,
@ -372,12 +456,13 @@ func (o *nativeObj) ConvertToNative(typeDesc reflect.Type) (any, error) {
if !fieldValue.IsValid() || fieldValue.IsZero() { if !fieldValue.IsValid() || fieldValue.IsZero() {
continue continue
} }
fieldName := toFieldName(o.valType.parseStructTags, fieldType)
fieldCELVal := o.NativeToValue(fieldValue.Interface()) fieldCELVal := o.NativeToValue(fieldValue.Interface())
fieldJSONVal, err := fieldCELVal.ConvertToNative(jsonValueType) fieldJSONVal, err := fieldCELVal.ConvertToNative(jsonValueType)
if err != nil { if err != nil {
return nil, err return nil, err
} }
fields[fieldType.Name] = fieldJSONVal.(*structpb.Value) fields[fieldName] = fieldJSONVal.(*structpb.Value)
} }
return &structpb.Struct{Fields: fields}, nil return &structpb.Struct{Fields: fields}, nil
} }
@ -469,8 +554,8 @@ func (o *nativeObj) Value() any {
return o.val return o.val
} }
func newNativeTypes(rawType reflect.Type) ([]*nativeType, error) { func newNativeTypes(parseStructTags bool, rawType reflect.Type) ([]*nativeType, error) {
nt, err := newNativeType(rawType) nt, err := newNativeType(parseStructTags, rawType)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -489,7 +574,7 @@ func newNativeTypes(rawType reflect.Type) ([]*nativeType, error) {
return return
} }
alreadySeen[t.String()] = struct{}{} alreadySeen[t.String()] = struct{}{}
nt, ntErr := newNativeType(t) nt, ntErr := newNativeType(parseStructTags, t)
if ntErr != nil { if ntErr != nil {
err = ntErr err = ntErr
return return
@ -505,7 +590,11 @@ func newNativeTypes(rawType reflect.Type) ([]*nativeType, error) {
return result, err return result, err
} }
func newNativeType(rawType reflect.Type) (*nativeType, error) { var (
errDuplicatedFieldName = errors.New("field name already exists in struct")
)
func newNativeType(parseStructTags bool, rawType reflect.Type) (*nativeType, error) {
refType := rawType refType := rawType
if refType.Kind() == reflect.Pointer { if refType.Kind() == reflect.Pointer {
refType = refType.Elem() refType = refType.Elem()
@ -513,15 +602,34 @@ func newNativeType(rawType reflect.Type) (*nativeType, error) {
if !isValidObjectType(refType) { if !isValidObjectType(refType) {
return nil, fmt.Errorf("unsupported reflect.Type %v, must be reflect.Struct", rawType) return nil, fmt.Errorf("unsupported reflect.Type %v, must be reflect.Struct", rawType)
} }
// Since naming collisions can only happen with struct tag parsing, we only check for them if it is enabled.
if parseStructTags {
fieldNames := make(map[string]struct{})
for idx := 0; idx < refType.NumField(); idx++ {
field := refType.Field(idx)
fieldName := toFieldName(parseStructTags, field)
if _, found := fieldNames[fieldName]; found {
return nil, fmt.Errorf("invalid field name `%s` in struct `%s`: %w", fieldName, refType.Name(), errDuplicatedFieldName)
} else {
fieldNames[fieldName] = struct{}{}
}
}
}
return &nativeType{ return &nativeType{
typeName: fmt.Sprintf("%s.%s", simplePkgAlias(refType.PkgPath()), refType.Name()), typeName: fmt.Sprintf("%s.%s", simplePkgAlias(refType.PkgPath()), refType.Name()),
refType: refType, refType: refType,
parseStructTags: parseStructTags,
}, nil }, nil
} }
type nativeType struct { type nativeType struct {
typeName string typeName string
refType reflect.Type refType reflect.Type
parseStructTags bool
} }
// ConvertToNative implements ref.Val.ConvertToNative. // ConvertToNative implements ref.Val.ConvertToNative.
@ -569,9 +677,26 @@ func (t *nativeType) Value() any {
return t.typeName return t.typeName
} }
// fieldByName returns the corresponding reflect.StructField for the give name either by matching
// field tag or field name.
func (t *nativeType) fieldByName(fieldName string) (reflect.StructField, bool) {
if !t.parseStructTags {
return t.refType.FieldByName(fieldName)
}
for i := 0; i < t.refType.NumField(); i++ {
f := t.refType.Field(i)
if toFieldName(t.parseStructTags, f) == fieldName {
return f, true
}
}
return reflect.StructField{}, false
}
// hasField returns whether a field name has a corresponding Golang reflect.StructField // hasField returns whether a field name has a corresponding Golang reflect.StructField
func (t *nativeType) hasField(fieldName string) (reflect.StructField, bool) { func (t *nativeType) hasField(fieldName string) (reflect.StructField, bool) {
f, found := t.refType.FieldByName(fieldName) f, found := t.fieldByName(fieldName)
if !found || !f.IsExported() || !isSupportedType(f.Type) { if !found || !f.IsExported() || !isSupportedType(f.Type) {
return reflect.StructField{}, false return reflect.StructField{}, false
} }
@ -579,21 +704,16 @@ func (t *nativeType) hasField(fieldName string) (reflect.StructField, bool) {
} }
func adaptFieldValue(adapter types.Adapter, refField reflect.Value) ref.Val { func adaptFieldValue(adapter types.Adapter, refField reflect.Value) ref.Val {
return adapter.NativeToValue(getFieldValue(adapter, refField)) return adapter.NativeToValue(getFieldValue(refField))
} }
func getFieldValue(adapter types.Adapter, refField reflect.Value) any { func getFieldValue(refField reflect.Value) any {
if refField.IsZero() { if refField.IsZero() {
switch refField.Kind() { switch refField.Kind() {
case reflect.Array, reflect.Slice:
return types.NewDynamicList(adapter, []ref.Val{})
case reflect.Map:
return types.NewDynamicMap(adapter, map[ref.Val]ref.Val{})
case reflect.Struct: case reflect.Struct:
if refField.Type() == timestampType { if refField.Type() == timestampType {
return types.Timestamp{Time: time.Unix(0, 0)} return time.Unix(0, 0)
} }
return reflect.New(refField.Type()).Elem().Interface()
case reflect.Pointer: case reflect.Pointer:
return reflect.New(refField.Type().Elem()).Interface() return reflect.New(refField.Type().Elem()).Interface()
} }

View File

@ -178,10 +178,8 @@ func numericValueEquals(value any, celValue ref.Val) bool {
// NewPartialAttributeFactory returns an AttributeFactory implementation capable of performing // NewPartialAttributeFactory returns an AttributeFactory implementation capable of performing
// AttributePattern matches with PartialActivation inputs. // AttributePattern matches with PartialActivation inputs.
func NewPartialAttributeFactory(container *containers.Container, func NewPartialAttributeFactory(container *containers.Container, adapter types.Adapter, provider types.Provider, opts ...AttrFactoryOption) AttributeFactory {
adapter types.Adapter, fac := NewAttributeFactory(container, adapter, provider, opts...)
provider types.Provider) AttributeFactory {
fac := NewAttributeFactory(container, adapter, provider)
return &partialAttributeFactory{ return &partialAttributeFactory{
AttributeFactory: fac, AttributeFactory: fac,
container: container, container: container,

View File

@ -126,21 +126,39 @@ type NamespacedAttribute interface {
Qualifiers() []Qualifier Qualifiers() []Qualifier
} }
// AttrFactoryOption specifies a functional option for configuring an attribute factory.
type AttrFactoryOption func(*attrFactory) *attrFactory
// EnableErrorOnBadPresenceTest error generation when a presence test or optional field selection
// is performed on a primitive type.
func EnableErrorOnBadPresenceTest(value bool) AttrFactoryOption {
return func(fac *attrFactory) *attrFactory {
fac.errorOnBadPresenceTest = value
return fac
}
}
// NewAttributeFactory returns a default AttributeFactory which is produces Attribute values // NewAttributeFactory returns a default AttributeFactory which is produces Attribute values
// capable of resolving types by simple names and qualify the values using the supported qualifier // capable of resolving types by simple names and qualify the values using the supported qualifier
// types: bool, int, string, and uint. // types: bool, int, string, and uint.
func NewAttributeFactory(cont *containers.Container, a types.Adapter, p types.Provider) AttributeFactory { func NewAttributeFactory(cont *containers.Container, a types.Adapter, p types.Provider, opts ...AttrFactoryOption) AttributeFactory {
return &attrFactory{ fac := &attrFactory{
container: cont, container: cont,
adapter: a, adapter: a,
provider: p, provider: p,
} }
for _, o := range opts {
fac = o(fac)
}
return fac
} }
type attrFactory struct { type attrFactory struct {
container *containers.Container container *containers.Container
adapter types.Adapter adapter types.Adapter
provider types.Provider provider types.Provider
errorOnBadPresenceTest bool
} }
// AbsoluteAttribute refers to a variable value and an optional qualifier path. // AbsoluteAttribute refers to a variable value and an optional qualifier path.
@ -149,12 +167,13 @@ type attrFactory struct {
// resolution rules. // resolution rules.
func (r *attrFactory) AbsoluteAttribute(id int64, names ...string) NamespacedAttribute { func (r *attrFactory) AbsoluteAttribute(id int64, names ...string) NamespacedAttribute {
return &absoluteAttribute{ return &absoluteAttribute{
id: id, id: id,
namespaceNames: names, namespaceNames: names,
qualifiers: []Qualifier{}, qualifiers: []Qualifier{},
adapter: r.adapter, adapter: r.adapter,
provider: r.provider, provider: r.provider,
fac: r, fac: r,
errorOnBadPresenceTest: r.errorOnBadPresenceTest,
} }
} }
@ -188,11 +207,12 @@ func (r *attrFactory) MaybeAttribute(id int64, name string) Attribute {
// RelativeAttribute refers to an expression and an optional qualifier path. // RelativeAttribute refers to an expression and an optional qualifier path.
func (r *attrFactory) RelativeAttribute(id int64, operand Interpretable) Attribute { func (r *attrFactory) RelativeAttribute(id int64, operand Interpretable) Attribute {
return &relativeAttribute{ return &relativeAttribute{
id: id, id: id,
operand: operand, operand: operand,
qualifiers: []Qualifier{}, qualifiers: []Qualifier{},
adapter: r.adapter, adapter: r.adapter,
fac: r, fac: r,
errorOnBadPresenceTest: r.errorOnBadPresenceTest,
} }
} }
@ -214,7 +234,7 @@ func (r *attrFactory) NewQualifier(objType *types.Type, qualID int64, val any, o
}, nil }, nil
} }
} }
return newQualifier(r.adapter, qualID, val, opt) return newQualifier(r.adapter, qualID, val, opt, r.errorOnBadPresenceTest)
} }
type absoluteAttribute struct { type absoluteAttribute struct {
@ -226,6 +246,8 @@ type absoluteAttribute struct {
adapter types.Adapter adapter types.Adapter
provider types.Provider provider types.Provider
fac AttributeFactory fac AttributeFactory
errorOnBadPresenceTest bool
} }
// ID implements the Attribute interface method. // ID implements the Attribute interface method.
@ -514,6 +536,8 @@ type relativeAttribute struct {
qualifiers []Qualifier qualifiers []Qualifier
adapter types.Adapter adapter types.Adapter
fac AttributeFactory fac AttributeFactory
errorOnBadPresenceTest bool
} }
// ID is an implementation of the Attribute interface method. // ID is an implementation of the Attribute interface method.
@ -577,7 +601,7 @@ func (a *relativeAttribute) String() string {
return fmt.Sprintf("id: %v, operand: %v", a.id, a.operand) return fmt.Sprintf("id: %v, operand: %v", a.id, a.operand)
} }
func newQualifier(adapter types.Adapter, id int64, v any, opt bool) (Qualifier, error) { func newQualifier(adapter types.Adapter, id int64, v any, opt, errorOnBadPresenceTest bool) (Qualifier, error) {
var qual Qualifier var qual Qualifier
switch val := v.(type) { switch val := v.(type) {
case Attribute: case Attribute:
@ -592,71 +616,138 @@ func newQualifier(adapter types.Adapter, id int64, v any, opt bool) (Qualifier,
}, nil }, nil
case string: case string:
qual = &stringQualifier{ qual = &stringQualifier{
id: id, id: id,
value: val, value: val,
celValue: types.String(val), celValue: types.String(val),
adapter: adapter, adapter: adapter,
optional: opt, optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
} }
case int: case int:
qual = &intQualifier{ qual = &intQualifier{
id: id, value: int64(val), celValue: types.Int(val), adapter: adapter, optional: opt, id: id,
value: int64(val),
celValue: types.Int(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
} }
case int32: case int32:
qual = &intQualifier{ qual = &intQualifier{
id: id, value: int64(val), celValue: types.Int(val), adapter: adapter, optional: opt, id: id,
value: int64(val),
celValue: types.Int(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
} }
case int64: case int64:
qual = &intQualifier{ qual = &intQualifier{
id: id, value: val, celValue: types.Int(val), adapter: adapter, optional: opt, id: id,
value: val,
celValue: types.Int(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
} }
case uint: case uint:
qual = &uintQualifier{ qual = &uintQualifier{
id: id, value: uint64(val), celValue: types.Uint(val), adapter: adapter, optional: opt, id: id,
value: uint64(val),
celValue: types.Uint(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
} }
case uint32: case uint32:
qual = &uintQualifier{ qual = &uintQualifier{
id: id, value: uint64(val), celValue: types.Uint(val), adapter: adapter, optional: opt, id: id,
value: uint64(val),
celValue: types.Uint(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
} }
case uint64: case uint64:
qual = &uintQualifier{ qual = &uintQualifier{
id: id, value: val, celValue: types.Uint(val), adapter: adapter, optional: opt, id: id,
value: val,
celValue: types.Uint(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
} }
case bool: case bool:
qual = &boolQualifier{ qual = &boolQualifier{
id: id, value: val, celValue: types.Bool(val), adapter: adapter, optional: opt, id: id,
value: val,
celValue: types.Bool(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
} }
case float32: case float32:
qual = &doubleQualifier{ qual = &doubleQualifier{
id: id, id: id,
value: float64(val), value: float64(val),
celValue: types.Double(val), celValue: types.Double(val),
adapter: adapter, adapter: adapter,
optional: opt, optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
} }
case float64: case float64:
qual = &doubleQualifier{ qual = &doubleQualifier{
id: id, value: val, celValue: types.Double(val), adapter: adapter, optional: opt, id: id,
value: val,
celValue: types.Double(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
} }
case types.String: case types.String:
qual = &stringQualifier{ qual = &stringQualifier{
id: id, value: string(val), celValue: val, adapter: adapter, optional: opt, id: id,
value: string(val),
celValue: val,
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
} }
case types.Int: case types.Int:
qual = &intQualifier{ qual = &intQualifier{
id: id, value: int64(val), celValue: val, adapter: adapter, optional: opt, id: id,
value: int64(val),
celValue: val,
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
} }
case types.Uint: case types.Uint:
qual = &uintQualifier{ qual = &uintQualifier{
id: id, value: uint64(val), celValue: val, adapter: adapter, optional: opt, id: id,
value: uint64(val),
celValue: val,
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
} }
case types.Bool: case types.Bool:
qual = &boolQualifier{ qual = &boolQualifier{
id: id, value: bool(val), celValue: val, adapter: adapter, optional: opt, id: id,
value: bool(val),
celValue: val,
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
} }
case types.Double: case types.Double:
qual = &doubleQualifier{ qual = &doubleQualifier{
id: id, value: float64(val), celValue: val, adapter: adapter, optional: opt, id: id,
value: float64(val),
celValue: val,
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
} }
case *types.Unknown: case *types.Unknown:
qual = &unknownQualifier{id: id, value: val} qual = &unknownQualifier{id: id, value: val}
@ -687,11 +778,12 @@ func (q *attrQualifier) IsOptional() bool {
} }
type stringQualifier struct { type stringQualifier struct {
id int64 id int64
value string value string
celValue ref.Val celValue ref.Val
adapter types.Adapter adapter types.Adapter
optional bool optional bool
errorOnBadPresenceTest bool
} }
// ID is an implementation of the Qualifier interface method. // ID is an implementation of the Qualifier interface method.
@ -774,7 +866,7 @@ func (q *stringQualifier) qualifyInternal(vars Activation, obj any, presenceTest
return obj, true, nil return obj, true, nil
} }
default: default:
return refQualify(q.adapter, obj, q.celValue, presenceTest, presenceOnly) return refQualify(q.adapter, obj, q.celValue, presenceTest, presenceOnly, q.errorOnBadPresenceTest)
} }
if presenceTest { if presenceTest {
return nil, false, nil return nil, false, nil
@ -788,11 +880,12 @@ func (q *stringQualifier) Value() ref.Val {
} }
type intQualifier struct { type intQualifier struct {
id int64 id int64
value int64 value int64
celValue ref.Val celValue ref.Val
adapter types.Adapter adapter types.Adapter
optional bool optional bool
errorOnBadPresenceTest bool
} }
// ID is an implementation of the Qualifier interface method. // ID is an implementation of the Qualifier interface method.
@ -898,7 +991,7 @@ func (q *intQualifier) qualifyInternal(vars Activation, obj any, presenceTest, p
return o[i], true, nil return o[i], true, nil
} }
default: default:
return refQualify(q.adapter, obj, q.celValue, presenceTest, presenceOnly) return refQualify(q.adapter, obj, q.celValue, presenceTest, presenceOnly, q.errorOnBadPresenceTest)
} }
if presenceTest { if presenceTest {
return nil, false, nil return nil, false, nil
@ -915,11 +1008,12 @@ func (q *intQualifier) Value() ref.Val {
} }
type uintQualifier struct { type uintQualifier struct {
id int64 id int64
value uint64 value uint64
celValue ref.Val celValue ref.Val
adapter types.Adapter adapter types.Adapter
optional bool optional bool
errorOnBadPresenceTest bool
} }
// ID is an implementation of the Qualifier interface method. // ID is an implementation of the Qualifier interface method.
@ -966,7 +1060,7 @@ func (q *uintQualifier) qualifyInternal(vars Activation, obj any, presenceTest,
return obj, true, nil return obj, true, nil
} }
default: default:
return refQualify(q.adapter, obj, q.celValue, presenceTest, presenceOnly) return refQualify(q.adapter, obj, q.celValue, presenceTest, presenceOnly, q.errorOnBadPresenceTest)
} }
if presenceTest { if presenceTest {
return nil, false, nil return nil, false, nil
@ -980,11 +1074,12 @@ func (q *uintQualifier) Value() ref.Val {
} }
type boolQualifier struct { type boolQualifier struct {
id int64 id int64
value bool value bool
celValue ref.Val celValue ref.Val
adapter types.Adapter adapter types.Adapter
optional bool optional bool
errorOnBadPresenceTest bool
} }
// ID is an implementation of the Qualifier interface method. // ID is an implementation of the Qualifier interface method.
@ -1017,7 +1112,7 @@ func (q *boolQualifier) qualifyInternal(vars Activation, obj any, presenceTest,
return obj, true, nil return obj, true, nil
} }
default: default:
return refQualify(q.adapter, obj, q.celValue, presenceTest, presenceOnly) return refQualify(q.adapter, obj, q.celValue, presenceTest, presenceOnly, q.errorOnBadPresenceTest)
} }
if presenceTest { if presenceTest {
return nil, false, nil return nil, false, nil
@ -1092,11 +1187,12 @@ func (q *fieldQualifier) Value() ref.Val {
// type may not be known ahead of time and may not conform to the standard types supported as valid // type may not be known ahead of time and may not conform to the standard types supported as valid
// protobuf map key types. // protobuf map key types.
type doubleQualifier struct { type doubleQualifier struct {
id int64 id int64
value float64 value float64
celValue ref.Val celValue ref.Val
adapter types.Adapter adapter types.Adapter
optional bool optional bool
errorOnBadPresenceTest bool
} }
// ID is an implementation of the Qualifier interface method. // ID is an implementation of the Qualifier interface method.
@ -1120,7 +1216,7 @@ func (q *doubleQualifier) QualifyIfPresent(vars Activation, obj any, presenceOnl
} }
func (q *doubleQualifier) qualifyInternal(vars Activation, obj any, presenceTest, presenceOnly bool) (any, bool, error) { func (q *doubleQualifier) qualifyInternal(vars Activation, obj any, presenceTest, presenceOnly bool) (any, bool, error) {
return refQualify(q.adapter, obj, q.celValue, presenceTest, presenceOnly) return refQualify(q.adapter, obj, q.celValue, presenceTest, presenceOnly, q.errorOnBadPresenceTest)
} }
// Value implements the ConstantQualifier interface // Value implements the ConstantQualifier interface
@ -1226,7 +1322,7 @@ func attrQualifyIfPresent(fac AttributeFactory, vars Activation, obj any, qualAt
// refQualify attempts to convert the value to a CEL value and then uses reflection methods to try and // refQualify attempts to convert the value to a CEL value and then uses reflection methods to try and
// apply the qualifier with the option to presence test field accesses before retrieving field values. // apply the qualifier with the option to presence test field accesses before retrieving field values.
func refQualify(adapter types.Adapter, obj any, idx ref.Val, presenceTest, presenceOnly bool) (ref.Val, bool, error) { func refQualify(adapter types.Adapter, obj any, idx ref.Val, presenceTest, presenceOnly, errorOnBadPresenceTest bool) (ref.Val, bool, error) {
celVal := adapter.NativeToValue(obj) celVal := adapter.NativeToValue(obj)
switch v := celVal.(type) { switch v := celVal.(type) {
case *types.Unknown: case *types.Unknown:
@ -1283,7 +1379,7 @@ func refQualify(adapter types.Adapter, obj any, idx ref.Val, presenceTest, prese
} }
return val, true, nil return val, true, nil
default: default:
if presenceTest { if presenceTest && !errorOnBadPresenceTest {
return nil, false, nil return nil, false, nil
} }
return nil, false, missingKey(idx) return nil, false, missingKey(idx)

View File

@ -140,15 +140,12 @@ func (p *parserHelper) id(ctx any) int64 {
var offset ast.OffsetRange var offset ast.OffsetRange
switch c := ctx.(type) { switch c := ctx.(type) {
case antlr.ParserRuleContext: case antlr.ParserRuleContext:
start, stop := c.GetStart(), c.GetStop() start := c.GetStart()
if stop == nil {
stop = start
}
offset.Start = p.sourceInfo.ComputeOffset(int32(start.GetLine()), int32(start.GetColumn())) offset.Start = p.sourceInfo.ComputeOffset(int32(start.GetLine()), int32(start.GetColumn()))
offset.Stop = p.sourceInfo.ComputeOffset(int32(stop.GetLine()), int32(stop.GetColumn())) offset.Stop = offset.Start + int32(len(c.GetText()))
case antlr.Token: case antlr.Token:
offset.Start = p.sourceInfo.ComputeOffset(int32(c.GetLine()), int32(c.GetColumn())) offset.Start = p.sourceInfo.ComputeOffset(int32(c.GetLine()), int32(c.GetColumn()))
offset.Stop = offset.Start offset.Stop = offset.Start + int32(len(c.GetText()))
case common.Location: case common.Location:
offset.Start = p.sourceInfo.ComputeOffset(int32(c.Line()), int32(c.Column())) offset.Start = p.sourceInfo.ComputeOffset(int32(c.Line()), int32(c.Column()))
offset.Stop = offset.Start offset.Stop = offset.Start
@ -164,10 +161,21 @@ func (p *parserHelper) id(ctx any) int64 {
return id return id
} }
func (p *parserHelper) deleteID(id int64) {
p.sourceInfo.ClearOffsetRange(id)
if id == p.nextID-1 {
p.nextID--
}
}
func (p *parserHelper) getLocation(id int64) common.Location { func (p *parserHelper) getLocation(id int64) common.Location {
return p.sourceInfo.GetStartLocation(id) return p.sourceInfo.GetStartLocation(id)
} }
func (p *parserHelper) getLocationByOffset(offset int32) common.Location {
return p.getSourceInfo().GetLocationByOffset(offset)
}
// buildMacroCallArg iterates the expression and returns a new expression // buildMacroCallArg iterates the expression and returns a new expression
// where all macros have been replaced by their IDs in MacroCalls // where all macros have been replaced by their IDs in MacroCalls
func (p *parserHelper) buildMacroCallArg(expr ast.Expr) ast.Expr { func (p *parserHelper) buildMacroCallArg(expr ast.Expr) ast.Expr {

View File

@ -382,13 +382,11 @@ func makeQuantifier(kind quantifierKind, eh ExprHelper, target ast.Expr, args []
step = eh.NewCall(operators.LogicalOr, eh.NewAccuIdent(), args[1]) step = eh.NewCall(operators.LogicalOr, eh.NewAccuIdent(), args[1])
result = eh.NewAccuIdent() result = eh.NewAccuIdent()
case quantifierExistsOne: case quantifierExistsOne:
zeroExpr := eh.NewLiteral(types.Int(0)) init = eh.NewLiteral(types.Int(0))
oneExpr := eh.NewLiteral(types.Int(1))
init = zeroExpr
condition = eh.NewLiteral(types.True) condition = eh.NewLiteral(types.True)
step = eh.NewCall(operators.Conditional, args[1], step = eh.NewCall(operators.Conditional, args[1],
eh.NewCall(operators.Add, eh.NewAccuIdent(), oneExpr), eh.NewAccuIdent()) eh.NewCall(operators.Add, eh.NewAccuIdent(), eh.NewLiteral(types.Int(1))), eh.NewAccuIdent())
result = eh.NewCall(operators.Equals, eh.NewAccuIdent(), oneExpr) result = eh.NewCall(operators.Equals, eh.NewAccuIdent(), eh.NewLiteral(types.Int(1)))
default: default:
return nil, eh.NewError(args[0].ID(), fmt.Sprintf("unrecognized quantifier '%v'", kind)) return nil, eh.NewError(args[0].ID(), fmt.Sprintf("unrecognized quantifier '%v'", kind))
} }

View File

@ -856,7 +856,8 @@ func (p *parser) reportError(ctx any, format string, args ...any) ast.Expr {
// ANTLR Parse listener implementations // ANTLR Parse listener implementations
func (p *parser) SyntaxError(recognizer antlr.Recognizer, offendingSymbol any, line, column int, msg string, e antlr.RecognitionException) { func (p *parser) SyntaxError(recognizer antlr.Recognizer, offendingSymbol any, line, column int, msg string, e antlr.RecognitionException) {
l := p.helper.source.NewLocation(line, column) offset := p.helper.sourceInfo.ComputeOffset(int32(line), int32(column))
l := p.helper.getLocationByOffset(offset)
// Hack to keep existing error messages consistent with previous versions of CEL when a reserved word // Hack to keep existing error messages consistent with previous versions of CEL when a reserved word
// is used as an identifier. This behavior needs to be overhauled to provide consistent, normalized error // is used as an identifier. This behavior needs to be overhauled to provide consistent, normalized error
// messages out of ANTLR to prevent future breaking changes related to error message content. // messages out of ANTLR to prevent future breaking changes related to error message content.
@ -916,10 +917,12 @@ func (p *parser) expandMacro(exprID int64, function string, target ast.Expr, arg
expr, err := macro.Expander()(eh, target, args) expr, err := macro.Expander()(eh, target, args)
// An error indicates that the macro was matched, but the arguments were not well-formed. // An error indicates that the macro was matched, but the arguments were not well-formed.
if err != nil { if err != nil {
if err.Location != nil { loc := err.Location
return p.reportError(err.Location, err.Message), true if loc == nil {
loc = p.helper.getLocation(exprID)
} }
return p.reportError(p.helper.getLocation(exprID), err.Message), true p.helper.deleteID(exprID)
return p.reportError(loc, err.Message), true
} }
// A nil value from the macro indicates that the macro implementation decided that // A nil value from the macro indicates that the macro implementation decided that
// an expansion should not be performed. // an expansion should not be performed.
@ -929,6 +932,7 @@ func (p *parser) expandMacro(exprID int64, function string, target ast.Expr, arg
if p.populateMacroCalls { if p.populateMacroCalls {
p.helper.addMacroCall(expr.ID(), function, target, args...) p.helper.addMacroCall(expr.ID(), function, target, args...)
} }
p.helper.deleteID(exprID)
return expr, true return expr, true
} }

View File

@ -1,28 +0,0 @@
// Code generated by "stringer -type=Kind"; DO NOT EDIT.
package width
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[Neutral-0]
_ = x[EastAsianAmbiguous-1]
_ = x[EastAsianWide-2]
_ = x[EastAsianNarrow-3]
_ = x[EastAsianFullwidth-4]
_ = x[EastAsianHalfwidth-5]
}
const _Kind_name = "NeutralEastAsianAmbiguousEastAsianWideEastAsianNarrowEastAsianFullwidthEastAsianHalfwidth"
var _Kind_index = [...]uint8{0, 7, 25, 38, 53, 71, 89}
func (i Kind) String() string {
if i < 0 || i >= Kind(len(_Kind_index)-1) {
return "Kind(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Kind_name[_Kind_index[i]:_Kind_index[i+1]]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,239 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package width
import (
"unicode/utf8"
"golang.org/x/text/transform"
)
type foldTransform struct {
transform.NopResetter
}
func (foldTransform) Span(src []byte, atEOF bool) (n int, err error) {
for n < len(src) {
if src[n] < utf8.RuneSelf {
// ASCII fast path.
for n++; n < len(src) && src[n] < utf8.RuneSelf; n++ {
}
continue
}
v, size := trie.lookup(src[n:])
if size == 0 { // incomplete UTF-8 encoding
if !atEOF {
err = transform.ErrShortSrc
} else {
n = len(src)
}
break
}
if elem(v)&tagNeedsFold != 0 {
err = transform.ErrEndOfSpan
break
}
n += size
}
return n, err
}
func (foldTransform) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
for nSrc < len(src) {
if src[nSrc] < utf8.RuneSelf {
// ASCII fast path.
start, end := nSrc, len(src)
if d := len(dst) - nDst; d < end-start {
end = nSrc + d
}
for nSrc++; nSrc < end && src[nSrc] < utf8.RuneSelf; nSrc++ {
}
n := copy(dst[nDst:], src[start:nSrc])
if nDst += n; nDst == len(dst) {
nSrc = start + n
if nSrc == len(src) {
return nDst, nSrc, nil
}
if src[nSrc] < utf8.RuneSelf {
return nDst, nSrc, transform.ErrShortDst
}
}
continue
}
v, size := trie.lookup(src[nSrc:])
if size == 0 { // incomplete UTF-8 encoding
if !atEOF {
return nDst, nSrc, transform.ErrShortSrc
}
size = 1 // gobble 1 byte
}
if elem(v)&tagNeedsFold == 0 {
if size != copy(dst[nDst:], src[nSrc:nSrc+size]) {
return nDst, nSrc, transform.ErrShortDst
}
nDst += size
} else {
data := inverseData[byte(v)]
if len(dst)-nDst < int(data[0]) {
return nDst, nSrc, transform.ErrShortDst
}
i := 1
for end := int(data[0]); i < end; i++ {
dst[nDst] = data[i]
nDst++
}
dst[nDst] = data[i] ^ src[nSrc+size-1]
nDst++
}
nSrc += size
}
return nDst, nSrc, nil
}
type narrowTransform struct {
transform.NopResetter
}
func (narrowTransform) Span(src []byte, atEOF bool) (n int, err error) {
for n < len(src) {
if src[n] < utf8.RuneSelf {
// ASCII fast path.
for n++; n < len(src) && src[n] < utf8.RuneSelf; n++ {
}
continue
}
v, size := trie.lookup(src[n:])
if size == 0 { // incomplete UTF-8 encoding
if !atEOF {
err = transform.ErrShortSrc
} else {
n = len(src)
}
break
}
if k := elem(v).kind(); byte(v) == 0 || k != EastAsianFullwidth && k != EastAsianWide && k != EastAsianAmbiguous {
} else {
err = transform.ErrEndOfSpan
break
}
n += size
}
return n, err
}
func (narrowTransform) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
for nSrc < len(src) {
if src[nSrc] < utf8.RuneSelf {
// ASCII fast path.
start, end := nSrc, len(src)
if d := len(dst) - nDst; d < end-start {
end = nSrc + d
}
for nSrc++; nSrc < end && src[nSrc] < utf8.RuneSelf; nSrc++ {
}
n := copy(dst[nDst:], src[start:nSrc])
if nDst += n; nDst == len(dst) {
nSrc = start + n
if nSrc == len(src) {
return nDst, nSrc, nil
}
if src[nSrc] < utf8.RuneSelf {
return nDst, nSrc, transform.ErrShortDst
}
}
continue
}
v, size := trie.lookup(src[nSrc:])
if size == 0 { // incomplete UTF-8 encoding
if !atEOF {
return nDst, nSrc, transform.ErrShortSrc
}
size = 1 // gobble 1 byte
}
if k := elem(v).kind(); byte(v) == 0 || k != EastAsianFullwidth && k != EastAsianWide && k != EastAsianAmbiguous {
if size != copy(dst[nDst:], src[nSrc:nSrc+size]) {
return nDst, nSrc, transform.ErrShortDst
}
nDst += size
} else {
data := inverseData[byte(v)]
if len(dst)-nDst < int(data[0]) {
return nDst, nSrc, transform.ErrShortDst
}
i := 1
for end := int(data[0]); i < end; i++ {
dst[nDst] = data[i]
nDst++
}
dst[nDst] = data[i] ^ src[nSrc+size-1]
nDst++
}
nSrc += size
}
return nDst, nSrc, nil
}
type wideTransform struct {
transform.NopResetter
}
func (wideTransform) Span(src []byte, atEOF bool) (n int, err error) {
for n < len(src) {
// TODO: Consider ASCII fast path. Special-casing ASCII handling can
// reduce the ns/op of BenchmarkWideASCII by about 30%. This is probably
// not enough to warrant the extra code and complexity.
v, size := trie.lookup(src[n:])
if size == 0 { // incomplete UTF-8 encoding
if !atEOF {
err = transform.ErrShortSrc
} else {
n = len(src)
}
break
}
if k := elem(v).kind(); byte(v) == 0 || k != EastAsianHalfwidth && k != EastAsianNarrow {
} else {
err = transform.ErrEndOfSpan
break
}
n += size
}
return n, err
}
func (wideTransform) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
for nSrc < len(src) {
// TODO: Consider ASCII fast path. Special-casing ASCII handling can
// reduce the ns/op of BenchmarkWideASCII by about 30%. This is probably
// not enough to warrant the extra code and complexity.
v, size := trie.lookup(src[nSrc:])
if size == 0 { // incomplete UTF-8 encoding
if !atEOF {
return nDst, nSrc, transform.ErrShortSrc
}
size = 1 // gobble 1 byte
}
if k := elem(v).kind(); byte(v) == 0 || k != EastAsianHalfwidth && k != EastAsianNarrow {
if size != copy(dst[nDst:], src[nSrc:nSrc+size]) {
return nDst, nSrc, transform.ErrShortDst
}
nDst += size
} else {
data := inverseData[byte(v)]
if len(dst)-nDst < int(data[0]) {
return nDst, nSrc, transform.ErrShortDst
}
i := 1
for end := int(data[0]); i < end; i++ {
dst[nDst] = data[i]
nDst++
}
dst[nDst] = data[i] ^ src[nSrc+size-1]
nDst++
}
nSrc += size
}
return nDst, nSrc, nil
}

View File

@ -1,30 +0,0 @@
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
package width
// elem is an entry of the width trie. The high byte is used to encode the type
// of the rune. The low byte is used to store the index to a mapping entry in
// the inverseData array.
type elem uint16
const (
tagNeutral elem = iota << typeShift
tagAmbiguous
tagWide
tagNarrow
tagFullwidth
tagHalfwidth
)
const (
numTypeBits = 3
typeShift = 16 - numTypeBits
// tagNeedsFold is true for all fullwidth and halfwidth runes except for
// the Won sign U+20A9.
tagNeedsFold = 0x1000
// The Korean Won sign is halfwidth, but SHOULD NOT be mapped to a wide
// variant.
wonSign rune = 0x20A9
)

View File

@ -1,206 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate stringer -type=Kind
//go:generate go run gen.go gen_common.go gen_trieval.go
// Package width provides functionality for handling different widths in text.
//
// Wide characters behave like ideographs; they tend to allow line breaks after
// each character and remain upright in vertical text layout. Narrow characters
// are kept together in words or runs that are rotated sideways in vertical text
// layout.
//
// For more information, see https://unicode.org/reports/tr11/.
package width // import "golang.org/x/text/width"
import (
"unicode/utf8"
"golang.org/x/text/transform"
)
// TODO
// 1) Reduce table size by compressing blocks.
// 2) API proposition for computing display length
// (approximation, fixed pitch only).
// 3) Implement display length.
// Kind indicates the type of width property as defined in https://unicode.org/reports/tr11/.
type Kind int
const (
// Neutral characters do not occur in legacy East Asian character sets.
Neutral Kind = iota
// EastAsianAmbiguous characters that can be sometimes wide and sometimes
// narrow and require additional information not contained in the character
// code to further resolve their width.
EastAsianAmbiguous
// EastAsianWide characters are wide in its usual form. They occur only in
// the context of East Asian typography. These runes may have explicit
// halfwidth counterparts.
EastAsianWide
// EastAsianNarrow characters are narrow in its usual form. They often have
// fullwidth counterparts.
EastAsianNarrow
// Note: there exist Narrow runes that do not have fullwidth or wide
// counterparts, despite what the definition says (e.g. U+27E6).
// EastAsianFullwidth characters have a compatibility decompositions of type
// wide that map to a narrow counterpart.
EastAsianFullwidth
// EastAsianHalfwidth characters have a compatibility decomposition of type
// narrow that map to a wide or ambiguous counterpart, plus U+20A9 ₩ WON
// SIGN.
EastAsianHalfwidth
// Note: there exist runes that have a halfwidth counterparts but that are
// classified as Ambiguous, rather than wide (e.g. U+2190).
)
// TODO: the generated tries need to return size 1 for invalid runes for the
// width to be computed correctly (each byte should render width 1)
var trie = newWidthTrie(0)
// Lookup reports the Properties of the first rune in b and the number of bytes
// of its UTF-8 encoding.
func Lookup(b []byte) (p Properties, size int) {
v, sz := trie.lookup(b)
return Properties{elem(v), b[sz-1]}, sz
}
// LookupString reports the Properties of the first rune in s and the number of
// bytes of its UTF-8 encoding.
func LookupString(s string) (p Properties, size int) {
v, sz := trie.lookupString(s)
return Properties{elem(v), s[sz-1]}, sz
}
// LookupRune reports the Properties of rune r.
func LookupRune(r rune) Properties {
var buf [4]byte
n := utf8.EncodeRune(buf[:], r)
v, _ := trie.lookup(buf[:n])
last := byte(r)
if r >= utf8.RuneSelf {
last = 0x80 + byte(r&0x3f)
}
return Properties{elem(v), last}
}
// Properties provides access to width properties of a rune.
type Properties struct {
elem elem
last byte
}
func (e elem) kind() Kind {
return Kind(e >> typeShift)
}
// Kind returns the Kind of a rune as defined in Unicode TR #11.
// See https://unicode.org/reports/tr11/ for more details.
func (p Properties) Kind() Kind {
return p.elem.kind()
}
// Folded returns the folded variant of a rune or 0 if the rune is canonical.
func (p Properties) Folded() rune {
if p.elem&tagNeedsFold != 0 {
buf := inverseData[byte(p.elem)]
buf[buf[0]] ^= p.last
r, _ := utf8.DecodeRune(buf[1 : 1+buf[0]])
return r
}
return 0
}
// Narrow returns the narrow variant of a rune or 0 if the rune is already
// narrow or doesn't have a narrow variant.
func (p Properties) Narrow() rune {
if k := p.elem.kind(); byte(p.elem) != 0 && (k == EastAsianFullwidth || k == EastAsianWide || k == EastAsianAmbiguous) {
buf := inverseData[byte(p.elem)]
buf[buf[0]] ^= p.last
r, _ := utf8.DecodeRune(buf[1 : 1+buf[0]])
return r
}
return 0
}
// Wide returns the wide variant of a rune or 0 if the rune is already
// wide or doesn't have a wide variant.
func (p Properties) Wide() rune {
if k := p.elem.kind(); byte(p.elem) != 0 && (k == EastAsianHalfwidth || k == EastAsianNarrow) {
buf := inverseData[byte(p.elem)]
buf[buf[0]] ^= p.last
r, _ := utf8.DecodeRune(buf[1 : 1+buf[0]])
return r
}
return 0
}
// TODO for Properties:
// - Add Fullwidth/Halfwidth or Inverted methods for computing variants
// mapping.
// - Add width information (including information on non-spacing runes).
// Transformer implements the transform.Transformer interface.
type Transformer struct {
t transform.SpanningTransformer
}
// Reset implements the transform.Transformer interface.
func (t Transformer) Reset() { t.t.Reset() }
// Transform implements the transform.Transformer interface.
func (t Transformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
return t.t.Transform(dst, src, atEOF)
}
// Span implements the transform.SpanningTransformer interface.
func (t Transformer) Span(src []byte, atEOF bool) (n int, err error) {
return t.t.Span(src, atEOF)
}
// Bytes returns a new byte slice with the result of applying t to b.
func (t Transformer) Bytes(b []byte) []byte {
b, _, _ = transform.Bytes(t, b)
return b
}
// String returns a string with the result of applying t to s.
func (t Transformer) String(s string) string {
s, _, _ = transform.String(t, s)
return s
}
var (
// Fold is a transform that maps all runes to their canonical width.
//
// Note that the NFKC and NFKD transforms in golang.org/x/text/unicode/norm
// provide a more generic folding mechanism.
Fold Transformer = Transformer{foldTransform{}}
// Widen is a transform that maps runes to their wide variant, if
// available.
Widen Transformer = Transformer{wideTransform{}}
// Narrow is a transform that maps runes to their narrow variant, if
// available.
Narrow Transformer = Transformer{narrowTransform{}}
)
// TODO: Consider the following options:
// - Treat Ambiguous runes that have a halfwidth counterpart as wide, or some
// generalized variant of this.
// - Consider a wide Won character to be the default width (or some generalized
// variant of this).
// - Filter the set of characters that gets converted (the preferred approach is
// to allow applying filters to transforms).

3
vendor/modules.txt vendored
View File

@ -311,7 +311,7 @@ github.com/google/cadvisor/utils/sysfs
github.com/google/cadvisor/utils/sysinfo github.com/google/cadvisor/utils/sysinfo
github.com/google/cadvisor/version github.com/google/cadvisor/version
github.com/google/cadvisor/watcher github.com/google/cadvisor/watcher
# github.com/google/cel-go v0.20.1 # github.com/google/cel-go v0.21.0
## explicit; go 1.18 ## explicit; go 1.18
github.com/google/cel-go/cel github.com/google/cel-go/cel
github.com/google/cel-go/checker github.com/google/cel-go/checker
@ -930,7 +930,6 @@ golang.org/x/text/secure/bidirule
golang.org/x/text/transform golang.org/x/text/transform
golang.org/x/text/unicode/bidi golang.org/x/text/unicode/bidi
golang.org/x/text/unicode/norm golang.org/x/text/unicode/norm
golang.org/x/text/width
# golang.org/x/time v0.3.0 # golang.org/x/time v0.3.0
## explicit ## explicit
golang.org/x/time/rate golang.org/x/time/rate