Skip to content

Commit

Permalink
update documentation/kdoc
Browse files Browse the repository at this point in the history
add autoboxing code for non-nullable ID types
  • Loading branch information
Justin Lee committed May 20, 2020
1 parent a848498 commit 8013545
Show file tree
Hide file tree
Showing 12 changed files with 717 additions and 710 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
package io.quarkus.hibernate.orm.panache.kotlin.deployment;

import static io.quarkus.gizmo.Gizmo.ASM_API_VERSION;
import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.CLASS_SIGNATURE;
import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.ID_TYPE_SIGNATURE;
import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.JPA_OPERATIONS;
import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.OBJECT_SIGNATURE;
import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.PANACHE_ENTITY_SIGNATURE;
import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.PANACHE_REPOSITORY_BASE_DOTNAME;
import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.PANACHE_REPOSITORY_BASE_SIGNATURE;
import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.PANACHE_REPOSITORY_SIGNATURE;
import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.autobox;
import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.sanitize;
import static io.quarkus.panache.common.deployment.JandexUtil.getDescriptor;
import static io.quarkus.panache.common.deployment.PanacheRepositoryEnhancer.PanacheRepositoryClassVisitor.findEntityTypeArgumentsForPanacheRepository;
import static org.jboss.jandex.DotName.createSimple;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.LLOAD;
import static org.objectweb.asm.Type.getArgumentTypes;
import static org.objectweb.asm.Type.getMethodDescriptor;
import static org.objectweb.asm.Type.getReturnType;

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;
Expand All @@ -39,17 +41,13 @@ class KotlinPanacheRepositoryClassVisitor extends ClassVisitor {
private final IndexView indexView;
private org.objectweb.asm.Type entityType;
private String entitySignature;
protected Map<String, String> typeArguments = new HashMap<>();
private String idBinaryType;
private String idSignature;

public KotlinPanacheRepositoryClassVisitor(String className, ClassVisitor outputClassVisitor, IndexView indexView) {
super(Gizmo.ASM_API_VERSION, outputClassVisitor);
this.indexView = indexView;
indexView.getClassByName(createSimple(className))
.methods()
.forEach(method -> {
if (method.hasAnnotation(JandexUtil.DOTNAME_GENERATE_BRIDGE)) {
bridgeMethods.put(method.name() + JandexUtil.getDescriptor(method, m -> null), method);
}
});
}

@Override
Expand All @@ -64,38 +62,80 @@ public void visit(int version, int access, String name, String signature, String
String entityBinaryType = foundTypeArguments[0];
entitySignature = "L" + entityBinaryType + ";";
entityType = Type.getType(entitySignature);
idBinaryType = foundTypeArguments[1];
idSignature = "L" + idBinaryType + ";";

typeArguments.put("Entity", entitySignature);
typeArguments.put("Id", idSignature);
indexView.getClassByName(createSimple(repositoryClassName))
.methods()
.forEach(method -> {
if (method.hasAnnotation(JandexUtil.DOTNAME_GENERATE_BRIDGE)) {
bridgeMethods.put(method.name() + getDescriptor(method, m -> typeArguments.get(m)), method);
}
});
}

@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (bridgeMethods.get(name + descriptor) != null) {
final Type[] argumentTypes = getArgumentTypes(descriptor);
int primitive = -1;

for (int i = 0; i < argumentTypes.length; i++) {
if (argumentTypes[i].getInternalName().length() == 1) {
primitive = i;
}
}

int finalPrimitive = primitive;
mv = new MethodVisitor(ASM_API_VERSION, mv) {
/**
* This method tracks the location of any primitive value (ID types) passed in. The javac-generated
* bytecode is expecting an Object but with non-nullable "primitive" types in kotlin, those are
* expressed as primitives and not the wrapper types (e.g., Long). This method will catch the cases
* where the ID values passed in are loaded on to the stack prior to invoking the call to JpaOperations.
* It will then inject an autoboxing-like call to ensure that any long values because Long values.
*/
@Override
public void visitVarInsn(int opcode, int var) {
if (opcode == ALOAD && var == 0) {
visitLdcInsn(entityType);
} else {
super.visitVarInsn(opcode, var);
if (opcode == LLOAD && var == (finalPrimitive + 1)) {
Type wrapper = autobox(argumentTypes[finalPrimitive]);
super.visitMethodInsn(INVOKESTATIC, wrapper.getInternalName(), "valueOf",
getMethodDescriptor(wrapper, argumentTypes[finalPrimitive]), false);

}
}
}

/**
* This method redirects the kotlinc generated call to $DefaultImpls to JpaOperations. In case of IDs,
* e.g., these parameters are represented using primitives by kotlinc since they can never be null.
* JpaOperation expects an object reference so we need to update the descriptor for the method
* invocation to reflect that rather than the primitive type.
*/
@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
public void visitMethodInsn(int opcode, String owner, String name, String descriptor,
boolean isInterface) {
if (opcode == INVOKESTATIC && DEFAULT_IMPLS.matcher(owner).matches()) {
String replace = descriptor
.replace(PANACHE_REPOSITORY_BASE_SIGNATURE, CLASS_SIGNATURE)
.replace(PANACHE_REPOSITORY_SIGNATURE, CLASS_SIGNATURE)
.replace(PANACHE_ENTITY_SIGNATURE, OBJECT_SIGNATURE)
.replace(ID_TYPE_SIGNATURE, OBJECT_SIGNATURE);
super.visitMethodInsn(opcode, JPA_OPERATIONS, name, replace, isInterface);
} else {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);

owner = JPA_OPERATIONS;
Type[] arguments = getArgumentTypes(descriptor);
sanitize(arguments);
descriptor = getMethodDescriptor(getReturnType(descriptor), arguments);
}

super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
};

}
return mv;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.hibernate.orm.panache.kotlin.deployment;

import static java.util.Arrays.asList;
import static org.jboss.jandex.DotName.createSimple;

import java.util.Collections;
Expand Down Expand Up @@ -63,6 +64,45 @@ public final class KotlinPanacheResourceProcessor {
static final String OBJECT_SIGNATURE = toBinarySignature(Object.class);
static final String CLASS_SIGNATURE = toBinarySignature(Class.class);

static final org.objectweb.asm.Type CLASS_TYPE = org.objectweb.asm.Type.getType(Class.class);
static final org.objectweb.asm.Type OBJECT_TYPE = org.objectweb.asm.Type.getType(Object.class);
static final List<org.objectweb.asm.Type> REPOSITORY_TYPES = asList(
org.objectweb.asm.Type.getType(PanacheRepositoryBase.class),
org.objectweb.asm.Type.getType(PanacheRepository.class));
static final List<org.objectweb.asm.Type> ENTITY_TYPES = asList(
org.objectweb.asm.Type.getType(PanacheEntityBase.class),
org.objectweb.asm.Type.getType(PanacheEntity.class),
org.objectweb.asm.Type.getType(PanacheCompanion.class));
public static final List<org.objectweb.asm.Type> PRIMITIVE_TYPES = asList(
org.objectweb.asm.Type.getType(Void.class),
org.objectweb.asm.Type.getType(Boolean.class),
org.objectweb.asm.Type.getType(Character.class),
org.objectweb.asm.Type.getType(Byte.class),
org.objectweb.asm.Type.getType(Short.class),
org.objectweb.asm.Type.getType(Integer.class),
org.objectweb.asm.Type.getType(Float.class),
org.objectweb.asm.Type.getType(Long.class));

static org.objectweb.asm.Type sanitize(org.objectweb.asm.Type[] argumentTypes) {
org.objectweb.asm.Type primitiveReplaced = null;

for (int i = 0; i < argumentTypes.length; i++) {
if (REPOSITORY_TYPES.contains(argumentTypes[i])) {
argumentTypes[i] = CLASS_TYPE;
} else if (ENTITY_TYPES.contains(argumentTypes[i])) {
argumentTypes[i] = OBJECT_TYPE;
} else if (argumentTypes[i].getInternalName().length() == 1) {
primitiveReplaced = argumentTypes[i];
argumentTypes[i] = OBJECT_TYPE;
}
}
return primitiveReplaced;
}

static org.objectweb.asm.Type autobox(org.objectweb.asm.Type primitive) {
return PRIMITIVE_TYPES.get(primitive.getSort());
}

@NotNull
static String toBinarySignature(Class<?> type) {
return org.objectweb.asm.Type.getType(type).getDescriptor();
Expand Down
40 changes: 39 additions & 1 deletion extensions/panache/hibernate-orm-panache-kotlin/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@
</dependency>
</dependencies>

<pluginRepositories>
<pluginRepository>
<id>jcenter</id>
<name>JCenter</name>
<url>https://jcenter.bintray.com/</url>
</pluginRepository>
</pluginRepositories>

<build>
<plugins>
<plugin>
Expand Down Expand Up @@ -137,7 +145,9 @@
<execution>
<id>java-compile</id>
<phase>compile</phase>
<goals> <goal>compile</goal> </goals>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>java-test-compile</id>
Expand All @@ -146,6 +156,34 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jetbrains.dokka</groupId>
<artifactId>dokka-maven-plugin</artifactId>
<version>0.10.1</version>
<executions>
<execution>
<id>javadoc</id>
<phase>pre-site</phase>
<goals>
<goal>dokka</goal>
</goals>
</execution>
</executions>
<configuration>
<jdkVersion>8</jdkVersion>
<outputFormat>html</outputFormat>
<sourceDirectories>
<sourceDirectory>src/main/kotlin</sourceDirectory>
</sourceDirectories>
<externalDocumentationLinks>
<link>
<!-- Root URL of the generated documentation to link with. The trailing slash is required! -->
<url>https://docs.oracle.com/javase/8/docs/api/</url>
<packageListUrl>https://docs.oracle.com/javase/8/docs/api/package-list</packageListUrl>
</link>
</externalDocumentationLinks>
</configuration>
</plugin>
</plugins>
</build>
</project>
Loading

0 comments on commit 8013545

Please sign in to comment.