Trebuchet is a Java to C++ source code translator.
For the most part there is no good reason not to run a Java program on a Java Virtual Machine or Android Device. Modern JVMs provide many benefits and are really really fast.
But sometimes there is a situation where you might want to statically compile your Java program or some parts of it.
- Cross-platform development such as targeting both Android and iOS.
- Porting Java based games to consoles or platforms which rely heavily on native APIs.
- Sharing data models and code from your Java server application with non-java devices.
- Moving strategic Java classes to the native side of JNI libraries.
- Targeting embedded platforms such as 32 bit microcontollers.
- Utilities that make sense distributed as a small native executables.
- Porting Java code to real-time environments. Code does not not magically become real time but the C++ equivelant of Java can be given clearer fixed time constraints.
- Trebuchet can be used as a basis for the experiemenation with new memory models and compiler teqniques.
Pop the code into Trebuchet and pull the handle.
Language transcoders often include a support library to implement language features, garbage collection etc. Our approach is to have supply the leanest possible support code but then allow optional extensions for mode advanced features. This should assist when integrating generated code into various platforms. You may wish to integrate with your own platform's memory management.
Trebuchet uses the Spoon Java source code parser https://github.com/INRIA/spoon.
Implemented features. High Priority features. Lower priority features. Maybe never. Use a JVM?
-
Bean
Parameter Assignment, Return Values, Constructors - Simple Arithmetic (some working cases)
- Interfaces, Abstract methods, Inner Classes
- Static Initializers, Class Field Initializers
- Incrementing heap allocator with no garbage collection
- The remaining flow control, assignment and math which is not yet implemented.
- Non-C++ operators. eg.
instanceof
- Common java.lang.Object and java.lang.Class methods
- Exceptions
- Generics handling (not templates)
- Synchronization, Volatile references, Threads
- JNI Folding (removal of JNI stubs)
- Alternate Memory Management
- Custom Collections Implementations
- Reflection, Bytecode Class Loading
- Extensive API support (AWT etc.)
Modern C++ compilers can produce some amazing optimisations. We will rely on this to do most of the heavy lifting. Yet there are some quick wins we can apply in the translation process. Examples..
- As all Java methods are virtual, demoting virtual methods to non-virtual where no inheritance or cast occurs.
- Do not generate java.lang.Class definitions where the class's definition is never referenced.
- Do not extend java.lang.Object (which introduces the overhead of a vtable to all object instances) where never referenced.
- Demote heap allocated objects to local scope where reference doesn't escape scope.
- Convert regular getters and setters to direct field access. (This would be something an optimising compiler does anyway so this might be more of a code style transformation.)
- Disable index and type safety checks when logically safe to do so.
- Use of different memory pools or memory managment by type.
Trebuchet will scan a source directory for Java source files, generating single CPP and Header files.
Class re-ordering needs to occur to support C++'s forward declaration requirements for inheritated classes.
The example source generates universe.cpp
and universe.h
.
These can then be compiled with trebuchet.cpp
which replaces the new operator and will provide support functions.
The following test case demonstrates a simple code structure featuring interfaces, abstract methods,
Bean
assignment and return values etc...
public class Spaceship {
public static final int FIRSTSHIP = 100;
private String name = "Nostromo";
private int serialNumber = 120;
private LongRangeScanner longRangeScanner;
public Spaceship(LongRangeScanner longRangeScanner) {
this.longRangeScanner = longRangeScanner;
}
public LongRangeScanner getLongRangeScanner() {
return longRangeScanner;
}
public void reset(LongRangeScanner longRangeScanner, int serialNumber) {
this.longRangeScanner = longRangeScanner;
this.serialNumber = serialNumber;
}
}
public abstract class Device {
public void explode() {
}
public abstract int getDeviceId();
}
public class LongRangeScanner extends Device implements Scanner {
private int range;
public int getRange() {
return this.range;
}
public void setRange(int range) {
this.range = range;
}
public void scan() {
range = range + 234;
range = range - 123;
}
public int getDeviceId() {
return 1337;
}
private class YourInnerScanner {
private long someNumber;
public YourInnerScanner() {
this.someNumber = 10;
}
public void scanAhoy() {
someNumber = someNumber + 2;
}
}
private class MyInnerScanner {
private long anotherNumber;
MyInnerScanner() {
this.anotherNumber = 20;
}
public void scanAhoy() {
this.anotherNumber = this.anotherNumber + 4;
}
}
}
public interface Scanner {
void scan();
}
/*** trebuchet.equipment.Scanner ***/
class Scanner {
public:
virtual void scan() = 0;
};
/*** trebuchet.equipment.Device ***/
class Device {
public:
Device();
void explode();
virtual int getDeviceId() = 0;
};
/*** trebuchet.equipment.LongRangeScanner ***/
class LongRangeScanner: public Device, public Scanner {
private:
int range;
public:
LongRangeScanner();
int getRange();
void setRange(int range);
void scan();
int getDeviceId();
/*** trebuchet.equipment.LongRangeScanner$TachyonScanner ***/
class MyInnerScanner {
private:
long long anotherNumber;
public:
MyInnerScanner();
void scanAhoy();
};
/*** trebuchet.equipment.LongRangeScanner$SubspaceScanner ***/
class YourInnerScanner {
private:
long long someNumber;
public:
YourInnerScanner();
void scanAhoy();
};
};
/*** trebuchet.craft.TowingVessel ***/
class Spaceship {
public:
static int FIRSTSHIP;
private:
const char * name;
int serialNumber;
LongRangeScanner * longRangeScanner;
public:
Spaceship(LongRangeScanner * longRangeScanner);
LongRangeScanner * getLongRangeScanner();
void reset(LongRangeScanner * longRangeScanner, int serialNumber);
};
/*** trebuchet.Universe ***/
class Universe {
public:
Universe();
static void main(char * args);
};
/*** trebuchet.equipment.Scanner ***/
void Scanner::scan() {
}
/*** trebuchet.equipment.Device ***/
Device::Device() {
}
void Device::explode() {
}
/*** trebuchet.equipment.LongRangeScanner$TachyonScanner ***/
LongRangeScanner::MyInnerScanner::MyInnerScanner() {
this->anotherNumber = 20;
}
void LongRangeScanner::MyInnerScanner::scanAhoy() {
this->anotherNumber = this->anotherNumber + 4;
}
/*** trebuchet.equipment.LongRangeScanner$SubspaceScanner ***/
LongRangeScanner::YourInnerScanner::YourInnerScanner() {
this->someNumber = 10;
}
void LongRangeScanner::YourInnerScanner::scanAhoy() {
this->someNumber = this->someNumber + 2;
}
/*** trebuchet.equipment.LongRangeScanner ***/
LongRangeScanner::LongRangeScanner() {
}
int LongRangeScanner::getRange() {
return this->range;
}
void LongRangeScanner::setRange(int range) {
this->range = range;
}
void LongRangeScanner::scan() {
this->range = this->range + 234;
this->range = this->range - 123;
}
int LongRangeScanner::getDeviceId() {
return 1337;
}
/*** trebuchet.craft.TowingVessel ***/
int Spaceship::FIRSTSHIP = 100;
Spaceship::Spaceship(LongRangeScanner * longRangeScanner) {
this->name = "Nostromo";
this->serialNumber = 120;
this->longRangeScanner = longRangeScanner;
}
LongRangeScanner * Spaceship::getLongRangeScanner() {
return this->longRangeScanner;
}
void Spaceship::reset(LongRangeScanner * longRangeScanner, int serialNumber) {
this->longRangeScanner = longRangeScanner;
this->serialNumber = serialNumber;
}
/*** trebuchet.Universe ***/
Universe::Universe() {
}
void Universe::main(char * args) {
;
}
int main(int argc, char* argv[]) {
Universe::main(0);
return 0;
}