Skip to content

Commit 9eebd65

Browse files
committed
make use of generic type info in *Utils classes
1 parent d0d0cd8 commit 9eebd65

File tree

5 files changed

+59
-203
lines changed

5 files changed

+59
-203
lines changed

sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/AutoValueUtils.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -347,11 +347,10 @@ public ByteCodeAppender appender(final Target implementationTarget) {
347347

348348
TypeConversion<Type> convertType = typeConversionsFactory.createTypeConversion(true);
349349
for (int i = 0; i < setters.size(); ++i) {
350-
Method setterMethod = checkNotNull(setters.get(i).getMethod());
351-
Parameter parameter = setterMethod.getParameters()[0];
350+
FieldValueTypeInformation setterType = setters.get(i);
351+
Method setterMethod = checkNotNull(setterType.getMethod());
352352
ForLoadedType convertedType =
353-
new ForLoadedType(
354-
(Class) convertType.convert(TypeDescriptor.of(parameter.getParameterizedType())));
353+
new ForLoadedType((Class) convertType.convert(setterType.getType()));
355354

356355
StackManipulation readParameter =
357356
new StackManipulation.Compound(
@@ -366,7 +365,7 @@ public ByteCodeAppender appender(final Target implementationTarget) {
366365
Duplication.SINGLE,
367366
typeConversionsFactory
368367
.createSetterConversions(readParameter)
369-
.convert(TypeDescriptor.of(parameter.getType())),
368+
.convert(setterType.getType()),
370369
MethodInvocation.invoke(new ForLoadedMethod(setterMethod)),
371370
Removal.SINGLE);
372371
}

sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/ByteBuddyUtils.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -426,12 +426,20 @@ protected Type convertDefault(TypeDescriptor<?> type) {
426426
return returnRawTypes ? type.getRawType() : type.getType();
427427
}
428428

429+
public static TypeDescriptor<?> primitiveToWrapper(TypeDescriptor<?> typeDescriptor) {
430+
Class<?> cls = typeDescriptor.getRawType();
431+
if (cls.isPrimitive()) {
432+
return TypeDescriptor.of(ClassUtils.primitiveToWrapper(cls));
433+
} else {
434+
return typeDescriptor;
435+
}
436+
}
437+
429438
@SuppressWarnings("unchecked")
430439
private <ElementT> TypeDescriptor<Collection<ElementT>> createCollectionType(
431440
TypeDescriptor<?> componentType) {
432441
TypeDescriptor<ElementT> wrappedComponentType =
433-
(TypeDescriptor<ElementT>)
434-
TypeDescriptor.of(ClassUtils.primitiveToWrapper(componentType.getRawType()));
442+
(TypeDescriptor<ElementT>) primitiveToWrapper(componentType);
435443
return new TypeDescriptor<Collection<ElementT>>() {}.where(
436444
new TypeParameter<ElementT>() {}, wrappedComponentType);
437445
}
@@ -440,8 +448,7 @@ private <ElementT> TypeDescriptor<Collection<ElementT>> createCollectionType(
440448
private <ElementT> TypeDescriptor<Iterable<ElementT>> createIterableType(
441449
TypeDescriptor<?> componentType) {
442450
TypeDescriptor<ElementT> wrappedComponentType =
443-
(TypeDescriptor<ElementT>)
444-
TypeDescriptor.of(ClassUtils.primitiveToWrapper(componentType.getRawType()));
451+
(TypeDescriptor<ElementT>) primitiveToWrapper(componentType);
445452
return new TypeDescriptor<Iterable<ElementT>>() {}.where(
446453
new TypeParameter<ElementT>() {}, wrappedComponentType);
447454
}
@@ -1510,10 +1517,10 @@ public ByteCodeAppender appender(final Target implementationTarget) {
15101517
// Push all creator parameters on the stack.
15111518
TypeConversion<Type> convertType = typeConversionsFactory.createTypeConversion(true);
15121519
for (int i = 0; i < parameters.size(); i++) {
1513-
Parameter parameter = parameters.get(i);
1520+
FieldValueTypeInformation fieldType =
1521+
fields.get(Preconditions.checkNotNull(fieldMapping.get(i)));
15141522
ForLoadedType convertedType =
1515-
new ForLoadedType(
1516-
(Class) convertType.convert(TypeDescriptor.of(parameter.getType())));
1523+
new ForLoadedType((Class) convertType.convert(fieldType.getType()));
15171524

15181525
// The instruction to read the parameter. Use the fieldMapping to reorder parameters as
15191526
// necessary.
@@ -1528,7 +1535,7 @@ public ByteCodeAppender appender(final Target implementationTarget) {
15281535
stackManipulation,
15291536
typeConversionsFactory
15301537
.createSetterConversions(readParameter)
1531-
.convert(TypeDescriptor.of(parameter.getParameterizedType())));
1538+
.convert(fieldType.getType()));
15321539
}
15331540
stackManipulation =
15341541
new StackManipulation.Compound(

sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/JavaBeanUtils.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import net.bytebuddy.ByteBuddy;
3232
import net.bytebuddy.asm.AsmVisitorWrapper;
3333
import net.bytebuddy.description.method.MethodDescription.ForLoadedMethod;
34+
import net.bytebuddy.description.type.TypeDescription;
3435
import net.bytebuddy.dynamic.DynamicType;
3536
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
3637
import net.bytebuddy.implementation.FixedValue;
@@ -39,6 +40,7 @@
3940
import net.bytebuddy.implementation.bytecode.ByteCodeAppender.Size;
4041
import net.bytebuddy.implementation.bytecode.Removal;
4142
import net.bytebuddy.implementation.bytecode.StackManipulation;
43+
import net.bytebuddy.implementation.bytecode.assign.TypeCasting;
4244
import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
4345
import net.bytebuddy.implementation.bytecode.member.MethodReturn;
4446
import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
@@ -439,6 +441,13 @@ public ByteCodeAppender appender(final Target implementationTarget) {
439441
return (methodVisitor, implementationContext, instrumentedMethod) -> {
440442
// this + method parameters.
441443
int numLocals = 1 + instrumentedMethod.getParameters().size();
444+
StackManipulation cast =
445+
typeInformation
446+
.getRawType()
447+
.isAssignableFrom(
448+
Preconditions.checkNotNull(typeInformation.getMethod()).getReturnType())
449+
? StackManipulation.Trivial.INSTANCE
450+
: TypeCasting.to(TypeDescription.ForLoadedType.of(typeInformation.getRawType()));
442451

443452
// StackManipulation that will read the value from the class field.
444453
StackManipulation readValue =
@@ -449,7 +458,8 @@ public ByteCodeAppender appender(final Target implementationTarget) {
449458
MethodInvocation.invoke(
450459
new ForLoadedMethod(
451460
Preconditions.checkNotNull(
452-
typeInformation.getMethod(), GETTER_WITH_NULL_METHOD_ERROR))));
461+
typeInformation.getMethod(), GETTER_WITH_NULL_METHOD_ERROR))),
462+
cast);
453463

454464
StackManipulation stackManipulation =
455465
new StackManipulation.Compound(

sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/POJOUtils.java

Lines changed: 29 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import net.bytebuddy.ByteBuddy;
3131
import net.bytebuddy.asm.AsmVisitorWrapper;
3232
import net.bytebuddy.description.field.FieldDescription.ForLoadedField;
33+
import net.bytebuddy.description.type.TypeDescription;
3334
import net.bytebuddy.description.type.TypeDescription.ForLoadedType;
3435
import net.bytebuddy.dynamic.DynamicType;
3536
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
@@ -151,18 +152,13 @@ private static <T> SchemaUserTypeCreator createSetFieldCreator(
151152
Schema schema,
152153
List<FieldValueTypeInformation> types,
153154
TypeConversionsFactory typeConversionsFactory) {
154-
// Get the list of class fields ordered by schema.
155-
List<Field> fields =
156-
types.stream()
157-
.map(type -> Preconditions.checkNotNull(type.getField()))
158-
.collect(Collectors.toList());
159155
try {
160156
DynamicType.Builder<SchemaUserTypeCreator> builder =
161157
BYTE_BUDDY
162158
.with(new InjectPackageStrategy(clazz))
163159
.subclass(SchemaUserTypeCreator.class)
164160
.method(ElementMatchers.named("create"))
165-
.intercept(new SetFieldCreateInstruction(fields, clazz, typeConversionsFactory));
161+
.intercept(new SetFieldCreateInstruction(types, clazz, typeConversionsFactory));
166162

167163
return builder
168164
.visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
@@ -305,11 +301,8 @@ public static <T> SchemaUserTypeCreator createStaticCreator(
305301
ByteBuddyUtils.subclassGetterInterface(
306302
BYTE_BUDDY,
307303
field.getDeclaringClass(),
308-
typeConversionsFactory
309-
.createTypeConversion(false)
310-
.convert(TypeDescriptor.of(field.getType())));
311-
builder =
312-
implementGetterMethods(builder, field, typeInformation.getName(), typeConversionsFactory);
304+
typeConversionsFactory.createTypeConversion(false).convert(typeInformation.getType()));
305+
builder = implementGetterMethods(builder, typeInformation, typeConversionsFactory);
313306
try {
314307
return builder
315308
.visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
@@ -331,107 +324,25 @@ public static <T> SchemaUserTypeCreator createStaticCreator(
331324
private static <ObjectT, ValueT>
332325
DynamicType.Builder<FieldValueGetter<@NonNull ObjectT, ValueT>> implementGetterMethods(
333326
DynamicType.Builder<FieldValueGetter<@NonNull ObjectT, ValueT>> builder,
334-
Field field,
335-
String name,
327+
FieldValueTypeInformation typeInformation,
336328
TypeConversionsFactory typeConversionsFactory) {
337329
return builder
338330
.visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
339331
.method(ElementMatchers.named("name"))
340-
.intercept(FixedValue.reference(name))
332+
.intercept(FixedValue.reference(typeInformation.getName()))
341333
.method(ElementMatchers.named("get"))
342-
.intercept(new ReadFieldInstruction(field, typeConversionsFactory));
343-
}
344-
345-
// The list of setters for a class is cached, so we only create the classes the first time
346-
// getSetters is called.
347-
private static final Map<TypeDescriptorWithSchema<?>, List<FieldValueSetter<?, ?>>>
348-
CACHED_SETTERS = Maps.newConcurrentMap();
349-
350-
public static <T> List<FieldValueSetter<@NonNull T, Object>> getSetters(
351-
TypeDescriptor<T> typeDescriptor,
352-
Schema schema,
353-
FieldValueTypeSupplier fieldValueTypeSupplier,
354-
TypeConversionsFactory typeConversionsFactory) {
355-
// Return the setters, ordered by their position in the schema.
356-
return (List)
357-
CACHED_SETTERS.computeIfAbsent(
358-
TypeDescriptorWithSchema.create(typeDescriptor, schema),
359-
c -> {
360-
List<FieldValueTypeInformation> types =
361-
fieldValueTypeSupplier.get(typeDescriptor, schema);
362-
return types.stream()
363-
.map(t -> createSetter(t, typeConversionsFactory))
364-
.collect(Collectors.toList());
365-
});
366-
}
367-
368-
/**
369-
* Generate the following {@link FieldValueSetter} class for the {@link Field}.
370-
*
371-
* <pre><code>
372-
* class Setter implements {@literal FieldValueSetter<POJO, FieldType>} {
373-
* {@literal @}Override public String name() { return field.getName(); }
374-
* {@literal @}Override public Class type() { return field.getType(); }
375-
* {@literal @}Override public Type elementType() { return elementType; }
376-
* {@literal @}Override public Type mapKeyType() { return mapKeyType; }
377-
* {@literal @}Override public Type mapValueType() { return mapValueType; }
378-
* {@literal @}Override public void set(POJO pojo, FieldType value) {
379-
* pojo.field = convert(value);
380-
* }
381-
* }
382-
* </code></pre>
383-
*/
384-
@SuppressWarnings("unchecked")
385-
private static <ObjectT, ValueT> FieldValueSetter<ObjectT, ValueT> createSetter(
386-
FieldValueTypeInformation typeInformation, TypeConversionsFactory typeConversionsFactory) {
387-
Field field = Preconditions.checkNotNull(typeInformation.getField());
388-
DynamicType.Builder<FieldValueSetter<ObjectT, ValueT>> builder =
389-
ByteBuddyUtils.subclassSetterInterface(
390-
BYTE_BUDDY,
391-
field.getDeclaringClass(),
392-
typeConversionsFactory
393-
.createTypeConversion(false)
394-
.convert(TypeDescriptor.of(field.getType())));
395-
builder = implementSetterMethods(builder, field, typeConversionsFactory);
396-
try {
397-
return builder
398-
.visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
399-
.make()
400-
.load(
401-
ReflectHelpers.findClassLoader(field.getDeclaringClass().getClassLoader()),
402-
getClassLoadingStrategy(field.getDeclaringClass()))
403-
.getLoaded()
404-
.getDeclaredConstructor()
405-
.newInstance();
406-
} catch (InstantiationException
407-
| IllegalAccessException
408-
| NoSuchMethodException
409-
| InvocationTargetException e) {
410-
throw new RuntimeException("Unable to generate a getter for field '" + field + "'.", e);
411-
}
412-
}
413-
414-
private static <ObjectT, ValueT>
415-
DynamicType.Builder<FieldValueSetter<ObjectT, ValueT>> implementSetterMethods(
416-
DynamicType.Builder<FieldValueSetter<ObjectT, ValueT>> builder,
417-
Field field,
418-
TypeConversionsFactory typeConversionsFactory) {
419-
return builder
420-
.visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
421-
.method(ElementMatchers.named("name"))
422-
.intercept(FixedValue.reference(field.getName()))
423-
.method(ElementMatchers.named("set"))
424-
.intercept(new SetFieldInstruction(field, typeConversionsFactory));
334+
.intercept(new ReadFieldInstruction(typeInformation, typeConversionsFactory));
425335
}
426336

427337
// Implements a method to read a public field out of an object.
428338
static class ReadFieldInstruction implements Implementation {
429339
// Field that will be read.
430-
private final Field field;
340+
private final FieldValueTypeInformation typeInformation;
431341
private final TypeConversionsFactory typeConversionsFactory;
432342

433-
ReadFieldInstruction(Field field, TypeConversionsFactory typeConversionsFactory) {
434-
this.field = field;
343+
ReadFieldInstruction(
344+
FieldValueTypeInformation typeInformation, TypeConversionsFactory typeConversionsFactory) {
345+
this.typeInformation = typeInformation;
435346
this.typeConversionsFactory = typeConversionsFactory;
436347
}
437348

@@ -446,19 +357,25 @@ public ByteCodeAppender appender(final Target implementationTarget) {
446357
// this + method parameters.
447358
int numLocals = 1 + instrumentedMethod.getParameters().size();
448359

360+
StackManipulation cast =
361+
typeInformation.getRawType().isAssignableFrom(typeInformation.getField().getType())
362+
? StackManipulation.Trivial.INSTANCE
363+
: TypeCasting.to(TypeDescription.ForLoadedType.of(typeInformation.getRawType()));
364+
449365
// StackManipulation that will read the value from the class field.
450366
StackManipulation readValue =
451367
new StackManipulation.Compound(
452368
// Method param is offset 1 (offset 0 is the this parameter).
453369
MethodVariableAccess.REFERENCE.loadFrom(1),
454370
// Read the field from the object.
455-
FieldAccess.forField(new ForLoadedField(field)).read());
371+
FieldAccess.forField(new ForLoadedField(typeInformation.getField())).read(),
372+
cast);
456373

457374
StackManipulation stackManipulation =
458375
new StackManipulation.Compound(
459376
typeConversionsFactory
460377
.createGetterConversions(readValue)
461-
.convert(TypeDescriptor.of(field.getGenericType())),
378+
.convert(typeInformation.getType()),
462379
MethodReturn.REFERENCE);
463380

464381
StackManipulation.Size size = stackManipulation.apply(methodVisitor, implementationContext);
@@ -513,13 +430,15 @@ public ByteCodeAppender appender(final Target implementationTarget) {
513430

514431
// Implements a method to construct an object.
515432
static class SetFieldCreateInstruction implements Implementation {
516-
private final List<Field> fields;
433+
private final List<FieldValueTypeInformation> typeInformations;
517434
private final Class<?> pojoClass;
518435
private final TypeConversionsFactory typeConversionsFactory;
519436

520437
SetFieldCreateInstruction(
521-
List<Field> fields, Class<?> pojoClass, TypeConversionsFactory typeConversionsFactory) {
522-
this.fields = fields;
438+
List<FieldValueTypeInformation> typeInformations,
439+
Class<?> pojoClass,
440+
TypeConversionsFactory typeConversionsFactory) {
441+
this.typeInformations = typeInformations;
523442
this.pojoClass = pojoClass;
524443
this.typeConversionsFactory = typeConversionsFactory;
525444
}
@@ -551,11 +470,12 @@ public ByteCodeAppender appender(final Target implementationTarget) {
551470
// The types in the POJO might be the types returned by Beam's Row class,
552471
// so we have to convert the types used by Beam's Row class.
553472
TypeConversion<Type> convertType = typeConversionsFactory.createTypeConversion(true);
554-
for (int i = 0; i < fields.size(); ++i) {
555-
Field field = fields.get(i);
473+
for (int i = 0; i < typeInformations.size(); ++i) {
474+
FieldValueTypeInformation typeInformation = typeInformations.get(i);
475+
Field field = typeInformation.getField();
556476

557477
ForLoadedType convertedType =
558-
new ForLoadedType((Class) convertType.convert(TypeDescriptor.of(field.getType())));
478+
new ForLoadedType((Class) convertType.convert(typeInformation.getType()));
559479

560480
// The instruction to read the parameter.
561481
StackManipulation readParameter =
@@ -572,7 +492,7 @@ public ByteCodeAppender appender(final Target implementationTarget) {
572492
// Do any conversions necessary.
573493
typeConversionsFactory
574494
.createSetterConversions(readParameter)
575-
.convert(TypeDescriptor.of(field.getType())),
495+
.convert(typeInformation.getType()),
576496
// Now update the field.
577497
FieldAccess.forField(new ForLoadedField(field)).write());
578498
stackManipulation = new StackManipulation.Compound(stackManipulation, updateField);

0 commit comments

Comments
 (0)