Javet Documentation

repository·main·Indexed 21 days ago

https://github.com/caoccao/javet

A high-performance library for embedding Node.js and the V8 engine within Java applications to enable JavaScript/Java interoperability. Documentation covers building V8 and Node.js from scratch, compiling the Javet JNI library for desktop and Android, Docker-based build processes, environment requirements, and debugging via Chrome Developer Tools using the CDT Shell.

Tokens
84.4K
Snippets
183
Records
279
Agent score
76%

What's inside Javet

  1. Overview of Javet

    main

    Javet (JAVa + V + EighT) is a library designed for embedding Node.js and V8 in Java. It provides high-performance JavaScript and Java interoperability, allowing developers to run JavaScript code within a JVM environment.

    Key capabilities include:

    • Dynamic switching between Node.js and V8 modes.
    • V8 API exposure directly in the JVM.
    • Support for Native BigInt and Date.
    • Integration with tools like Chrome DevTools for live debugging and swc4j for AST analysis and TypeScript transformation.
    • Support for various architectures including Android, Linux, MacOS, and Windows.
  2. What is JavetProxyConverter and when to use it

    main

    JavetProxyConverter allows you to inject arbitrary Java objects into the V8 runtime and call their APIs directly from JavaScript. This provides a developer experience similar to GraalJS, effectively opening the JVM to V8.

    Warning: Because this converter exposes almost the entire JVM to the V8 runtime, it is considered dangerous. It is not enabled by default to prevent end-users from accessing sensitive Java internals. Only enable it if you trust the JavaScript code being executed.

  3. V8 Feature Flags and Platform Support

    main

    Javet builds include specific V8 features. Note that official releases are always release builds (Debug is Off) and the V8 snapshot is embedded directly into the monolithic library (External Startup Data is Off).

    Custom libc++

    • V8 Mode: Uses Chromium's custom libc++ on Linux and Windows. On macOS, it uses the system libc++. On Android, it uses the NDK's c++_static.
    • Node.js Mode: Uses the system's standard C++ library on all platforms.

    Pointer Compression

    • Enabled for V8 mode on all 64-bit platforms (Linux, macOS, Windows, Android arm64/x86_64) since v5.0.8.
    • Disabled on 32-bit platforms and in Node.js mode.

    Sandbox

    • Enabled for V8 mode on Linux, Windows, and macOS (requires V8's hermetic libc++).
    • Disabled on Android (uses system libc++) and in Node.js mode.

    i18n

    • Both i18n and non-i18n builds are produced for every platform.
    • Node.js mode uses --with-intl=full-icu (i18n) or --without-intl (non-i18n).
  4. Compare Node.js Mode vs. V8 Mode

    main

    Javet allows both Node.js and V8 modes to coexist in a single JVM. They are lazily loaded in dedicated custom classloaders and do not interfere with each other.

    FeatureNode.js ModeV8 Mode
    Lazy LoadableYesYes
    CustomizationHighHigh
    Node.js EcosystemCompleteNo
    SecurityLowHigh
    UnloadYesYes
    V8 EcosystemCompleteComplete
    V8 VersionLowHigh

    Key Capabilities in both modes:

    • Virtualization: Node.js modules (e.g., console, fs, HTTP) can be virtualized.
    • Zero-copy: JVM can share byte buffers with both Node.js and V8 for zero-copy performance.
    • Multi-threading: Multiple Java threads can host multiple runtime instances sharing V8 objects.
    • Beyond Node.js: Explicit await() allows fine-grained control over async execution.
  5. Bypass built-in converters for fine-grained control

    main
    While built-in converters are convenient for bi-directional conversion of arbitrary objects, you can bypass them if you need specific control. Javet APIs allow you to pass and return V8Value objects directly. When you use these low-level APIs, the built-in converter is ignored, allowing you to manually choose which converter (e.g., JavetObjectConverter vs JavetProxyConverter) to apply to specific parts of your data.
  6. Use JavetBridgeConverter to proxy all Java types

    main

    Use JavetBridgeConverter when you want to ensure that Java types (including primitives like Integer, Long, String, and arrays like int[], Object[]) are always represented as proxies in JavaScript rather than being converted to native JavaScript objects.

    Unlike JavetProxyConverter, which skips proxies for certain types, JavetBridgeConverter:

    1. Enables all 6 built-in proxy plugins by default (JavetProxyPluginMap, JavetProxyPluginSet, JavetProxyPluginList, JavetProxyPluginArray, JavetProxyPluginClass, and JavetProxyPluginDefault).
    2. Always creates a proxy for non-primitive, non-null Java objects, preventing accidental conversion to native JS objects.
    // Step 1: Create an instance of JavetBridgeConverter.
    JavetBridgeConverter javetBridgeConverter = new JavetBridgeConverter();
    // Step 2: Set the V8Runtime converter to JavetBridgeConverter.
    v8Runtime.setConverter(javetBridgeConverter);
  7. How Javet handles argument count mismatches

    main

    When a JavaScript function call does not match the expected number of parameters in the corresponding Java callback, Javet follows two rules to mimic JavaScript behavior:

    1. Redundant parameters: If the JavaScript call provides more arguments than the Java method defines, the extra arguments are dropped.
    2. Absent parameters: If the JavaScript call provides fewer arguments than the Java method defines, the missing parameters are filled with their default values.
  8. Manage thread synchronization with V8Locker

    main

    Javet provides two modes for managing thread access to a V8Runtime, allowing you to simplify or optimize your concurrency model compared to J2V8.

    • Implicit Mode: Automatically handles synchronization. This allows multiple threads to share the same V8Runtime without manual acquire() and release() calls, reducing the risk of runtime exceptions and mental overhead.
    • Explicit Mode: Designed for performance-sensitive scenarios where manual control over locking is required.

    Use Implicit Mode by default to eliminate the need for V8Locker in your codebase.

  9. How the Chrome DevTools handshake works

    main

    Javet implements a node-flavored remote target protocol to communicate with Chrome DevTools.

    Connection Details:

    • Default Port: 9229.
    • Target Discovery: CDT queries http://127.0.0.1:9229/json/list to find remote targets. Javet identifies itself as a node-flavored target via this endpoint.
    • WebSocket Address: ws://127.0.0.1:9229/javet.
    • Protocol Version: Javet currently uses v1.3 of the Chrome DevTools Protocol.

    Handshake Process: CDT connects via WebSocket and sends a sequence of 8 messages to complete the handshake. The final message, Debugger.runIfWaitingForDebugger, signals the completion of the handshake. In Javet, this completion is exposed as a callback in the IV8InspectorListener interface.

    // Sequence of handshake messages exchanged between Chrome and Javet
    Chrome: {"id":1,"method":"Runtime.enable","params":{}}
    Javet Notification: {"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"origin":"","name":"Javet Inspector 00000000","uniqueId":"00000000.00000000"}}}
    Javet Response: {"id":1,"result":{}}
    Chrome: {"id":2,"method":"Debugger.enable","params":{"maxScriptsCacheSize":100000000}}
    Javet Response: {"id":2,"result":{"debuggerId":"00000000.00000000"}}
    Chrome: {"id":3,"method":"Debugger.setPauseOnExceptions","params":{"state":"none"}}
    Javet Response: {"id":3,"result":{}}
    Chrome: {"id":4,"method":"Debugger.setAsyncCallStackDepth","params":{"maxDepth":32}}
    Javet Response: {"id":4,"result":{}}
    Chrome: {"id":5,"method":"Profiler.enable","params":{}}
    Javet Response: {"id":5,"result":{}}
    Chrome: {"id":6,"method":"Runtime.getIsolateId","params":{}}
    Javet Response: {"id":6,"result":{"id":"00000000"}}
    Chrome: {"id":7,"method":"Debugger.setBlackboxPatterns","params":{"patterns":[]}}
    Javet Response: {"id":7,"result":{}}
    Chrome: {"id":8,"method":"Debugger.runIfWaitingForDebugger","params":{}}
    Javet Response: {"id":8,"result":{}}
  10. How Proxy Plugins work in Javet

    main

    Proxy plugins are used by the proxy converter and its derived converters to manage how Java types are mapped to JavaScript. They provide three main capabilities:

    1. Granular Control: Conversion for specific Java types can be toggled on or off.
    2. Type Simulation: Plugins can extend Java types to simulate native JavaScript types (e.g., mapping a Java List to a JS Array).
    3. Hybrid Access: Both public Java fields/methods and simulated JavaScript properties/methods remain accessible.

    Important Note on Ordering: JavetProxyPluginDefault is a catch-all plugin that matches any non-null class. It must be placed last in the plugin list to ensure more specific plugins (like JavetProxyPluginList) are checked first.

  11. Implement Java interfaces dynamically with JavaScript

    main

    Using JavetProxyConverter, you can implement Java interfaces using JavaScript functions. When a Java constructor or method expects an interface, Javet can dynamically implement that interface by injecting a JavaScript function.

    For example, if a Java Thread constructor requires a Runnable interface, you can pass a JavaScript arrow function () => { ... } directly.

    // JavaScript function passed to a Java constructor requiring an interface
    Thread thread = v8Runtime.getExecutor(
            "let count = 0;" +
            "let thread = new java.lang.Thread(() => { count++; });" +
            "thread.start();" +
            "thread; "
    ).executeObject();
    thread.join();