Polyglot Programming using GraalVM in Kotlin

PriyanshPriyansh
4 min read

The meaning of the word polyglot is knowing or using several languages. In this article, we'll see what that means from a programming perspective and create a hello world program demonstrating the concept.

Git repository for this demo: https://github.com/priyansh-07/polyglotKotlinJs

Polyglot Program

Simply put, a polyglot program is a program which consists of multiple programming languages and is executed in a single VM while also passing values to each other. This is done so that we can leverage the advantages of multiple languages in a single application.

What is GraalVM?

GraalVM is a JDK which optimizes the performance of JVM languages and provides runtimes for other languages such JavaScript, Python, Ruby, R and WASM at the time of writing this article.

Java's Polyglot API for GraalVM

This article uses GraalVM's Polyglot API to demonstrate how we can execute JavaScript and Java code on the same VM.

GraalVM provides an extensive API in java for Polyglot execution of code, but this article will cover only a couple of them.

  • Context: A Context is responsible to execute guest language code and maintain the runtime environment for all the installed & permitted languages.

  • Value: When a Context executes a guest language code, it returns the value returned by that code as a Value. This class has some methods to determine the datatypes of the value returned and to cast those values in the host language data types.

Pre-requisites

In this demo, we will be using certain tools but feel free to use their alternatives as a matter of personal preference.

  • Language: Kotlin

  • IDE: Intellij

  • Build tool: Gradle

  • Test Framework: Spock

Setup

  1. Create a new Gradle project in Intellij and select Kotlin/JVM

  2. Name the project

  3. Set up the build.gradle

    • Add a plugin for groovy for tests

    • Add dependencies for graalvm js, spock and groovy

    plugins {
      id 'org.jetbrains.kotlin.jvm' version '1.6.10'
      id 'groovy'
    }

    group 'org.example'
    version '1.0-SNAPSHOT'

    repositories {
      mavenCentral()
    }

    dependencies {
      implementation group: 'org.graalvm.js', name: 'js', version: '22.3.1'
      testImplementation 'org.spockframework:spock-core:2.3-groovy-4.0'
      implementation 'org.apache.groovy:groovy:4.0.11'
    }

    test {
      useJUnitPlatform()
    }

Write a failing test case

Following the practices described by Uncle Bob, we will be following a test-driven approach and the first thing to do is to write a failing test case.

package org.example.polyglot

import org.graalvm.polyglot.Value
import spock.lang.Specification

class JsPolyglotSpec extends Specification {

  def "test the value returned by a js function"() {
    given:
    String jsFunction = """
      |function square(x) {
      |  return x * x; 
      |}""".stripMargin()
    String jsFunctionCall = "square(2)"

    when:
    new JsPolyglot.execute(jsFunction)
    Value value = JsPolyglot.execute(jsFunctionCall)

    then:
    value.isNumber()
    value.asInt() == 4
  }
}
  • First, we define a JavaScript function ,jsFunction, and a call to that function, jsFunctionCall, in the given block.

  • In the when block, we call API to execute the JavaScript code, JsPolyglot.execute(), and execute the jsFunction to put it into the environment so that we can execute jsFunctionCall later.

  • In the then block, we assert that the Value is a Number and it is 4.

    Note: This should not compile as we have not created the JsPolyglot class yet.

Write the API that passes the test

package org.example.polyglot

import org.graalvm.polyglot.Context
import org.graalvm.polyglot.Value

class JsPolyglot {

  companion object {
    private val context: Context = Context
      .newBuilder("js")
      .build()
  }

  fun execute(jsCode: String): Value {
    return context.eval("js", jsCode)
  }
}
  1. Create a Kotlin class JsPolyglot in the same package in the main directory.

  2. Create a Context object in the companion object of the class so that the context retains the function definition in the environment. We pass "js", which is a unique identifier for JavaScript in GraalVM, as an argument to the newBuilder() to tell GraalVM that only JavaScript is permitted.

  3. In the execute method, we call the context.eval() to execute the JavaScript code and return its Value. This takes a language identifier and the code to be executed as parameters.

That's it, the test should pass, feel free to fiddle around with the parameter we pass to the square function.

Conclusion

Polyglot programming is an amazing concept which can serve as a tool to creatively solve problems and GraalVM's Java API is a powerful tool that makes it easy to implement.

24
Subscribe to my newsletter

Read articles from Priyansh directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Priyansh
Priyansh

Software developer with a passion for computer science and clean code. I love to design robust and extensible software and share knowledge.