Skip to content

Commit

Permalink
add support of code generation for array types
Browse files Browse the repository at this point in the history
  • Loading branch information
kalaninja committed May 13, 2022
1 parent 942bf3c commit 3257336
Show file tree
Hide file tree
Showing 34 changed files with 661 additions and 137 deletions.
13 changes: 7 additions & 6 deletions api/build.gradle
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
dependencies {
implementation project(':common')
implementation project(':pallet')
implementation project(':rpc')
implementation project(':rpc:rpc-core')
implementation project(':rpc:rpc-sections')
implementation project(':transport')
implementation project(':pallet')
implementation project(':scale')
implementation project(':types')
implementation project(':rpc:rpc-types')
implementation project(':scale')
implementation project(':storage')
implementation project(':transport')
implementation project(':types')

testImplementation project(':tests')

testImplementation 'org.testcontainers:testcontainers:1.16.3'
testImplementation 'org.testcontainers:junit-jupiter:1.16.3'
testImplementation 'org.testcontainers:testcontainers:1.17.1'
testImplementation 'org.testcontainers:junit-jupiter:1.17.1'

testAnnotationProcessor project(':pallet:pallet-codegen')
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.strategyobject.substrateclient.common;

public interface CommonType {
class Array<T> implements CommonType {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.strategyobject.substrateclient.common.codegen;

import com.strategyobject.substrateclient.common.CommonType;

public class Constants {
public static final Class<?> GENERIC_ARRAY_TYPE = CommonType.Array.class;

private Constants() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import javax.annotation.processing.Messager;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
Expand All @@ -33,11 +34,16 @@ public boolean isAssignable(@NonNull TypeMirror candidate, @NonNull TypeMirror s
public boolean isSubtype(@NonNull TypeMirror candidate, @NonNull TypeMirror supertype) {
return typeUtils.isSubtype(candidate, supertype);
}

public boolean isGeneric(@NonNull TypeMirror type) {
return ((TypeElement) typeUtils.asElement(type))
.getTypeParameters()
.size() > 0;

public boolean isNonGeneric(@NonNull TypeMirror type) {
if (type instanceof ArrayType) {
return isNonGeneric(((ArrayType) type).getComponentType());
}

return type.getKind().isPrimitive() ||
((TypeElement) typeUtils.asElement(type))
.getTypeParameters()
.size() == 0;
}

public TypeMirror erasure(@NonNull TypeMirror type) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.strategyobject.substrateclient.common.codegen;

import javax.lang.model.type.TypeMirror;

public class TypeNotSupportedException extends IllegalArgumentException {
public TypeNotSupportedException(TypeMirror type) {
super("Type is not supported: " + type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public TypeTraverser(Class<T> clazz) {

protected abstract T whenGenericType(@NonNull DeclaredType type, TypeMirror override, @NonNull T[] subtypes);

protected abstract T whenArrayPrimitiveType(@NonNull ArrayType type, TypeMirror override);

protected abstract T whenArrayType(@NonNull ArrayType type, TypeMirror override, @NonNull T subtype);

protected boolean doTraverseArguments(@NonNull DeclaredType type, TypeMirror override) {
return true;
}
Expand All @@ -40,20 +44,32 @@ public T traverse(@NonNull TypeMirror type) {
return whenPrimitiveType((PrimitiveType) type, null);
}

if (type instanceof ArrayType) {
val arrayType = (ArrayType) type;
return arrayType.getComponentType().getKind().isPrimitive() ?
whenArrayPrimitiveType(arrayType, null) :
whenArrayType(
arrayType,
null,
traverse(arrayType.getComponentType()));
}

if (!(type instanceof DeclaredType)) {
throw new IllegalArgumentException("Type is not supported: " + type);
throw new TypeNotSupportedException(type);
}

val declaredType = (DeclaredType) type;
val typeArguments = getTypeArgumentsOrDefault(declaredType, null);
return typeArguments.size() == 0 ?
whenNonGenericType(declaredType, null) :
whenGenericType(
declaredType,
null,
typeArguments.stream()
.map(this::traverse)
.toArray(x -> (T[]) Array.newInstance(clazz, typeArguments.size())));
if (typeArguments.size() == 0) {
return whenNonGenericType(declaredType, null);
}

return whenGenericType(
declaredType,
null,
typeArguments.stream()
.map(this::traverse)
.toArray(x -> (T[]) Array.newInstance(clazz, typeArguments.size())));
}

@SuppressWarnings({"unchecked", "UnstableApiUsage"})
Expand All @@ -66,8 +82,30 @@ public T traverse(@NonNull TypeMirror type, @NonNull TypeTraverser.TypeTreeNode
return whenPrimitiveType((PrimitiveType) type, typeOverride.type);
}

if (type instanceof ArrayType) {
val arrayType = (ArrayType) type;
if (arrayType.getComponentType().getKind().isPrimitive()) {
return whenArrayPrimitiveType(arrayType, typeOverride.type);
}

switch (typeOverride.children.size()) {
case 0:
return whenArrayType(
arrayType,
typeOverride.type,
traverse(arrayType.getComponentType()));
case 1:
return whenArrayType(
arrayType,
typeOverride.type,
traverse(arrayType.getComponentType(), typeOverride.children.get(0)));
default:
throw new IllegalArgumentException("Array type cannot be overridden by a generic type with more than one parameter");
}
}

if (!(type instanceof DeclaredType)) {
throw new IllegalArgumentException("Type is not supported: " + type);
throw new TypeNotSupportedException(type);
}

val declaredType = (DeclaredType) type;
Expand Down Expand Up @@ -107,8 +145,18 @@ public T traverse(@NonNull TypeTraverser.TypeTreeNode typeOverride) {
return whenPrimitiveType((PrimitiveType) typeOverride.type, typeOverride.type);
}

if (typeOverride.type instanceof ArrayType) {
val arrayType = (ArrayType) typeOverride.type;
return arrayType.getComponentType().getKind().isPrimitive() ?
whenArrayPrimitiveType(arrayType, arrayType) :
whenArrayType(
arrayType,
arrayType,
traverse(arrayType.getComponentType()));
}

if (!(typeOverride.type instanceof DeclaredType)) {
throw new IllegalArgumentException("Type is not supported: " + typeOverride.type);
throw new TypeNotSupportedException(typeOverride.type);
}

val declaredType = (DeclaredType) typeOverride.type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import lombok.val;

import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;

import static com.strategyobject.substrateclient.common.utils.StringUtils.capitalize;

Expand All @@ -29,4 +32,20 @@ public static String getGetterName(VariableElement field) {

return prefix + capitalize(fieldName);
}

public static String getSimpleName(TypeMirror type) {
if (type.getKind().isPrimitive()) {
return type.toString();
}

if (type instanceof DeclaredType) {
return ((DeclaredType) type).asElement().getSimpleName().toString();
}

if (type instanceof ArrayType) {
return String.format("%s[]", getSimpleName(((ArrayType) type).getComponentType()));
}

throw new IllegalArgumentException(String.format("Cannot populate the name of %s", type));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@ public class Constants {

public static final String SCALE_WRITER_REGISTRY = "scaleWriterRegistry";
public static final String SCALE_READER_REGISTRY = "scaleReaderRegistry";

private Constants() {
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.strategyobject.substrateclient.rpc.codegen.decoder;

import com.squareup.javapoet.CodeBlock;
import com.strategyobject.substrateclient.common.codegen.Constants;
import com.strategyobject.substrateclient.common.codegen.ProcessorContext;
import com.strategyobject.substrateclient.common.codegen.TypeTraverser;
import com.strategyobject.substrateclient.rpc.core.DecoderPair;
import com.strategyobject.substrateclient.rpc.core.RpcDecoder;
Expand All @@ -10,44 +12,42 @@
import lombok.NonNull;
import lombok.var;

import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.util.Types;
import javax.lang.model.type.*;
import java.util.Map;

import static com.strategyobject.substrateclient.rpc.codegen.Constants.PAIR_FACTORY_METHOD;
import static com.strategyobject.substrateclient.rpc.codegen.Constants.RESOLVE_AND_INJECT_METHOD;

public class DecoderCompositor extends TypeTraverser<CodeBlock> {
private final Types typeUtils;
private final ProcessorContext context;
private final Map<String, Integer> typeVarMap;
private final String decoderAccessor;
private final String readerAccessor;
private final String readerMethod;
private final String decoderRegistryVarName;
private final String scaleRegistryVarName;
private final TypeMirror genericArrayType;

public DecoderCompositor(@NonNull Types typeUtils,
public DecoderCompositor(@NonNull ProcessorContext context,
@NonNull Map<String, Integer> typeVarMap,
@NonNull String decoderAccessor,
@NonNull String readerAccessor,
@NonNull String readerMethod,
@NonNull String decoderRegistryVarName,
@NonNull String scaleRegistryVarName) {
super(CodeBlock.class);
this.typeUtils = typeUtils;

this.context = context;
this.typeVarMap = typeVarMap;
this.decoderAccessor = decoderAccessor;
this.readerAccessor = readerAccessor;
this.readerMethod = readerMethod;
this.decoderRegistryVarName = decoderRegistryVarName;
this.scaleRegistryVarName = scaleRegistryVarName;
this.genericArrayType = context.erasure(context.getType(Constants.GENERIC_ARRAY_TYPE));
}

@Override
protected CodeBlock whenTypeVar(@NonNull TypeVariable type, TypeMirror _override) {
private CodeBlock getTypeVarCodeBlock(TypeVariable type) {
return CodeBlock.builder()
.add("$T.$L(($T) ", DecoderPair.class, PAIR_FACTORY_METHOD, RpcDecoder.class)
.add(decoderAccessor, typeVarMap.get(type.toString()))
Expand All @@ -57,16 +57,6 @@ protected CodeBlock whenTypeVar(@NonNull TypeVariable type, TypeMirror _override
.build();
}

@Override
protected CodeBlock whenPrimitiveType(@NonNull PrimitiveType type, TypeMirror _override) {
return getNonGenericCodeBlock(type);
}

@Override
protected CodeBlock whenNonGenericType(@NonNull DeclaredType type, TypeMirror _override) {
return getNonGenericCodeBlock(type);
}

private CodeBlock getNonGenericCodeBlock(TypeMirror type) {
return CodeBlock.builder()
.add("$T.$L(", DecoderPair.class, PAIR_FACTORY_METHOD)
Expand All @@ -77,9 +67,8 @@ private CodeBlock getNonGenericCodeBlock(TypeMirror type) {
.build();
}

@Override
protected CodeBlock whenGenericType(@NonNull DeclaredType type, TypeMirror _override, @NonNull CodeBlock[] subtypes) {
TypeMirror resolveType = typeUtils.erasure(type);
private CodeBlock getGenericCodeBlock(TypeMirror type, CodeBlock[] subtypes) {
TypeMirror resolveType = context.erasure(type);

var builder = CodeBlock.builder()
.add("$T.$L(", DecoderPair.class, PAIR_FACTORY_METHOD)
Expand All @@ -100,4 +89,34 @@ protected CodeBlock whenGenericType(@NonNull DeclaredType type, TypeMirror _over
return builder
.add(")").build();
}

@Override
protected CodeBlock whenTypeVar(@NonNull TypeVariable type, TypeMirror _override) {
return getTypeVarCodeBlock(type);
}

@Override
protected CodeBlock whenPrimitiveType(@NonNull PrimitiveType type, TypeMirror _override) {
return getNonGenericCodeBlock(type);
}

@Override
protected CodeBlock whenNonGenericType(@NonNull DeclaredType type, TypeMirror _override) {
return getNonGenericCodeBlock(type);
}

@Override
protected CodeBlock whenGenericType(@NonNull DeclaredType type, TypeMirror _override, @NonNull CodeBlock[] subtypes) {
return getGenericCodeBlock(type, subtypes);
}

@Override
protected CodeBlock whenArrayPrimitiveType(@NonNull ArrayType type, TypeMirror _override) {
return getNonGenericCodeBlock(type);
}

@Override
protected CodeBlock whenArrayType(@NonNull ArrayType type, TypeMirror _override, @NonNull CodeBlock subtype) {
return getGenericCodeBlock(genericArrayType, new CodeBlock[]{subtype});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,17 @@ private void addMethodBody(MethodSpec.Builder methodSpec, ProcessorContext conte
}

private void setFields(MethodSpec.Builder methodSpec, ProcessorContext context) throws ProcessingException {
val decoderCompositor = new DecoderCompositor(context.getTypeUtils(),
val decoderCompositor = new DecoderCompositor(
context,
typeVarMap,
String.format("%s[$L].%s", DECODERS_ARG, DECODER_UNSAFE_ACCESSOR),
String.format("%s[$L].%s", DECODERS_ARG, READER_UNSAFE_ACCESSOR),
READER_UNSAFE_ACCESSOR,
DECODER_REGISTRY,
SCALE_READER_REGISTRY);
val scaleAnnotationParser = new ScaleAnnotationParser(context);
val scaleReaderCompositor = ReaderCompositor.forAnyType(context,
val scaleReaderCompositor = ReaderCompositor.forAnyType(
context,
typeVarMap,
String.format("%s[$L].%s", DECODERS_ARG, READER_ACCESSOR),
SCALE_READER_REGISTRY);
Expand Down
Loading

0 comments on commit 3257336

Please sign in to comment.