RoboVM 1.0 Released! A Look Under the Hood

Reddit, you killed our server at http://www.robovm.com. You can still give RoboVM a whirl over here. This is a mirror of our blog post, we’ll be back.

After months of hard work we are proud to announce our first stable release, RoboVM 1.0! This would not have been possible without the help from our fantastic community who provided feedback, filed issue reports and worked with us to track down bugs. A big THANK YOU to all of the over 2000 beta subscribers!

With the 1.0 release, we hope to grow our community even further. For this reason, we’ll also take a look under the hood of RoboVM today, giving newcomers a bit of insight what RoboVM is, how it works, and what our motivation behind it is. If you want to know more, jump to “What’s RoboVM?”

What’s new?

Many new features and bug fixes went into 1.0:

  • The debugger has received another round of updates. All JDWP functionality is now implemented.
  • Support for RAM disk caches. This can help speed up the compilation and linking process on slow HDDs.
  • Support for HFS+ file compression. All files put into the cache are now compressed, further speeding up the compilation process and reducing the amount of space used for the RoboVM cache.
  • Initial project template and Maven archetype support.
  • A new Eclipse project wizard, using the templates project
  • Support for invoking tools via robovm.xml. We currently support the TextureAtlas tool, with more to come.
  • Cleanup and simplification of Objective-C bindings, see the binding status page

Announcing Commercial Licenses

First off: nothing will change for users of our OSS core! You will continue to be able to create apps without any artificial limitations, free of charge. With our new commercial licenses we add convenience features and support services on top of the OSS core. This will allow us to support our work on the free OSS core and ensure the longevity of the project as a whole.

Our commercial license scale with the size of your business, covering solo developers, startups, SMEs and big enterprises. Depending on your plan, you’ll get access to the following features and services:

  • JDWP compliant debugger for both simulator and on-device debugging
  • Improved crash reporting
  • Private e-mail support, with and without SLA
  • JavaFX support
  • Hotfix support
  • Alpha channel access to new commercial features

Our full pricing information is available at http://www.robovm.com/buy.

Over 2000 developers have subscribed to our beta program over the last few months. You have provided invaluable feedback for which we’d like to thank you with more than just words.

All beta users are eligible for a discount of 50% for the solo and startup license tiers (one year subscriptions)! Please sign up for a trial with the e-mail address you used for the beta. We’ll get back to you with a discounted license in the next 14 days!

What’s next?

The 1.0 release marks an important milestone for us. But we are far from being done. We’ll continue with our fast paced releases, with the following features scheduled for the near term future:

  • Improved documentation, users guides and screencasts
  • Intellij IDEA & Android Studio integration, a preliminary Gradle-based plugin is available
  • Interface Builder integration (commercial feature)
  • Support for extensions (dynamic libraries) to enable peripherals like Apple Watch
  • Support for statically linking RoboVM into Objective-C apps

Exciting times ahead! With all of this out of the way, let’s take a look at what’s under the hood of RoboVM.

What’s RoboVM?

At Trillian Mobile, we really love the JVM ecosystem. With an abundance of great IDEs, build systems, languages for every taste and a huge amount of 3rd party libraries, we think it’s one of the most productive environments around. Over the years, the JVM ecosystem has been slowly fading from the client-side, for better or worse (anyone remember applets?). With Android, client-side Java is a thing again, but that’s only one of the two big mobile platforms.

Our goal with RoboVM is to bridge the gap between backend, Android and iOS. We want to unify the workflow of millions of Java, Scala, Kotlin, Groovy and Clojure developers, so they can use their tools and skills while creating truly native apps for every platform.

To achieve this goal, RoboVM is setup as a platform with multiple components:

All of these components allow you to write native iOS apps, share code with Android and the backend, have a sane build and test environment and make you a happier developer overall. Apart from the debugger, everything is OSS and available to be torn apart and remixed on Github!

The AOT Compiler

At the heart of RoboVM lies its AOT compiler. JVMs usually employ just-in-time (JIT) compilation: JVM bytecode is loaded at runtime and transformed to native code. Apple does not allow JIT compilers on iOS, memory pages can not be set as executable. For this reason, RoboVM takes your Java 8 bytecode and transforms it to a native executable before your app is deployed to a device. As a result, RoboVM can not load bytecode at runtime, which is pretty much the only limitation of RoboVM.

By taking bytecode as input, RoboVM does not need to care about what produced this bytecode. You can feed RoboVM’s compiler bytecode generated by Java, Scala, Groovy, Kotlin, Clojure or any other alternative JVM language compiler, and it will happily compile it to native code. This is in contrast to approaches such as J2ObjC, which takes Java source code instead of bytecode and transforms it to mostly equivalent Objective-C code. Not having to rely on the original source makes it possible to integrate any 3rd party JAR easily.

As a first step in the compilation process, RoboVM parses the bytecode of a class file via Soot. Soot allows us to transform the stack-machine based JVM bytecode to more manageable 3-address code representation called Jimple. We then apply many optimization and simplification passes on this Jimple code, such as dead code elimination.

Once we are done jimpling up all the things, we apply custom transformations that allow us to support Java 8 language features such as lambdas and our custom binding bridge called Bro, responsible for easily interfacing with C and Objective-C code from Java.

After all these transformations we translate our heavily modified Jimple to LLVM IR. LLVM is the compiler infrastructure used by Clang to compile C, C++, Objective-C and Swift, among other languages. By employing LLVM, we reap the same benefits as those “native” languages: highly optimized emitters for a broad range of architectures such as x86, x86_64, ARM thumbv7 and ARM 64-bit. With the LLVM IR in hand, we generate an architecture specific assembly file that we then assemble to a final object file.

The compiler is modular, so we can add any amount of plugins that interfer with this compilation process. One such plugin allows us to add DWARF metadata to the LLVM IR, which we later use to know where local variables are located on the stack or where to insert instrumentation for debugging.

Every class file is hence compiled to a corresponding object file. Doing this for every compilation would be rather time-consuming, which is why RoboVM employs incremental compilation: only the set of class files that changed since the last compilation is recompiled, along with their dependent classes. The resulting object files are cached for re-use.

Just blindly compiling all classes specified in the classpath of a project would also not be the smartest thing to do. Given a main class as the main entry point, RoboVM will figure out the set of classes that are actually required. We can bring down the number of classes, methods and fields being compiled even further by tree-shaking, an area we are currently investigating.

With all the object files for a project in hand, we can link the final executable. RoboVM allows you to easily specify any additional native frameworks or libraries you want to link in, either via annotations in code or the project’s configuration file for non-system frameworks and libraries. We also need to link in RoboVM’s runtime, consisting of things like the GC, debugging support, native code for the class library and so on.

An executable alone is not enough, so RoboVM will also take any resources you have, optionally run them through tools such as TextureAtlas, and compile the final application bundle, ready for submission to the app store!

The Runtime

A virtual machine requires a runtime to provide low-level services to the user code. Such services include garbage collection, threading, reflection and so on. Let’s look at a few pieces of RoboVM’s runtime.

One central part of RoboVM’s runtime is the garbage collector GC. RoboVM uses the Boehm-Demers-Weisser GC, a conservative GC originally designed for languages like C or C++. A conservative GC has to scan the heap and registers under the assumption that anything can be a pointer to managed data. This sounds suboptimal, and it is, but with some tuning and optimizations, the Boehm GC can be a rather capable GC.

RoboVM tunes the Boehm GC in such a way, that it’s working in mostly-precise mode. The GC does not have to blindly scan large portions of the heap, but can instead rely on some additional information we give to it. This way, the GC only has to scan registers and the stack conservatively, greatly improving performance. On top of this, we also enable thread local allocation, meaning we do not have to take a global lock when allocating memory for an object. Finally, we also enable parallel mark & sweep, distributing the GC load to multiple threads, reducing GC latency.

The runtime is also responsible for supporting reflection. A RoboVM executable stores all information required to support the full reflection capabilities found in standard JVMs, including method invocation and proxies. We use a few assembler tricks for the latter two features, which makes RoboVM a very capable runtime for Java bytecode.

The Class Library

Java is known for its extensive standard class library. Any 3rd party library your app depends on expects certain classes of that standard library to be in place. RoboVM takes its class library from Android, which itself is a fork based on the now defunct Apache Harmony. This means that any 3rd party library that works on Android will also work on RoboVM, with the exclusion of Android specific APIs, such as Android’s UI layer.

Just taking Android’s class library and hoping for the best when using it on iOS is of course not going to work. The class library does not only consist of Java code. To interface with system services such as the file system, there’s also a considerable amount of native C/C++ code which is exposed to Java via JNI.

We therefore have to massage those native bits a little bit to make them work on iOS. Both Android and iOS are supposed to implement the Posix standard. In reality, they both have a slightly different interpretation of this standard when it comes to things like networking or threading.

A standard class library has to be rock solid, especially if you port it to a new operating system. We run tests against 3 huge test suites that guarantee the proper behaviour of both the class library and the VM features the class library relies upon. These test suites come from the original Apache Harmony project and the Android Dalivk/ART runtime.

The Bindings

With RoboVM, we want to enable you to write native applications instead of pushing your UI to a webview. This means that you need full access to the iOS frameworks required to implement your UIs, interface with the hardware or use services such as in-app purchasing or notifications. Historically, JVMs interface with native code through the Java Native Interface, a rather cumbersome way of talking to C/C++/Objective-C code.

We wanted something simpler, with better performance. Which is why we came up with Bro, our custom Java-to-native bridge. Bro is inspired by JNA and other similar JVM solutions, as well as .NET’s p/invoke.

With Bro, you can wrap any native C or Objective-C API in pure Java code via annotations. Here’s a basic example that gives you access to the native implementations of abs():

import org.robovm.rt.bro.*;
import org.robovm.rt.bro.annotation.*;

@Library("c") 
public class Abs {
    static {
        Bro.bind(); 
    }
    @Bridge private static native int abs(int i); 
    public static void main(String[] args) {
        System.out.println(abs(-100));
    }
}

The @Library annotation tells the compiler which library or framework to link. The @Bridge annotation tells the compiler that the abs method should be bound to the C function of the same name.

This is of course only the tip of the ice-berg. RoboVM will automatically marshal more complex data types between the Java and native side. You can also bind C structs, Objective-C classes, which is what we do to give you full access to all of iOS’ frameworks and APIs. You can even subclass Objective-C classes!

Bro allows you to use iOS frameworks and APIs in a very natural way. We already did all the heavy lifting and created bindings for all of iOS frameworks and APIs. Here’s an example of a custom view controller:

public class MyViewController extends UIViewController {
    private final UIButton button;
    private final UILabel label;
    private int clickCount;

    public MyViewController () {
        // Get the view of this view controller.
        UIView view = getView();

        // Setup background.
        view.setBackgroundColor(UIColor.white());

        // Setup label.
        label = new UILabel(new CGRect(20, 250, 280, 44));
        label.setFont(UIFont.getSystemFont(24));
        label.setTextAlignment(NSTextAlignment.Center);
        view.addSubview(label);

        // Setup button.
        button = UIButton.create(UIButtonType.RoundedRect);
        button.setFrame(new CGRect(110, 150, 100, 40));
        button.setTitle("Click me!", UIControlState.Normal);
        button.getTitleLabel().setFont(UIFont.getBoldSystemFont(22));

        button.addOnTouchUpInsideListener(() -> {          
           label.setText("Click Nr. " + (++clickCount));
        });
        view.addSubview(button);
    }
}

The MyViewController class actually extends an Objective-C class called UIViewController! The rest of the code also flows quite naturally, especially with Java 8 lambdas.

RoboVM will also take care of the interaction with Objective-C’s Automatic Reference Counting (ARC) mechamism. RoboVM will do the “right thing”, so you do not have to think about it too hard. Should you still require to interact with ARC, RoboVM lets you do it.

The bindings to iOS frameworks & APIs are generated semi-automatically. If you want to create a binding for an existing iOS library, you can employ our binding generator and modify things to suite your needs. To date, we’ve bound 98% of all iOS 8.1 framworks and APIs.

Now, not every scenario lends itself well to writing two separate UI layers. Especially in the enterprise, there’s a need to get things going quickly, with minor compromises regarding the UIs native look and feel. For this use-case, we support JavaFX on both iOS and Android. This approach allows you to share 100% of your code between both platforms. We partner with LogdON to ensure JavaFX works smoothly across iOS and Android.

The Debugger

A RoboVM commercial license give you access to a Java Debug Wire Protocol (JDWP) compatible debugger. JDWP is what allows you to debug a Java process from within your IDE. RoboVM supports JDWP as well, so you can debug apps an both the simulator and the device, using the tools you are used to.

RoboVM’s implements a soft-debugger: the runtime cooperates with the debugger at specific safe-points in the user code to implement thread suspension, stepping, breakpoints and memory inspection. In debugging mode, the app will spin up an additional thread that listens for commands over TCP. On the other side of the TCP channel sits our JDWP server, implementing the protocol on-top of our custom, minimal debugging protocol.

Alternative approaches such as GDB and LLDB use OS services to control the process. We actually investigated the use of LLDB in the beginning but ultimately decided against it. First, LLDB is process-centric: if one thread stops, all other threads have to stop as well. This is ultimately incompatible with JDWP’s thread-centric model: you can stop one or more threads, while the remaining threads continue to run. The second reason for deciding against LLDB is that our JDWP server would have to take over the process via LLDB. This would prevent you from debugging the native side of your app while also debugging the Java-side. As it stands, you can simultaneously debug your app from your Java IDE as well as XCode!

The RoboVM debugger allows you to do anything you are able to do with a normal JVM debugger: suspend/resume threads, set (conditional) breakpoints, step in/out/over source lines, modify variables at runtime and invoke methods, e.g. via Eclipse’ display view or Intellij IDEA’s “Watch Expressions” dialog. All of this works both on the simulator and on the device.

IDE Integration

One of the biggest reason we are working on RoboVM is to bring the tooling around the JVM to iOS. For the past year we have focused on Eclipse, which is our current default development environment. Our Eclipse plugin (update url: http://download.robovm.org/eclipse/) supports the easy creation of both console and iOS projects. It integrates the debugger and JUnit support for a smooth development experience. You can also pair Eclipse with either Gradle or Maven for saner builds and continuous integration.

Android has since shifted fully to Android Studio, which is based on JetBrain’s Intellij IDEA. We have an initial version of the RoboVM Intellij IDEA plugin ready and are working hard on bringing it up to the quality of the Eclipse plugin. We realize the importance of support for both IDEA and Android Studio, especially if you do cross-platform development. Expect more in this area in the upcoming weeks!

Finally, there’s also some community work on NetBeans integration.

For a full featured experience, we also have to play nice with the tools provided by Apple. We are currently working on full interface builder integration, which should make creating basic UIs even simpler. Interface builder integration will be a commercial feature and is planned to be released in Q2 2015.

Build System Integration

RoboVM supports a wide range of build systems. For the die-hard, we provide a distribution of RoboVM that you can invoke from your command line like any other compiler system. That way you can integrate RoboVM with shell scripts should the need arise.

However, we’d strongly suggest to use more established build systems! We created a dedicated Maven plugin as well as a Gradle plugin, which should make your life considerably easier.

Going forward, we’ll provide project templates both via the Maven archetype as well as the IDE integration so setup not only iOS RoboVM projects, but also cross-platform iOS and Android projects.

Where to go from here?

By now, you should have a pretty good idea of what RoboVM is and how it can help you. If we peeked your interest, head over to our documentation page, and get started today!

If you just want to check out some code, we recommend looking through our ports of Apple’s iOS sample apps.

Finally, if you are very adventurous, you can also check out the RoboVM internals, right on Github!

About RoboVM

The goal of the RoboVM open-source project is to bring Java and other JVM languages to iOS. RoboVM’s ahead-of-time compiler translates Java bytecode into native ARM or x86 machine code that runs directly on the target CPU without being interpreted. The runtime is based on Android’s runtime classes and includes a Java to Objective-C bridge which makes it easy to use the native iOS Cocoa Touch APIs from Java or your JVM language of choice.

Contact

Web site: http://www.robovm.com
GitHub: https://github.com/robovm/robovm
Google Groups: https://groups.google.com/group/robovm
Twitter: https://twitter.com/robovm

What i’ve been up to

I’ve been in stealth mode for almost two months now (with a wedding somewhere in between the busy days). Some have already wondered what i’m up to due to my mysterious and at times mad-manish babbling on Twitter. Today i can finally announce what i’m working on!

That’s right, i’m working on RoboVM! It all began when Niklas send out a job ad sometime in May/June for the position of a developer based in Sweden, with some interesting skills. Jokingly i replied to the ad, saying that i lack any kind of swedish skills. After some back and forth it turned out that i may be a good fit and we agreed to collaborate.

I started working full-time on RoboVM by the end of July, learning the ins and outs of RoboVM and all technologies attached to it (mainly LLVM). It’s been a blast so far. Technology-wise, i’ve learned more in the last few weeks than in the last two years at my old job, where i was mostly concerned with management tasks. I’m finally back to full-time coding and it’s been extremely satisfying so far.

Getting into the code-base was one of my biggest concerns before starting at RoboVM. I’ve worked on quite a few legacy projects over the years, and it has often been a struggle due to shitty code, architecture astronauts going berserk or simply the shear amount of code. I’m probably also responsible for a few mental breakdowns of others who have to maintain my code. Interestingly enough, getting into the RoboVM code base turned out to be quite a smooth ride so far. This is especially surprising to me as i have to touch a multitude of different technologies and languages along the way. I guess this is a testament to the engineering effort that Niklas has put into it so far. My previous adventures into building a JVM also helped a little. Getting paid to work on something like RoboVM has always been a bit of a dream for me. I guess i’m pretty happy at the moment, even though this is probably the toughest thing i’ve worked on in my life so far.

I’m working remotely for RoboVM, which has resulted in a bit of a life-style change as well. I can now work from anywhere at any time. I found a new home at the office of BYTEPOETS here in Graz for when i’m sick of being alone. They are an amazing start-up in the web and mobile space. Seeing their processes at work is pretty fantastic and enlightening. I get to know a lot of new talented folks with interesting stories to tell. If your business is in need of services in the web and/or mobile space, check them out (and i don’t say this because they let me sit in their office). Seal of approval from me! When working from home it can become hard to differentiate between work time and free time, but i guess that will work out eventually as well. It definitely helps having an office to go to. Gives me a feeling of closure for the day when i leave.

I can’t tell what exactly i’m currently working on, but i hope it will make your life easier once it’s complete and working. The pressure is on, wish me luck :)

Ingress launches on IOS – powered by libGDX

Google’s augmented reality game Ingress has launched on iOS. It appears to be using libGDX on iOS as well, which means it either runs on the old Xamarin backend or our more stable and faster RoboVM backend (performance seems to indicate the latter). Here’s a pic of the credits on my old iPad2:

It’s gonna be interesting to follow the reviews on the App Store.