Reduce unchecked warnings
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / CodecDataObjectGenerator.java
1 /*
2  * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.binding.dom.codec.impl;
9
10 import static com.google.common.base.Verify.verify;
11 import static com.google.common.base.Verify.verifyNotNull;
12 import static java.util.Objects.requireNonNull;
13 import static net.bytebuddy.implementation.bytecode.member.MethodVariableAccess.loadThis;
14 import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.getField;
15 import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.invokeMethod;
16 import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.putField;
17
18 import com.google.common.collect.ImmutableMap;
19 import java.lang.invoke.MethodHandles;
20 import java.lang.invoke.MethodHandles.Lookup;
21 import java.lang.invoke.VarHandle;
22 import java.lang.reflect.Method;
23 import java.util.Map;
24 import java.util.Map.Entry;
25 import java.util.Optional;
26 import net.bytebuddy.ByteBuddy;
27 import net.bytebuddy.description.field.FieldDescription;
28 import net.bytebuddy.description.type.TypeDefinition;
29 import net.bytebuddy.description.type.TypeDescription;
30 import net.bytebuddy.description.type.TypeDescription.ForLoadedType;
31 import net.bytebuddy.description.type.TypeDescription.Generic;
32 import net.bytebuddy.dynamic.DynamicType.Builder;
33 import net.bytebuddy.dynamic.scaffold.InstrumentedType;
34 import net.bytebuddy.implementation.Implementation;
35 import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
36 import net.bytebuddy.implementation.bytecode.StackManipulation;
37 import net.bytebuddy.implementation.bytecode.assign.TypeCasting;
38 import net.bytebuddy.implementation.bytecode.constant.ClassConstant;
39 import net.bytebuddy.implementation.bytecode.constant.TextConstant;
40 import net.bytebuddy.implementation.bytecode.member.MethodReturn;
41 import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
42 import net.bytebuddy.jar.asm.Opcodes;
43 import org.eclipse.jdt.annotation.Nullable;
44 import org.opendaylight.mdsal.binding.dom.codec.impl.ClassGeneratorBridge.CodecContextSupplierProvider;
45 import org.opendaylight.mdsal.binding.dom.codec.impl.ClassGeneratorBridge.LocalNameProvider;
46 import org.opendaylight.mdsal.binding.loader.BindingClassLoader;
47 import org.opendaylight.mdsal.binding.loader.BindingClassLoader.ClassGenerator;
48 import org.opendaylight.mdsal.binding.loader.BindingClassLoader.GeneratorResult;
49 import org.opendaylight.yangtools.yang.binding.contract.Naming;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 /**
54  * Private support for generating {@link CodecDataObject} and {@link AugmentableCodecDataObject} specializations.
55  *
56  * <p>
57  * Code generation here is probably more involved than usual mainly due to the fact we *really* want to express the
58  * strong connection between a generated class to the extent possible. In most cases (grouping-generated types) this
59  * involves one level of indirection, which is a safe approach. If we are dealing with a type generated outside of a
60  * grouping statement, though, we are guaranteed instantiation-invariance and hence can hard-wire to a runtime-constant
61  * {@link CodecContextSupplier} -- which  provides significant boost to JITs ability to optimize code -- especially with
62  * inlining and constant propagation.
63  *
64  * <p>
65  * The accessor mapping performance is critical due to users typically not taking care of storing the results acquired
66  * by an invocation, assuming the accessors are backed by a normal field -- which of course is not true, as the results
67  * are lazily computed.
68  *
69  * <p>
70  * The design is such that for a particular structure like:
71  * <pre>
72  *     container foo {
73  *         leaf bar {
74  *             type string;
75  *         }
76  *     }
77  * </pre>
78  * we end up generating a class with the following layout:
79  * <pre>
80  *     public final class Foo$$$codecImpl extends CodecDataObject implements Foo {
81  *         private static final VarHandle getBar$$$V;
82  *         private volatile Object getBar;
83  *
84  *         public Foo$$$codecImpl(DistinctNodeContainer data) {
85  *             super(data);
86  *         }
87  *
88  *         public Bar getBar() {
89  *             return (Bar) codecMember(getBar$$$V, "bar");
90  *         }
91  *     }
92  * </pre>
93  *
94  * <p>
95  * This strategy minimizes the bytecode footprint and follows the generally good idea of keeping common logic in a
96  * single place in a maintainable form. The glue code is extremely light (~6 instructions), which is beneficial on both
97  * sides of invocation:
98  * <ul>
99  *   <li>generated method can readily be inlined into the caller</li>
100  *   <li>it forms a call site into which codeMember() can be inlined with VarHandle being constant</li>
101  * </ul>
102  *
103  * <p>
104  * The second point is important here, as it allows the invocation logic around VarHandle to completely disappear,
105  * becoming synonymous with operations on a field. Even though the field itself is declared as volatile, it is only ever
106  * accessed through helper method using VarHandles -- and those helpers are using relaxed field ordering
107  * of {@code getAcquire()}/{@code setRelease()} memory semantics.
108  *
109  * <p>
110  * Furthermore there are distinct {@code codecMember} methods, each of which supports a different invocation style:
111  * <ul>
112  *   <li>with {@code String}, which ends up looking up a {@link ValueNodeCodecContext}</li>
113  *   <li>with {@code Class}, which ends up looking up a {@link DataContainerCodecContext}</li>
114  *   <li>with {@code NodeContextSupplier}, which performs a direct load</li>
115  * </ul>
116  * The third mode of operation requires that the object being implemented is not defined in a {@code grouping}, because
117  * it welds the object to a particular namespace -- hence it trades namespace mobility for access speed.
118  *
119  * <p>
120  * The sticky point here is the NodeContextSupplier, as it is a heap object which cannot normally be looked up from the
121  * static context in which the static class initializer operates -- so we need perform some sort of a trick here.
122  * Even though ByteBuddy provides facilities for bridging references to type fields, those facilities operate on
123  * volatile fields -- hence they do not quite work for us.
124  *
125  * <p>
126  * Another alternative, which we used in Javassist-generated DataObjectSerializers, is to muck with the static field
127  * using reflection -- which works, but requires redefinition of Field.modifiers, which is something Java 9+ complains
128  * about quite noisily.
129  *
130  * <p>
131  * We take a different approach here, which takes advantage of the fact we are in control of both code generation (here)
132  * and class loading (in {@link BindingClassLoader}). The process is performed in four steps:
133  * <ul>
134  * <li>During code generation, the context fields are pointed towards
135  *     {@link ClassGeneratorBridge#resolveCodecContextSupplier(String)} and
136  *     {@link ClassGeneratorBridge#resolveKey(String)} methods, which are public and static, hence perfectly usable
137  *     in the context of a class initializer.</li>
138  * <li>During class loading of generated byte code, the original instance of the generator is called to wrap the actual
139  *     class loading operation. At this point the generator installs itself as the current generator for this thread via
140  *     {@link ClassGeneratorBridge#setup(CodecDataObjectGenerator)} and allows the class to be loaded.
141  * <li>After the class has been loaded, but before the call returns, we will force the class to initialize, at which
142  *     point the static invocations will be redirected to {@link #resolveCodecContextSupplier(String)} and
143  *     {@link #resolveKey(String)} methods, thus initializing the fields to the intended constants.</li>
144  * <li>Before returning from the class loading call, the generator will detach itself via
145  *     {@link ClassGeneratorBridge#tearDown(CodecDataObjectGenerator)}.</li>
146  * </ul>
147  *
148  * <p>
149  * This strategy works due to close cooperation with the target ClassLoader, as the entire code generation and loading
150  * block runs with the class loading lock for this FQCN and the reference is not leaked until the process completes.
151  */
152 abstract class CodecDataObjectGenerator<T extends CodecDataObject<?>> implements ClassGenerator<T> {
153     // Not reusable definition: we can inline NodeContextSuppliers without a problem
154     // FIXME: MDSAL-443: wire this implementation, which requires that BindingRuntimeTypes provides information about
155     //                   types being generated from within a grouping
156     private static final class Fixed<T extends CodecDataObject<?>> extends CodecDataObjectGenerator<T>
157             implements CodecContextSupplierProvider<T> {
158         private final ImmutableMap<Method, CodecContextSupplier> properties;
159
160         Fixed(final TypeDescription superClass, final ImmutableMap<Method, CodecContextSupplier> properties,
161                 final @Nullable Method keyMethod) {
162             super(superClass, keyMethod);
163             this.properties = requireNonNull(properties);
164         }
165
166         @Override
167         Builder<T> generateGetters(final Builder<T> builder) {
168             Builder<T> tmp = builder;
169             for (Method method : properties.keySet()) {
170                 LOG.trace("Generating for fixed method {}", method);
171                 final String methodName = method.getName();
172                 final TypeDescription retType = ForLoadedType.of(method.getReturnType());
173                 tmp = tmp.defineMethod(methodName, retType, PUB_FINAL).intercept(
174                     new SupplierGetterMethodImplementation(methodName, retType));
175             }
176             return tmp;
177         }
178
179         @Override
180         public CodecContextSupplier resolveCodecContextSupplier(final String methodName) {
181             final Optional<Entry<Method, CodecContextSupplier>> found = properties.entrySet().stream()
182                     .filter(entry -> methodName.equals(entry.getKey().getName())).findAny();
183             verify(found.isPresent(), "Failed to find property for %s in %s", methodName, this);
184             return verifyNotNull(found.orElseThrow().getValue());
185         }
186     }
187
188     // Reusable definition: we have to rely on context lookups
189     private static final class Reusable<T extends CodecDataObject<?>> extends CodecDataObjectGenerator<T>
190             implements LocalNameProvider<T> {
191         private final ImmutableMap<Method, ValueNodeCodecContext> simpleProperties;
192         private final Map<Class<?>, PropertyInfo> daoProperties;
193
194         Reusable(final TypeDescription superClass, final ImmutableMap<Method, ValueNodeCodecContext> simpleProperties,
195                 final Map<Class<?>, PropertyInfo> daoProperties, final @Nullable Method keyMethod) {
196             super(superClass, keyMethod);
197             this.simpleProperties = requireNonNull(simpleProperties);
198             this.daoProperties = requireNonNull(daoProperties);
199         }
200
201         @Override
202         Builder<T> generateGetters(final Builder<T> builder) {
203             Builder<T> tmp = builder;
204             for (Method method : simpleProperties.keySet()) {
205                 LOG.trace("Generating for simple method {}", method);
206                 final String methodName = method.getName();
207                 final TypeDescription retType = ForLoadedType.of(method.getReturnType());
208                 tmp = tmp.defineMethod(methodName, retType, PUB_FINAL).intercept(
209                     new SimpleGetterMethodImplementation(methodName, retType));
210             }
211             for (Entry<Class<?>, PropertyInfo> entry : daoProperties.entrySet()) {
212                 final PropertyInfo info = entry.getValue();
213                 final Method method = info.getterMethod();
214                 LOG.trace("Generating for structured method {}", method);
215                 final String methodName = method.getName();
216                 final TypeDescription retType = ForLoadedType.of(method.getReturnType());
217                 tmp = tmp.defineMethod(methodName, retType, PUB_FINAL).intercept(
218                         new StructuredGetterMethodImplementation(methodName, retType, entry.getKey()));
219
220                 if (info instanceof PropertyInfo.GetterAndNonnull orEmpty) {
221                     final String nonnullName = orEmpty.nonnullMethod().getName();
222                     tmp = tmp.defineMethod(nonnullName, retType, PUB_FINAL).intercept(
223                         new NonnullMethodImplementation(nonnullName, retType, entry.getKey(), method));
224                 }
225             }
226
227             return tmp;
228         }
229
230         @Override
231         public String resolveLocalName(final String methodName) {
232             final Optional<Entry<Method, ValueNodeCodecContext>> found = simpleProperties.entrySet().stream()
233                     .filter(entry -> methodName.equals(entry.getKey().getName())).findAny();
234             verify(found.isPresent(), "Failed to find property for %s in %s", methodName, this);
235             return found.orElseThrow().getValue().getSchema().getQName().getLocalName();
236         }
237     }
238
239     private static final Logger LOG = LoggerFactory.getLogger(CodecDataObjectGenerator.class);
240     private static final Generic BB_BOOLEAN = TypeDefinition.Sort.describe(boolean.class);
241     private static final Generic BB_OBJECT = TypeDefinition.Sort.describe(Object.class);
242     private static final Generic BB_INT = TypeDefinition.Sort.describe(int.class);
243     private static final Generic BB_STRING = TypeDefinition.Sort.describe(String.class);
244     private static final TypeDescription BB_CDO = ForLoadedType.of(CodecDataObject.class);
245     private static final TypeDescription BB_ACDO = ForLoadedType.of(AugmentableCodecDataObject.class);
246
247     private static final StackManipulation FIRST_ARG_REF = MethodVariableAccess.REFERENCE.loadFrom(1);
248
249     private static final int PROT_FINAL = Opcodes.ACC_PROTECTED | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC;
250     private static final int PUB_FINAL = Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC;
251
252     private static final ByteBuddy BB = new ByteBuddy();
253
254     private final TypeDescription superClass;
255     private final Method keyMethod;
256
257     CodecDataObjectGenerator(final TypeDescription superClass, final @Nullable Method keyMethod) {
258         this.superClass = requireNonNull(superClass);
259         this.keyMethod = keyMethod;
260     }
261
262     static <T extends CodecDataObject<T>> Class<T> generate(final BindingClassLoader loader,
263             final Class<?> bindingInterface, final ImmutableMap<Method, ValueNodeCodecContext> simpleProperties,
264             final Map<Class<?>, PropertyInfo> daoProperties, final Method keyMethod) {
265         return CodecPackage.CODEC.generateClass(loader, bindingInterface,
266             new Reusable<>(BB_CDO, simpleProperties, daoProperties, keyMethod));
267     }
268
269     static <T extends CodecDataObject<T>> Class<T> generateAugmentable(final BindingClassLoader loader,
270             final Class<?> bindingInterface, final ImmutableMap<Method, ValueNodeCodecContext> simpleProperties,
271             final Map<Class<?>, PropertyInfo> daoProperties, final Method keyMethod) {
272         return CodecPackage.CODEC.generateClass(loader, bindingInterface,
273             new Reusable<>(BB_ACDO, simpleProperties, daoProperties, keyMethod));
274     }
275
276     @Override
277     public final GeneratorResult<T> generateClass(final BindingClassLoader loader, final String fqcn,
278             final Class<?> bindingInterface) {
279         LOG.trace("Generating class {}", fqcn);
280
281         final Generic bindingDef = TypeDefinition.Sort.describe(bindingInterface);
282         @SuppressWarnings("unchecked")
283         Builder<T> builder = (Builder<T>) BB.subclass(Generic.Builder.parameterizedType(superClass, bindingDef).build())
284             .name(fqcn).implement(bindingDef);
285
286         builder = generateGetters(builder);
287
288         if (keyMethod != null) {
289             LOG.trace("Generating for key {}", keyMethod);
290             final String methodName = keyMethod.getName();
291             final TypeDescription retType = ForLoadedType.of(keyMethod.getReturnType());
292             builder = builder.defineMethod(methodName, retType, PUB_FINAL).intercept(
293                 new KeyMethodImplementation(methodName, retType));
294         }
295
296         // Final bits:
297         return GeneratorResult.of(builder
298                 // codecHashCode() ...
299                 .defineMethod("codecHashCode", BB_INT, PROT_FINAL)
300                 .intercept(codecHashCode(bindingInterface))
301                 // ... equals(Object) ...
302                 .defineMethod("codecEquals", BB_BOOLEAN, PROT_FINAL).withParameter(BB_OBJECT)
303                 .intercept(codecEquals(bindingInterface))
304                 // ... toString() ...
305                 .defineMethod("toString", BB_STRING, PUB_FINAL)
306                 .intercept(toString(bindingInterface))
307                 // ... and build it
308                 .make());
309     }
310
311     abstract Builder<T> generateGetters(Builder<T> builder);
312
313     private static Implementation codecHashCode(final Class<?> bindingInterface) {
314         return new Implementation.Simple(
315             // return Foo.bindingHashCode(this);
316             loadThis(),
317             invokeMethod(bindingInterface, Naming.BINDING_HASHCODE_NAME, bindingInterface),
318             MethodReturn.INTEGER);
319     }
320
321     private static Implementation codecEquals(final Class<?> bindingInterface) {
322         return new Implementation.Simple(
323             // return Foo.bindingEquals(this, obj);
324             loadThis(),
325             FIRST_ARG_REF,
326             invokeMethod(bindingInterface, Naming.BINDING_EQUALS_NAME, bindingInterface, Object.class),
327             MethodReturn.INTEGER);
328     }
329
330     private static Implementation toString(final Class<?> bindingInterface) {
331         return new Implementation.Simple(
332             // return Foo.bindingToString(this);
333             loadThis(),
334             invokeMethod(bindingInterface, Naming.BINDING_TO_STRING_NAME, bindingInterface),
335             MethodReturn.REFERENCE);
336     }
337
338     private abstract static class AbstractMethodImplementation implements Implementation {
339         final TypeDescription retType;
340         // getFoo, usually
341         final String methodName;
342
343         AbstractMethodImplementation(final String methodName, final TypeDescription retType) {
344             this.methodName = requireNonNull(methodName);
345             this.retType = requireNonNull(retType);
346         }
347     }
348
349     private abstract static class AbstractCachedMethodImplementation extends AbstractMethodImplementation {
350         private static final Generic BB_HANDLE = TypeDefinition.Sort.describe(VarHandle.class);
351         private static final Generic BB_OBJECT = TypeDefinition.Sort.describe(Object.class);
352         private static final StackManipulation OBJECT_CLASS = ClassConstant.of(ForLoadedType.of(Object.class));
353         private static final StackManipulation LOOKUP = invokeMethod(MethodHandles.class, "lookup");
354         private static final StackManipulation FIND_VAR_HANDLE = invokeMethod(Lookup.class,
355             "findVarHandle", Class.class, String.class, Class.class);
356
357         static final int PRIV_CONST = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL
358                 | Opcodes.ACC_SYNTHETIC;
359         private static final int PRIV_VOLATILE = Opcodes.ACC_PRIVATE | Opcodes.ACC_VOLATILE | Opcodes.ACC_SYNTHETIC;
360
361         // getFoo$$$V
362         final String handleName;
363
364         AbstractCachedMethodImplementation(final String methodName, final TypeDescription retType) {
365             super(methodName, retType);
366             handleName = methodName + "$$$V";
367         }
368
369         @Override
370         public InstrumentedType prepare(final InstrumentedType instrumentedType) {
371             final InstrumentedType tmp = instrumentedType
372                     // private static final VarHandle getFoo$$$V;
373                     .withField(new FieldDescription.Token(handleName, PRIV_CONST, BB_HANDLE))
374                     // private volatile Object getFoo;
375                     .withField(new FieldDescription.Token(methodName, PRIV_VOLATILE, BB_OBJECT));
376
377             return tmp.withInitializer(new ByteCodeAppender.Simple(
378                 // TODO: acquiring lookup is expensive, we should share it across all initialization
379                 // getFoo$$$V = MethodHandles.lookup().findVarHandle(This.class, "getFoo", Object.class);
380                 LOOKUP,
381                 ClassConstant.of(tmp),
382                 new TextConstant(methodName),
383                 OBJECT_CLASS,
384                 FIND_VAR_HANDLE,
385                 putField(tmp, handleName)));
386         }
387     }
388
389     private static final class KeyMethodImplementation extends AbstractCachedMethodImplementation {
390         private static final StackManipulation CODEC_KEY = invokeMethod(CodecDataObject.class,
391             "codecKey", VarHandle.class);
392
393         KeyMethodImplementation(final String methodName, final TypeDescription retType) {
394             super(methodName, retType);
395         }
396
397         @Override
398         public ByteCodeAppender appender(final Target implementationTarget) {
399             return new ByteCodeAppender.Simple(
400                 // return (FooType) codecKey(getFoo$$$V);
401                 loadThis(),
402                 getField(implementationTarget.getInstrumentedType(), handleName),
403                 CODEC_KEY,
404                 TypeCasting.to(retType),
405                 MethodReturn.REFERENCE);
406         }
407     }
408
409     private static final class NonnullMethodImplementation extends AbstractMethodImplementation {
410         private static final StackManipulation NONNULL_MEMBER = invokeMethod(CodecDataObject.class,
411                 "codecMemberOrEmpty", Object.class, Class.class);
412
413         private final Class<?> bindingClass;
414         private final Method getterMethod;
415
416         NonnullMethodImplementation(final String methodName, final TypeDescription retType,
417                 final Class<?> bindingClass, final Method getterMethod) {
418             super(methodName, retType);
419             this.bindingClass = requireNonNull(bindingClass);
420             this.getterMethod = requireNonNull(getterMethod);
421         }
422
423         @Override
424         public ByteCodeAppender appender(final Target implementationTarget) {
425             return new ByteCodeAppender.Simple(
426                     // return (FooType) codecMemberOrEmpty(getFoo(), FooType.class)
427                     loadThis(),
428                     loadThis(),
429                     invokeMethod(getterMethod),
430                     ClassConstant.of(TypeDefinition.Sort.describe(bindingClass).asErasure()),
431                     NONNULL_MEMBER,
432                     TypeCasting.to(retType),
433                     MethodReturn.REFERENCE);
434         }
435
436         @Override
437         public InstrumentedType prepare(final InstrumentedType instrumentedType) {
438             // No-op
439             return instrumentedType;
440         }
441     }
442
443     /*
444      * A simple leaf method, which looks up child by a String constant. This is slightly more complicated because we
445      * want to make sure we are using the same String instance as the one stored in associated DataObjectCodecContext,
446      * so that during lookup we perform an identity check instead of comparing content -- speeding things up as well
447      * as minimizing footprint. Since that string is not guaranteed to be interned in the String Pool, we cannot rely
448      * on the constant pool entry to resolve to the same object.
449      */
450     private static final class SimpleGetterMethodImplementation extends AbstractCachedMethodImplementation {
451         private static final StackManipulation CODEC_MEMBER = invokeMethod(CodecDataObject.class,
452             "codecMember", VarHandle.class, String.class);
453         private static final StackManipulation BRIDGE_RESOLVE = invokeMethod(ClassGeneratorBridge.class,
454             "resolveLocalName", String.class);
455         private static final Generic BB_STRING = TypeDefinition.Sort.describe(String.class);
456
457         // getFoo$$$S
458         private final String stringName;
459
460         SimpleGetterMethodImplementation(final String methodName, final TypeDescription retType) {
461             super(methodName, retType);
462             stringName = methodName + "$$$S";
463         }
464
465         @Override
466         public InstrumentedType prepare(final InstrumentedType instrumentedType) {
467             final InstrumentedType tmp = super.prepare(instrumentedType)
468                     // private static final String getFoo$$$S;
469                     .withField(new FieldDescription.Token(stringName, PRIV_CONST, BB_STRING));
470
471             return tmp.withInitializer(new ByteCodeAppender.Simple(
472                 // getFoo$$$S = CodecDataObjectBridge.resolveString("getFoo");
473                 new TextConstant(methodName),
474                 BRIDGE_RESOLVE,
475                 putField(tmp, stringName)));
476         }
477
478         @Override
479         public ByteCodeAppender appender(final Target implementationTarget) {
480             final TypeDescription instrumentedType = implementationTarget.getInstrumentedType();
481             return new ByteCodeAppender.Simple(
482                 // return (FooType) codecMember(getFoo$$$V, getFoo$$$S);
483                 loadThis(),
484                 getField(instrumentedType, handleName),
485                 getField(instrumentedType, stringName),
486                 CODEC_MEMBER,
487                 TypeCasting.to(retType),
488                 MethodReturn.REFERENCE);
489         }
490     }
491
492     private static final class StructuredGetterMethodImplementation extends AbstractCachedMethodImplementation {
493         private static final StackManipulation CODEC_MEMBER = invokeMethod(CodecDataObject.class,
494             "codecMember", VarHandle.class, Class.class);
495
496         private final Class<?> bindingClass;
497
498         StructuredGetterMethodImplementation(final String methodName, final TypeDescription retType,
499                 final Class<?> bindingClass) {
500             super(methodName, retType);
501             this.bindingClass = requireNonNull(bindingClass);
502         }
503
504         @Override
505         public ByteCodeAppender appender(final Target implementationTarget) {
506             return new ByteCodeAppender.Simple(
507                 // return (FooType) codecMember(getFoo$$$V, FooType.class);
508                 loadThis(),
509                 getField(implementationTarget.getInstrumentedType(), handleName),
510                 ClassConstant.of(TypeDefinition.Sort.describe(bindingClass).asErasure()),
511                 CODEC_MEMBER,
512                 TypeCasting.to(retType),
513                 MethodReturn.REFERENCE);
514         }
515     }
516
517     private static final class SupplierGetterMethodImplementation extends AbstractCachedMethodImplementation {
518         private static final StackManipulation CODEC_MEMBER = invokeMethod(CodecDataObject.class,
519             "codecMember", VarHandle.class, CodecContextSupplier.class);
520         private static final StackManipulation BRIDGE_RESOLVE = invokeMethod(ClassGeneratorBridge.class,
521             "resolveNodeContextSupplier", String.class);
522         private static final Generic BB_NCS = TypeDefinition.Sort.describe(CodecContextSupplier.class);
523
524         // getFoo$$$C
525         private final String contextName;
526
527         SupplierGetterMethodImplementation(final String methodName, final TypeDescription retType) {
528             super(methodName, retType);
529             contextName = methodName + "$$$C";
530         }
531
532         @Override
533         public InstrumentedType prepare(final InstrumentedType instrumentedType) {
534             final InstrumentedType tmp = super.prepare(instrumentedType)
535                     // private static final NodeContextSupplier getFoo$$$C;
536                     .withField(new FieldDescription.Token(contextName, PRIV_CONST, BB_NCS));
537
538             return tmp.withInitializer(new ByteCodeAppender.Simple(
539                 // getFoo$$$C = CodecDataObjectBridge.resolve("getFoo");
540                 new TextConstant(methodName),
541                 BRIDGE_RESOLVE,
542                 putField(tmp, contextName)));
543         }
544
545         @Override
546         public ByteCodeAppender appender(final Target implementationTarget) {
547             final TypeDescription instrumentedType = implementationTarget.getInstrumentedType();
548             return new ByteCodeAppender.Simple(
549                 // return (FooType) codecMember(getFoo$$$V, getFoo$$$C);
550                 loadThis(),
551                 getField(instrumentedType, handleName),
552                 getField(instrumentedType, contextName),
553                 CODEC_MEMBER,
554                 TypeCasting.to(retType),
555                 MethodReturn.REFERENCE);
556         }
557     }
558 }