Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider supporting GraalJS' foreign-object-prototype #34

Open
pintomau opened this issue Sep 26, 2021 · 9 comments
Open

Consider supporting GraalJS' foreign-object-prototype #34

pintomau opened this issue Sep 26, 2021 · 9 comments
Assignees
Milestone

Comments

@pintomau
Copy link
Contributor

As explained in the documentation https://www.graalvm.org/reference-manual/js/Options/#to-the-launcher

--js.foreign-object-prototype: provide JavaScript’s default prototype to foreign objects that mimic JavaScript’s own types (foreign Arrays, Objects and Functions). Boolean value, default is false.

So, for example, List could extend Array and so on.

@bsorrentino
Copy link
Owner

Hi @pintomau thanks for feedbak

Really this project is most related to generate Typescript compatible declarations for Java Object useful to make easier interoperability with Java if you'd like use Typescript to develop on top of GraalJS

Your suggestion is mostly for the GraalJS runtime and I'll take it in consideration in the examples

@pintomau
Copy link
Contributor Author

pintomau commented Sep 27, 2021

Just want to add an example to this.

This flag makes it more natural to work with Java objects from the PoV of someone coming from the JS/TS world.

Instead of working with stream().map() or .get(0), you'd simply use .map() and [0].

Currently solved this in our code like this:

/**
 * This type represents the union of Java's List and Javascript's Array types.
 *
 * It's useful because we're using the js.foreign-object-prototype GraalJS option which
 * wraps the List type with a compatible Array API.
 *
 * @template E the concrete type hold by the list
 */
type JavaListProxy<E> = List<E> & [E]

// @ts-expect-error Java Lists are wrapped with a JS Array compatible type, therefore we're using an intersection type
const technicalEquipments: JavaListProxy<TechEquipmentItem> = vehicleItem.getTechnicalEquipments() ?? []
const techEquipment = technicalEquipments.find(...)

And by the way. Thanks a lot for the work you did here. Saved me a looot of time.

@pintomau
Copy link
Contributor Author

pintomau commented Sep 27, 2021

Actually found a better way, it seems.

Create an overrides.d.ts file, add your own interface List<E> extends Array<E> (sort conflicts, I simply removed it, but ymmv), and add it last in the .tsconfig includes list.

@bsorrentino
Copy link
Owner

Hi @pintomau could you provide me a more complete example of that so I could integrate it inside processor ?

@pintomau
Copy link
Contributor Author

pintomau commented Oct 11, 2021

Hi @bsorrentino , if you mean the overrides logic, then it looks like this:

declare namespace java.util {

  /**
   * List<E> overwrite that takes into account GraalJS' foreign-object-prototype where Lists are enriched with Javascript methods.
   *
   * Please note that due to conflicts, we're delegating sort, forEach, indexOf, lastIndexOf to JS' Array
   */
  interface List<E> extends Array<E>/* extends Collection<E> */ {

    // static copyOf<E>( arg0:Collection<E> ):List<E>;
    // static of<E>(  ):List<E>;
    // static of<E>( ...arg0:E[] ):List<E>;
    // static of<E>( arg0:E ):List<E>;
    // static of<E>( arg0:E, arg1:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E, arg3:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E, arg3:E, arg4:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E, arg3:E, arg4:E, arg5:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E, arg3:E, arg4:E, arg5:E, arg6:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E, arg3:E, arg4:E, arg5:E, arg6:E, arg7:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E, arg3:E, arg4:E, arg5:E, arg6:E, arg7:E, arg8:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E, arg3:E, arg4:E, arg5:E, arg6:E, arg7:E, arg8:E, arg9:E ):List<E>;
    add(arg0: E): boolean
    add(arg0: int, arg1: E): void
    addAll(arg0: Collection<E>): boolean
    addAll(arg0: int, arg1: Collection<E>): boolean
    clear(): void
    contains(arg0: any /* java.lang.Object */): boolean
    containsAll(arg0: Collection<any /* java.lang.Object */>): boolean
    equals(arg0: any /* java.lang.Object */): boolean
    // forEach<T>(arg0: Consumer<T>): void
    forEach(callbackfn: (value: E, index: number, array: E[]) => void, thisArg?: any): void;
    get(arg0: int): E
    // indexOf(arg0: any /* java.lang.Object */): int
    indexOf(searchElement: E, fromIndex?: number): number;
    isEmpty(): boolean
    iterator(): Iterator<E>
    // lastIndexOf(arg0: any /* java.lang.Object */): int
    lastIndexOf(searchElement: E, fromIndex?: number): number;
    listIterator(): any /* java.util.ListIterator */
    listIterator(arg0: int): any /* java.util.ListIterator */
    parallelStream(): java.util.stream.Stream<E>
    remove(arg0: any /* java.lang.Object */): boolean
    remove(arg0: int): E
    removeAll(arg0: Collection<any /* java.lang.Object */>): boolean
    removeIf(arg0: Predicate<E>): boolean
    replaceAll(arg0: UnaryOperator<E>): void
    retainAll(arg0: Collection<any /* java.lang.Object */>): boolean
    set(arg0: int, arg1: E): E
    size(): int
    // sort(arg0: any /* java.util.Comparator */): void
    sort(compareFn?: (a: E, b: E) => number): this;
    spliterator(): any /* java.util.Spliterator */
    stream(): java.util.stream.Stream<E>
    subList(arg0: int, arg1: int): List<E>
    toArray(): [any /* java.lang.Object */]
    toArray<T>(arg0: [T]): [T]
    toArray<T>(arg0: any /* java.util.function.IntFunction */): [T]

  } // end List

}

Notice that I had to comment some of Java's forEach, indexOf, lastIndexOf and sort methods because of clashing signatures with JS's Array.

And actually, override might not be the ideal name here. This does actually not replace the List type, but this works through Typescript's namespace merging, which I was not familiar with when I wrote the last comment. So, it's not the perfect solution.

@bsorrentino bsorrentino self-assigned this Oct 12, 2021
@bsorrentino bsorrentino added this to the 1.4.0 milestone Oct 12, 2021
bsorrentino added a commit that referenced this issue Oct 14, 2021
if options compatibility=graaljs and foreignobjectprototype=true the 
conversion on a java.util.List remove methods which signature clash with 
javascript Array

issue #34
bsorrentino added a commit that referenced this issue Oct 15, 2021
add unit test for 'foreign-object-prototype'

issue #34
bsorrentino added a commit that referenced this issue Oct 15, 2021
add unit test for 'foreign-object-prototype'

issue #34
bsorrentino added a commit that referenced this issue Oct 15, 2021
bsorrentino added a commit that referenced this issue Oct 15, 2021
@bsorrentino
Copy link
Owner

Hi @pintomau

I've deployed dev release 1.4-SNAPSHOT with fix.

please take a chance to test it an let me know

Thanks in advance

bsorrentino added a commit that referenced this issue Oct 15, 2021
@pintomau
Copy link
Contributor Author

pintomau commented Oct 15, 2021

Something's weird.

It's not correctly closing classes/interfaces/namespaces:
image

installed localy from 9d1bfba

bsorrentino added a commit that referenced this issue Oct 15, 2021
@bsorrentino
Copy link
Owner

Hi @pintomau

I've update dev release 1.4-SNAPSHOT with fix.

please take a chance to test it an let me know

Take note that to enable foreign-object-prototype yo have add the following processor options

<options>
	<ts.outfile>jdk8</ts.outfile>
        <compatibility>GRAALJS</compatibility>
        <foreignobjectprototype>true</foreignobjectprototype>
</options>

Thanks in advance.

@pintomau
Copy link
Contributor Author

Yup, now everything is looking good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants