Skip to content

Giving a class both a static and instance members with a matching union type causes invalid java to be generated #46

@niloc132

Description

@niloc132

Given two members on a class, one static and one instance, one will be generated with a _STATIC suffix. That is,

Foo.noArgMethod = function() {};
Foo.prototype.noArgMethod = function() {};

will cause this to be generated:

  @JsMethod(name = "noArgMethod")
  public static native Object noArgMethod_STATIC();

  public native Object noArgMethod();

(removed an incorrect statement, will follow up separately)

However, if both of these have a parameter which is unioned, then the generated *UnionType will be emitted twice, as the names are not distinct - unlike the actual member name, the union type's name is only based on the method name and argument name.

Additionally, once a native method is generated for this, two or more @JsOverlay methods will be generated as well, and when those are renamed to append _STATIC to them, a @JsMethod/@JsProperty annotation will also be decorated on them, which will cause a compiler error in J2CL.

Example js with a method:

/**
 * @param {string|number} arg
 */
Foo.unionArgMethod = function(arg) {};
/**
 * @param {string|number} arg
 */
Foo.prototype.unionArgMethod = function(arg) {};

Resulting java:

  @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL)
  public interface UnionArgMethodArgUnionType {
    @JsOverlay
    static Foo.UnionArgMethodArgUnionType of(Object o) {
      return Js.cast(o);
    }

    @JsOverlay
    default double asDouble() {
      return Js.asDouble(this);
    }

    @JsOverlay
    default String asString() {
      return Js.asString(this);
    }

    @JsOverlay
    default boolean isDouble() {
      return (Object) this instanceof Double;
    }

    @JsOverlay
    default boolean isString() {
      return (Object) this instanceof String;
    }
  }

  @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL)
  public interface UnionArgMethodArgUnionType {
    @JsOverlay
    static Foo.UnionArgMethodArgUnionType of(Object o) {
      return Js.cast(o);
    }

    @JsOverlay
    default double asDouble() {
      return Js.asDouble(this);
    }

    @JsOverlay
    default String asString() {
      return Js.asString(this);
    }

    @JsOverlay
    default boolean isDouble() {
      return (Object) this instanceof Double;
    }

    @JsOverlay
    default boolean isString() {
      return (Object) this instanceof String;
    }
  }

  @JsOverlay
  @JsMethod(name = "unionArgMethod")
  public static final Object unionArgMethod_STATIC(String arg) {
    return unionArgMethod(Js.<Foo.UnionArgMethodArgUnionType>uncheckedCast(arg));
  }

  @JsMethod(name = "unionArgMethod")
  public static native Object unionArgMethod_STATIC(Foo.UnionArgMethodArgUnionType arg);

  @JsOverlay
  @JsMethod(name = "unionArgMethod")
  public static final Object unionArgMethod_STATIC(double arg) {
    return unionArgMethod(Js.<Foo.UnionArgMethodArgUnionType>uncheckedCast(arg));
  }

  @JsOverlay
  public final Object unionArgMethod(String arg) {
    return unionArgMethod(Js.<Foo.UnionArgMethodArgUnionType>uncheckedCast(arg));
  }

  public native Object unionArgMethod(Foo.UnionArgMethodArgUnionType arg);

  @JsOverlay
  public final Object unionArgMethod(double arg) {
    return unionArgMethod(Js.<Foo.UnionArgMethodArgUnionType>uncheckedCast(arg));
  }

Example JS with a property:

/**
 * @type {string|number}
 */
Foo.unionProperty;
/**
 * @type {string|number}
 */
Foo.prototype.unionProperty;

Resulting Java:

  @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL)
  public interface UnionPropertyUnionType {
    @JsOverlay
    static Foo.UnionPropertyUnionType of(Object o) {
      return Js.cast(o);
    }

    @JsOverlay
    default double asDouble() {
      return Js.asDouble(this);
    }

    @JsOverlay
    default String asString() {
      return Js.asString(this);
    }

    @JsOverlay
    default boolean isDouble() {
      return (Object) this instanceof Double;
    }

    @JsOverlay
    default boolean isString() {
      return (Object) this instanceof String;
    }
  }

  @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL)
  public interface UnionPropertyUnionType {
    @JsOverlay
    static Foo.UnionPropertyUnionType of(Object o) {
      return Js.cast(o);
    }

    @JsOverlay
    default double asDouble() {
      return Js.asDouble(this);
    }

    @JsOverlay
    default String asString() {
      return Js.asString(this);
    }

    @JsOverlay
    default boolean isDouble() {
      return (Object) this instanceof Double;
    }

    @JsOverlay
    default boolean isString() {
      return (Object) this instanceof String;
    }
  }

  public static Foo.UnionPropertyUnionType unionProperty;

There is also a separate correctness issue which I'll file separately - in some cases, there is only one unionProperty, the static variant, as the non-static member is failed to be emitted.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions