Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gizmo ClassCreator - make it possible to set class signature + builder #8

Merged
merged 1 commit into from
Aug 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,7 @@ public FunctionCreator createFunction(Class<?> functionalInterface) {

final String functionName = declaringClassName + FUNCTION + functionCount.incrementAndGet();
ResultHandle ret = new ResultHandle(type, this);
ClassCreator cc = new ClassCreator(classOutput, functionName, Object.class, functionalInterface);
ClassCreator cc = ClassCreator.builder().classOutput(classOutput).className(functionName).interfaces(functionalInterface).build();
MethodCreatorImpl mc = (MethodCreatorImpl) cc.getMethodCreator(functionMethod.getName(), functionMethod.getReturnType(), functionMethod.getParameterTypes());
FunctionCreatorImpl fc = new FunctionCreatorImpl(ret, functionName, cc, mc, this);
operations.add(new Operation() {
Expand Down
90 changes: 77 additions & 13 deletions gizmo/src/main/java/org/jboss/protean/gizmo/ClassCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,39 @@
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.RETURN;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class ClassCreator implements AutoCloseable {

public static Builder builder() {
return new Builder();
}

private final ClassOutput classOutput;
private final String superClass;
private final String[] interfaces;
private final Map<MethodDescriptor, MethodCreatorImpl> methods = new HashMap<>();
private final Map<FieldDescriptor, FieldCreatorImpl> fields = new HashMap<>();
private final String className;
private final String signature;

public ClassCreator(ClassOutput classOutput, String name, Class<?> superClass, Class<?>... interfaces) {
this.classOutput = classOutput;
this.superClass = superClass.getName().replace(".", "/");
this.interfaces = new String[interfaces.length];
for (int i = 0; i < interfaces.length; ++i) {
this.interfaces[i] = interfaces[i].getName().replace(".", "/");
}
this.className = name.replace(".", "/");
}

public ClassCreator(ClassOutput classOutput, String name, String superClass, String... interfaces) {
public ClassCreator(ClassOutput classOutput, String name, String signature, String superClass, String... interfaces) {
this.classOutput = classOutput;
this.superClass = superClass.replace(".", "/");
this.interfaces = new String[interfaces.length];
for (int i = 0; i < interfaces.length; ++i) {
this.interfaces[i] = interfaces[i].replace(".", "/");
}
this.className = name.replace(".", "/");
this.signature = signature;
}

public MethodCreator getMethodCreator(MethodDescriptor methodDescriptor) {
Expand Down Expand Up @@ -99,7 +98,7 @@ public void close() {
for (int i = 0; i < interfaces.length; ++i) {
interfaces[i] = this.interfaces[i];
}
file.visit(Opcodes.V1_8, ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC, className, null, superClass, interfaces);
file.visit(Opcodes.V1_8, ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC, className, signature, superClass, interfaces);

boolean requiresCtor = true;
for (MethodDescriptor m : methods.keySet()) {
Expand Down Expand Up @@ -132,4 +131,69 @@ public void close() {

classOutput.write(className, file.toByteArray());
}
}

public static class Builder {

private ClassOutput classOutput;

private String className;

private String signature;

private String superClass;

private final List<String> interfaces;

Builder() {
superClass(Object.class);
this.interfaces = new ArrayList<>();
}

public Builder classOutput(ClassOutput classOutput) {
this.classOutput = classOutput;
return this;
}

public Builder className(String className) {
this.className = className;
return this;
}

public Builder signature(String signature) {
this.signature = signature;
return this;
}

public Builder superClass(String superClass) {
this.superClass = superClass;
return this;
}

public Builder superClass(Class<?> superClass) {
return superClass(superClass.getName());
}

public Builder interfaces(String... interfaces) {
for (String val : interfaces) {
this.interfaces.add(val);
}
return this;
}

public Builder interfaces(Class<?>... interfaces) {
for (Class<?> val : interfaces) {
this.interfaces.add(val.getName());
}
return this;
}

public ClassCreator build() {
Objects.requireNonNull(className);
Objects.requireNonNull(classOutput);
Objects.requireNonNull(superClass);
return new ClassCreator(classOutput, className, signature, superClass, interfaces.toArray(new String[0]));
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class ArrayTestCase {
@Test
public void testNewArray() throws Exception {
TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
try (ClassCreator creator = new ClassCreator(cl, "com.MyTest", Object.class, Supplier.class)) {
try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className("com.MyTest").interfaces(Supplier.class).build()) {
MethodCreator method = creator.getMethodCreator("get", Object.class);
ResultHandle arrayHandle = method.newArray(String.class, method.load(10));
method.returnValue(arrayHandle);
Expand All @@ -27,7 +27,7 @@ public void testNewArray() throws Exception {
@Test
public void testWriteArray() throws Exception {
TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
try (ClassCreator creator = new ClassCreator(cl, "com.MyTest", Object.class, Supplier.class)) {
try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className("com.MyTest").interfaces(Supplier.class).build()) {
MethodCreator method = creator.getMethodCreator("get", Object.class);
ResultHandle arrayHandle = method.newArray(String.class, method.load(1));
method.writeArrayValue(arrayHandle, method.load(0), method.load("hello"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class ExceptionThrowingTestCase {
@Test
public void testThrowException() throws Exception {
TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
try (ClassCreator creator = new ClassCreator(cl, "com.MyTest", Object.class, MyInterface.class)) {
try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className("com.MyTest").interfaces(MyInterface.class).build()) {
MethodCreator method = creator.getMethodCreator("transform", String.class, "java.lang.String");
method.throwException(IllegalStateException.class, "ERROR");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class FieldModifiersTest {
@Test
public void testFieldModifiers() throws Exception {
TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
try (ClassCreator creator = new ClassCreator(cl, "com/MyTest", Object.class)) {
try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className("com.MyTest").build()) {
creator.getFieldCreator("foo", DescriptorUtils.classToStringRepresentation(String.class));
creator.getFieldCreator("list", DescriptorUtils.classToStringRepresentation(List.class)).setModifiers(ACC_FINAL | ACC_PROTECTED);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class FunctionTestCase {
@Test
public void testSimpleFunction() throws Exception {
TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
try (ClassCreator creator = new ClassCreator(cl, "com/MyTest", Object.class, MyInterface.class)) {
try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className("com.MyTest").interfaces(MyInterface.class).build()) {
MethodCreator method = creator.getMethodCreator("transform", String.class, String.class);

//create a function that appends '-func' to its input
Expand All @@ -33,7 +33,7 @@ public void testSimpleFunction() throws Exception {
@Test
public void testSimpleFunctionWithCapture() throws Exception {
TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
try (ClassCreator creator = new ClassCreator(cl, "com/MyTest", Object.class, MyInterface.class)) {
try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className("com.MyTest").interfaces(MyInterface.class).build()) {
MethodCreator method = creator.getMethodCreator("transform", String.class, String.class);

//create a function that appends '-func' to its input
Expand All @@ -55,7 +55,7 @@ public void testSimpleFunctionWithCapture() throws Exception {
@Test
public void testInvokeSuperMethodFromFunction() throws Exception {
TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
try (ClassCreator creator = new ClassCreator(cl, "com/MyTest", Superclass.class)) {
try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className("com.MyTest").superClass(Superclass.class).build()) {
MethodCreator method = creator.getMethodCreator("getMessage", String.class);

//create a function that calls super appends '-func' to its input
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class LoadClassTestCase {
@Test
public void testLoadClass() throws Exception {
TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
try (ClassCreator creator = new ClassCreator(cl, "com.MyTest", Object.class, Supplier.class)) {
try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className("com.MyTest").interfaces(Supplier.class).build()) {
MethodCreator method = creator.getMethodCreator("get", Object.class);
ResultHandle stringHandle = method.loadClass(String.class);
method.returnValue(stringHandle);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class LoadNullTestCase {
@Test
public void testLoadNull() throws Exception {
TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
try (ClassCreator creator = new ClassCreator(cl, "com.MyTest", Object.class, Supplier.class)) {
try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className("com.MyTest").interfaces(Supplier.class).build()) {
MethodCreator method = creator.getMethodCreator("get", Object.class);
method.returnValue(method.loadNull());
}
Expand Down
6 changes: 3 additions & 3 deletions gizmo/src/test/java/org/jboss/protean/gizmo/SampleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class SampleTest {
@Test
public void testSimpleGetMessage() throws Exception {
TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
try (ClassCreator creator = new ClassCreator(cl, "com/MyTest", Object.class, MyInterface.class)) {
try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className("com.MyTest").interfaces(MyInterface.class).build()) {
MethodCreator method = creator.getMethodCreator("transform", String.class, String.class);
ResultHandle message = method.invokeStaticMethod(MethodDescriptor.ofMethod(MessageClass.class.getName(), "getMessage", "Ljava/lang/String;"));
method.returnValue(message);
Expand All @@ -22,7 +22,7 @@ public void testSimpleGetMessage() throws Exception {
@Test
public void testStringTransform() throws Exception {
TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
try (ClassCreator creator = new ClassCreator(cl, "com/MyTest", Object.class, MyInterface.class)) {
try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className("com.MyTest").interfaces(MyInterface.class).build()) {
MethodCreator method = creator.getMethodCreator("transform", String.class, String.class);
ResultHandle message = method.invokeStaticMethod(MethodDescriptor.ofMethod(MessageClass.class.getName(), "getMessage", "Ljava/lang/String;"));
ResultHandle constant = method.load(":CONST:");
Expand All @@ -40,7 +40,7 @@ public void testStringTransform() throws Exception {
@Test
public void testIfStatement() throws Exception {
TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
try (ClassCreator creator = new ClassCreator(cl, "com/MyTest", Object.class, MyInterface.class)) {
try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className("com.MyTest").interfaces(MyInterface.class).build()) {
MethodCreator method = creator.getMethodCreator("transform", String.class, String.class);
ResultHandle equalsResult = method.invokeVirtualMethod(MethodDescriptor.ofMethod(Object.class, "equals", boolean.class, Object.class), method.getMethodParam(0), method.load("TEST"));
BranchResult branch = method.ifNonZero(equalsResult);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class TryCatchTestCase {
@Test
public void testTryCatch() throws Exception {
TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
try (ClassCreator creator = new ClassCreator(cl, "com/MyTest", Object.class, MyInterface.class)) {
try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className("com.MyTest").interfaces(MyInterface.class).build()) {
MethodCreator method = creator.getMethodCreator("transform", String.class, String.class);
ExceptionTable table = method.addTryCatch();
BytecodeCreator illegalState = table.addCatchClause(IllegalStateException.class);
Expand All @@ -26,7 +26,7 @@ public void testTryCatch() throws Exception {
@Test
public void testTryCatchEmptyCatchBlock() throws Exception {
TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
try (ClassCreator creator = new ClassCreator(cl, "com/MyTest", Object.class, MyInterface.class)) {
try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className("com.MyTest").interfaces(MyInterface.class).build()) {
MethodCreator method = creator.getMethodCreator("transform", String.class, String.class);
ResultHandle existingVal = method.load("complete");
ExceptionTable table = method.addTryCatch();
Expand Down