POLYGLOT WITH GRAALVM

OSCON 2019 / MICHAEL HUNGER / @MESIRII MICHAEL HUNGER Caretaker General, Neo4j Head of Neo4j Labs Disturber of the Peace Champion

(graphs)-[:ARE]->(everywhere)

Twitter & Medium: @mesirii WRITING ABOUT GRAAL SINCE 2014 WARNING: WHIRLWIND TOUR

DONT‘T READ THE CODE POLYGLOT? NOT EVERYONE IS A JAVA DEVELOPER! POLYGLOT? We have: • Isn‘t the JVM already polyglot? – Scala, Groovy, Kotlin, Clojure, Frege … – JRuby, Jython, … L

We want: • More languages, better performance WHY SHOULD I CARE? WHATS IN • Better JVM performance FOR ME? • Maintainable JIT compiler • Faster evolution of Java Lego box • With Truffle Language Runtime – Run JavaScript, Ruby, , Python, LLVM code efficiently on the JVM • With Substrate VM – Binaries for Language Runtimes – AOT compiled native images of your applications GETTING STARTED WHO READS THE INSTRUCTIONS HOW CAN • Dedicated GraalVM Download I USE IT? or using sdkman gu (graal-updater) Utility js/node, ruby, python, R runtimes native-image tool • Java 11 with command line flags -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler

https://www.graalvm.org/downloads/ JAVA 11 sdk use java 11.0.1-open java -Diterations=3 CountUppercase \ I‘m happy to be back in Portland, OR for OSCON 2019

-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler

-Dgraal.PrintCompilation=true sdk install java 19.1.0-grl GRAALVM gu install R python ruby native-image gu list Lego Instructions built ComponentId Version Component name ------graalvm 19.1.0 GraalVM Core R 19.1.0 FastR native-image 19.1.0 Native Image python 19.1.0 Graal.Python ruby 19.1.0 TruffleRuby java –version OpenJDK 64-Bit GraalVM CE 19.1.0 (build 25.212-b03-jvmci-20-b04, mixed mode) PYTHON # graalpython fac.py 2500 import sys def fac(n): if n==1: return 1 else: return fac(n-1)*n x = int(sys.argv[1]) print("Factorial for {} is {}" .format(x,fac(x))) LLVM #include BITCODE int main() { printf("Hello from GraalVM!\n"); return 0; } clang - -O1 -emit- hello.c lli hello.bc R --version:graalvm R PLOTTING data <- "https://raw.githubusercontent.com/selva86/datasets/master/proglanguages.csv" library(ggplot2) library(treemapify) proglangs <- read.csv(data) ggplot(proglangs, aes(area = value, fill = parent, label = id, subgroup = parent)) + geom_treemap() + geom_treemap_subgroup_border() + geom_treemap_subgroup_text() + geom_treemap_text() JS POLYGLOT node --version:graalvm node --jvm const BigInteger = Java.type("java.math.BigInteger") let a = new BigInteger("10") .add(new BigInteger("8") .multiply(new BigInteger("4"))) console.log(a.toString()) > 42 GRAAL COMPILER OPTIMIZING COMPILER IN JAVA VISION STATEMENT

Create an extensible, modular, dynamic, and aggressive compiler using object- oriented and reflective Java programming, a graph- based and visualizable intermediate representation, and Java snippets. —Thomas Würthinger GRAAL !? • JIT-Compiler implemented in Java !?! • Aggressively optimizing – e.g. inlining POJOS/DTOS – Inlining streams – Speeds up many typical Java/Scala programs • Uses compute graph for optimization • New compiler interface (JVMCI) GRAAL COMPILER OPTIMIZATIONS GRAALVM BOX OF JOY GRAAL ❤ TRUFFLE ❤ SUBSTRATE GraalVM is a high-performance, BIGGER ON embeddable, polyglotVirtual Machine for THE INSIDE running applications written in JavaScript, Python, Ruby, R, JVM-based languages like Java, Scala, Kotlin, and LLVM-based languages such as C and C++.

Additionally, GraalVM allows efficient interoperability between programming languages and compiling Java applications ahead-of-time into native executables for faster startup time and lower overhead. https://github.com/oracle/graal/releases

HISTORY • Collection of Research Projects – TruffleRuby / FastR • Maxine (Research) VM in Java • „A Joke?“ • „Java-on-Java“ John Rose – Project Metropolis • Power Combo: – Substrate-VM – Truffle – Graal Compiler – AOT Compilation • Oracle Labs Project • Versions GRAALVM – 19.1.1 (quarterly release) • Integrated – JVM 1.8.x – Node.js 10.x / ECMAScript 2019 – LLVM bitcode runtime • Supports – Truffle Runtime – Language Packs (via gu) – Native Image AOT • Editions – Community (GPL v2 w/ CP-Exception – Enterprise (faster, sandboxing, commercial support) – Oracle Database Engine NATIVE IMAGE MACHINE CODE BABY • Aggressive Ahead of time compilation (AOT) NATIVE • Extremely fast startup time IMAGE • Small binary executables for current OS • Class initialization during build • For FaaS, Commandline • Microservices: Micronaut, Helidon, , Spring (soon) • No classloading / class metadata • Limitations: – no reflection, no later classloading, no initializer dynamics – Slow build https://medium.com/graalvm/lightweight-cloud-native-java-applications- 35d56bc45673 TRUFFLE LANGUAGE RUNTIME TRUFFLE • Language Runtime • API & Type system • Implement language constructs • Annotated Java Methods – Tooling, Testing • Generic or specialized operations TRUFFLE + GRAAL TRUFFLE • Integrates with Graal Compiler GRAAL • Partial Evaluation • Optimize special cases based on steady state assumption • Deoptimize (trap) on failed assumptions SAMPLE LANGUAGE TRUFFLE EXAMPLE LANGUAGE TRUFFLE: ADDITION-NODE (SL) @NodeInfo(shortName = "+") public abstract class SLAdditionNode extends SLBinaryNode { @Fallback protected Object typeError(Object left, Object right) { throw SLException.typeError(this, left, right); }

@Specialization(rewriteOn = ArithmeticException.class) protected long add(long left, long right) { return Math.addExact(left, right); } @Specialization @TruffleBoundary protected SLBigNumber add(SLBigNumber left, SLBigNumber right) { return new SLBigNumber(left.getValue().add(right.getValue())); }

@Specialization(guards = "isString(left, right)") @TruffleBoundary protected String add(Object left, Object right) { return left.toString() + right.toString(); } protected boolean isString(Object a, Object b) {…} LANGUAGES BOX OF COLORS JAVASCRIP T • Main target language via graaljs • Replacement for Rhino/Nashorn • EcmaScript 2019 & Node.js (10.15.2) compat • 90% of 95k npm packages • Graaljs can run slowly w/o Graal • ScriptEngine support • org.graalvm.js:js/js-scriptengine https://www.graalvm.org/docs/reference-manual/languages/js/ GRAAL PYTHON • Early stage support Python 3.7 • Goal: „SciPy“ support • No python debugger, but GraalVMs

https://www.graalvm.org/docs/reference-manual/languages/python/ FAST-R • Compatible with GNU R (3.5.1) • Much faster than other R implementations • R/Rscript • Install packages (e.g. ggplot2, Shiny) • Minimal: graphics package • Compatibility checker • To o l s (debugger, profiler) • Java based Graphics

https://www.graalvm.org/docs/reference-manual/languages/r/ TRUFFLE RUBY • Initial research project • Quite complete coverage (2.6.2) incl. c-extensions • Parallel threads • Faster than MRI/JRuby (up to 31x) • Recent: fibers • Missing: suspend, continuation, fork

https://www.graalvm.org/docs/reference-manual/languages/ruby/ LLVM • LLVM 6.0.0 Bitcode • Via Sulong a LLVM implementation in Java via Truffle • Can use native libraries • lli to execute LLVM Bitcode • Sandbox in GraalVM Enterprise – sandbox libraries – virtualize syscalls – memory on managed heap https://medium.com/graalvm/safe-and-sandboxed-execution-of- native-code-f6096b35c360 POLYGLOT DO WHAT YOU WANT THURSDAY TRUFFLE • Based on Truffle Implementations of dynamic languages • Joint underlying API / Typesystem • Context – eval – bind – invoke • Source • Value.* docs.oracle.com/en/graalvm/enterprise/19/sdk/org/graalvm/polyglot/Context.html VALUE • The „Any“ type across languages – Scalar – List/Array – Host/Proxy Object w/ Members – Function/Invoke/Executable • Provides some semantics and conversions • Removes need for (de)serialization • Thread safety depends on language support docs.oracle.com/en/graalvm/enterprise/19/sdk/org/graalvm/polyglot/Value.html Polyglot support: --polyglot Eval CAPABILITIES • Polyglot.eval("python","21*2") • polyglot.eval(language="ruby", file="./my_ruby_file.rb") Export / Import • polyglot.import_value(„name“) • Polyglot.export_value(„name“,value) • ctx.getBindings.put/getMember(name,value) Object Access • Object.size() / call / invoke • Object[name] / • Via Value.* JAVA INTEROP JVM Support: --jvm Flag Import Java.import “java.util.UUID“ java.type("java.math.BigInteger") from java.util import ArrayList Helpers • isFunction / isObject / isSymbol / isNull / instance_of Access • allowAccess POLYGLOT EXAMPLES POLYGLOT EXAMPLE (1)

// gu install ruby python R // groovy PolyTest1.groovy @Grab("org.graalvm.sdk:graal-sdk:19.1.0") import org.graalvm.polyglot.* ctx = Context.newBuilder().allowAllAccess(true).build() ctx.eval("js", "print('Hello JavaScript!');") ctx.eval("R", "print('Hello R!');"); ctx.eval("ruby", "puts 'Hello Ruby!'"); ctx.eval("python", "print('Hello Python!')"); POLYGLOT EXAMPLE (PYTHON-JAVA) import java generic = java.type('org.testcontainers.containers.GenericContainer') container = generic('nginx') container.setExposedPorts([80]) container.start(); print('%s:%s' % (container.getContainerIpAddress(), container.getMappedPort(80)));

https://medium.com/graalvm/using-testcontainers-from-a-node-js-application-3aa2273bf3bb POLYGLOT EXAMPLE (C)

#include #include long request() { CURL *curl = curl_easy_init(); long response_code = -1;

if(curl) { CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "http://example.com"); res = curl_easy_perform(curl); if(res == CURLE_OK) { curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); } curl_easy_cleanup(curl); } return response_code; } POLYGLOT EXAMPLE (JAVA+C-LLVM)

// clang -c -O1 -emit-llvm use-curl.c && groovy CurlTest.groovy @Grab("org.graalvm.sdk:graal-sdk:1.9.10") import org.graalvm.polyglot.* polyglot = Context.newBuilder() .allowAllAccess(true) .option("llvm.libraries", "/usr/lib/libcurl.dylib") .build() source = Source .newBuilder("llvm", new File("use-curl.bc")) .build() result = polyglot.eval(source) responseValue = result.getMember("request").execute() responseCode = responseValue.asLong() print(responseCode) R+PYTHON pycode <- ‚ library polyglot PI=polyglot.import("PI") def area(radius): return 2*radius*PI area ' export("PI",pi) area <- eval.polyglot("python",pycode) print(area(5)) @Value(value = "classpath:plot.R") private Resource rSource; JAVA+R @Autowired private Function plotFunction;

@Bean Function getPlotFunction(@Autowired Context ctx) { Context ctx = Context.newBuilder().allowAllAccess(true).build(); Source source = Source.newBuilder("R", rSource.getURL()).build(); return ctx.eval(source).as(Function.class); }

@RequestMapping(value = "/load", produces = "image/svg+xml") public synchronized ResponseEntity load() { HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.set("Refresh", "1"); double load = getOperatingSystemMXBean().getSystemLoadAverage(); String svg = plotFunction.apply(new DataHolder(load));

return new ResponseEntity(svg,responseHeaders,HttpStatus.OK); } POLYGLOT EXAMPLE (JAVA+R) library(ggplot2) data <<- numeric(100)

function(dataHolder) { svg() data <<- c(data[2:100],dataHolder$value)

plot <- ggplot(data= data.frame(systemLoad=data, time =-99:0), aes(x=time, y=systemLoad, group=1)) + geom_line(color="orange") + expand_limits(x=0, y=0)

print(plot) svg.off() } POLYGLOT SHELL

Context context = Context.newBuilder().allowAllAccess(true).build();

Set languages = context.getEngine().getLanguages().keySet(); out.println("Shell for " + languages + ":");

String language = languages.iterator().next(); while (true) { out.print(language + "> "); String line = input.readLine(); if (line == null) break; else if (languages.contains(line)) language = line; else { Source source = Source.newBuilder(language, line, "") .interactive(true).buildLiteral(); context.eval(source); } } Print GraalVM version information and exit. --version:graalvm POLYGLOT Print GraalVM version information and continue execution. --show-version:graalvm OPTIONS Start with Debugger --inspect

Run using the native launcher with limited Java access --native Run on the JVM with Java access. --jvm Run with all other guest languages accessible. --polyglot Pass options to the host VM --vm.[option]

Options for all installed languages, tools, host VM --help:languages,tools,vm,expert Internal options for debugging language impl and tools. --help:internal TOOLING MAKING OUR LIFES EASIER DEBUGGER CUSTOM JVISUALVM IDEAL VISUALIZER EXTENDING DATABASES MORE POWER TO THE MACHINE WHY? • Allow “programmatic“ extensions • Move processing to data, avoid network transfers • Not just procedures in – PL/SQL – Java • Reuse existing code – public packages (e.g. validators, data science, visualization) – domain specific code • Because you can! EXTENDING • „Multilingual Engine“ DATABASES – native library integration • Available from Oracle Labs • For Oracle and MySQL • Deploy custom functions in JavaScript, R, Python with dbjs

dbjs deploy -u -p -c localhost:1521/ORCLCDB

• Create statically and call from SQL https://www.graalvm.org/docs/examples/mle-oracle/ SQL FUNCTION IN JAVASCRIPT

CREATE OR REPLACE JAVASCRIPT SOURCE NAMED "hello.js" AS module.exports.greet = function(a) { return "Hello " + a; };

CREATE OR REPLACE FUNCTION greet(a IN VARCHAR2) RETURN VARCHAR2 AS LANGUAGE JAVASCRIPT NAME 'hello.js.greet(a string) return string‘;

SELECT greet('GraalVM') FROM DUAL;

Hello GraalVM NEO4J (GRAPHS)- [:ARE]- >(EVERYWHERE) NEO4J IN @Neo4j is an open-source native A TWEET graph database designed to store, manage and query highly connected data efficiently with the Cypher Query Language. It runs transactional and analytic workloads, supports visualization and is extendable with custom functions.

neo4j.com/developer NEO4J • Runs on the JVM • Full Stack database • Declarative Graph Query Language • Binary Protocol • Drivers for most languages • Visual Browser • Integrations like GraphQL, Kafka • Custom Procedures and Functions OSCON CONFERENCE GRAPH

https://github.com/neo4j-examples/oscon-graph TALK RECOMMENDATION

// top 10 talks (that I‘ve not see) // that peers who like the same talks I did also liked MATCH (me:User {name:“Michael“}) -[:FAVORITED]->(:Event)<-[:FAVORITED]-(peer:User) -[:FAVORITED]->(reco:Event) WHERE NOT (me)-[:FAVORITED]->(reco) RETURN reco.name, count(*) AS freq ORDER BY freq DESC LIMIT 10 TALK RECOMMENDATION NEO4J CUSTOM PROCEDURES PUT YOUR SMARTS IN THE DATABASE NEO4J CUSTOM • Annotated Java Methods FUNCTIONS • Loaded at startup • Computation or Aggregation with Functions • Data processing & streaming with Procedures • Accessible from Cypher Query Language CUSTOM FUNCTION @UserFunction @Description(„Generates an UUID“) public String uuid() { return UUID.randomUUID().toString(); }

CREATE (:Event {id: uuid(), name:“Graph Algorithms“ }); POLYGLOT FUNCTIONS USING TRUFFLE & GRAALVM POLYGLOT • Using Truffle API FUNCTIONS • Declare + invoke function with params, or • Execute code with bindings

1. Use Context to run polyglot code 2. Use Cypher to declare dynamic language functions 3. Install language files from directory 4. Store code in db to restore at restart / in cluster EVAL CODE AD HOC EXECUTION EVAL CODE PROCEDURE

@Procedure(„scripts.execute“) public Object executeCode(String lang, String code, Map props) { Context ctx = Context.newBuilder().allowAllAccess(true).build(); Bindings bindings = ctx.getBindings(); props.forEach(bindings::putMember); bindings.putMember("label", ctx.eval("js", "s => org.neo4j.graphdb.Label.label(s)"));

bindings.putMember("db", graphDatabase); return ctx.eval(lang, code).asHostObject(); } EVAL CODE EVAL CODE

CALL scripts.execute(' Java.import "org.neo4j.graphdb.Label db = Polyglot.import("db") props = db.findNode(Label.label("Event"), "name",“OSCON") .getAllProperties().entrySet().toArray() Polyglot.as_enumerable(props) .map{|e| "#{e.getKey()} -> #{e.getValue()}"} .join(",") ',{},'ruby') REGISTER FUNCTIONS DYNAMICALLY INSTALL FUNCTION (2)

Context ctx = Context.newBuilder().allowAllAccess(true).build()

@Procedure(„scripts.register“) public void registerFunction(String lang, String name, String code) { Value function = ctx.eval(lang, code); ctx.getBindings(lang) .putMember(name, function); } INVOKE EXISTING FUNCTION (2)

@UserFunction(„scripts.run“) public Object executeFun(String lang, String name, Object…args) { return ctx.getBindings(lang) .getMember(name) .execute(params).asHostObject(); } REGISTER NATIVE FUNCTION (2)

@UserFunction public void registerFunction(String code, String name, String lang) { ctx.getBindings(lang).putMember(name, ctx.eval(code)); procedures.register(new BasicUserFunction(signature(name)) { @Override public AnyValue apply(org.neo4j.proc.Context c, AnyValue[] input) { return resultOf(context.getBindings(lang).getMember(name) .execute(paramsFor(input))); } }); } SCRIPT DIRECTORY AUTOLOAD SCRIPT FILES SOURCE WATCHER watchService = FileSystems.getDefault().newWatchService(); pathName = new File(scriptsDir, target.getDirName()) pathToWatch = getTargetPath(pathName); pathToWatch.register(watchService, ENTRY_CREATE,ENTRY_DELETE, ENTRY_MODIFY); public void run() { WatchKey watchKey; while ((watchKey = watchService.take()) != null) { for (WatchEvent event : watchKey.pollEvents()) updateScript(event.kind(), event.context()); } } IMPLEMENT SCRIPT FUNCTION (1) public class ScriptFunction implements CallableUserFunction { private final UserFunctionSignature signature; private final String name; private final String sourceCode; private transient volatile Source source;

public ScriptFunction(String language, String name, String sourceCode) { this.name = name; this.sourceCode = sourceCode; this.source = Source.newBuilder(this.language, this.sourceCode, this.name).build(); this.signature = generateSignature(); }

private UserFunctionSignature generateSignature() { final QualifiedName qualifiedName = new QualifiedName(Arrays.asList("scripts", "fn"), name); final List input = IntStream.range(0, numberOfArguments); .mapToObj(i -> FieldSignature.inputField("p" + i, NTAny, DefaultParameterValue.nullValue(NTAny))) .collect(toList()); return new UserFunctionSignature(qualifiedName, input, NTAny, null, new String[0], null, false); } IMPLEMENT SCRIPT FUNCTION (2)

@Override public AnyValue apply(Context ctx, AnyValue[] input) throws ProcedureException {

try (org.graalvm.polyglot.Context context = PolyglotContext.newInstance()) { GraphDatabaseAPI db = ctx.get(Context.DATABASE_API); Log log = ctx.get(Context.DEPENDENCY_RESOLVER).resolveDependency(LogService.class) .getUserLog(ScriptFunction.class); Value bindings = context.getPolyglotBindings(); bindings.putMember("db", db); bindings.putMember("log", log);

Value langBindings = context.getBindings(language); langBindings.putMember(name, context.eval(source)); return resultFor(langBindings.getMember(name).invoke(input)); } } IMPLEMENT SCRIPT FUNCTION (3) private AnyValue resultFor(Value result) { if (result.isNull()) { return null; } if (result.isNumber()) { return ValueUtils.asAnyValue(result.asDouble()); } if (result.isBoolean()) { return ValueUtils.asAnyValue(result.asBoolean()); } if (result.isHostObject()) { return ValueUtils.asAnyValue(result.asHostObject()); } return ValueUtils.asAnyValue(result.asString()); } NEXT STEPS NEXT • Better bi-directional conversions STEPS • Handle Isolation / Threading • Integrate with Python / R ~ – Graph processing / algorithms – Data Science / ML / graph_net – Large Scale Plotting – Investigate GraphBlas (C-Library) • Allow installing packages (e.g npm) • Move into Neo4j Labs library THE GOOD AND EXCELLENT THE GOOD • It works! – All the languages – JVM Interop – Polyglot Interop • Continuous improvements by the Oracle Team • Lots of great docs & articles • Adding those database extensions was really easy THE BAD AND UGLY THE BAD • Python support still preliminary • To o many polyglot indirections are costly • Interop a bit kludgy • Error messages not that helpful • No automatic mapping of collection types (map/hash/dict) and (list/collection) – only for arrays FIND MORE ALL THE THINGS • graalvm.org MORE • graalvm.org/docs • medium.com/graalvm ~/graalvm-ten-things-12d9111f307d • github.com/oracle/graal • graalvm.org/docs/reference-manual/compatibility – package compat checker • youtu.be/a-XEZobXspo – 3hr deep dive

• neo4j.com/developer • r.neo4j.com/algo-book • github.com/neo4j-contrib/neo4j-script-procedures

Twitter & Medium: @mesirii October 10 8am NYC, 1pm London, 530pm Mumbai

All-new SDN-RX: Reactive Spring Data Neo4j Keynote and Announcements Gerrit Meier, Software Engineer, Neo4j Emil Eifrem, CEO and Co-Creator of Neo4j Graph Embeddings Alicia Frame, Senior Data Scientist, Neo4j Graphs in AI and ML Alicia Frame, Senior Data Scientist, Neo4j Graph Modeling Tips and Tricks Jake Graham, Lead Product Manager for AI and Graph Analytics Max De Marzi, Field Engineer and Graph Expert, Neo4j

Intro to Neo4j for Developers APOC Pearls - The best Tips and Tricks Jennifer Reif, Developer Relations Engineer, Neo4j Michael Hunger, Director of Neo4j Labs

Neo4j Bolt Driver Architecture Now and in The Future Visualizing Graph Data in JavaScript Nigel Small, Tech Lead for Neo4j Drivers team Will Lyon, Software Engineer, Neo4j Labs

GRANDstack: Graphs ALL the Way Down Creating a Data Marvel (Comics) with Spring and Neo4j William Lyon, Software Engineer, Neo4j Labs Jennifer Reif, Developer Relations Engineer, Neo4j PLEASE RATE MY SESSION THANK YOU! QUESTIONS IN HALLWAY