Skip to content

Commit

Permalink
Support synthetic classes/fields/methods (#218)
Browse files Browse the repository at this point in the history
  • Loading branch information
dstepanov authored Jan 8, 2025
1 parent 86e5c40 commit 2637369
Show file tree
Hide file tree
Showing 14 changed files with 141 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_RECORD;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
import static org.objectweb.asm.Opcodes.V17;

/**
Expand Down Expand Up @@ -141,6 +142,9 @@ private MethodDef createStaticInitializer(StatementDef statement) {
*/
public void writeField(ClassVisitor classVisitor, ObjectDef objectDef, FieldDef fieldDef) {
int modifiersFlag = getModifiersFlag(fieldDef.getModifiers());
if (fieldDef.isSynthetic()) {
modifiersFlag |= ACC_SYNTHETIC;
}
if (EnumGenUtils.isEnumField(objectDef, fieldDef)) {
modifiersFlag |= ACC_ENUM;
}
Expand All @@ -166,8 +170,12 @@ public void writeField(ClassVisitor classVisitor, ObjectDef objectDef, FieldDef
* @param outerType The outer type
*/
public void writeInterface(ClassVisitor classVisitor, InterfaceDef interfaceDef, @Nullable ClassTypeDef outerType) {
int modifiersFlag = ACC_INTERFACE | ACC_ABSTRACT | getModifiersFlag(interfaceDef.getModifiers());
if (interfaceDef.isSynthetic()) {
modifiersFlag |= ACC_SYNTHETIC;
}
classVisitor.visit(V17,
ACC_INTERFACE | ACC_ABSTRACT | getModifiersFlag(interfaceDef.getModifiers()),
modifiersFlag,
TypeUtils.getType(interfaceDef.asTypeDef()).getInternalName(),
SignatureWriterUtils.getInterfaceSignature(interfaceDef),
TypeUtils.OBJECT_TYPE.getInternalName(),
Expand Down Expand Up @@ -204,9 +212,13 @@ public void writeRecord(ClassVisitor classVisitor, RecordDef recordDef) {
* @param outerType The outer type
*/
public void writeRecord(ClassVisitor classVisitor, RecordDef recordDef, @Nullable ClassTypeDef outerType) {
int modifiersFlag = ACC_RECORD | getModifiersFlag(recordDef.getModifiers());
if (recordDef.isSynthetic()) {
modifiersFlag |= ACC_SYNTHETIC;
}
classVisitor.visit(
V17,
ACC_RECORD | getModifiersFlag(recordDef.getModifiers()),
modifiersFlag,
TypeUtils.getType(recordDef.asTypeDef()).getInternalName(),
SignatureWriterUtils.getRecordSignature(recordDef),
Type.getType(Record.class).getInternalName(),
Expand Down Expand Up @@ -237,6 +249,9 @@ public void writeClass(ClassVisitor classVisitor, ClassDef classDef, @Nullable C

int modifiersFlag = getModifiersFlag(classDef.getModifiers());

if (classDef.isSynthetic()) {
modifiersFlag |= ACC_SYNTHETIC;
}
if (EnumGenUtils.isEnum(classDef)) {
modifiersFlag |= ACC_ENUM;
}
Expand Down Expand Up @@ -426,15 +441,17 @@ private void writeProperty(ClassVisitor classWriter, ObjectDef objectDef, Proper
public void writeMethod(ClassVisitor classVisitor, @Nullable ObjectDef objectDef, MethodDef methodDef) {
String name = methodDef.getName();
String methodDescriptor = TypeUtils.getMethodDescriptor(objectDef, methodDef);
int access = getModifiersFlag(methodDef.getModifiers());

int modifiersFlag = getModifiersFlag(methodDef.getModifiers());
if (methodDef.isSynthetic()) {
modifiersFlag |= ACC_SYNTHETIC;
}
GeneratorAdapter generatorAdapter = new GeneratorAdapter(classVisitor.visitMethod(
access,
modifiersFlag,
name,
methodDescriptor,
SignatureWriterUtils.getMethodSignature(objectDef, methodDef),
null
), access, name, methodDescriptor);
), modifiersFlag, name, methodDescriptor);
for (AnnotationDef annotation : methodDef.getAnnotations()) {
generatorAdapter.visitAnnotation(TypeUtils.getType(annotation.getType(), null).getDescriptor(), true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ public static ClassDef toClassDef(EnumDef enumDef) {
.addSuperinterfaces(enumDef.getSuperinterfaces())
.addInnerType(enumDef.getInnerTypes());

if (enumDef.isSynthetic()) {
classDefBuilder.synthetic();
}

int i = 0;
for (Map.Entry<String, List<ExpressionDef>> e : enumDef.getEnumConstants().entrySet()) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import io.micronaut.sourcegen.model.StatementDef;
import io.micronaut.sourcegen.model.TypeDef;
import io.micronaut.sourcegen.model.VariableDef;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.util.CheckClassAdapter;
Expand All @@ -31,6 +32,60 @@

class ByteCodeWriterTest {

@Test
void testSynthetic() {
ClassDef def = ClassDef.builder("example.Example")
.addModifiers(Modifier.PUBLIC)
.synthetic()
.addField(FieldDef.builder("myField", String.class).synthetic().build())
.addMethod(MethodDef.builder("myMethod").synthetic().returns(int.class)
.build((aThis, methodParameters) -> ExpressionDef.constant(1).returning()))
.build();

StringWriter bytecodeWriter = new StringWriter();
byte[] bytes = generateFile(def, bytecodeWriter);

String bytecode = bytecodeWriter.toString();
Assertions.assertEquals("""
// class version 61.0 (61)
// access flags 0x1001
// signature Ljava/lang/Object;
// declaration: example/Example
public synthetic class example/Example {
// access flags 0x1000
synthetic Ljava/lang/String; myField
// access flags 0x1
public <init>()V
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
// access flags 0x1000
synthetic myMethod()I
ICONST_1
IRETURN
}
""", bytecode);

Assertions.assertEquals("""
package example;
// $FF: synthetic class
public class Example {
// $FF: synthetic field
String myField;
// $FF: synthetic method
int myMethod() {
return 1;
}
}
""", decompileToJava(bytes));
}

@Test
void testDefaultPublicConstructor() {
ClassDef def = ClassDef.builder("example.Example")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,21 @@ abstract sealed class AbstractElement permits ObjectDef, FieldDef, MethodDef, Pa
protected final EnumSet<Modifier> modifiers;
protected final List<AnnotationDef> annotations;
protected final List<String> javadoc;
protected final boolean synthetic;

AbstractElement(String name, EnumSet<Modifier> modifiers, List<AnnotationDef> annotations, List<String> javadoc) {
AbstractElement(String name, EnumSet<Modifier> modifiers, List<AnnotationDef> annotations, List<String> javadoc, boolean synthetic) {
this.name = name;
this.modifiers = modifiers;
this.annotations = Collections.unmodifiableList(annotations);
this.javadoc = Collections.unmodifiableList(javadoc);
this.synthetic = synthetic;
}

/**
* @return Is synthetic element
*/
public boolean isSynthetic() {
return synthetic;
}

public final String getName() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,23 @@ public sealed class AbstractElementBuilder<ThisType> permits ObjectDefBuilder, F
protected final List<AnnotationDef> annotations = new ArrayList<>();
protected final List<String> javadoc = new ArrayList<>();
protected final ThisType thisInstance;
protected boolean synthetic;

protected AbstractElementBuilder(String name) {
this.name = name;
this.thisInstance = (ThisType) this;
}

/**
* Marks the element as synthetic.
*
* @return The builder
*/
public final ThisType synthetic() {
synthetic = true;
return thisInstance;
}

public final ThisType addModifiers(Collection<Modifier> modifiers) {
this.modifiers.addAll(modifiers);
return thisInstance;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ private ClassDef(ClassTypeDef.ClassName className,
List<TypeDef> superinterfaces,
ClassTypeDef superclass,
List<ObjectDef> innerTypes,
StatementDef staticInitializer) {
super(className, modifiers, annotations, javadoc, methods, properties, superinterfaces, innerTypes);
StatementDef staticInitializer,
boolean synthetic) {
super(className, modifiers, annotations, javadoc, methods, properties, superinterfaces, innerTypes, synthetic);
ClassTypeDef.of(this);
this.fields = fields;
this.typeVariables = typeVariables;
Expand All @@ -62,7 +63,7 @@ private ClassDef(ClassTypeDef.ClassName className,

@Override
public ClassDef withClassName(ClassTypeDef.ClassName className) {
return new ClassDef(className, modifiers, fields, methods, properties, annotations, javadoc, typeVariables, superinterfaces, superclass, innerTypes, staticInitializer);
return new ClassDef(className, modifiers, fields, methods, properties, annotations, javadoc, typeVariables, superinterfaces, superclass, innerTypes, staticInitializer, synthetic);
}

@Override
Expand Down Expand Up @@ -203,7 +204,7 @@ public ClassDefBuilder addStaticInitializer(StatementDef staticInitializer) {
}

public ClassDef build() {
return new ClassDef(new ClassTypeDef.ClassName(name), modifiers, fields, methods, properties, annotations, javadoc, typeVariables, superinterfaces, superclass, innerTypes, staticInitializer);
return new ClassDef(new ClassTypeDef.ClassName(name), modifiers, fields, methods, properties, annotations, javadoc, typeVariables, superinterfaces, superclass, innerTypes, staticInitializer, synthetic);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,16 @@ private EnumDef(ClassTypeDef.ClassName className,
List<String> javadoc,
LinkedHashMap<String, List<ExpressionDef>> enumConstants,
List<TypeDef> superinterfaces,
List<ObjectDef> innerTypes) {
super(className, modifiers, annotations, javadoc, methods, properties, superinterfaces, innerTypes);
List<ObjectDef> innerTypes,
boolean synthetic) {
super(className, modifiers, annotations, javadoc, methods, properties, superinterfaces, innerTypes, synthetic);
this.fields = fields;
this.enumConstants = enumConstants;
}

@Override
public EnumDef withClassName(ClassTypeDef.ClassName className) {
return new EnumDef(className, modifiers, fields, methods, properties, annotations, javadoc, enumConstants, superinterfaces, innerTypes);
return new EnumDef(className, modifiers, fields, methods, properties, annotations, javadoc, enumConstants, superinterfaces, innerTypes, synthetic);
}

public static EnumDefBuilder builder(String name) {
Expand Down Expand Up @@ -172,7 +173,7 @@ public EnumDef build() {
}
}
}
return new EnumDef(new ClassTypeDef.ClassName(name), modifiers, fields, methods, properties, annotations, javadoc, enumConstants, superinterfaces, innerTypes);
return new EnumDef(new ClassTypeDef.ClassName(name), modifiers, fields, methods, properties, annotations, javadoc, enumConstants, superinterfaces, innerTypes, synthetic);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ private FieldDef(String name,
TypeDef type,
ExpressionDef initializer,
List<AnnotationDef> annotations,
List<String> javadoc) {
super(name, modifiers, annotations, javadoc);
List<String> javadoc,
boolean synthetic) {
super(name, modifiers, annotations, javadoc, synthetic);
this.type = type;
this.initializer = initializer;
}
Expand Down Expand Up @@ -120,7 +121,7 @@ public FieldDefBuilder ofType(TypeDef type) {
public FieldDef build() {
Objects.requireNonNull(name, "Name cannot be null");
Objects.requireNonNull(type, "Type cannot be null");
return new FieldDef(name, modifiers, type, initializer, annotations, javadoc);
return new FieldDef(name, modifiers, type, initializer, annotations, javadoc, synthetic);
}

public FieldDefBuilder initializer(ExpressionDef expr) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,15 @@ private InterfaceDef(ClassTypeDef.ClassName className,
List<String> javadoc,
List<TypeDef.TypeVariable> typeVariables,
List<TypeDef> superinterfaces,
List<ObjectDef> innerTypes) {
super(className, modifiers, annotations, javadoc, methods, properties, superinterfaces, innerTypes);
List<ObjectDef> innerTypes,
boolean synthetic) {
super(className, modifiers, annotations, javadoc, methods, properties, superinterfaces, innerTypes, synthetic);
this.typeVariables = typeVariables;
}

@Override
public InterfaceDef withClassName(ClassTypeDef.ClassName className) {
return new InterfaceDef(className, modifiers, methods, properties, annotations, javadoc, typeVariables, superinterfaces, innerTypes);
return new InterfaceDef(className, modifiers, methods, properties, annotations, javadoc, typeVariables, superinterfaces, innerTypes, synthetic);
}

@Override
Expand Down Expand Up @@ -88,7 +89,7 @@ public InterfaceDefBuilder addTypeVariable(TypeDef.TypeVariable typeVariable) {
}

public InterfaceDef build() {
return new InterfaceDef(new ClassTypeDef.ClassName(name), modifiers, methods, properties, annotations, javadoc, typeVariables, superinterfaces, innerTypes);
return new InterfaceDef(new ClassTypeDef.ClassName(name), modifiers, methods, properties, annotations, javadoc, typeVariables, superinterfaces, innerTypes, synthetic);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ public final class MethodDef extends AbstractElement {
List<StatementDef> statements,
List<AnnotationDef> annotations,
List<String> javadoc,
boolean override) {
super(name, modifiers, annotations, javadoc);
boolean override,
boolean synthetic) {
super(name, modifiers, annotations, javadoc, synthetic);
this.returnType = Objects.requireNonNullElse(returnType, TypeDef.VOID);
this.parameters = Collections.unmodifiableList(parameters);
this.statements = statements;
Expand Down Expand Up @@ -492,7 +493,7 @@ public MethodDef build() {
if (returnType == null && !name.equals(CONSTRUCTOR)) {
returnType = TypeDef.VOID;
}
return new MethodDef(name, modifiers, returnType, parameters, statements, annotations, javadoc, overrides);
return new MethodDef(name, modifiers, returnType, parameters, statements, annotations, javadoc, overrides, synthetic);
}

private static TypeDef findReturnType(StatementDef statement) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ public abstract sealed class ObjectDef extends AbstractElement permits ClassDef,
List<MethodDef> methods,
List<PropertyDef> properties,
List<TypeDef> superinterfaces,
List<ObjectDef> innerTypes
List<ObjectDef> innerTypes,
boolean synthetic
) {
super(className.getName(), modifiers, annotations, javadoc);
super(className.getName(), modifiers, annotations, javadoc, synthetic);
this.className = className;
this.methods = methods;
this.properties = properties;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public final class ParameterDef extends AbstractElement {
private ParameterDef(String name, EnumSet<Modifier> modifiers,
List<AnnotationDef> annotations,
List<String> javadoc,
TypeDef type) {
super(name, modifiers, annotations, javadoc);
TypeDef type, boolean synthetic) {
super(name, modifiers, annotations, javadoc, synthetic);
this.type = type;
}

Expand Down Expand Up @@ -81,7 +81,7 @@ private ParameterDefBuilder(String name, TypeDef type) {
}

public ParameterDef build() {
return new ParameterDef(name, modifiers, annotations, javadoc, type);
return new ParameterDef(name, modifiers, annotations, javadoc, type, synthetic);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ private PropertyDef(String name,
EnumSet<Modifier> modifiers,
TypeDef type,
List<AnnotationDef> annotations,
List<String> javadoc) {
super(name, modifiers, annotations, javadoc);
List<String> javadoc,
boolean synthetic) {
super(name, modifiers, annotations, javadoc, synthetic);
if (type == null) {
throw new IllegalStateException("The type of property: " + name + " is not specified!");
}
Expand Down Expand Up @@ -77,7 +78,7 @@ public PropertyDefBuilder ofType(Class<?> type) {
}

public PropertyDef build() {
return new PropertyDef(name, modifiers, type, annotations, javadoc);
return new PropertyDef(name, modifiers, type, annotations, javadoc, synthetic);
}

}
Expand Down
Loading

0 comments on commit 2637369

Please sign in to comment.