Daniel Sun created GROOVY-12445:
-----------------------------------
Summary: STC: refuse unknown SignatureCodec versions without
reading payload bytes
Key: GROOVY-12445
URL: https://issues.apache.org/jira/browse/GROOVY-12445
Project: Groovy
Issue Type: Improvement
Reporter: Daniel Sun
h2. Context
{{@TypeChecked}} stores an inferred method return type on bytecode as
{{@TypeChecked.TypeCheckingInfo}}. The annotation has two members:
* {{version}} — an {{int}}, default {{0}}
* {{inferredType}} — a Base64 string
The protocol version is that {{version}} member. The payload string has no
version byte, magic number, or extra length prefix. This release implements
version {{1}} only. {{SignatureCodecFactory.getCodec(int)}} returns
{{SignatureCodecVersion1}} for {{1}}, and {{null}} for every other {{int}}: the
annotation default {{0}}, any negative value, and {{2}} or higher.
h2. Problem
The {{SignatureCodec}} Javadoc said the encoded string itself must carry a
version so later Groovy releases can decode older payloads. That puts the
version in the wrong place and invites a reader to parse bytes whose layout it
does not implement.
On the write path, {{addTypeCheckingInfoAnnotation}} called
{{codec.encode(...)}} with no null check. A {{null}} codec would throw
{{NullPointerException}}.
On the read path there was no helper that treated a {{null}} codec as "there is
no inferred signature". Feeding an unknown version to the version-1 codec, or
decoding anyway, would throw or invent a type from bytes that belong to another
layout. Two experimental compilers may use the same unpublished version {{int}}
for different layouts. A release compiler refuses those payloads the same way
it refuses a higher version it does not implement.
h2. Contract
A {{null}} codec means there is no inferred signature. The caller uses the
method's declared return type. The payload string is left unread.
||Version||getCodec||Read path||
|{{1}}|{{SignatureCodecVersion1}}|decode {{inferredType}}|
|{{0}}, negative, or {{2}} and higher|{{null}}|declared return type; payload
unread|
A version {{int}} that has shipped in a release is append-only: one {{int}}
keeps one layout. A later codec that claims an older version still decodes that
older version. The version stays a single {{int}} on the annotation.
{{getCodec}} continues to return {{null}} for an unimplemented version. The
write path returns before {{encode}} when the codec is {{null}}.
{code:java}
static ClassNode inferredReturnOrDeclared(int version, String signature,
ClassLoader loader, ClassNode declared) {
SignatureCodec codec = getCodec(version, loader);
if (codec == null) {
return declared;
}
return codec.decode(signature);
}
{code}
{{addTypeCheckingInfoAnnotation}} currently has no production callers
(disconnected under GROOVY-6401). There is also no production reader of
{{@TypeCheckingInfo}} today. Locking the unknown-version rule now means a later
reader uses the declared return type for any version this compiler does not
implement.
h2. Touch points
* {{org.codehaus.groovy.transform.stc.SignatureCodec}} — protocol Javadoc
* {{StaticTypeCheckingVisitor.addTypeCheckingInfoAnnotation}} — return when the
codec is {{null}}
* {{SignatureCodecFactory.inferredReturnOrDeclared}} — decode helper
*
{{src/test/groovy/org/codehaus/groovy/transform/stc/SignatureCodecTest.groovy}}
h2. Scope
This issue covers the unknown-version rule only. Union payload layout, rewiring
{{addTypeCheckingInfoAnnotation}} onto {{startMethodInference}}, and a new
compiler diagnostic are separate work.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)