Friends of OpenJDK

Fuzion: A New Language For The OpenJDK Unifying Java's Concepts

D.openjdk
Fridtjof Siebert
<p>Fuzion is a modern general purpose programming language that unifies concepts found in structured, functional and object-oriented programming languages into the concept of a Fuzion feature. It combines a powerful syntax and safety features based on the design-by-contract principle with a simple intermediate representation that enables powerful optimizing compilers and static analysis tools to verify correctness aspects.</p> <p>This talk will explain how Java's concepts such as classes, interfaces, methods, constructors, packages, etc. are mapped to the single concept of a Fuzion feature. The fzjava tool will be explained that provides Fuzion interfaces to Java libraries. Finally, the Fuzion interpreter and a (planned) Java byte-code back-end are presented.</p>
Introduction Fuzion is a modern general purpose programming language that unifies concepts found in structured, functional and object-oriented programming languages into the concept of a Fuzion feature. It combines a powerful syntax and safety features based on the design-by-contract principle with a simple intermediate representation that enables powerful optimizing compilers and static analysis tools to verify correctness aspects. Fuzion was influenced by many other languages including Java, Python, Eiffel, Rust, Go, Lua, Kotlin, C#, F#, Nim, Julia, Clojure, C/C++, and many more. The goal of Fuzion is to define a language that has the expressive power present in these languages and allow high-performance implementations and powerful analysis tools. Furthermore, Fuzion addresses requirements for safety-critical applications by adding support for contracts that enable formal specification and enable detailed control over run-time checks. Many current programming language are getting more and more overloaded with new concepts and syntax to solve particular development or performance issues. Languages like Java/C# provide classes, interfaces, methods, packages, anonymous inner classes, local variables, fields, closures, etc. And these languages are currently further extended by the introductions of records/structs, value types, etc. The possibility of nesting these different concepts results in complexity for the developer and the tools (compilers, VMs) that process and execute the code. For example, the possibility to access a local variable as part of the closure of a lambda expression may result in the compiler allocating heap space to hold the contents of that local variable. Hence, the developer has lost control over the allocation decisions made by the compiler. In Fuzion, the concepts of classes, interfaces, methods, packages, fields and local variables are unified in the concept of a Fuzion feature. The decision where to allocate the memory associated with a feature (on the heap, the stack or in a register) is left to the compiler just as well as the decision if dynamic type information is needed. The developer is left with the single concept of a feature, the language implementation takes care of all the rest. Fuzion Feature Declarations A Fuzion feature has a name, similar to the name of a class or a function. The main operation that can be performed on a feature is a feature call. The constituents of a feature declaration are as follows: Formal Arguments Features may have a list of formal arguments, which are themselves features implemented as fields. On a call to a feature with formal arguments, actual arguments have to be provided to the call, unless the list of formal arguments is empty. Feature Result The result of a feature call is an instance of the feature. Alternatively, a feature may declare a different result type, then it must return a value of that type on a call. Closures Features are nested, i.e., every feature is declared within the context of an outer feature. The only exception is the universe, which is the outermost feature in Fuzion. A feature can access features declared in its outer feature or, recursively, any outer feature of these outer features. This means, a feature declaration also defines a closure of the feature and its context. When calling a feature f1 declared as an inner feature of f2, the call must include a target value which is the result of a call to f2, e.g., f2.f1. Generics Features may have generic type parameters. E.g. a feature declaration may leave the actual type used within that feature open and to be defined by the user of the feature. The list of generic type parameters may be open, i.e., the number of actual generic type parameters is not fixed at feature declaration. This turns out to be useful in the declaration of choice types and functions as explained below. Inheritance Fuzion features can inherit from one or several other features. When inheriting from an existing features, all inner features of the parent automatically become inner features of the heir feature. It is possible to redefine inherited features. In particular, when inheriting from a feature with abstract inner features, one can implement the inherited abstract features. A redefinition of an inherited feature may implement an inherited feature as a routine or as a field. An inherited feature that is implemented as a field, however, cannot be redefined as something else since fields might be mutable. Inheritance may result in conflicts. An example would be two features with the same name that are inherited from two different parents. In this case, the heir must resolve the conflict either by redefining the inherited features and providing a new implementation or by renaming the inherited features resulting in two inner features in the heir feature. Inheritance and redefinition in Fuzion does not require dynamic binding. By default, the types defined by features are value types and no run-time overhead for dynamic binding is imposed by inheritance. A Contract A feature may declare a contract that specifies what the features does and under which conditions the feature may be called. An implementation Features must have one of the following implementations a routine is a feature implementation with code that is executed on a call a field is a memory slot that stores a value and whose contents are returned on a call an abstract feature has no implementation and cannot be called directly, but can be implemented by heir features an intrinsic feature is a low-level feature implemented by the compiler or run-time system, e.g., the infix + operator to add two 32-bit integer values may be an intrinsic operation. A feature implemented as a routine can contain inner feature declarations. Feature examples Here is an example that declares a feature point that functions similar to a struct or record in other languages: point(x, y i32) is # empty p1 := point 3 4 say "p1.x is {p1.x}" # will print "p1.x is 3" say "p1.y is {p1.y}" # will print "p1.y is 4" The next example shows a feature base that provides an inner feature plus that adds its argument to the value passed to the enclosing base: base(v i32) is plus(w i32) =&gt; v + w b1 := base 30 b2 := base 100 say (b1.plus 23) # will print "53" say (b2.plus 23) # will print "123" Fuzion FZJava Tool Fuzion provides a tool fzjava that takes a Java module file and converts it into Fuzion features. In the spirit of Fuzion, Java's packages, classes, interfaces, methods, constructors, static methods and fields are all converted into Fuzion features. Java methods that may throw an exception are converted into features resulting in a choice type that is either the exception type or the result type of that method. A few intrinsic functions in the Java interpreter back-end use Java's reflection API to access the corresponding Java code. Basic approach The FZJava tool converts each Java class into three Fuzion Features. Say you have a Java class as follows package x.y; class MyClass { MyClass(String arg) { ... } void myMethod() { } void myStaticMethod() { } } This will be converted into a Fuzion feature x.y.MyClass that may not be instantiated directly: Java.x.y.MyClass(redef forbidden void) ref : Java.java.lang.Object(forbidden), fuzion.java.JavaObject(forbidden) is unit myMethod is ... This feature defines a Fuzion type x.y.MyClass and contains wrappers for the instance methods. It is, however, not permitted to directly create instances of this feature, which is ensured the forbidden parameter of type void (which makes this feature 'absurd', it cannot be called directly since void values cannot be created). Additionally, a Fuzion feature containing features for static methods and constructors is generated as follows: Java.x.y.MyClass_static is new(arg string) is ... myStaticMethod is ... This Feature defines a unit type. Finally, a Fuzion feature returning an instance of this unit type is generated for convenience Java.x.y.MyClass =&gt; x.y.MayClass_static With this, the following code can be used to create an instance of MyClass within Fuzion and call myMethod and myStaticMethod of this class: o := Java.x.y.MyClass.new "test" o.myMethod Java.x.y.MyClass.myStaticMethod The counterpart to import a Java class in Fuzion would be to declare a field and assign the class' unit type value to it, i.e., the code above could be simplified as MyClass := Java.x.y.MyClass o := MyClass.new "test" o.myMethod MyClass.myStaticMethod or even mc := Java.x.y.MyClass o := mc.new "test" o.myMethod mc.myStaticMethod using mc as an alias of Java.x.y.MyClass. Note that since the value of field mc is a unit type, this assignment or any accesses of mc will not execute any code at runtime. Small Example Here is a example how Java code can be used from Fuzion: javaString := java.lang.String.new "Hello Java 🌍!" # create Java string, type Java.java.lang.String javaBytes := javaString.getBytes "UTF8" # get its UTF8 bytes, type is fuzion.java.Array&lt;i8&gt; match javaBytes err error =&gt; say "got an error: $err" bytes fuzion.java.Array =&gt; say "string has {bytes.count} bytes: $bytes" javaString2 := java.lang.String.new bytes 6 bytes.count-6 # create Java string from bytes subset, say "Hello "+javaString2 # append Java string to Fuzion string and print it Web Server Example The following code shows how Java APIs can be used to create a minimalistic web server in Fuzion. webserver is # declare short hands to access Java net and io packages net := Java.java.net io := Java.java.io # open socket port := 8080 serversocket := net.ServerSocket.new port match serversocket err error =&gt; say "#### $err ####" ss Java.java.net.ServerSocket =&gt; for n in 1.. do say "accepting connections to localhost:$port" match accept unit =&gt; say "ok." err error =&gt; say "#### $err ####" accept outcome&lt;unit&gt; is # accept and handle connection s := serversocket.accept? input := io.BufferedReader.new (io.InputStreamReader.new s.getInputStream?) output := io.DataOutputStream.new s.getOutputStream? req := read? say "got request ({req.byteLength} bytes): $req" if req.startsWith "GET " (send200 "&lt;html&gt;Hello Fuzion $n!&lt;/html&gt;")? # close streams input.close? output.close # helper to read request # read outcome&lt;string&gt; is for r := "", "$r$s\n" s := input.readLine? ready := input.ready? until s = "" || !ready r # helper to send data in HTTP response with status 200 # send200(data string) outcome&lt;unit&gt; is output.writeBytes ( "HTTP/1.1 200 OK\n" + "Connection: close\n" + "Server: Fuzion demo WebServer v0.01\n" + "Content-Length: " + data.byteLength + "\n" + "Content-Type: text/html\n" + "\n" + data)? Java methods that may result in an exceptions are represented by Fuzion features that result in the type outcome&lt;T> where T is the Fuzion type corresponding to the Java result type of the method. outcome is a union type that may be either error, which wraps the Java exception, or the actual result type T. Java methods that result in void are mapped to Fuzion features that result in unit, or, outcome&lt;unit> in case the method has any declared exception.f Fuzion Interpreter Fuzion currently supports two back-ends: An interpreter implemented in Java and running on top of OpenJDK and a C code generator implemented in Java using clang to create machine code. The goal of the interpreter was to quickly obtain a way to execute Fuzion applications. Performance was not the main concern. The interpreter operates directly on Fuzion's abstract syntax tree. For better performance, a byte-code back-end is planned that will operate on Fuzion's intermediate code instead. Fuzion Byte-Code Back-End Similar to Fuzion's C back-end, the byte-code back-end is planned to work on top of Fuzion's intermediate code. Fuzion intermediate code Fuzion uses intermediate files during different stages of compilation: module files that contain library code, application files for whole applications and Fuzion intermediate representation files that serve as input to the back ends. Fuzion uses a simple intermediate code to represent pre-compiled modules and whole applications. This intermediate code serves as the input for static analysis tools, optimizers and for different back-ends that produce executable applications. The goal in the design of the intermediate file format was high performance and simplicity for tools using this code. The intermediate code is a binary format containing features and types in a way that may be mapped to memory and used directly, so overhead of parsing this format into an in-memory representation is avoided. In particular, if only parts of a pre-compiled module are used by an application, there is no need to read and unpack parts of the module intermediate representation that are not used. For features containing code, a very simple stack-based format is used. There are currently only ten different instructions: * Unit -- produce a value of unit type * Current -- produce the current instance as a value * Constant -- produce a constant value * Assign -- perform an assignment to a field * Call -- perform a call to a given feature * Tag -- convert a value into a tagged value of a choice type * Match -- match a choice type * Box -- convert a value type to a ref type * Unbox -- extract the value from a ref type * Pop -- drop the top element from the stack The intermediate code uses indices to refer to features and types within intermediate files. This means that lookup is very efficient, but it also means that a change in a library module requires recompilation of all dependent modules. Any incompatibilities would be found at compile time instead of resulting in something like Java's IncompatibleClassChangeError at run-time. Instance Implementation Fuzion instances should be mapped directly to instances of Java classes generated for the corresponding features. Fuzion fields should be mapped to Java fields and Fuzion routines to Java methods. Fuzion instances that are accessed only locally by their defining features code, i.e., they do not live longer than their code is executed and they are not passed to any other features, might be optimized and implemented as Java methods using local variables instead of fields. Call Implementation Fuzion's support multiple inheritance similar to Eiffel. Java support multiple inheritance only for interfaces, so one way to implement dynamic binding in calls would be to define interfaces for every Fuzion feature and use the JVM's invokeinterface byte-code for calls. A more flexible alternative might be to use invokedynamic to implement dynamic dispatch, but this will likely result in higher overhead compared to highly optimized invokeinterface implementations. Conclusion and Next Steps The Fuzion language definition and implementation are far from stable, but are getting closer to become useful. Big improvements come from the ability to pre-compile modules and from the foreign language interface for Java, which makes a giant code base accessible for Fuzion applications to build on. Additionally, new projects such as the language server implementation for Fuzion by Michael Lill help by integrating Fuzion support in popular IDEs and editors. Main points that are missing right now are a powerful standard library additional library modules for all sorts of application needs low-level foreign language interface for C actual implementations of static analyzers and optimizers highly optimizing back-ends garbage collection for the C back-end documentation, tutorials enthusiastic contributors and users! Please feel free to contact me in case you want to use Fuzion or want to help making it a success!

Additional information

Type devroom

More sessions

2/5/22
Friends of OpenJDK
Simon Ritter
D.openjdk
<p>Despite being nearly 27 years old, Java is still consistently rated in the top three most popular programming languages in use. The reason for this popularity extends beyond the syntax of the language to the power of the Java Virtual Machine (JVM). With its ability to handle internet-scale workloads, the JVM delivers the performance required for mission-critical enterprise applications.</p> <p>In this session, we’ll explore Java as it is in 2022. JDK 17 is the ninth (yes, ninth) release in ...
2/5/22
Friends of OpenJDK
Rudy De Busscher
D.openjdk
<p>By now, we all know that JDK 17 is the latest Java long-term supported release with several new language features. There are already many resources that explain the Java Record and Text Blocks in detail. But JDK 17 has much more interesting changes since JDK 11.</p> <p>In this session, I go into more detail about some other interesting changes that are not language features but are a good reason to switch to JDK 17.</p> <ul> <li>Improvements in the Class Data Sharing feature.</li> <li>JDK ...
2/5/22
Friends of OpenJDK
Julien Lengrand-Lambert
D.openjdk
<p>A little guide on how to convince management to try out new technologies!</p> <p>For you it's obvious, Kotlin is the next big thing and it'll bring your team to the next level. You see clear advantages, but others are not quite there yet. In this talk, I'll describe the ways we found to get Kotlin inside ING, one of the largest European bank. We'll look into simple ways to convince your colleagues, your management and make it a success for everyone!</p>
2/5/22
Friends of OpenJDK
Deepu K Sasidharan
D.openjdk
<p>Java 17 finally gave us the first incubator of Foreign Function &amp; Memory API. Let's do a test drive of what is in store and see if it is good enough to replace JNI. Let's be honest, JNI is not that secure and is controversial and it's high time we move away from it and adopt FFI like in many other modern languages. But is the new API enough or do we need to consider external libraries like JNA or JNR-FFI? I'll showcase some examples of using the new API with some of my other favorite ...
2/5/22
Friends of OpenJDK
Carl Dea
D.openjdk
<p>A brief introductory talk on Java 17's New project Panama APIs to get your feet wet into the world of native language access or better known as Foreign Function Interface APIs (JEP 412).</p>
2/5/22
Friends of OpenJDK
Erik Costlow
D.openjdk
<p>A demonstration of log4j exploits, which defenses people tried, and which worked. We'll cover how groups responded effectively to patch to see what was common. We'll also look at open source funding models, subscriptions, and bug bounty programs to see why it's sometimes hard to donate.</p>
2/5/22
Friends of OpenJDK
Brian Vermeer
D.openjdk
<p>Building cloud-native Java applications is undoubtedly awesome.</p> <p>However, it comes with undeniable new risks. Next to your own code, you are relying on so many other things.</p> <p>Blindly depending on open-source libraries and Docker images can form a massive risk for your application.</p> <p>The wrong package can introduce severe vulnerabilities into your application, exposing your application and your user's data.</p> <p>Join this talk where we’ll show common threats, ...