8000 fix(graalvm): walk superclass hierarchy for unnamed @Inject fields in NativeComponentInjector by kawasima · Pull Request #158 · enkan/enkan · GitHub
[go: up one dir, main page]

Skip to content

fix(graalvm): walk superclass hierarchy for unnamed @Inject fields in NativeComponentInjector - #158

Merged
kawasima merged 2 commits into
developfrom
feature/fix-unnamed-inject-hierarchy
Apr 4, 2026
Merged

kawasima merged 2 commits into
developfrom
feature/fix-unnamed-inject-hierarchy

Conversation

@kawasima
@kawasima kawasima commented Apr 4, 2026
Copy link
Copy Markdown
Collaborator

Summary

  • NativeComponentInjector.newInstance() was using getDeclaredFields() which only scanned the immediate class, silently skipping unnamed @Inject fields declared in superclasses
  • Replaced the inline stream with a new collectUnnamedInjectFields() helper that walks the full class hierarchy up to (but not including) SystemComponent, mirroring the existing collectNamedInjectFields() pattern in EnkanFeature
  • Added NativeComponentRegistry.unregister() to support test teardown and prevent registry state from leaking between tests

Closes #152

Test plan

  • newInstanceInjectsUnnamedFieldFromSuperclass — regression test that fails on old code, passes after fix
  • collectNamedInjectFieldsIgnoresUnnamedFieldInSuperclass — confirms named-field collector still ignores inherited unnamed fields
  • All 19 existing tests continue to pass: mvn test -pl kotowari-graalvm -am -DexcludedGroups=integration

🤖 Generated with Claude Code

… NativeComponentInjector

NativeComponentInjector.newInstance() was calling getDeclaredFields() which
only scanned the immediate class, silently skipping unnamed @Inject fields
declared in superclasses.

Replace the inline stream with collectUnnamedInjectFields() which walks the
full hierarchy up to (but not including) SystemComponent, mirroring the
existing collectNamedInjectFields() pattern in EnkanFeature.

Add NativeComponentRegistry.unregister() to support test teardown, and add
@AfterEach cleanup in EnkanFeatureTest to prevent registry state leaking
between tests.

Closes #152

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes GraalVM native injection for unnamed @Inject fields declared in superclasses by making NativeComponentInjector scan the full class hierarchy, and adds test scaffolding to prevent binder registry state leakage between tests.

Changes:

  • Update NativeComponentInjector.newInstance() to collect unnamed @Inject fields across superclasses (up to SystemComponent).
  • Add NativeComponentRegistry.unregister() to support test teardown.
  • Add regression tests covering inherited unnamed injection and ensuring named-field collection still ignores unnamed inherited fields.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
kotowari-graalvm/src/test/java/enkan/graalvm/EnkanFeatureTest.java Adds superclass-hierarchy regression tests and cleans registry state after each test.
kotowari-graalvm/src/main/java/enkan/graalvm/NativeComponentRegistry.java Adds unregister() to remove binders (used by tests to prevent cross-test leakage).
kotowari-graalvm/src/main/java/enkan/graalvm/NativeComponentInjector.java Walks superclass hierarchy to inject unnamed @Inject fields and updates reflection-requirement wording.
Comments suppressed due to low confidence (1)

kotowari-graalvm/src/main/java/enkan/graalvm/NativeComponentInjector.java:30

  • The Javadoc here says ComponentInjector#injectField calls Field.setAccessible(true), but the current implementation uses Field.trySetAccessible() (see enkan-system/src/main/java/enkan/system/inject/ComponentInjector.java:42-45). Also, now that collectUnnamedInjectFields() walks superclasses, the reflection-registration requirement applies to the declaring classes of those unnamed @Inject fields as well (not just the concrete component class). Please update this comment to reflect both points so native-image users don’t get misled.
 * <p><b>Reflection requirement:</b> unnamed {@code @Inject} fields (those annotated with
 * {@code @Inject} but <em>not</em> with {@code @Named}) are resolved by
 * {@link ComponentInjector#injectField} which calls {@code Field.setAccessible(true)}.
 * Any component class that has such fields must therefore have
 * {@code "allDeclaredFields": true} in its {@code reflect-config.json} entry,
 * or be registered via {@code EnkanFeature} with
 * {@code RuntimeReflection.registerAllFields(componentClass)}.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

8000
Comment on lines +62 to +64
while (current != null && current != Object.class
&& !current.getName().equals("enkan.component.SystemComponent")) {
for (Field f : current.getDeclaredFields()) {
Copilot AI Apr 4, 2026
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

collectUnnamedInjectFields uses a hard-coded fully-qualified class name string to stop at SystemComponent. Since this class already imports enkan.component.SystemComponent, consider using SystemComponent.class.getName() (or a private static constant shared with EnkanFeature) to avoid a fragile magic string/typo risk.

Copilot uses AI. Check for mistakes.
…ing-class requirement

- Correct Field.setAccessible(true) to Field.trySetAccessible() per actual impl
- Note that reflect-config.json registration is required for every declaring class
  in the superclass hierarchy, not only the concrete component class

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kawasima
kawasima merged commit fefede3 into develop Apr 4, 2026
1 check passed
@kawasima
kawasima deleted the feature/fix-unnamed-inject-hierarchy branch April 4, 2026 09:12 8000
Sign up for free to 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

0