question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Injecting a full Java instance into the V8 Context

See original GitHub issue

So I am trying to expose Java functionality to the V8 context and for this and I would need to be able to inject instances of objects (not necessarily POJOS) into the V8 context. And while this is already possible, the process itself is a bit painful and hard to mantain ( unless I’m doing something wrong). I created a very small basic sample of what I mean.

Let’s imagine this is the class of the instance I want to inject:


public class FileUtils {

    private String prefix;

    public FileUtils(String prefix) {
        this.prefix = prefix;
    }

    public void doA() {
        System.out.println(prefix+ "A");
    }

    public void doB() {
        System.out.println(prefix+"B");
    }

    public void add(Integer v1, Integer v2) {
        System.out.println(v1 + v2);
    }

}

Let’s imagine this class belongs to an external library and has many many more methods with all sorts of diferent signatures. When I create a new instance of FileUtils, I would like to be able to inject that instance directly into the V8 context along with all those methods inside it, which, when called from javascript would trigger the java code inside them as shown above. So this is how I’m doing this at the moment:

I created a JavetObject converter and created a callback for every method inside the FileUtils class:

@SuppressWarnings("unchecked")
public class EntityConverter extends JavetObjectConverter {

    @Override
    public V8Value toV8Value(V8Runtime v8, Object object) throws JavetException {
        V8Value v8Value = null;
        V8ValueObject v8ValueObject = null;

        v8Value = super.toV8Value(v8, object);
        if (v8Value != null && !(v8Value.isUndefined())) {
            return v8Value;
        }
        Class objectClass = object.getClass();
        v8ValueObject = v8.createV8ValueObject();
        for (Method method : objectClass.getMethods()) {
            String methodName = method.getName();

            try {
                FileUtilsCallbacks call = new FileUtilsCallbacks(v8, (FileUtils) object);
                JavetCallbackContext callback = new JavetCallbackContext(call, call.getMethod(methodName, method.getParameterTypes()));

                v8ValueObject.setFunction(methodName, callback);
            } catch (NoSuchMethodException ex) {
                Logger.getLogger(EntityConverter.class.getName()).log(Level.SEVERE, null, ex);
            }

        }

        v8Value = v8ValueObject;
        return v8.decorateV8Value(v8Value);

    }
}

And I created a JavetCallbackReceiver:

public class FileUtilsCallbacks extends JavetCallbackReceiver implements IJavetCallbackReceiver {

    private FileUtils myInstance;

    public FileUtilsCallbacks(V8Runtime v8Runtime, FileUtils myInstance) {
        super(v8Runtime);
        this.myInstance = myInstance;
    }

    //delegate the calls to the original instance 1 by 1
    public void doA() {
        this.myInstance.doA();
    }

    public void doB() {
        this.myInstance.doB();
    }

    public void add(Integer v1, Integer v2) {
        this.myInstance.add(v1, v2);
    }

}

And finally putting it all together:

 public static void main(String[] args) throws JavetException {
        V8Runtime v8 = V8Host.getNodeInstance().createV8Runtime();

        FileUtils fileUtils = new FileUtils("Hello ");

       //create converter
        EntityConverter converter = new EntityConverter(); 
     

      //convert the fileUtils instance to a V8ValueObject
        V8ValueObject val = (V8ValueObject) converter.toV8Value(v8, fileUtils); 
        v8.getGlobalObject().set("fileUtils", val); //inject it into the V8 context
        
        //call the methods inside the fileUtils instance from javascript
        v8.getExecutor("fileUtils.doA(); fileUtils.doB(); fileUtils.add(1,1);").execute();

    }

All of the above is working perfectly without any issues. So what is the problem? Well, my issue here is with the JavetCallbackReceiver (FileUtilsCallbacks ), I had to duplicate every function inside the FileUtils class to put it inside the JavetCallbackReceiver (FileUtilsCallbacks ). For classes with many many methods, it’s painful to have to duplicate each and every method. Is there a better way to achieve this without duplicating code and making sure it applies to all methods inside the classes?

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:9 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
newk5commented, Apr 6, 2021

Thanks! I tested it and I can confirm its working.

1reaction
caoccaocommented, Apr 4, 2021

So far that’s by design. There’s a note at https://github.com/caoccao/Javet/blob/main/docs/tutorial/manipulate_v8_function.rst.

Note: Primitive types must be in their object form in the method signature. E.g. boolean must be set to Boolean, int must be set to Integer, etc. Why? Because the converted value could be null which would cause JDK to complain with an exception.

I guess I was lazy while designing the core algorithm. Javet may handle this automatically, of course, the performance will be slightly impacted. I’ll think it over.

Read more comments on GitHub >

github_iconTop Results From Across the Web

V8 Function - Javet 2.0.2 documentation
This method is for binding a Java code based function in complete manual way. The caller is expected to do the following steps....
Read more >
Getting started with embedding V8 - V8 JavaScript engine
A context is an execution environment that allows separate, unrelated, JavaScript code to run in a single instance of V8. You must explicitly...
Read more >
Create V8 instance in node.js with low latency - Stack Overflow
Runtime creation and execution of the script are two separated tasks. Note, that the actual execution time for 10000 recursive calls is <...
Read more >
Constructor Dependency Injection in Spring - Baeldung
Quick and practical intro to Constructor based injection with Spring. ... To get started, we need to import the spring-context dependency in our...
Read more >
V8JavaClassInterceptor (V8 Adapter 1.21 API) - Javadoc.io
Represents a class that intercepts a Java object of a known type and translates it into a set of key-value pairs before injecting...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found