Preserve Flag.Varargs in bytecode/reflection type mappers#7988
Open
knutwannheden wants to merge 1 commit into
Open
Preserve Flag.Varargs in bytecode/reflection type mappers#7988knutwannheden wants to merge 1 commit into
knutwannheden wants to merge 1 commit into
Conversation
The bytecode `ACC_VARARGS` access flag occupies bit `0x0080`, which is the same bit `Flag.Transient` uses. `Flag.Varargs`, however, is modeled as `1L << 34` (matching javac's `Flags.VARARGS`). Type mappers that fed raw JVM/reflection access flags into `JavaType.Method` therefore produced a spurious `Transient` flag and dropped `Varargs`. `GroovyTypeMapping` (`MethodNode.getModifiers()`) and `JavaReflectionTypeMapping` (`Member.getModifiers()`) both did this; `KotlinTypeMapping` already compensated. Add a shared `Flag.mapBytecodeAccessFlagsToBitMap` helper that remaps `ACC_VARARGS` to `Varargs` and route both mappers through it. Methods missing `Flag.Varargs` cause anything that reasons about varargs to misbehave; e.g. UseDiamondOperator indexed past the end of `getParameterTypes()` (ArrayIndexOutOfBoundsException) on such method types.
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Flag.Varargswas silently dropped fromJavaType.Methodfor varargs methods attributed through some (non-javac) paths, because of a bit collision:ACC_VARARGSaccess flag — also the bit returned byjava.lang.reflect.Member#getModifiers()and ASM — occupies bit0x0080.Flagassigns that same bit (1L << 7) toTransient.Flag.Varargsis instead1L << 34, matching javac'scom.sun.tools.javac.code.Flags.VARARGS.Any type mapper that fed raw JVM/reflection access flags into
JavaType.Method's flags bitmap therefore produced a spuriousTransientand lostVarargs.GroovyTypeMapping(MethodNode.getModifiers()) andJavaReflectionTypeMapping(Member.getModifiers()) both did this.KotlinTypeMappingalready compensated for the same collision (KotlinTypeMapping.kt, search for the1L shl 7/1L shl 34remap), confirming the convention.This is a broad-impact attribution gap: anything that branches on
Flag.Varargsor reasons about varargs (overload resolution, method matching, templating, etc.) can misbehave on these method types. It was the upstream cause of anArrayIndexOutOfBoundsExceptioninUseDiamondOperator.getMethodParamType(rewrite-static-analysis) seen in a flagship recipe run — that recipe maps invocation arguments by index and is only reachable when a call has more arguments than the method's reported parameter types, which in valid Java only happens for a varargs method whoseFlag.Varargswas missing. (A defensive guard for the recipe itself is handled separately inrewrite-static-analysis.)Scope / where the flag is and isn't dropped
I exercised each attribution path:
Flag.Varargspreserved before this PR?1L << 34natively)TypeTablejar round-trip (writeJar→ javac)KotlinTypeMappingGroovyTypeMappingJavaReflectionTypeMappingNote: the in-memory
TypeTableSinkconsumer that buildsJavaTypedirectly from bytecode (used for fast classpath type loading) lives outside this repo and receives the same raw ASMaccessint; it needs the equivalent remap and is tracked separately.Summary
Flag.mapBytecodeAccessFlagsToBitMap(long)— remapsACC_VARARGS(0x0080) toFlag.Varargsfor method/constructor access flags; leaves everything else (and field flags, where0x0080legitimately meanstransient) untouched.GroovyTypeMapping#methodTypeandJavaReflectionTypeMapping's method and constructor mapping through the helper.Test plan
FlagTest— unit-tests the helper (remaps varargs, leaves non-varargs flags identical).JavaReflectionTypeMappingTest#varargsMethodHasVarargsFlag— mapsArrays.asList(Object...)reflectively; assertsVarargspresent andTransientabsent.GroovyVarargsTypeMappingTest#varargsMethodHasVarargsFlag— parses Groovy callingArrays.asList; asserts the resolved method type hasVarargsand notTransient.TypeTableTest#varargsFlagPreservedThroughTypeTableRoundtrip— positive regression guard proving theTypeTablejar path already preserves the flag../gradlew :rewrite-java:test :rewrite-groovy:test— full suites pass.