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.