3030import net .bytebuddy .ByteBuddy ;
3131import net .bytebuddy .asm .AsmVisitorWrapper ;
3232import net .bytebuddy .description .field .FieldDescription .ForLoadedField ;
33+ import net .bytebuddy .description .type .TypeDescription ;
3334import net .bytebuddy .description .type .TypeDescription .ForLoadedType ;
3435import net .bytebuddy .dynamic .DynamicType ;
3536import 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