mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
81589c1244
### Why the changes in this PR are needed? `text/template`'s field evaluator (`text/template.(*state).evalField`, `exec.go`) calls`reflect.Value.MethodByName` with a non-constant name. The Go linker treats a reachable non-constant`MethodByName` as a signal to disable **method-level dead-code elimination for the whole binary** (see `cmd/link/internal/ld/deadcode.go` and golang/go#72895). Two OPA code paths pull stdlib `text/template` into the reachable graph of ordinary embedders: 1. **Compiler frontend** — `ast.Compiler.Compile → … → gojsonschema.formatErrorDescription → text/template`. Reached unconditionally by anything that compiles Rego. 2. **`strings.render_template` builtin** (`v1/topdown/template.go`) — registered in the topdown builtin table, reachable in anything that links Rego evaluation. So an embedder of OPA's compiler/eval retains its entire reachable method surface — a large binary-size regression, hundreds of MB in the reporter's case (#7903). Both edges must go before the linker re-enables method-level DCE for that embedder. ### What are the changes in this PR? Vendor a self-contained, method-less copy of `text/template` under `internal/methodlesstemplate` and point both call sites at it. **No external dependency** (`go.mod`/`go.sum` unchanged). - Copied verbatim from **Go 1.25.8**: `doc.go`, `exec.go`, `funcs.go`, `option.go`, `template.go`, plus `internal/fmtsort/sort.go`. Go's BSD `LICENSE` is preserved in the vendored directory and every file keeps its `The Go Authors` copyright header. - Stdlib `text/template/parse` is reused unchanged (the parser has no `MethodByName`/`evalField` edge, so it does not defeat DCE). - `helper.go` (`ParseFiles`/`ParseGlob`/`ParseFS`) is dropped — the OPA call sites only need `New`/`Parse`/`Execute`, and nothing in the kept files references it. - **The only edit to the copied code** is removing the `MethodByName` branch in `exec.go`'s `evalField` (method resolution on the data value). Everything else is byte-identical, so re-syncing to a newer Go release is a diff-and-reapply of that single branch removal. - `internal/gojsonschema` (commit 1) and `v1/topdown` (commit 2) import the vendored package. The gojsonschema engine is retained in full, so `ErrorTemplateFuncs` (its `FuncMap` extension point) keeps working — **no public symbol is removed**. Rego values and gojsonschema `ErrorDetails` decode to `map[string]any`/`[]any`/scalars, which have no methods, so removing method resolution is a provable no-op for these callers. ### Notes to assist PR review: - **Diff review tip**: `doc.go`/`funcs.go`/`option.go`/`template.go`/`internal/fmtsort/sort.go` are **byte-identical** to the Go 1.25.8 originals. Only `exec.go` differs, in exactly two hunks: the `internal/fmtsort` → vendored import path, and the removed `MethodByName` block (replaced by a comment explaining the DCE rationale). - **Fidelity — render_template**: the `rendertemplate` conformance cases (incl. `complex` range/if/vars, `simpleint` `%v`, `missingkey` → `<undefined>`) pass **unchanged**. - **Fidelity — gojsonschema**: same engine (method-less), validation-error output unchanged; existing `internal/gojsonschema` and `v1/ast` tests pass. - **Tests**: `TestNoStdlibTextTemplateImport` in both `internal/gojsonschema` and `v1/topdown` scans every non-test file and asserts none import stdlib `text/template`/`html/template`. `go build ./...`, `go vet ./...` OK; `go mod tidy` is a no-op. - **Lint**: the vendored directory is added to the golangci-lint path exclusions, mirroring the existing `internal/gojsonschema` precedent — the copy is verbatim stdlib, and linting it against OPA's house rules would force divergence from upstream Go (it trips ~31 stdlib-idiom issues) and break the diff-and-reapply re-sync. - **Attribution**: the vendored code is Go stdlib only (BSD, `The Go Authors`); it contains no third-party/DataDog code. ### Further comments: - **Scope**: this restores method-level DCE for embedders of OPA's **compiler/eval**. The standalone `opa` binary additionally links `v1/server`, which imports `html/template` (a wrapper over `text/template`) — a separate, independent edge left as a follow-up. Embedders that don't link the server (the common case) get the full win from this PR. - Root cause: golang/go#72895. Closes #7903 for compiler/eval embedders. --------- Signed-off-by: Dick Childress <dick.childress@icearp.net> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
116 lines
2.9 KiB
YAML
116 lines
2.9 KiB
YAML
version: "2"
|
|
run:
|
|
timeout: 5m
|
|
linters:
|
|
default: none
|
|
enable:
|
|
- copyloopvar
|
|
- errcheck
|
|
- gocritic
|
|
- govet
|
|
- ineffassign
|
|
- intrange
|
|
- mirror
|
|
- misspell
|
|
- perfsprint
|
|
- prealloc
|
|
- revive # replacement for golint
|
|
- staticcheck
|
|
- unconvert
|
|
- unused
|
|
- usetesting
|
|
# - gosec # too many false positives
|
|
settings:
|
|
gocritic:
|
|
enabled-checks:
|
|
# NOTE that these are rules enabled in addition to the default set
|
|
- filepathJoin
|
|
- dupImport
|
|
- redundantSprint
|
|
- stringConcatSimplify
|
|
disabled-checks:
|
|
- appendAssign
|
|
# NOTE(ae): this one should be enabled, but there were too
|
|
# many violations to fix in one go... revisit later
|
|
- singleCaseSwitch
|
|
# Reasonable rule, but not sure what to replace with in
|
|
# many locations, so disabling for now
|
|
- exitAfterDefer
|
|
# This is disabled from the perfomance tag enabled further down.
|
|
- hugeParam
|
|
enabled-tags:
|
|
- performance
|
|
settings:
|
|
ifElseChain:
|
|
# ridiculous value set for now, but this should be
|
|
# lowered to something more reasonable, as the rule
|
|
# is reasonable (replace long if-else chains with
|
|
# switch)... just too many violations right now
|
|
minThreshold: 10
|
|
govet:
|
|
disable:
|
|
# enable later and fix
|
|
- buildtag
|
|
enable:
|
|
- deepequalerrors
|
|
- nilness
|
|
lll:
|
|
line-length: 200
|
|
perfsprint:
|
|
# only rule disabled by default, but it's a good one
|
|
err-error: true
|
|
revive:
|
|
# this mainly complains about us using min/max for variable names,
|
|
# which seems like an unlikely source of actual issues
|
|
rules:
|
|
- name: redefines-builtin-id
|
|
disabled: true
|
|
- name: unused-receiver
|
|
disabled: false
|
|
staticcheck:
|
|
checks:
|
|
# https://staticcheck.dev/docs/checks/#<check-id> for more details on these rules
|
|
[
|
|
"all",
|
|
"-ST1000",
|
|
"-ST1003",
|
|
"-ST1016",
|
|
"-ST1020",
|
|
"-ST1021",
|
|
"-ST1022",
|
|
"-QF1001",
|
|
"-QF1002",
|
|
"-QF1003",
|
|
"-QF1008",
|
|
]
|
|
exclusions:
|
|
warn-unused: true
|
|
generated: lax
|
|
presets:
|
|
- comments
|
|
- common-false-positives
|
|
- legacy
|
|
- std-error-handling
|
|
rules:
|
|
- linters:
|
|
- staticcheck
|
|
text: This package is intended for older projects transitioning from OPA v0.x
|
|
path-except: v1/
|
|
- linters:
|
|
- staticcheck
|
|
text: SA1019 # Using a deprecated function, variable, constant or field
|
|
path: _test\.go
|
|
paths:
|
|
- internal/gojsonschema
|
|
- internal/methodlesstemplate
|
|
- node_modules
|
|
issues:
|
|
# don't hide issues in CI runs because they are the same type
|
|
max-same-issues: 0
|
|
formatters:
|
|
enable:
|
|
- gofmt
|
|
- goimports
|
|
exclusions:
|
|
generated: lax
|