Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Compose UI.Graphics: SolidColor(long) constructor stripped due to Kotlin @JvmInline value class param (sibling of java-interop#1440 for ctors)

Aperta
#1,470 0 commenti 1 reazione 2 assegnatari Vedi su GitHub

@jonathanpeppers ci sta già lavorando.

Dal 11/6/2026.

Valutazione

Questa issue non è ancora stata valutata.

Descrizione

Background

Kotlin @JvmInline value class parameters strip the constructor of a
binding even when the JVM bytecode signature is plain primitive shape and
the surrounding type is otherwise fully bound. This is a constructor-side
sibling of dotnet/java-interop#1440 (which targets static methods like
Button-bWB7cM8 / Typography(...)) and the closest analogue is
#1453 — but
that issue focuses on a stripped static factory function. Stripped
constructors are not currently tracked.

Concrete repro: androidx.compose.ui.graphics.SolidColor

Kotlin source (1.11.x):

@Immutable
class SolidColor(val value: Color) : ShaderBrush() { … }

Color is @JvmInline value class Color(val value: ULong), so the JVM
bytecode for the ctor is plain primitive — <init>(J)V. There is no
mangling on the constructor itself
(Kotlin's hash-mangling only applies
to methods), but the binder still drops it.

Xamarin.AndroidX.Compose.UI.Graphics.Android 1.11.2.1 decompiled with
ilspycmd:

[Register("androidx/compose/ui/graphics/SolidColor", DoNotGenerateAcw = true)]
public sealed class SolidColor : Brush, IInterpolatable, …
{
    // value-class-param methods with mangled names ARE kept:
    [Register("getValue-0d7_KjU", "()J", "")]               public long Value { get; }
    [Register("applyTo-Pq9zytI", "(JLandroidx/compose/ui/graphics/Paint;F)V", "")]
    public sealed override void ApplyTo(long size, IPaint p, float alpha) { … }

    [Register("lerp", "(Ljava/lang/Object;F)Ljava/lang/Object;", "")]
    public Object? Lerp(Object? other, float t) { … }

    // Only the internal acw ctor survives. The public (J)V ctor is gone:
    internal SolidColor(nint javaReference, JniHandleOwnership transfer)
        : base(javaReference, transfer) { }
    // <-- where is `public SolidColor(long packedColor)` ?
}

source/androidx.compose.ui/ui-graphics-android/PublicAPI/PublicAPI.Unshipped.txt
confirms the gap — the only SolidColor ctor in the public API is the
internal (nint, JniHandleOwnership) one. There is no SolidColor(long).

This is not unique to SolidColor: every Compose class whose primary
ctor takes a value-class parameter has the same problem (Path,
Outline.Rectangle, several BrushPainter shapes, …). The
androidx.compose.ui.graphics.Color type itself loses both its (J)V
ctor and its box-impl(J)Color static factory for the same reason.

Real-world impact

jonathanpeppers/Microsoft.AndroidX.Compose#251 adds
Brush.SolidColor(Color) and the gradient factories. Because the bound
SolidColor has no callable ctor, the PR ships a hand-written JNI
fallback in BrushBridges.cs:

// `new androidx.compose.ui.graphics.SolidColor(Color)` — the ctor
// is stripped because its parameter is a value-class `Color`.
internal static unsafe IntPtr BrushSolidColor(long color)
{
    if (s_solidColor_ctor == IntPtr.Zero)
    {
        s_solidColor_class = Java.Lang.Class.FromType(
            typeof(AndroidX.Compose.UI.Graphics.SolidColor)).Handle;
        s_solidColor_ctor = JNIEnv.GetMethodID(
            s_solidColor_class, "<init>", "(J)V");
    }
    var args = stackalloc JValue[1];
    args[0] = new JValue(color);
    return JNIEnv.NewObject(s_solidColor_class, s_solidColor_ctor, args);
}

The same PR also reaches for Color.box-impl(J) (a static factory) and
Brush.Companion (a static field) directly via JNI — those have other
trackers — but the ctor case is the new one and is what this issue is
about.

Question: would <add-node> + <constructor> be an acceptable metadata workaround until #1440 lands?

There is precedent in this repo for synthesizing missing
constructors via <add-node>:

Applied to SolidColor, the proposed metadata would look like:

<add-node path="/api/package[@name='androidx.compose.ui.graphics']/class[@name='SolidColor']">
  <constructor
      deprecated="not deprecated" final="false"
      name="SolidColor" static="false" visibility="public"
      bridge="false" synthetic="false"
      jni-signature="(J)V">
    <parameter name="value" type="long" jni-type="J" />
  </constructor>
</add-node>

The bytecode-level ctor <init>(J)V already exists — the binder just
isn't emitting it because Kotlin metadata flags the param as a
value class. Manually re-introducing it via <add-node> should cause
the generator to bind it as a normal public SolidColor(long value),
the same way the existing applyTo-Pq9zytI(JL…/Paint;F)V and
getValue-0d7_KjU()J methods are bound today (those keep their
hash-mangled JNI names and project the value-class params as their
underlying primitives).

Question for maintainers: is the <add-node> + <constructor> recipe
above an accepted workaround pattern for value-class-param ctors? If so,
it would unblock several hand-rolled-JNI hotspots in
Microsoft.AndroidX.Compose (SolidColor, Color's box-impl,
several Painter shapes) ahead of the eventual #1440 generator fix.

If <add-node> for value-class-param ctors is not expected to work
(e.g. there's a downstream fixup that resurfaces the strip), please say
so here and we'll keep the JNI bridges. Either answer is useful.

Why a separate issue from the existing trackers

Issue Scope Mechanism
dotnet/java-interop#1431 (open) Umbrella: inline-class projection + sibling-collision Generator
dotnet/java-interop#1432 (merged) Phase 1 — sibling-collision dedup for hashed methods Generator
dotnet/java-interop#1440 (open) Phase 2 — project inline classes as readonly structs Generator
dotnet/android-libraries#1453 (open) Stripped TypographyKt.Typography(...) static factory Material3 binding + (#1440)
This issue Stripped constructors of types whose Kotlin primary ctor takes a value class UI.Graphics binding + Metadata.xml

#1432/#1440/#1431 cover the method side (mangled names like
Button-bWB7cM8). Constructors aren't mangled (they're always <init>),
so the issue presents differently — but the same Kotlin-metadata-driven
strip path drops them. This issue exists so that both halves of the
pattern are tracked, and so the metadata workaround question can be
answered explicitly.

References

Lingua principale
C#
Stelle
318
Fork
73
Merge medio
1g 22h
PR unite (30g)
8

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di dotnet/android-libraries

Tutte le issue di dotnet/android-libraries

Issue simili

Altre issue su C#

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.