Closed tryone144 closed 4 months ago
Adding generic type parameters made this class quite a bit more "complex". Is there a default location for JSO specific tests?
No, never even tried to test JSO APIs.
Don't see any purpose in that. With JSO you don't provide any implementations, just declarations. So what really need to be tested here is JSO itself (whether annotations propertly recognized). JSO declarations don't have regression, so never seen any point to write automatic tests.
With JSO you don't provide any implementations, just declarations. …
Okay, makes sense. In this concrete case, this would mostly be to make sure my type parameters make sense. I'll push a WIP shortly.
Thanks! BTW, how is your project with TeaVM going?
Thanks! BTW, how is your project with TeaVM going?
I have built a working prototype around july last year. Currently working on integrating the latest changes in TeaVM, polishing my changes to TeaVM and updating our build-system.
I will try replacing my hacky workaround for exporting Java functions and constants with the newly landed @JSExport
annotations next. The new module generator is nicer than the one I had written. :smile:
Be prepared for a PR regarding the long
-emulation layer in the JS backend. I found some bottlenecks (at least on Firefox) with the use of BigInt
s. Right now I am writing a benchmark sample to generate some baseline metrics in different environments.
The new module generator is nicer than the one I had written.
Thanks. This is the very first prototype. I'm going to add various improvements eventually:
d.ts
filesAnyway, I'm glad to get any feedback from users regarding this issue and possible improvements.
Hi. Is this available in version 0.9.2?
@aghasemi no, it's available in 0.10.0-dev-8
and will be available in 0.10.0
Hi. I know the answer is very likely "It's done when it's done", but still, is there an estimate of when 0.10.0
will be out? Given how many Javascript APIs and libraries use Promises, this is a big change.
@aghasemi I still don't undestand why it's a show-stopper for you. There's no Promise
definition in 0.9.0, but what prevents you from defining your own?
Lack of expertise? :)
It seems non-intuitive for me to convert between a Java Future and a JS Promise, unlike the case of the data types. Can you give me some hints to start with?
Currently what I do as a workaround is to to write a call back in Javascript which does a postMessage
and then listen to that message in TeaVM Java code.
It seems non-intuitive for me to convert between a Java Future and a JS Promise, unlike the case of the data types. Can you give me some hints to start with?
Sorry, I don't fully understand what you want? Just JS promise declarations or some connection of Future
interface? This PR addresses only former.
Currently what I do as a workaround is to to write a call back in Javascript which does a postMessage and then listen to that message in TeaVM Java code.
How is it related to Promise
class?
It seems non-intuitive for me to convert between a Java Future and a JS Promise, unlike the case of the data types.
This PR only introduces an interface to interact with the Promise
type in JavaScript. It shares its scope with the other JavaScript interop classes (JSString
, JSNumber
, JSArray
, etc.) to allow interfacing with JavaScript methods that return Promise
s or expect a Promise
as a parameter.
This does neither provide nor intend to provide an asynchronous executor for Java code and the Future
interface. You might provide your own implementation using the JSPromise
as a backend, though.
Can you give me some hints to start with?
If you can't update to a 0.10 snapshot release, you can get away with copying this implementation into your project (minus the recent changes) and using this class directly.
If you really need an executor and the Future
interface in the classlib, you have to provide your own implementation in your project and reference it in a META-INF/teavm.properties
file. Easiest way would be to put these classes/interfaces in their own package and adjust the map*
and strip*
entries accordingly to have them appear in their proper place in the classlib hierarchy.
Currently what I do as a workaround is to to write a call back in Javascript which does a postMessage and then listen to that message in TeaVM Java code.
What kind of problem are you trying to solve? Async execution in Java? :raised_eyebrow:
What kind of problem are you trying to solve? Async execution in Java? 🤨
Consider the following Javascript expression:
import('https://cdn.jsdelivr.net/npm/@xenova/transformers').then(async ({pipeline}) => {
return await pipeline('sentiment-analysis', 'Xenova/bert-base-multilingual-uncased-sentiment').then(async pipe => {
return await pipe('It may not be that bad, actually!').then(out => {
console.log(JSON.stringify(out))
return out
})
})
})
If you evaluate it, the result is a Promise object, which when itself evaluated/executed, returns a JSON object (in this case [{label=3 stars, score=0.33625108003616333}]
).
My "problem" is, how to get to that JSON object in my TeaVM Java code, as a Future object or not.
@aghasemi you can get it as a Promise
object. You don't necessarily need Promise
declaration out-of-the box (honestly, the declarations in TeaVM are only for "quick start", and for real world scenarios you may need to declare your own bindings), but rather to write your own Promise
"implementation". The key is that it's super easy, read documentation
Thanks for the hint. For future reference, the following program correctly works for the code snippet mentioned above:
package io.aghasemi;
import java.io.IOException;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject;
import org.teavm.jso.json.JSON;
public class Client {
private static final String jsCode = """
import('https://cdn.jsdelivr.net/npm/@xenova/transformers').then(async ({pipeline}) => {
return await pipeline('sentiment-analysis', 'Xenova/bert-base-multilingual-uncased-sentiment').then(async pipe => {
return await pipe('It may not be that bad, actually!').then(out => {
console.log(JSON.stringify(out))
return out
})
})
}).then(result => {
handler(result)
})
""";
@JSFunctor
public interface PromiseHandler extends JSObject {
void then(JSObject result);
}
@JSBody(params = { "handler" }, script = jsCode)
static native void runPromiseCode(PromiseHandler handler);
public static void main(String[] args) throws IOException {
runPromiseCode(new PromiseHandler() {
@Override
public void then(JSObject result) {
System.out.println("In Java code, the result is "+ JSON.stringify(result));
}
});
}
}
Now, as the a rather minor issue, you see above that I had to stringify
the returned JS object and likely later will have to again convert it to JSON in Java code to process it further. Can I directly get a usable (in Java) JSON object as result
, or there is no way around it?
Thanks again.
You can do with this JSObject
whatever you want, it's just direct reference to real JavaScript object. For example, write a type declaration for this JSON object and cast it to required type. Or just declare this particular type
void then(JSObject result);
instead of JSObject
. You can also use something like JSMapLike<JSObject>
, but then you'll end up with lots of casting and stringly typing. BTW, there's nothing mystical about JSMapLike
, it's simply declared as
public interface JSMapLike<T> extends JSObject {
@JSIndexer
T get(String key);
@JSIndexer
void set(String key, T object);
}
Amazing. many thanks.
This adds the
jso.core.JSPromise
interface to interact with native JavaScriptPromise
s.I've added this to return Promises (and
async
computation results) back to the JavaScript context. Is there a built in way for asynchronous execution we canawait
on the JavaScript side? I like theasync/await
syntax with Promises more like completion callbacks (which are already possible in TeaVM).Usage example:
I use this in my project by spawning a new thread and calling the `resolve` and `reject` methods after the inner thread has finished (or failed). The inner runner could be a simple function as well. ```java import org.teavm.jso.JSObject; import org.teavm.jso.core.JSFunction; import org.teavm.jso.core.JSObjects; import org.teavm.jso.core.JSPromise; import org.teavm.jso.core.JSString; /** * Thread executor that returns a JavaScript Promise which resolves/rejects depending on the execution result. */ static class ThreadExecutor extends Thread implements JSPromise.Executor { private Thread _thread; private int _timeout; private JSFunction _resolve; private JSFunction _reject; public ThreadExecutor(Thread thread, int timeout) { _thread = thread; _timeout = timeout; } /** Thread runner entry point */ @Override public void run() { _thread.start(); try { _thread.join(); } catch (InterruptedException e) { e.printStackTrace(); if (_reject != null && !JSObjects.isUndefined(_reject)) { _reject.call(this, JSString.valueOf("Interrupted")); } } catch (Exception e) { if (_reject != null && !JSObjects.isUndefined(_reject)) { _reject.call(this, JSString.valueOf(e.toString())); } } finally { if (_resolve != null && !JSObjects.isUndefined(_resolve)) { _resolve.call(this); } } } /** Promise runner entry point */ @Override public void onExecute(JSFunction resolveFunc, JSFunction rejectFunc) { _resolve = resolveFunc; _reject = rejectFunc; this.start(); } } /** Use it in a function */ public JSPromise execute(String code, JSNumber timeoutMs) { Thread runner = new Thread(() -> {}); // create Thread that does something with `code` int timeout = JSObjects.isUndefined(timeoutMs) ? 0 : timeoutMs.intValue(); ThreadExecutor executor = new ThreadExecutor(runner, timeout); JSPromise promise = JSPromise.create(executor); return promise; } ``` I think, this has quite a bit of boilerplate code, that would be the same for most use-cases. Maybe this can be abstracted away as well as part of the API?