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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions