ActiveCodegen Examples

Note: To run the examples, you need to clone ActiveJ project from GitHub:
$ git clone https://github.com/activej/activej
And import it as a Maven project. Check out branch master. Before running the examples, build the project.
These examples are located at activej -> examples -> core -> codegen

Bytecode Expressions Example

Let’s create a simple sayHello() method that prints out “Hello world!”. First, we’ll define a simple Example interface that has a single sayHello() method:

public interface Example {
	void sayHello();
}

Now we can proceed to the description of Example subclass behaviour. For this purpose we will use ClassBuilder class.

Class<Example> example = ClassBuilder
		// DefiningClassLoader represents a loader for defining dynamically generated classes
		.create(DefiningClassLoader.create(Thread.currentThread().getContextClassLoader()), Example.class)
		.withMethod("sayHello", call(staticField(System.class, "out"), "println", value("Hello world")))
		.build();

To instantiate the described class, simply use newInstance():

Example instance = example.getDeclaredConstructor().newInstance();
instance.sayHello();

See full example code sources on GitHub.

Dynamic Class Creation Example

In this example we will dynamically create a class that implements an interface. So let’s first create a simple Person interface:

@SuppressWarnings("unused")
public interface Person extends Comparable<Person> {
	void setIdAndName(int id, String name);

	int getId();

	String getName();

	int hashOfPojo(ExamplePojo personPojo);

	int hash();

	@Override
	int compareTo(@NotNull Person o);

	@Override
	String toString();

	@Override
	boolean equals(Object obj);
}

Move on to constructing a class that implements Person interface:

Class<Person> personClass = ClassBuilder.create(DefiningClassLoader.create(Thread.currentThread().getContextClassLoader()), Person.class)

		// declare fields
		.withField("id", int.class)
		.withField("name", String.class)

		// setter for both fields - a sequence of actions
		.withMethod("setIdAndName", sequence(
				set(property(self(), "id"), arg(0)),
				set(property(self(), "name"), arg(1))))
		.withMethod("getId", property(self(), "id"))
		.withMethod("getName", property(self(), "name"))

		// compareTo, equals, hashCode and toString methods implementations follow the standard convention
		.withMethod("int compareTo(Person)", compareToImpl("id", "name"))
		.withMethod("equals", equalsImpl("id", "name"))
		.withMethod("hashOfPojo", hash(property(arg(0), "id"), property(arg(0), "name")))
		.withMethod("hash", hash(property(self(), "id"), property(self(), "name")))
		.withMethod("toString", ExpressionToString.create()
				.withQuotes("{", "}", ", ")
				.with("id: ", property(self(), "id"))
				.with("name: ", property(self(), "name")))
		.build();

Now we can test our dynamically generated classes:

// Instantiate two objects of dynamically defined class
Person jack = personClass.getDeclaredConstructor().newInstance();
Person martha = personClass.getDeclaredConstructor().newInstance();

jack.setIdAndName(5, "Jack");
martha.setIdAndName(jack.getId() * 2, "Martha");

System.out.println("First person: " + jack);
System.out.println("Second person: " + martha);

System.out.println("jack.equals(martha) ? : " + jack.equals(martha));

See full example code sources on GitHub.

Calculator Example

In this example we will create a calculator that parses an input equation string to an AST. Then, it generates an optimized class to calculate the expression.

First, create a parser that returns an AST of the expression:

private static final Parser<Expression> EXPRESSION = new OperatorTable<Expression>()
		.infixl(DELIMITERS.token("+").retn(Expressions::add), 10)
		.infixl(DELIMITERS.token("-").retn(Expressions::sub), 10)
		.infixl(DELIMITERS.token("*").retn(Expressions::mul), 20)
		.infixl(DELIMITERS.token("/").retn(Expressions::div), 20)
		.infixl(DELIMITERS.token("%").retn(Expressions::rem), 20)
		.prefix(DELIMITERS.token("-").retn(Expressions::neg), 30)
		.infixr(DELIMITERS.token("^").retn((left, right) -> Expressions.staticCall(Math.class, "pow", left, right)), 40)
		.build(ATOM);

Next, create a ClassBuilder that describes the class that will be generated. It will implement DoubleUnaryOperator interface and will have an applyAsDouble method.

Let’s create the appropriate builder:

public static Class<DoubleUnaryOperator> compile(String expression) {
	return ClassBuilder.create(DEFINING_CLASS_LOADER, DoubleUnaryOperator.class)
			.withMethod("applyAsDouble", PARSER.parse(expression))
			.build();
}

The method will have a var1 parameter for the unknown x:

private static final Parser<Expression> UNKNOWN = DELIMITERS.token("x").retn(Expressions.arg(0));

As a result, ActiveCodegen will generate bytecode of the following class:

public final class Class1 implements DoubleUnaryOperator {
    public Class1() {
    }

    public double applyAsDouble(double var1) {
        return (2.0D + 2.0D * 2.0D) * -var1 + 5.0D + 1024.0D / (100.0D + 58.0D) * 50.0D * 37.0D - 100.0D + 2.0D * Math.pow(var1, 2.0D) % 3.0D;
    }
}

Now let’s processes a manually written code and dynamically generated instance evaluation:

public static void main(String[] args) throws Exception {
	double x = -1;

	// manual code, super fast
	System.out.println(((2.0 + 2.0 * 2.0) * -x) + 5.0 + 1024.0 / (100.0 + 58.0) * 50.0 * 37.0 - 100.0 + 2.0 * Math.pow(x, 2.0) % 3.0);

	DoubleUnaryOperator instance = compile("((2 + 2 * 2) * -x) + 5 + 1024 / (100 + 58) * 50 * 37 - 100 + 2 * x ^ 2 % 3").getDeclaredConstructor().newInstance();

	// generated instance evaluation, literally equivalent to manual code (with a method call around it), except it was dynamically generated
	System.out.println(instance.applyAsDouble(x));
}

We’ve also ran benchmarks for this expression to compare the performance:

Benchmark                        Mode  Cnt    Score    Error  Units
CalculatorBenchmark.generated    avgt   10  115.882 ±  1.082  ns/op
CalculatorBenchmark.manual       avgt   10  115.222 ±  1.600  ns/op

You can find example sources on GitHub