Introduce ValidateMessage to enable alternative protobuf runtimes - #480

Open
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure
Open

Introduce ValidateMessage to enable alternative protobuf runtimes#480
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure

Conversation

@andrewparmet

@andrewparmetandrewparmet commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Adds a runtime-independent ValidateMessage interface so alternative protobuf runtimes (for example, protokt) can be validated without first converting their messages to com.google.protobuf.Message.

Changes

  • Add the public ValidateMessage and Value surface: getDescriptorForType, hasField, getField, celValue, plus rawValue / jvmValue for the value side.
  • Add ProtobufValidateMessage, which adapts com.google.protobuf.Message to ValidateMessage, so the existing Message validation path is unchanged.
  • Route the evaluators (FieldEvaluator, OneofEvaluator, MessageOneofEvaluator, AnyEvaluator, MapEvaluator, list/object values, WrappedValueEvaluator) through ValidateMessage instead of Message directly.
  • Expose Validator.validate(ValidateMessage) as the entry point for alternative-runtime callers; the Message overload delegates through it.

Related work

Testing

./gradlew :conformance:conformance passes.

@andrewparmet
andrewparmet marked this pull request as ready for review June 14, 2026 15:22
pkwarren pushed a commit that referenced this pull request Jun 15, 2026
Moves protovalidate-java's CEL setup from the deprecated standard
runtime to the planner runtime.
## Notable callouts
- Wire the compiler and runtime by hand rather than through
`plannerCelBuilder()`. `CelRuntimeImpl` directs callers to subset the
standard library via `setStandardFunctions` rather than
`setStandardEnvironmentEnabled`, so the runtime drops stdlib `matches`
that way while the checker continues to use
`setStandardEnvironmentEnabled(false)`. This keeps `CustomOverload`'s
caching `matches` replacement (cel-java#1038).
- Set
`CelOptions.current().enableHeterogeneousNumericComparisons(true)`,
which `CelRuntimeImpl` requires.
- Update `cel-java` to 0.13.1 preparing to support alternative Protobuf
runtimes.
## Related work:
- cel-java: 0.13.1 carries the CelValue-path fixes this work relies on
(fixed32/64 treated as unsigned; FieldMask navigable as a message,
cel-expr/cel-java#1074).
- protovalidate-java: #480 introduces `MessageReflector` to support
alternative Protobuf runtimes, which builds on this runtime change
(supersedes #202).
- protokt: open-toast/protokt#402 is the downstream consumer that
validates protokt messages through `MessageReflector` on this planner
runtime.
## Testing
`./gradlew :conformance:conformance` passes.
@jonbodner-buf

Copy link
Copy Markdown
Contributor

@andrewparmet can you update the branch from main?

@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

@jonbodner-buf done!

@jonbodner-buf

Copy link
Copy Markdown
Contributor

My biggest concern is there are no tests to validate these changes. Yes, the existing tests still pass, but nothing in the existing tests calls public ValidationResult validate(MessageReflector message) throws ValidationException .

The exception thrown by build.buf.protovalidate.MessageValue#jvmValue and build.buf.protovalidate.MessageReflectorValue#jvmValue also concern me, want to make sure that there is no code path where that could be triggered, especially for MessageReflectorValue, since it will be used with external types that implement MessageReflector.

andrewparmetand others added 2 commits June 17, 2026 23:15
Rename the MessageReflector interface to ValidateMessage, and accordingly
ProtobufMessageReflector -> ProtobufValidateMessage and MessageReflectorValue ->
ValidateMessageValue (with its `reflector` field/locals renamed to `message`).
Also give the jvmValue UnsupportedOperationException on the message-backed Value
types a descriptive message: MessageValue notes it carries the rule message and
should be read via messageValue()/celValue(); the ValidateMessage-backed value
notes that a getField() implementation returned a message value for a field
evaluated as a scalar.
Add ValidateMessageTest, which validates through a map-backed ValidateMessage
(no com.google.protobuf.Message): scalar / nested / repeated / map /
repeated-message / map-message violations and their field paths, a valid
message, the well-known Timestamp type, bytes, enum, unsigned (uint32), the
Int32Value wrapper, a required oneof, the clear error when a message field is
not backed by a ValidateMessage, and that jvmValue is unsupported on the
message-backed Value types.
Co-authored-by: Jon Bodner <jbodner@buf.build>
@andrewparmetandrewparmet changed the title Introduce MessageReflector to enable alternative protobuf runtimesIntroduce ValidateMessage to enable alternative protobuf runtimesJun 18, 2026
@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

Pushed up some tests - thanks for the starter patch from your investigation! The existing Validator tests do indirectly call validate(ValidateMessage message) via the Message overload, which hands off directly to it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@andrewparmet@jonbodner-buf
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Introduce ValidateMessage to enable alternative protobuf runtimes - #480

Open
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure
Open

Introduce ValidateMessage to enable alternative protobuf runtimes#480
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure

Conversation

@andrewparmet

@andrewparmetandrewparmet commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Adds a runtime-independent ValidateMessage interface so alternative protobuf runtimes (for example, protokt) can be validated without first converting their messages to com.google.protobuf.Message.

Changes

  • Add the public ValidateMessage and Value surface: getDescriptorForType, hasField, getField, celValue, plus rawValue / jvmValue for the value side.
  • Add ProtobufValidateMessage, which adapts com.google.protobuf.Message to ValidateMessage, so the existing Message validation path is unchanged.
  • Route the evaluators (FieldEvaluator, OneofEvaluator, MessageOneofEvaluator, AnyEvaluator, MapEvaluator, list/object values, WrappedValueEvaluator) through ValidateMessage instead of Message directly.
  • Expose Validator.validate(ValidateMessage) as the entry point for alternative-runtime callers; the Message overload delegates through it.

Related work

Testing

./gradlew :conformance:conformance passes.

@andrewparmet
andrewparmet marked this pull request as ready for review June 14, 2026 15:22
pkwarren pushed a commit that referenced this pull request Jun 15, 2026
Moves protovalidate-java's CEL setup from the deprecated standard
runtime to the planner runtime.
## Notable callouts
- Wire the compiler and runtime by hand rather than through
`plannerCelBuilder()`. `CelRuntimeImpl` directs callers to subset the
standard library via `setStandardFunctions` rather than
`setStandardEnvironmentEnabled`, so the runtime drops stdlib `matches`
that way while the checker continues to use
`setStandardEnvironmentEnabled(false)`. This keeps `CustomOverload`'s
caching `matches` replacement (cel-java#1038).
- Set
`CelOptions.current().enableHeterogeneousNumericComparisons(true)`,
which `CelRuntimeImpl` requires.
- Update `cel-java` to 0.13.1 preparing to support alternative Protobuf
runtimes.
## Related work:
- cel-java: 0.13.1 carries the CelValue-path fixes this work relies on
(fixed32/64 treated as unsigned; FieldMask navigable as a message,
cel-expr/cel-java#1074).
- protovalidate-java: #480 introduces `MessageReflector` to support
alternative Protobuf runtimes, which builds on this runtime change
(supersedes #202).
- protokt: open-toast/protokt#402 is the downstream consumer that
validates protokt messages through `MessageReflector` on this planner
runtime.
## Testing
`./gradlew :conformance:conformance` passes.
@jonbodner-buf

Copy link
Copy Markdown
Contributor

@andrewparmet can you update the branch from main?

@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

@jonbodner-buf done!

@jonbodner-buf

Copy link
Copy Markdown
Contributor

My biggest concern is there are no tests to validate these changes. Yes, the existing tests still pass, but nothing in the existing tests calls public ValidationResult validate(MessageReflector message) throws ValidationException .

The exception thrown by build.buf.protovalidate.MessageValue#jvmValue and build.buf.protovalidate.MessageReflectorValue#jvmValue also concern me, want to make sure that there is no code path where that could be triggered, especially for MessageReflectorValue, since it will be used with external types that implement MessageReflector.

andrewparmetand others added 2 commits June 17, 2026 23:15
Rename the MessageReflector interface to ValidateMessage, and accordingly
ProtobufMessageReflector -> ProtobufValidateMessage and MessageReflectorValue ->
ValidateMessageValue (with its `reflector` field/locals renamed to `message`).
Also give the jvmValue UnsupportedOperationException on the message-backed Value
types a descriptive message: MessageValue notes it carries the rule message and
should be read via messageValue()/celValue(); the ValidateMessage-backed value
notes that a getField() implementation returned a message value for a field
evaluated as a scalar.
Add ValidateMessageTest, which validates through a map-backed ValidateMessage
(no com.google.protobuf.Message): scalar / nested / repeated / map /
repeated-message / map-message violations and their field paths, a valid
message, the well-known Timestamp type, bytes, enum, unsigned (uint32), the
Int32Value wrapper, a required oneof, the clear error when a message field is
not backed by a ValidateMessage, and that jvmValue is unsupported on the
message-backed Value types.
Co-authored-by: Jon Bodner <jbodner@buf.build>
@andrewparmetandrewparmet changed the title Introduce MessageReflector to enable alternative protobuf runtimesIntroduce ValidateMessage to enable alternative protobuf runtimesJun 18, 2026
@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

Pushed up some tests - thanks for the starter patch from your investigation! The existing Validator tests do indirectly call validate(ValidateMessage message) via the Message overload, which hands off directly to it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@andrewparmet@jonbodner-buf
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Introduce ValidateMessage to enable alternative protobuf runtimes - #480

Open
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure
Open

Introduce ValidateMessage to enable alternative protobuf runtimes#480
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure

Conversation

@andrewparmet

@andrewparmetandrewparmet commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Adds a runtime-independent ValidateMessage interface so alternative protobuf runtimes (for example, protokt) can be validated without first converting their messages to com.google.protobuf.Message.

Changes

  • Add the public ValidateMessage and Value surface: getDescriptorForType, hasField, getField, celValue, plus rawValue / jvmValue for the value side.
  • Add ProtobufValidateMessage, which adapts com.google.protobuf.Message to ValidateMessage, so the existing Message validation path is unchanged.
  • Route the evaluators (FieldEvaluator, OneofEvaluator, MessageOneofEvaluator, AnyEvaluator, MapEvaluator, list/object values, WrappedValueEvaluator) through ValidateMessage instead of Message directly.
  • Expose Validator.validate(ValidateMessage) as the entry point for alternative-runtime callers; the Message overload delegates through it.

Related work

Testing

./gradlew :conformance:conformance passes.

@andrewparmet
andrewparmet marked this pull request as ready for review June 14, 2026 15:22
pkwarren pushed a commit that referenced this pull request Jun 15, 2026
Moves protovalidate-java's CEL setup from the deprecated standard
runtime to the planner runtime.
## Notable callouts
- Wire the compiler and runtime by hand rather than through
`plannerCelBuilder()`. `CelRuntimeImpl` directs callers to subset the
standard library via `setStandardFunctions` rather than
`setStandardEnvironmentEnabled`, so the runtime drops stdlib `matches`
that way while the checker continues to use
`setStandardEnvironmentEnabled(false)`. This keeps `CustomOverload`'s
caching `matches` replacement (cel-java#1038).
- Set
`CelOptions.current().enableHeterogeneousNumericComparisons(true)`,
which `CelRuntimeImpl` requires.
- Update `cel-java` to 0.13.1 preparing to support alternative Protobuf
runtimes.
## Related work:
- cel-java: 0.13.1 carries the CelValue-path fixes this work relies on
(fixed32/64 treated as unsigned; FieldMask navigable as a message,
cel-expr/cel-java#1074).
- protovalidate-java: #480 introduces `MessageReflector` to support
alternative Protobuf runtimes, which builds on this runtime change
(supersedes #202).
- protokt: open-toast/protokt#402 is the downstream consumer that
validates protokt messages through `MessageReflector` on this planner
runtime.
## Testing
`./gradlew :conformance:conformance` passes.
@jonbodner-buf

Copy link
Copy Markdown
Contributor

@andrewparmet can you update the branch from main?

@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

@jonbodner-buf done!

@jonbodner-buf

Copy link
Copy Markdown
Contributor

My biggest concern is there are no tests to validate these changes. Yes, the existing tests still pass, but nothing in the existing tests calls public ValidationResult validate(MessageReflector message) throws ValidationException .

The exception thrown by build.buf.protovalidate.MessageValue#jvmValue and build.buf.protovalidate.MessageReflectorValue#jvmValue also concern me, want to make sure that there is no code path where that could be triggered, especially for MessageReflectorValue, since it will be used with external types that implement MessageReflector.

andrewparmetand others added 2 commits June 17, 2026 23:15
Rename the MessageReflector interface to ValidateMessage, and accordingly
ProtobufMessageReflector -> ProtobufValidateMessage and MessageReflectorValue ->
ValidateMessageValue (with its `reflector` field/locals renamed to `message`).
Also give the jvmValue UnsupportedOperationException on the message-backed Value
types a descriptive message: MessageValue notes it carries the rule message and
should be read via messageValue()/celValue(); the ValidateMessage-backed value
notes that a getField() implementation returned a message value for a field
evaluated as a scalar.
Add ValidateMessageTest, which validates through a map-backed ValidateMessage
(no com.google.protobuf.Message): scalar / nested / repeated / map /
repeated-message / map-message violations and their field paths, a valid
message, the well-known Timestamp type, bytes, enum, unsigned (uint32), the
Int32Value wrapper, a required oneof, the clear error when a message field is
not backed by a ValidateMessage, and that jvmValue is unsupported on the
message-backed Value types.
Co-authored-by: Jon Bodner <jbodner@buf.build>
@andrewparmetandrewparmet changed the title Introduce MessageReflector to enable alternative protobuf runtimesIntroduce ValidateMessage to enable alternative protobuf runtimesJun 18, 2026
@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

Pushed up some tests - thanks for the starter patch from your investigation! The existing Validator tests do indirectly call validate(ValidateMessage message) via the Message overload, which hands off directly to it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@andrewparmet@jonbodner-buf
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Introduce ValidateMessage to enable alternative protobuf runtimes - #480

Open
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure
Open

Introduce ValidateMessage to enable alternative protobuf runtimes#480
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure

Conversation

@andrewparmet

@andrewparmetandrewparmet commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Adds a runtime-independent ValidateMessage interface so alternative protobuf runtimes (for example, protokt) can be validated without first converting their messages to com.google.protobuf.Message.

Changes

  • Add the public ValidateMessage and Value surface: getDescriptorForType, hasField, getField, celValue, plus rawValue / jvmValue for the value side.
  • Add ProtobufValidateMessage, which adapts com.google.protobuf.Message to ValidateMessage, so the existing Message validation path is unchanged.
  • Route the evaluators (FieldEvaluator, OneofEvaluator, MessageOneofEvaluator, AnyEvaluator, MapEvaluator, list/object values, WrappedValueEvaluator) through ValidateMessage instead of Message directly.
  • Expose Validator.validate(ValidateMessage) as the entry point for alternative-runtime callers; the Message overload delegates through it.

Related work

Testing

./gradlew :conformance:conformance passes.

@andrewparmet
andrewparmet marked this pull request as ready for review June 14, 2026 15:22
pkwarren pushed a commit that referenced this pull request Jun 15, 2026
Moves protovalidate-java's CEL setup from the deprecated standard
runtime to the planner runtime.
## Notable callouts
- Wire the compiler and runtime by hand rather than through
`plannerCelBuilder()`. `CelRuntimeImpl` directs callers to subset the
standard library via `setStandardFunctions` rather than
`setStandardEnvironmentEnabled`, so the runtime drops stdlib `matches`
that way while the checker continues to use
`setStandardEnvironmentEnabled(false)`. This keeps `CustomOverload`'s
caching `matches` replacement (cel-java#1038).
- Set
`CelOptions.current().enableHeterogeneousNumericComparisons(true)`,
which `CelRuntimeImpl` requires.
- Update `cel-java` to 0.13.1 preparing to support alternative Protobuf
runtimes.
## Related work:
- cel-java: 0.13.1 carries the CelValue-path fixes this work relies on
(fixed32/64 treated as unsigned; FieldMask navigable as a message,
cel-expr/cel-java#1074).
- protovalidate-java: #480 introduces `MessageReflector` to support
alternative Protobuf runtimes, which builds on this runtime change
(supersedes #202).
- protokt: open-toast/protokt#402 is the downstream consumer that
validates protokt messages through `MessageReflector` on this planner
runtime.
## Testing
`./gradlew :conformance:conformance` passes.
@jonbodner-buf

Copy link
Copy Markdown
Contributor

@andrewparmet can you update the branch from main?

@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

@jonbodner-buf done!

@jonbodner-buf

Copy link
Copy Markdown
Contributor

My biggest concern is there are no tests to validate these changes. Yes, the existing tests still pass, but nothing in the existing tests calls public ValidationResult validate(MessageReflector message) throws ValidationException .

The exception thrown by build.buf.protovalidate.MessageValue#jvmValue and build.buf.protovalidate.MessageReflectorValue#jvmValue also concern me, want to make sure that there is no code path where that could be triggered, especially for MessageReflectorValue, since it will be used with external types that implement MessageReflector.

andrewparmetand others added 2 commits June 17, 2026 23:15
Rename the MessageReflector interface to ValidateMessage, and accordingly
ProtobufMessageReflector -> ProtobufValidateMessage and MessageReflectorValue ->
ValidateMessageValue (with its `reflector` field/locals renamed to `message`).
Also give the jvmValue UnsupportedOperationException on the message-backed Value
types a descriptive message: MessageValue notes it carries the rule message and
should be read via messageValue()/celValue(); the ValidateMessage-backed value
notes that a getField() implementation returned a message value for a field
evaluated as a scalar.
Add ValidateMessageTest, which validates through a map-backed ValidateMessage
(no com.google.protobuf.Message): scalar / nested / repeated / map /
repeated-message / map-message violations and their field paths, a valid
message, the well-known Timestamp type, bytes, enum, unsigned (uint32), the
Int32Value wrapper, a required oneof, the clear error when a message field is
not backed by a ValidateMessage, and that jvmValue is unsupported on the
message-backed Value types.
Co-authored-by: Jon Bodner <jbodner@buf.build>
@andrewparmetandrewparmet changed the title Introduce MessageReflector to enable alternative protobuf runtimesIntroduce ValidateMessage to enable alternative protobuf runtimesJun 18, 2026
@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

Pushed up some tests - thanks for the starter patch from your investigation! The existing Validator tests do indirectly call validate(ValidateMessage message) via the Message overload, which hands off directly to it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@andrewparmet@jonbodner-buf
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Introduce ValidateMessage to enable alternative protobuf runtimes - #480

Open
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure
Open

Introduce ValidateMessage to enable alternative protobuf runtimes#480
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure

Conversation

@andrewparmet

@andrewparmetandrewparmet commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Adds a runtime-independent ValidateMessage interface so alternative protobuf runtimes (for example, protokt) can be validated without first converting their messages to com.google.protobuf.Message.

Changes

  • Add the public ValidateMessage and Value surface: getDescriptorForType, hasField, getField, celValue, plus rawValue / jvmValue for the value side.
  • Add ProtobufValidateMessage, which adapts com.google.protobuf.Message to ValidateMessage, so the existing Message validation path is unchanged.
  • Route the evaluators (FieldEvaluator, OneofEvaluator, MessageOneofEvaluator, AnyEvaluator, MapEvaluator, list/object values, WrappedValueEvaluator) through ValidateMessage instead of Message directly.
  • Expose Validator.validate(ValidateMessage) as the entry point for alternative-runtime callers; the Message overload delegates through it.

Related work

Testing

./gradlew :conformance:conformance passes.

@andrewparmet
andrewparmet marked this pull request as ready for review June 14, 2026 15:22
pkwarren pushed a commit that referenced this pull request Jun 15, 2026
Moves protovalidate-java's CEL setup from the deprecated standard
runtime to the planner runtime.
## Notable callouts
- Wire the compiler and runtime by hand rather than through
`plannerCelBuilder()`. `CelRuntimeImpl` directs callers to subset the
standard library via `setStandardFunctions` rather than
`setStandardEnvironmentEnabled`, so the runtime drops stdlib `matches`
that way while the checker continues to use
`setStandardEnvironmentEnabled(false)`. This keeps `CustomOverload`'s
caching `matches` replacement (cel-java#1038).
- Set
`CelOptions.current().enableHeterogeneousNumericComparisons(true)`,
which `CelRuntimeImpl` requires.
- Update `cel-java` to 0.13.1 preparing to support alternative Protobuf
runtimes.
## Related work:
- cel-java: 0.13.1 carries the CelValue-path fixes this work relies on
(fixed32/64 treated as unsigned; FieldMask navigable as a message,
cel-expr/cel-java#1074).
- protovalidate-java: #480 introduces `MessageReflector` to support
alternative Protobuf runtimes, which builds on this runtime change
(supersedes #202).
- protokt: open-toast/protokt#402 is the downstream consumer that
validates protokt messages through `MessageReflector` on this planner
runtime.
## Testing
`./gradlew :conformance:conformance` passes.
@jonbodner-buf

Copy link
Copy Markdown
Contributor

@andrewparmet can you update the branch from main?

@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

@jonbodner-buf done!

@jonbodner-buf

Copy link
Copy Markdown
Contributor

My biggest concern is there are no tests to validate these changes. Yes, the existing tests still pass, but nothing in the existing tests calls public ValidationResult validate(MessageReflector message) throws ValidationException .

The exception thrown by build.buf.protovalidate.MessageValue#jvmValue and build.buf.protovalidate.MessageReflectorValue#jvmValue also concern me, want to make sure that there is no code path where that could be triggered, especially for MessageReflectorValue, since it will be used with external types that implement MessageReflector.

andrewparmetand others added 2 commits June 17, 2026 23:15
Rename the MessageReflector interface to ValidateMessage, and accordingly
ProtobufMessageReflector -> ProtobufValidateMessage and MessageReflectorValue ->
ValidateMessageValue (with its `reflector` field/locals renamed to `message`).
Also give the jvmValue UnsupportedOperationException on the message-backed Value
types a descriptive message: MessageValue notes it carries the rule message and
should be read via messageValue()/celValue(); the ValidateMessage-backed value
notes that a getField() implementation returned a message value for a field
evaluated as a scalar.
Add ValidateMessageTest, which validates through a map-backed ValidateMessage
(no com.google.protobuf.Message): scalar / nested / repeated / map /
repeated-message / map-message violations and their field paths, a valid
message, the well-known Timestamp type, bytes, enum, unsigned (uint32), the
Int32Value wrapper, a required oneof, the clear error when a message field is
not backed by a ValidateMessage, and that jvmValue is unsupported on the
message-backed Value types.
Co-authored-by: Jon Bodner <jbodner@buf.build>
@andrewparmetandrewparmet changed the title Introduce MessageReflector to enable alternative protobuf runtimesIntroduce ValidateMessage to enable alternative protobuf runtimesJun 18, 2026
@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

Pushed up some tests - thanks for the starter patch from your investigation! The existing Validator tests do indirectly call validate(ValidateMessage message) via the Message overload, which hands off directly to it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@andrewparmet@jonbodner-buf
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Introduce ValidateMessage to enable alternative protobuf runtimes - #480

Open
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure
Open

Introduce ValidateMessage to enable alternative protobuf runtimes#480
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure

Conversation

@andrewparmet

@andrewparmetandrewparmet commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Adds a runtime-independent ValidateMessage interface so alternative protobuf runtimes (for example, protokt) can be validated without first converting their messages to com.google.protobuf.Message.

Changes

  • Add the public ValidateMessage and Value surface: getDescriptorForType, hasField, getField, celValue, plus rawValue / jvmValue for the value side.
  • Add ProtobufValidateMessage, which adapts com.google.protobuf.Message to ValidateMessage, so the existing Message validation path is unchanged.
  • Route the evaluators (FieldEvaluator, OneofEvaluator, MessageOneofEvaluator, AnyEvaluator, MapEvaluator, list/object values, WrappedValueEvaluator) through ValidateMessage instead of Message directly.
  • Expose Validator.validate(ValidateMessage) as the entry point for alternative-runtime callers; the Message overload delegates through it.

Related work

Testing

./gradlew :conformance:conformance passes.

@andrewparmet
andrewparmet marked this pull request as ready for review June 14, 2026 15:22
pkwarren pushed a commit that referenced this pull request Jun 15, 2026
Moves protovalidate-java's CEL setup from the deprecated standard
runtime to the planner runtime.
## Notable callouts
- Wire the compiler and runtime by hand rather than through
`plannerCelBuilder()`. `CelRuntimeImpl` directs callers to subset the
standard library via `setStandardFunctions` rather than
`setStandardEnvironmentEnabled`, so the runtime drops stdlib `matches`
that way while the checker continues to use
`setStandardEnvironmentEnabled(false)`. This keeps `CustomOverload`'s
caching `matches` replacement (cel-java#1038).
- Set
`CelOptions.current().enableHeterogeneousNumericComparisons(true)`,
which `CelRuntimeImpl` requires.
- Update `cel-java` to 0.13.1 preparing to support alternative Protobuf
runtimes.
## Related work:
- cel-java: 0.13.1 carries the CelValue-path fixes this work relies on
(fixed32/64 treated as unsigned; FieldMask navigable as a message,
cel-expr/cel-java#1074).
- protovalidate-java: #480 introduces `MessageReflector` to support
alternative Protobuf runtimes, which builds on this runtime change
(supersedes #202).
- protokt: open-toast/protokt#402 is the downstream consumer that
validates protokt messages through `MessageReflector` on this planner
runtime.
## Testing
`./gradlew :conformance:conformance` passes.
@jonbodner-buf

Copy link
Copy Markdown
Contributor

@andrewparmet can you update the branch from main?

@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

@jonbodner-buf done!

@jonbodner-buf

Copy link
Copy Markdown
Contributor

My biggest concern is there are no tests to validate these changes. Yes, the existing tests still pass, but nothing in the existing tests calls public ValidationResult validate(MessageReflector message) throws ValidationException .

The exception thrown by build.buf.protovalidate.MessageValue#jvmValue and build.buf.protovalidate.MessageReflectorValue#jvmValue also concern me, want to make sure that there is no code path where that could be triggered, especially for MessageReflectorValue, since it will be used with external types that implement MessageReflector.

andrewparmetand others added 2 commits June 17, 2026 23:15
Rename the MessageReflector interface to ValidateMessage, and accordingly
ProtobufMessageReflector -> ProtobufValidateMessage and MessageReflectorValue ->
ValidateMessageValue (with its `reflector` field/locals renamed to `message`).
Also give the jvmValue UnsupportedOperationException on the message-backed Value
types a descriptive message: MessageValue notes it carries the rule message and
should be read via messageValue()/celValue(); the ValidateMessage-backed value
notes that a getField() implementation returned a message value for a field
evaluated as a scalar.
Add ValidateMessageTest, which validates through a map-backed ValidateMessage
(no com.google.protobuf.Message): scalar / nested / repeated / map /
repeated-message / map-message violations and their field paths, a valid
message, the well-known Timestamp type, bytes, enum, unsigned (uint32), the
Int32Value wrapper, a required oneof, the clear error when a message field is
not backed by a ValidateMessage, and that jvmValue is unsupported on the
message-backed Value types.
Co-authored-by: Jon Bodner <jbodner@buf.build>
@andrewparmetandrewparmet changed the title Introduce MessageReflector to enable alternative protobuf runtimesIntroduce ValidateMessage to enable alternative protobuf runtimesJun 18, 2026
@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

Pushed up some tests - thanks for the starter patch from your investigation! The existing Validator tests do indirectly call validate(ValidateMessage message) via the Message overload, which hands off directly to it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@andrewparmet@jonbodner-buf
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Introduce ValidateMessage to enable alternative protobuf runtimes - #480

Open
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure
Open

Introduce ValidateMessage to enable alternative protobuf runtimes#480
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure

Conversation

@andrewparmet

@andrewparmetandrewparmet commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Adds a runtime-independent ValidateMessage interface so alternative protobuf runtimes (for example, protokt) can be validated without first converting their messages to com.google.protobuf.Message.

Changes

  • Add the public ValidateMessage and Value surface: getDescriptorForType, hasField, getField, celValue, plus rawValue / jvmValue for the value side.
  • Add ProtobufValidateMessage, which adapts com.google.protobuf.Message to ValidateMessage, so the existing Message validation path is unchanged.
  • Route the evaluators (FieldEvaluator, OneofEvaluator, MessageOneofEvaluator, AnyEvaluator, MapEvaluator, list/object values, WrappedValueEvaluator) through ValidateMessage instead of Message directly.
  • Expose Validator.validate(ValidateMessage) as the entry point for alternative-runtime callers; the Message overload delegates through it.

Related work

Testing

./gradlew :conformance:conformance passes.

@andrewparmet
andrewparmet marked this pull request as ready for review June 14, 2026 15:22
pkwarren pushed a commit that referenced this pull request Jun 15, 2026
Moves protovalidate-java's CEL setup from the deprecated standard
runtime to the planner runtime.
## Notable callouts
- Wire the compiler and runtime by hand rather than through
`plannerCelBuilder()`. `CelRuntimeImpl` directs callers to subset the
standard library via `setStandardFunctions` rather than
`setStandardEnvironmentEnabled`, so the runtime drops stdlib `matches`
that way while the checker continues to use
`setStandardEnvironmentEnabled(false)`. This keeps `CustomOverload`'s
caching `matches` replacement (cel-java#1038).
- Set
`CelOptions.current().enableHeterogeneousNumericComparisons(true)`,
which `CelRuntimeImpl` requires.
- Update `cel-java` to 0.13.1 preparing to support alternative Protobuf
runtimes.
## Related work:
- cel-java: 0.13.1 carries the CelValue-path fixes this work relies on
(fixed32/64 treated as unsigned; FieldMask navigable as a message,
cel-expr/cel-java#1074).
- protovalidate-java: #480 introduces `MessageReflector` to support
alternative Protobuf runtimes, which builds on this runtime change
(supersedes #202).
- protokt: open-toast/protokt#402 is the downstream consumer that
validates protokt messages through `MessageReflector` on this planner
runtime.
## Testing
`./gradlew :conformance:conformance` passes.
@jonbodner-buf

Copy link
Copy Markdown
Contributor

@andrewparmet can you update the branch from main?

@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

@jonbodner-buf done!

@jonbodner-buf

Copy link
Copy Markdown
Contributor

My biggest concern is there are no tests to validate these changes. Yes, the existing tests still pass, but nothing in the existing tests calls public ValidationResult validate(MessageReflector message) throws ValidationException .

The exception thrown by build.buf.protovalidate.MessageValue#jvmValue and build.buf.protovalidate.MessageReflectorValue#jvmValue also concern me, want to make sure that there is no code path where that could be triggered, especially for MessageReflectorValue, since it will be used with external types that implement MessageReflector.

andrewparmetand others added 2 commits June 17, 2026 23:15
Rename the MessageReflector interface to ValidateMessage, and accordingly
ProtobufMessageReflector -> ProtobufValidateMessage and MessageReflectorValue ->
ValidateMessageValue (with its `reflector` field/locals renamed to `message`).
Also give the jvmValue UnsupportedOperationException on the message-backed Value
types a descriptive message: MessageValue notes it carries the rule message and
should be read via messageValue()/celValue(); the ValidateMessage-backed value
notes that a getField() implementation returned a message value for a field
evaluated as a scalar.
Add ValidateMessageTest, which validates through a map-backed ValidateMessage
(no com.google.protobuf.Message): scalar / nested / repeated / map /
repeated-message / map-message violations and their field paths, a valid
message, the well-known Timestamp type, bytes, enum, unsigned (uint32), the
Int32Value wrapper, a required oneof, the clear error when a message field is
not backed by a ValidateMessage, and that jvmValue is unsupported on the
message-backed Value types.
Co-authored-by: Jon Bodner <jbodner@buf.build>
@andrewparmetandrewparmet changed the title Introduce MessageReflector to enable alternative protobuf runtimesIntroduce ValidateMessage to enable alternative protobuf runtimesJun 18, 2026
@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

Pushed up some tests - thanks for the starter patch from your investigation! The existing Validator tests do indirectly call validate(ValidateMessage message) via the Message overload, which hands off directly to it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@andrewparmet@jonbodner-buf
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Introduce ValidateMessage to enable alternative protobuf runtimes - #480

Open
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure
Open

Introduce ValidateMessage to enable alternative protobuf runtimes#480
andrewparmet wants to merge 6 commits into
bufbuild:mainfrom
andrewparmet:pv-message-reflector-pure

Conversation

@andrewparmet

@andrewparmetandrewparmet commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Adds a runtime-independent ValidateMessage interface so alternative protobuf runtimes (for example, protokt) can be validated without first converting their messages to com.google.protobuf.Message.

Changes

  • Add the public ValidateMessage and Value surface: getDescriptorForType, hasField, getField, celValue, plus rawValue / jvmValue for the value side.
  • Add ProtobufValidateMessage, which adapts com.google.protobuf.Message to ValidateMessage, so the existing Message validation path is unchanged.
  • Route the evaluators (FieldEvaluator, OneofEvaluator, MessageOneofEvaluator, AnyEvaluator, MapEvaluator, list/object values, WrappedValueEvaluator) through ValidateMessage instead of Message directly.
  • Expose Validator.validate(ValidateMessage) as the entry point for alternative-runtime callers; the Message overload delegates through it.

Related work

Testing

./gradlew :conformance:conformance passes.

@andrewparmet
andrewparmet marked this pull request as ready for review June 14, 2026 15:22
pkwarren pushed a commit that referenced this pull request Jun 15, 2026
Moves protovalidate-java's CEL setup from the deprecated standard
runtime to the planner runtime.
## Notable callouts
- Wire the compiler and runtime by hand rather than through
`plannerCelBuilder()`. `CelRuntimeImpl` directs callers to subset the
standard library via `setStandardFunctions` rather than
`setStandardEnvironmentEnabled`, so the runtime drops stdlib `matches`
that way while the checker continues to use
`setStandardEnvironmentEnabled(false)`. This keeps `CustomOverload`'s
caching `matches` replacement (cel-java#1038).
- Set
`CelOptions.current().enableHeterogeneousNumericComparisons(true)`,
which `CelRuntimeImpl` requires.
- Update `cel-java` to 0.13.1 preparing to support alternative Protobuf
runtimes.
## Related work:
- cel-java: 0.13.1 carries the CelValue-path fixes this work relies on
(fixed32/64 treated as unsigned; FieldMask navigable as a message,
cel-expr/cel-java#1074).
- protovalidate-java: #480 introduces `MessageReflector` to support
alternative Protobuf runtimes, which builds on this runtime change
(supersedes #202).
- protokt: open-toast/protokt#402 is the downstream consumer that
validates protokt messages through `MessageReflector` on this planner
runtime.
## Testing
`./gradlew :conformance:conformance` passes.
@jonbodner-buf

Copy link
Copy Markdown
Contributor

@andrewparmet can you update the branch from main?

@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

@jonbodner-buf done!

@jonbodner-buf

Copy link
Copy Markdown
Contributor

My biggest concern is there are no tests to validate these changes. Yes, the existing tests still pass, but nothing in the existing tests calls public ValidationResult validate(MessageReflector message) throws ValidationException .

The exception thrown by build.buf.protovalidate.MessageValue#jvmValue and build.buf.protovalidate.MessageReflectorValue#jvmValue also concern me, want to make sure that there is no code path where that could be triggered, especially for MessageReflectorValue, since it will be used with external types that implement MessageReflector.

andrewparmetand others added 2 commits June 17, 2026 23:15
Rename the MessageReflector interface to ValidateMessage, and accordingly
ProtobufMessageReflector -> ProtobufValidateMessage and MessageReflectorValue ->
ValidateMessageValue (with its `reflector` field/locals renamed to `message`).
Also give the jvmValue UnsupportedOperationException on the message-backed Value
types a descriptive message: MessageValue notes it carries the rule message and
should be read via messageValue()/celValue(); the ValidateMessage-backed value
notes that a getField() implementation returned a message value for a field
evaluated as a scalar.
Add ValidateMessageTest, which validates through a map-backed ValidateMessage
(no com.google.protobuf.Message): scalar / nested / repeated / map /
repeated-message / map-message violations and their field paths, a valid
message, the well-known Timestamp type, bytes, enum, unsigned (uint32), the
Int32Value wrapper, a required oneof, the clear error when a message field is
not backed by a ValidateMessage, and that jvmValue is unsupported on the
message-backed Value types.
Co-authored-by: Jon Bodner <jbodner@buf.build>
@andrewparmetandrewparmet changed the title Introduce MessageReflector to enable alternative protobuf runtimesIntroduce ValidateMessage to enable alternative protobuf runtimesJun 18, 2026
@andrewparmet

Copy link
Copy Markdown
ContributorAuthor

Pushed up some tests - thanks for the starter patch from your investigation! The existing Validator tests do indirectly call validate(ValidateMessage message) via the Message overload, which hands off directly to it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@andrewparmet@jonbodner-buf