Data Types

JFuncMachine supports the same native data types as the Java Virtual Machine:

  • Boolean
  • Byte
  • Char
  • Double
  • Float
  • Int
  • Long
  • Short
  • Void (called Unit in JFuncMachine)

In addition, it has an Object type that contains a class name indicating the type of object, an Array type that contains a type indicating the type of array element, and

The Type class

The Type class in the package org.jfuncmachine.compiler.types is the base class for all JFuncMachine types.

Simple Types

For types that represent Java native types, and also for strings, JFuncMachine has a SimpleTypes class that contains static fields for each type. These are:

public class SimpleTypes {
    public static final Type BOOLEAN = new BooleanType();
    public static final Type BYTE = new ByteType();
    public static final Type CHAR = new CharType();
    public static final Type DOUBLE = new DoubleType();
    public static final Type FLOAT = new FloatType();
    public static final Type INT = new IntType();
    public static final Type LONG = new LongType();
    public static final Type SHORT = new ShortType();
    public static final Type STRING = new StringType();
    public static final Type UNIT = new UnitType();
}

You can use these static fields when specifying types or comparing types. As long as you stick to the SimpleTypes class, and never create instances of the simple types, you can use == to compare these types.

For example:

if (expr.getType() == SimpleTypes.INT) {

Object Types

The ObjectType class represents the type of a Java object. It has a string member variable indicating the fully-qualified (i.e. including the package name) class name that the object is an instance of. JFuncMachine can compile code to access classes that are not in its classpath, which is why the class names are specified with a string and not by java Class instances. This has good and bad points. An advantage is that there may be fewer classpath issues, but a disadvantage is that it will not do any compile-time checking to see if two object types are compatible.

To create an instance of ObjectType for java.io.PrintStream:

Type psType = new ObjectType("java.io.PrintStream");

Compound Types

Array Type

The ArrayType class represents the type of an array of some object type. When you create an instance of ArrayType you supply the type of the contained object. For example, to create an ArrayType for an array of strings:

Type arrayType = new ArrayType(SimpleTypes.STRING);

Function Type

JFuncMachine has support for lambdas and function references. The FunctionType class represents the type of a function, containing the types of each function parameter and the function’s return type. For example, to create a FunctionType describing a function that takes an int and a double and returns a String:

Type funcType = new FunctionType(
    new Type[] { SimpleTypes.INT, SimpleTypes.DOUBLE}, SimpleTypes.STRING);

Type Conversions

You may occasionally need to convert from one numeric type to another. The org.jfuncmachine.model.expr.conv package contains expressions that take an expression and convert its result to a different type. This includes a conversion to the Unit type (a.k.a. Void), which really means to just get rid of the value. For example, the following expression creates an integer constant and converts it to a byte:

Expression byteValue = new ToByte(new IntConstant(5));

The above expression is just for illustration. If you needed a byte constant of 5 you could just do new ByteConstant(5).

JFuncMachine does not include any overflow checking for these conversions.

Boxing and Unboxing

Java sometimes needs to treat native types as objects, for example when storing them in an ArrayList or HashMap. Each native type has a corresponding Object type, such as java.lang.Integer for int types or java.lang.Boolean for boolean types. Converting a native type to an object type is called “boxing”, while converting from a numeric object type to a native type is called “unboxing”.

JFuncMachine provides a Box and an Unbox class to either box or unbox a value. In the simplest case, you can create a Box or Unbox expression from another expression, and JFuncMachine will automatically determine whether or not there is a boxed or unboxed equivalent for the given type.

For example, the following expression would result in an expression whose type is java.lang.Integer (or in JFuncMachine representation, the type new ObjectType("java.lang.Integer)):

Expression expr = new Box(new IntConstant(42));

Sometimes you may want to explicitly specify the type of boxed or unboxed value to generate. For example, you might have boxed Byte value that you want to unbox as an int. You can provide the unboxed type to the Unbox constructor to unbox a value as a particular type. For example:

Expression expr = new Unbox(new Box(new ByteConstant(42)), SimpleTypes.INT);

You can do something similar for boxing values, where you supply the type of the boxed value. In the following expression, we use the getBoxType method of the Type class to get an object type representing the boxed equivalent of the type:

Expression expr = new Box(new ByteConstant(42), SimpleTypes.INT.getBoxType());

If there is no box type for the given type (e.g. String doesn’t need a box), the getBoxType method returns null.

Autoboxing

Although boxing and unboxing can be a hassle, the Java compiler is pretty good about boxing and unboxing automatically, a process that is called “autoboxing”. JFuncMachine provides autoboxing on two levels. First, JFuncMachine has autoboxing turned on by default, so it will insert Box or Unbox expressions as necessary to try to get types to match up correctly.

You can also disable autoboxing by specifying different options for the code generator. In that case, you may occasionaly need to make use of the same autoboxing code that JFuncMachine uses, which is provided by the Autobox class. The autobox method takes an expression and a desiret type and tries to determine whether a box or unbox is necessary and if so, returns the expression wrapped with the appropriate Box or Unbox expression. You can also use the autoboxNeeded method to determine whether autoboxing is necessary to convert an expression to a desired type.

A Note About Java Native Types

Although JFuncMachine masks a lot of the technicalities of the Java Virtual Machine, it is occasionally helpful to know how it does some things. One useful thing to know is that while the JVM has some support for boolean, byte, char, and short data types, those values usually have an int representation when the JVM is actually working with them. Now, an array of byte values is stored in memory as bytes, but whenever you fetch a byte value from an array of bytes, that value is actually an int.

The JVM essentially supports 5 types - int, long, float, double, and object. The only operations for byte, char, and short are to truncate an int to those values, and loading from and storing to arrays of those types.