Paso de variables de Java a JavaScript: de LiveConnect a GraalJS
Veinte años de puentes entre dos lenguajes: JSObject, Rhino, Nashorn y el presente polyglota de GraalVM. La historia de una frontera que se movió cuatro veces.
«¿Cómo paso esta variable de Java a JavaScript?» fue una pregunta real de 2011, con applets por medio, y su respuesta ha cambiado de dirección cuatro veces en veinte años: LiveConnect, Rhino, Nashorn, GraalVM. Ninguna tecnología de esta lista murió por no funcionar; murieron por evolución de la plataforma. Repasar esa frontera es repasar la historia reciente de la JVM, y las lecciones siguen vigentes para cualquier integración entre lenguajes.
La era de los applets: JSObject y LiveConnect
Cuando el código Java vivía dentro del navegador, la comunicación era de doble sentido: JavaScript llamaba métodos públicos del applet y el applet manipulaba la página mediante JSObject:
// dentro de un applet, anno 2011
JSObject ventana = JSObject.getWindow(this);
ventana.setMember("totalCarrito", 240); // variable hacia JS
Object r = ventana.eval("validarCarrito()");
ventana.call("mostrarAviso", new Object[]{"Pago procesado"});Ese era el «paso de variables» literal del título original. Los applets fueron retirados del JDK en 11 (2018) y del navegador mucho antes, pero el patrón de esa API (getMember, setMember, call, eval) sobrevive casi intacto en GraalVM: las fronteras entre lenguajes envejecen mejor que las tecnologías que las unen.
Nashorn: JavaScript como ciudadano del JDK
Java 8 (2014) trajo Nashorn, un motor JavaScript completo dentro del JDK. Pasar variables se volvió trivial con ScriptEngine:
import javax.script.*;
ScriptEngine motor = new ScriptEngineManager()
.getEngineByName("nashorn");
motor.put("unidades", 3);
motor.put("precio", 79.90);
Object total = motor.eval("unidades * precio * 1.21");
System.out.println(total); // 290.27...
// bindings aislados para cada ejecucion
Bindings b = motor.createBindings();
b.put("nombre", "Ada");
Object saludo = motor.eval(
"var s = 'Hola, ' + nombre; s.toUpperCase()", b);put publica la variable en el motor y eval la consume; los Bindings aislados evitan que ejecuciones concurrentes pisen las variables unas a otras: el equivalente exacto de un ámbito local. Nashorn fue deprecado en 11 y retirado en 15: el ritmo de ECMAScript superaba al de un motor mantenido dentro del JDK. La lección institucional: los lenguajes invitados exigen ritmo de liberación propio, algo que la JVM aprendió y resolvió de otra manera.
El presente: GraalVM y el poliglotismo
import org.graalvm.polyglot.*;
try (Context ctx = Context.create()) {
Value bindings = ctx.getBindings("js");
bindings.putMember("unidades", 3);
bindings.putMember("precio", 79.90);
Value total = ctx.eval("js",
"(unidades * precio * 1.21).toFixed(2)");
System.out.println(total.asString()); // "290.28"
}En GraalVM, el Context delimita una máquina virtual invitada completa: dentro puede vivir JavaScript, Python, Ruby o WebAssembly, y las fronteras se cruzan con putMember/getMember: nombres heredados casi literalmente de JSObject. Value es el tipo universal de la otra orilla: se pregunta (isNumber, hasMembers) antes de convertir (asDouble, asString), porque el sistema de tipos del otro lado nunca es el tuyo.
Reglas de la frontera, válidas en todas las eras
- Copia, no referencia: los valores cruzan como copias o como referencias opacas; asumir semántica compartida de objetos es la fuente número uno de bugs en cualquier puente.
- Tipos en la frontera: números, cadenas y booleanos cruzan bien; fechas, colecciones y objetos complejos necesitan conversión explícita o mappings declarados.
- Errores traducidos: una excepción del lado invitado llega como
PolyglotException(oScriptExceptionen Nashorn): captura la envoltura y pregunta por la causa. - Cuándo no hacerlo: si solo necesitas calcular un importe, escribe el
doubleen Java. El puente se justifica cuando el otro lado tiene un ecosistema que el tuyo no: librerías npm, reglas de negocio ya escritas, fórmulas del usuario.
Los puentes entre lenguajes se construyen con la mejor tecnología del día y se jubilan por la siguiente. Diseña la frontera para poder cambiar el puente sin cambiar las orillas.
De la misma época tecnológica quedan otras dos piezas visitables: la reflexión, que todos estos motores usaban por debajo para tocar objetos Java, y el juego de la serpiente, que vio nacer y morir el mundo de los applets en primera fila.