Interception

Javet provides @V8Property and @V8Function which allow Java applications to intercept JavaScript properties and functions in an automatic way as following.

  • Decorate a Java class with @V8Property or @V8Function.

  • Bind an instance of that Java class to a V8 value object.

  • Call the properties or functions of that V8 value object in JavaScript.

  • The calls are intercepted by that instance of the Java class.

Sample

@V8Property and @V8Function

@V8Property is for registering getters and setters. Javet is good at guessing the property name, e.g. getName => name, setValue => value.

@V8Function is for registering functions. By default, the Java function name is identical to the JavaScript function name, e.g. increaseAndGet => increaseAndGet, add => add.

If the default name is not suitable, please tell Javet which one to bind via @V8Property(name = "...") and @V8Function(name = "...").

public class TestInterception {
    private String name;
    private int value;

    @V8Property
    public String getName() {
        return name;
    }

    @V8Property
    public void setName(String name) {
        this.name = name;
    }

    @V8Property
    public int getValue() {
        return value;
    }

    @V8Property
    public void setValue(int value) {
        this.value = value;
    }

    @V8Function
    public int increaseAndGet() {
        return ++value;
    }

    @V8Function
    public int add(int delta) {
        value += delta;
        return value;
    }
}

Test

// Step 1: Create a V8 runtime from V8 host in try-with-resource.
try (V8Runtime v8Runtime = V8Host.getV8Instance().createV8Runtime()) {
    // Step 2: Register console.
    JavetStandardConsoleInterceptor javetStandardConsoleInterceptor = new JavetStandardConsoleInterceptor(v8Runtime);
    javetStandardConsoleInterceptor.register(v8Runtime.getGlobalObject());
    // Step 3: Create an interceptor.
    TestInterception testInterceptor = new TestInterception();
    // Step 4: Bind the interceptor to a variable.
    try (V8ValueObject v8ValueObject = v8Runtime.createV8ValueObject()) {
        v8Runtime.getGlobalObject().set("a", v8ValueObject);
        v8ValueObject.bind(testInterceptor);
    }

    // Test property name
    v8Runtime.getExecutor("console.log(`a.name is initially ${a.name}.`);").executeVoid(); // null
    // a.name setter => setName(String name)
    v8Runtime.getExecutor("a.name = 'Javet';").executeVoid();
    // name is changed
    System.out.println("Interceptor name is " + testInterceptor.getName() + "."); // Javet
    // a.name getter => getName()
    v8Runtime.getExecutor("console.log(`a.name is now ${a.name}.`);").executeVoid(); // Javet

    // Test property value
    v8Runtime.getExecutor("console.log(`a.value is initially ${a.value}.`);").executeVoid(); // 0
    // a.value setter => setValue(String value)
    v8Runtime.getExecutor("a.value = 123;").executeVoid();
    // value is changed
    System.out.println("Interceptor value is " + testInterceptor.getValue() + "."); // 123
    // a.value getter => getValue()
    v8Runtime.getExecutor("console.log(`a.value is now ${a.value}.`);").executeVoid(); // 123

    // Test functions
    v8Runtime.getExecutor("console.log(`a.increaseAndGet() is ${a.increaseAndGet()}.`);").executeVoid(); // 124
    v8Runtime.getExecutor("console.log(`a.add(76) is ${a.add(76)}.`);").executeVoid(); // 200

    // Step 5: Delete the interceptor.
    v8Runtime.getGlobalObject().delete("a");
    // Step 6: Unregister console.
    javetStandardConsoleInterceptor.unregister(v8Runtime.getGlobalObject());
    // Step 7: Notify V8 to perform GC. (Optional)
    v8Runtime.lowMemoryNotification();
}

Please refer to the source code for more detail.