Switch NormalizedNode->Binding codegen to ByteBuddy 78/81778/35
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 23 Apr 2019 20:28:22 +0000 (22:28 +0200)
committerRobert Varga <nite@hq.sk>
Tue, 30 Apr 2019 03:30:30 +0000 (03:30 +0000)
This patch switches code generation for streamers and codec-based
binding objects to use ByteBuddy instead of Javassist.

While this increases the size of our jar considerably, it is
leads to more maintainable and safer code.

JIRA: MDSAL-444
Change-Id: Ib810dd0df6e569b0bb20603a5b0f0180c28ec25c
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
19 files changed:
binding/mdsal-binding-dom-codec/pom.xml
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentableCodecDataObject.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ByteBuddyUtils.java [new file with mode: 0644]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObject.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectBridge.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectCustomizer.java [deleted file]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectGenerator.java [new file with mode: 0644]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamer.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerBridge.java [deleted file]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerCustomizer.java [deleted file]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerGenerator.java [new file with mode: 0644]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/OpaqueNodeCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/loader/CodecClassLoader.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/loader/LeafCodecClassLoader.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/loader/RootCodecClassLoader.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/loader/StaticClassPool.java [deleted file]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/loader/package-info.java

index 6927a3062b5bf8fb69d42cd38d2f5699d59eb7a4..84c66fcb10f42d9a9a09dc695657f254ca8332e8 100644 (file)
     <artifactId>mdsal-binding-dom-codec</artifactId>
     <packaging>bundle</packaging>
 
+    <properties>
+        <shade.source>net.bytebuddy</shade.source>
+        <shade.target>org.opendaylight.mdsal.binding.dom.codec.jar.bytebuddy</shade.target>
+    </properties>
+
     <dependencies>
         <dependency>
             <groupId>org.javassist</groupId>
             <artifactId>javassist</artifactId>
         </dependency>
+        <dependency>
+            <!-- We are going to shade this -->
+            <groupId>net.bytebuddy</groupId>
+            <artifactId>byte-buddy</artifactId>
+            <version>1.9.12</version>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
             <artifactId>mdsal-binding-generator-impl</artifactId>
                         <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
                         <Export-Package>
                             org.opendaylight.mdsal.binding.dom.codec.*,
-                            org.opendaylight.mdsal.binding.dom.codec.gen.impl.*,
-                            org.opendaylight.mdsal.binding.dom.codec.impl.*,
                             ;-split-package:=error
                         </Export-Package>
+                        <Private-Package>
+                            org.opendaylight.mdsal.binding.dom.codec.loader,
+                        </Private-Package>
+                        <Import-Package>
+                            !net.bytebuddy.*,
+                            *
+                        </Import-Package>
                     </instructions>
                 </configuration>
             </plugin>
+
+            <!-- Shade Byte-Buddy -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <shadedArtifactAttached>false</shadedArtifactAttached>
+                            <createDependencyReducedPom>true</createDependencyReducedPom>
+                            <createSourcesJar>true</createSourcesJar>
+                            <shadeSourcesContent>true</shadeSourcesContent>
+                            <minimizeJar>true</minimizeJar>
+                            <relocations>
+                                <relocation>
+                                    <pattern>${shade.source}</pattern>
+                                    <shadedPattern>${shade.target}</shadedPattern>
+                                </relocation>
+                            </relocations>
+                            <artifactSet>
+                                <includes>
+                                    <include>net.bytebuddy:byte-buddy</include>
+                                </includes>
+                            </artifactSet>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-checkstyle-plugin</artifactId>
index 43edeadb86a597cab8533ecf1a868b759533e87a..3cf75fbf70b01a84f8217c0423248809c978e382 100644 (file)
@@ -42,7 +42,7 @@ public abstract class AugmentableCodecDataObject<T extends DataObject & Augmenta
                 ImmutableClassToInstanceMap.class, "cachedAugmentations");
     private volatile ImmutableClassToInstanceMap<Augmentation<T>> cachedAugmentations;
 
-    public AugmentableCodecDataObject(final DataObjectCodecContext<T, ?> context,
+    protected AugmentableCodecDataObject(final DataObjectCodecContext<T, ?> context,
             final NormalizedNodeContainer<?, ?, ?> data) {
         super(data);
         this.context = requireNonNull(context, "Context must not be null");
index 2c12aaa26cb2e207605d0aa19fba46f4aacb3061..daa0b9239f82839af91cf2fc4379722aa8498da5 100644 (file)
@@ -31,8 +31,6 @@ import java.util.Map.Entry;
 import java.util.Optional;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
-import javassist.CannotCompileException;
-import javassist.NotFoundException;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
@@ -40,7 +38,6 @@ import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
 import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
 import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader;
-import org.opendaylight.mdsal.binding.dom.codec.loader.StaticClassPool;
 import org.opendaylight.mdsal.binding.dom.codec.util.BindingSchemaMapping;
 import org.opendaylight.mdsal.binding.generator.util.BindingRuntimeContext;
 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
@@ -105,12 +102,10 @@ final class BindingCodecContext implements CodecContextFactory, BindingCodecTree
     private final LoadingCache<Class<?>, DataObjectStreamer<?>> streamers = CacheBuilder.newBuilder().build(
         new CacheLoader<Class<?>, DataObjectStreamer<?>>() {
             @Override
-            public DataObjectStreamer<?> load(final Class<?> key) throws CannotCompileException, IOException,
-                    NotFoundException, ReflectiveOperationException {
-                final Class<?> streamer = loader.generateSubclass(DataObjectStreamerCustomizer.CT_DOS, key, "streamer",
-                    DataObjectStreamerCustomizer.create(BindingCodecContext.this, key));
-
-                final Field instance = streamer.getDeclaredField(DataObjectStreamerCustomizer.INSTANCE_FIELD);
+            public DataObjectStreamer<?> load(final Class<?> key) throws ReflectiveOperationException {
+                final Class<?> streamer = DataObjectStreamerGenerator.generateStreamer(loader, BindingCodecContext.this,
+                    key);
+                final Field instance = streamer.getDeclaredField(DataObjectStreamerGenerator.INSTANCE_FIELD);
                 return (DataObjectStreamer<?>) instance.get(null);
             }
         });
@@ -122,7 +117,7 @@ final class BindingCodecContext implements CodecContextFactory, BindingCodecTree
             }
         });
 
-    private final @NonNull CodecClassLoader loader = StaticClassPool.createLoader();
+    private final @NonNull CodecClassLoader loader = CodecClassLoader.create();
     private final InstanceIdentifierCodec instanceIdentifierCodec;
     private final IdentityCodec identityCodec;
     private final BindingNormalizedNodeCodecRegistry registry;
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ByteBuddyUtils.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ByteBuddyUtils.java
new file mode 100644 (file)
index 0000000..c5bdc77
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.dom.codec.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import net.bytebuddy.asm.AsmVisitorWrapper;
+import net.bytebuddy.description.field.FieldDescription;
+import net.bytebuddy.description.field.FieldDescription.ForLoadedField;
+import net.bytebuddy.description.field.FieldList;
+import net.bytebuddy.description.method.MethodDescription.ForLoadedMethod;
+import net.bytebuddy.description.method.MethodList;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.implementation.Implementation;
+import net.bytebuddy.implementation.bytecode.StackManipulation;
+import net.bytebuddy.implementation.bytecode.member.FieldAccess;
+import net.bytebuddy.implementation.bytecode.member.FieldAccess.Defined;
+import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
+import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
+import net.bytebuddy.jar.asm.ClassVisitor;
+import net.bytebuddy.jar.asm.ClassWriter;
+import net.bytebuddy.jar.asm.Label;
+import net.bytebuddy.jar.asm.MethodVisitor;
+import net.bytebuddy.jar.asm.Opcodes;
+import net.bytebuddy.matcher.ElementMatchers;
+import net.bytebuddy.pool.TypePool;
+
+final class ByteBuddyUtils {
+    static final StackManipulation THIS = MethodVariableAccess.loadThis();
+
+    private ByteBuddyUtils() {
+
+    }
+
+    static StackManipulation invokeMethod(final Method method) {
+        return MethodInvocation.invoke(describe(method));
+    }
+
+    static StackManipulation invokeMethod(final Class<?> clazz, final String name, final Class<?>... args) {
+        return MethodInvocation.invoke(describe(clazz, name, args));
+    }
+
+    static AsmVisitorWrapper computeFrames() {
+        return ComputeFrames.INSTANCE;
+    }
+
+    static StackManipulation ifEq(final Label label) {
+        return new IfEq(label);
+    }
+
+    static StackManipulation markLabel(final Label label) {
+        return new Mark(label);
+    }
+
+    static StackManipulation loadThis() {
+        return THIS;
+    }
+
+    static StackManipulation getField(final Field field) {
+        return FieldAccess.forField(new ForLoadedField(field).asDefined()).read();
+    }
+
+    static StackManipulation getField(final TypeDescription instrumentedType, final String fieldName) {
+        return fieldAccess(instrumentedType, fieldName).read();
+    }
+
+    static StackManipulation putField(final TypeDescription instrumentedType, final String fieldName) {
+        return fieldAccess(instrumentedType, fieldName).write();
+    }
+
+    private static ForLoadedMethod describe(final Method method) {
+        return new ForLoadedMethod(method);
+    }
+
+    private static ForLoadedMethod describe(final Class<?> clazz, final String name, final Class<?>... args) {
+        return describe(getMethod(clazz, name, args));
+    }
+
+    private static Defined fieldAccess(final TypeDescription instrumentedType, final String fieldName) {
+        return FieldAccess.forField(instrumentedType.getDeclaredFields().filter(ElementMatchers.named(fieldName))
+            .getOnly());
+    }
+
+    /**
+     * Utility wrapper to force ASM to compute frames.
+     */
+    private enum ComputeFrames implements AsmVisitorWrapper {
+        INSTANCE;
+
+        @Override
+        public int mergeWriter(final int flags) {
+            return flags | ClassWriter.COMPUTE_FRAMES;
+        }
+
+        @Override
+        public int mergeReader(final int flags) {
+            return flags | ClassWriter.COMPUTE_FRAMES;
+        }
+
+        @Override
+        public ClassVisitor wrap(final TypeDescription td, final ClassVisitor cv, final Implementation.Context ctx,
+                final TypePool tp, final FieldList<FieldDescription.InDefinedShape> fields, final MethodList<?> methods,
+                final int wflags, final int rflags) {
+            return cv;
+        }
+    }
+
+    /**
+     * IFEQ opcode invocation, jumping to a particular label.
+     */
+    private static final class IfEq implements StackManipulation {
+        private static final StackManipulation.Size SIZE = new StackManipulation.Size(-1, 0);
+
+        private final Label label;
+
+        IfEq(final Label label) {
+            this.label = requireNonNull(label);
+        }
+
+        @Override
+        public boolean isValid() {
+            return true;
+        }
+
+        @Override
+        public StackManipulation.Size apply(final MethodVisitor mv, final Implementation.Context ctx) {
+            mv.visitJumpInsn(Opcodes.IFEQ, label);
+            return SIZE;
+        }
+    }
+
+    /**
+     * A label definition, marking the spot where IfEq should jump.
+     */
+    private static final class Mark implements StackManipulation {
+        private static final StackManipulation.Size SIZE = new StackManipulation.Size(0, 0);
+
+        private final Label label;
+
+        Mark(final Label label) {
+            this.label = requireNonNull(label);
+        }
+
+        @Override
+        public boolean isValid() {
+            return true;
+        }
+
+        @Override
+        public StackManipulation.Size apply(final MethodVisitor mv, final Implementation.Context ctx) {
+            mv.visitLabel(label);
+            return SIZE;
+        }
+    }
+
+    private static Method getMethod(final Class<?> clazz, final String name, final Class<?>... args) {
+        try {
+            return clazz.getDeclaredMethod(name, args);
+        } catch (NoSuchMethodException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+}
index b63e2928a731c27011fd04cd719103bc3af7acf5..cec788f3fdc050209d3b5bde997a23a86347ec87 100644 (file)
@@ -36,7 +36,7 @@ public abstract class CodecDataObject<T extends DataObject> implements DataObjec
 
     private volatile Integer cachedHashcode = null;
 
-    public CodecDataObject(final NormalizedNodeContainer<?, ?, ?> data) {
+    protected CodecDataObject(final NormalizedNodeContainer<?, ?, ?> data) {
         this.data = requireNonNull(data, "Data must not be null");
     }
 
index 8324f11dd83a741afc2da832e500efaebecb1141..cfc3994423d29b6151dd87ed99746ff91f888d62 100644 (file)
@@ -19,7 +19,7 @@ import org.eclipse.jdt.annotation.Nullable;
  */
 @Beta
 public final class CodecDataObjectBridge {
-    private static final ThreadLocal<CodecDataObjectCustomizer> CURRENT_CUSTOMIZER = new ThreadLocal<>();
+    private static final ThreadLocal<CodecDataObjectGenerator> CURRENT_CUSTOMIZER = new ThreadLocal<>();
 
     private CodecDataObjectBridge() {
 
@@ -33,13 +33,13 @@ public final class CodecDataObjectBridge {
         return current().resolveKey(methodName);
     }
 
-    static @Nullable CodecDataObjectCustomizer setup(final @NonNull CodecDataObjectCustomizer next) {
-        final CodecDataObjectCustomizer prev = CURRENT_CUSTOMIZER.get();
+    static @Nullable CodecDataObjectGenerator setup(final @NonNull CodecDataObjectGenerator next) {
+        final CodecDataObjectGenerator prev = CURRENT_CUSTOMIZER.get();
         CURRENT_CUSTOMIZER.set(verifyNotNull(next));
         return prev;
     }
 
-    static void tearDown(final @Nullable CodecDataObjectCustomizer prev) {
+    static void tearDown(final @Nullable CodecDataObjectGenerator prev) {
         if (prev == null) {
             CURRENT_CUSTOMIZER.remove();
         } else {
@@ -47,7 +47,7 @@ public final class CodecDataObjectBridge {
         }
     }
 
-    private static @NonNull CodecDataObjectCustomizer current() {
+    private static @NonNull CodecDataObjectGenerator current() {
         return verifyNotNull(CURRENT_CUSTOMIZER.get(), "No customizer attached");
     }
 }
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectCustomizer.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectCustomizer.java
deleted file mode 100644 (file)
index f9a3b11..0000000
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.mdsal.binding.dom.codec.impl;
-
-import static com.google.common.base.Verify.verify;
-import static com.google.common.base.Verify.verifyNotNull;
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.base.Supplier;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Optional;
-import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
-import javassist.CannotCompileException;
-import javassist.CtClass;
-import javassist.CtField;
-import javassist.CtMethod;
-import javassist.NotFoundException;
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader;
-import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.Customizer;
-import org.opendaylight.mdsal.binding.dom.codec.loader.StaticClassPool;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Private support for generating AbstractDataObject specializations.
- */
-final class CodecDataObjectCustomizer implements Customizer {
-    private static final Logger LOG = LoggerFactory.getLogger(CodecDataObjectCustomizer.class);
-    private static final CtClass CT_ARFU = StaticClassPool.findClass(AtomicReferenceFieldUpdater.class);
-    private static final CtClass CT_BOOLEAN = StaticClassPool.findClass(boolean.class);
-    private static final CtClass CT_DATAOBJECT = StaticClassPool.findClass(DataObject.class);
-    private static final CtClass CT_HELPER = StaticClassPool.findClass(ToStringHelper.class);
-    private static final CtClass CT_IIC = StaticClassPool.findClass(IdentifiableItemCodec.class);
-    private static final CtClass CT_INT = StaticClassPool.findClass(int.class);
-    private static final CtClass CT_NCS = StaticClassPool.findClass(NodeContextSupplier.class);
-    private static final CtClass CT_OBJECT = StaticClassPool.findClass(Object.class);
-    private static final CtClass[] EMPTY_ARGS = new CtClass[0];
-    private static final CtClass[] EQUALS_ARGS = new CtClass[] { CT_DATAOBJECT };
-    private static final CtClass[] TOSTRING_ARGS = new CtClass[] { CT_HELPER };
-
-    private final ImmutableMap<Method, NodeContextSupplier> properties;
-    private final Entry<Method, IdentifiableItemCodec> keyMethod;
-
-    CodecDataObjectCustomizer(final ImmutableMap<Method, NodeContextSupplier> properties,
-            final @Nullable Entry<Method, IdentifiableItemCodec> keyMethod) {
-        this.properties = requireNonNull(properties);
-        this.keyMethod = keyMethod;
-    }
-
-    @Override
-    public List<Class<?>> customize(final CodecClassLoader loader, final CtClass bindingClass, final CtClass generated)
-            throws NotFoundException, CannotCompileException {
-        // Generate members for all methods ...
-        LOG.trace("Generating class {}", generated.getName());
-        generated.addInterface(bindingClass);
-
-        for (Method method : properties.keySet()) {
-            generateMethod(loader, generated, method, CT_NCS, "resolve");
-        }
-        if (keyMethod != null) {
-            generateMethod(loader, generated, keyMethod.getKey(), CT_IIC, "resolveKey");
-        }
-
-        // Final bits: codecHashCode() ...
-        final CtMethod codecHashCode = new CtMethod(CT_INT, "codecHashCode", EMPTY_ARGS, generated);
-        codecHashCode.setModifiers(Modifier.PROTECTED | Modifier.FINAL);
-        codecHashCode.setBody(codecHashCodeBody());
-        generated.addMethod(codecHashCode);
-
-        // ... equals
-        final CtMethod codecEquals = new CtMethod(CT_BOOLEAN, "codecEquals", EQUALS_ARGS, generated);
-        codecEquals.setModifiers(Modifier.PROTECTED | Modifier.FINAL);
-        codecEquals.setBody(codecEqualsBody(bindingClass.getName()));
-        generated.addMethod(codecEquals);
-
-        // ... and codecFillToString()
-        final CtMethod codecFillToString = new CtMethod(CT_HELPER, "codecFillToString", TOSTRING_ARGS, generated);
-        codecFillToString.setModifiers(Modifier.PROTECTED | Modifier.FINAL);
-        codecFillToString.setBody(codecFillToStringBody());
-        generated.addMethod(codecFillToString);
-
-        generated.setModifiers(Modifier.FINAL | Modifier.PUBLIC);
-        return ImmutableList.of();
-    }
-
-    @Override
-    public Class<?> customizeLoading(final @NonNull Supplier<Class<?>> loader) {
-        final CodecDataObjectCustomizer prev = CodecDataObjectBridge.setup(this);
-        try {
-            final Class<?> result = loader.get();
-
-            /*
-             * This a bit of magic to support NodeContextSupplier constants. These constants need to be resolved while
-             * we have the information needed to find them -- that information is being held in this instance and we
-             * leak it to a thread-local variable held by CodecDataObjectBridge.
-             *
-             * By default the JVM will defer class initialization to first use, which unfortunately is too late for
-             * us, and hence we need to force class to initialize.
-             */
-            try {
-                Class.forName(result.getName(), true, result.getClassLoader());
-            } catch (ClassNotFoundException e) {
-                throw new LinkageError("Failed to find newly-defined " + result, e);
-            }
-
-            return result;
-        } finally {
-            CodecDataObjectBridge.tearDown(prev);
-        }
-    }
-
-
-    @NonNull NodeContextSupplier resolve(final @NonNull String methodName) {
-        final Optional<Entry<Method, NodeContextSupplier>> found = properties.entrySet().stream()
-                .filter(entry -> methodName.equals(entry.getKey().getName())).findAny();
-        verify(found.isPresent(), "Failed to find property for %s in %s", methodName, this);
-        return verifyNotNull(found.get().getValue());
-    }
-
-    @NonNull IdentifiableItemCodec resolveKey(final @NonNull String methodName) {
-        return verifyNotNull(verifyNotNull(keyMethod, "No key method attached for %s in %s", methodName, this)
-            .getValue());
-    }
-
-    private String codecHashCodeBody() {
-        final StringBuilder sb = new StringBuilder()
-                .append("{\n")
-                .append("final int prime = 31;\n")
-                .append("int result = 1;\n");
-
-        for (Method method : properties.keySet()) {
-            sb.append("result = prime * result + java.util.").append(utilClass(method)).append(".hashCode(")
-            .append(method.getName()).append("());\n");
-        }
-
-        return sb.append("return result;\n")
-                .append('}').toString();
-    }
-
-    private String codecEqualsBody(final String ifaceName) {
-        final StringBuilder sb = new StringBuilder()
-                .append("{\n")
-                .append("final ").append(ifaceName).append(" other = $1;")
-                .append("return true");
-
-        for (Method method : properties.keySet()) {
-            final String methodName = method.getName();
-            sb.append("\n&& java.util.").append(utilClass(method)).append(".equals(").append(methodName)
-            .append("(), other.").append(methodName).append("())");
-        }
-
-        return sb.append(";\n")
-                .append('}').toString();
-    }
-
-    private String codecFillToStringBody() {
-        final StringBuilder sb = new StringBuilder()
-                .append("{\n")
-                .append("return $1");
-        for (Method method : properties.keySet()) {
-            final String methodName = method.getName();
-            sb.append("\n.add(\"").append(methodName).append("\", ").append(methodName).append("())");
-        }
-
-        return sb.append(";\n")
-                .append('}').toString();
-    }
-
-    private static void generateMethod(final CodecClassLoader loader, final CtClass generated, final Method method,
-            final CtClass contextType, final String resolveMethod) throws CannotCompileException, NotFoundException {
-        LOG.trace("Generating for method {}", method);
-        final String methodName = method.getName();
-        final String methodArfu = methodName + "$$$ARFU";
-        final String methodNcs = methodName + "$$$NCS";
-
-        // NodeContextSupplier ...
-        final CtField ncsField = new CtField(contextType, methodNcs, generated);
-        ncsField.setModifiers(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL);
-        generated.addField(ncsField, new StringBuilder().append(CodecDataObjectBridge.class.getName())
-            .append('.').append(resolveMethod).append("(\"").append(methodName).append("\")").toString());
-
-        // ... AtomicReferenceFieldUpdater ...
-        final CtField arfuField = new CtField(CT_ARFU, methodArfu, generated);
-        arfuField.setModifiers(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL);
-        generated.addField(arfuField, new StringBuilder().append(CT_ARFU.getName()).append(".newUpdater(")
-            .append(generated.getName()).append(".class, java.lang.Object.class, \"").append(methodName)
-            .append("\")").toString());
-
-        // ... corresponding volatile field ...
-        final CtField field = new CtField(CT_OBJECT, methodName, generated);
-        field.setModifiers(Modifier.PRIVATE | Modifier.VOLATILE);
-        generated.addField(field);
-
-        // ... and the getter
-        final Class<?> retType = method.getReturnType();
-        final CtMethod getter = new CtMethod(loader.findClass(retType), methodName, EMPTY_ARGS, generated);
-        final String retName = retType.isArray() ? retType.getSimpleName() : retType.getName();
-
-        getter.setBody(new StringBuilder()
-            .append("{\n")
-            .append("return (").append(retName).append(") codecMember(").append(methodArfu).append(", ")
-                .append(methodNcs).append(");\n")
-            .append('}').toString());
-        getter.setModifiers(Modifier.PUBLIC | Modifier.FINAL);
-        generated.addMethod(getter);
-    }
-
-    private static String utilClass(final Method method) {
-        // We can either have objects or byte[], we cannot have Object[]
-        return method.getReturnType().isArray() ? "Arrays" : "Objects";
-    }
-}
\ No newline at end of file
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectGenerator.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectGenerator.java
new file mode 100644 (file)
index 0000000..2f42087
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.dom.codec.impl;
+
+import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.THIS;
+import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.getField;
+import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.invokeMethod;
+import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.putField;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.description.field.FieldDescription;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.type.TypeDefinition;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.description.type.TypeDescription.Generic;
+import net.bytebuddy.dynamic.DynamicType.Builder;
+import net.bytebuddy.dynamic.scaffold.InstrumentedType;
+import net.bytebuddy.implementation.Implementation;
+import net.bytebuddy.implementation.Implementation.Context;
+import net.bytebuddy.implementation.bytecode.Addition;
+import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
+import net.bytebuddy.implementation.bytecode.Multiplication;
+import net.bytebuddy.implementation.bytecode.StackManipulation;
+import net.bytebuddy.implementation.bytecode.assign.TypeCasting;
+import net.bytebuddy.implementation.bytecode.constant.ClassConstant;
+import net.bytebuddy.implementation.bytecode.constant.IntegerConstant;
+import net.bytebuddy.implementation.bytecode.constant.TextConstant;
+import net.bytebuddy.implementation.bytecode.member.MethodReturn;
+import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
+import net.bytebuddy.jar.asm.Label;
+import net.bytebuddy.jar.asm.MethodVisitor;
+import net.bytebuddy.jar.asm.Opcodes;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader;
+import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.ClassGenerator;
+import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.GeneratorResult;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Private support for generating {@link CodecDataObject} and {@link AugmentableCodecDataObject} specializations.
+ *
+ * <p>
+ * Code generation here is probably more involved than usual mainly due to the fact we *really* want to express the
+ * strong connection between a generated class and BindingCodecContext in terms of a true constant, which boils down to
+ * {@code private static final NodeContextSupplier NCS}. Having such constants provides significant boost to JITs
+ * ability to optimize code -- especially with inlining and constant propagation.
+ *
+ * <p>
+ * The accessor mapping performance is critical due to users typically not taking care of storing the results acquired
+ * by an invocation, assuming the accessors are backed by a normal field -- which of course is not true, as the results
+ * are lazily computed.
+ *
+ * <p>
+ * The design is such that for a particular structure like:
+ * <pre>
+ *     container foo {
+ *         leaf bar {
+ *             type string;
+ *         }
+ *     }
+ * </pre>
+ * we end up generating a class with the following layout:
+ * <pre>
+ *     public final class Foo$$$codecImpl extends CodecDataObject implements Foo {
+ *         private static final AtomicRefereceFieldUpdater&lt;Foo$$$codecImpl, Object&gt; getBar$$$A;
+ *         private static final NodeContextSupplier getBar$$$C;
+ *         private volatile Object getBar;
+ *
+ *         public Foo$$$codecImpl(NormalizedNodeContainer data) {
+ *             super(data);
+ *         }
+ *
+ *         public Bar getBar() {
+ *             return (Bar) codecMember(getBar$$$A, getBar$$$C);
+ *         }
+ *     }
+ * </pre>
+ *
+ * <p>
+ * This strategy minimizes the bytecode footprint and follows the generally good idea of keeping common logic in a
+ * single place in a maintainable form. The glue code is extremely light (~6 instructions), which is beneficial on both
+ * sides of invocation:
+ * - generated method can readily be inlined into the caller
+ * - it forms a call site into which codeMember() can be inlined with both AtomicReferenceFieldUpdater and
+ *   NodeContextSupplier being constant
+ *
+ * <p>
+ * The second point is important here, as it allows the invocation logic around AtomicRefereceFieldUpdater to completely
+ * disappear, becoming synonymous with operations of a volatile field. NodeContextSupplier being constant also means
+ * it will resolve to one of its two implementations, allowing NodeContextSupplier.get() to be resolved to a constant
+ * (pointing to the supplier itself) or to a simple volatile read (which will be non-null after first access).
+ *
+ * <p>
+ * The sticky point here is the NodeContextSupplier, as it is a heap object which cannot normally be looked up from the
+ * static context in which the static class initializer operates -- so we need perform some sort of a trick here.
+ *
+ * <p>
+ * Eventhough ByteBuddy provides facilities for bridging references to type fields, those facilities operate on volatile
+ * fields -- hence they do not quite work for us.
+ *
+ * <p>
+ * Another alternative, which we used in Javassist-generated DataObjectSerializers, is to muck with the static field
+ * using reflection -- which works, but requires redefinition of Field.modifiers, which is something Java 9 complains
+ * about quite noisily.
+ *
+ * <p>
+ * We take a different approach here, which takes advantage of the fact we are in control of both code generation (here)
+ * and class loading (in {@link CodecClassLoader}). The process is performed in four steps:
+ * <ul>
+ * <li>During code generation, the context fields are pointed towards {@link CodecDataObjectBridge#resolve(String)} and
+ *     {@link CodecDataObjectBridge#resolveKey(String)} methods, which are public and static, hence perfectly usable
+ *     in the context of a class initializer.</li>
+ * <li>During class loading of generated byte code, the original instance of the generator is called to wrap the actual
+ *     class loading operation. At this point the generator installs itself as the current generator for this thread via
+ *     {@link CodecDataObjectBridge#setup(CodecDataObjectGenerator)} and allows the class to be loaded.
+ * <li>After the class has been loaded, but before the call returns, we will force the class to initialize, at which
+ *     point the static invocations will be redirect to {@link #resolve(String)} and {@link #resolveKey(String)}
+ *     methods, thus initializing the fields to the intended constants.</li>
+ * <li>Before returning from the class loading call, the generator will detach itself via
+ *     {@link CodecDataObjectBridge#tearDown(CodecDataObjectGenerator)}.</li>
+ * </ul>
+ *
+ * <p>
+ * This strategy works due to close cooperation with the target ClassLoader, as the entire code generation and loading
+ * block runs with the class loading lock for this FQCN and the reference is not leaked until the process completes.
+ */
+final class CodecDataObjectGenerator<T extends CodecDataObject<?>> implements ClassGenerator<T> {
+    private static final Logger LOG = LoggerFactory.getLogger(CodecDataObjectGenerator.class);
+    private static final Generic BB_BOOLEAN = TypeDefinition.Sort.describe(boolean.class);
+    private static final Generic BB_DATAOBJECT = TypeDefinition.Sort.describe(DataObject.class);
+    private static final Generic BB_HELPER = TypeDefinition.Sort.describe(ToStringHelper.class);
+    private static final Generic BB_INT = TypeDefinition.Sort.describe(int.class);
+    private static final Generic BB_IIC = TypeDefinition.Sort.describe(IdentifiableItemCodec.class);
+    private static final Generic BB_NCS = TypeDefinition.Sort.describe(NodeContextSupplier.class);
+
+    private static final StackManipulation BRIDGE_RESOLVE = invokeMethod(CodecDataObjectBridge.class,
+        "resolve", String.class);
+    private static final StackManipulation BRIDGE_RESOLVE_KEY = invokeMethod(CodecDataObjectBridge.class,
+        "resolveKey", String.class);
+    private static final StackManipulation CODEC_MEMBER = invokeMethod(CodecDataObject.class,
+        "codecMember", AtomicReferenceFieldUpdater.class, NodeContextSupplier.class);
+    private static final StackManipulation CODEC_MEMBER_KEY = invokeMethod(CodecDataObject.class,
+        "codecMember",  AtomicReferenceFieldUpdater.class, IdentifiableItemCodec.class);
+
+    private static final StackManipulation ARRAYS_EQUALS = invokeMethod(Arrays.class, "equals",
+        byte[].class, byte[].class);
+    private static final StackManipulation OBJECTS_EQUALS = invokeMethod(Objects.class, "equals",
+        Object.class, Object.class);
+    private static final StackManipulation HELPER_ADD = invokeMethod(ToStringHelper.class, "add",
+        String.class, Object.class);
+
+    private static final StackManipulation FIRST_ARG_REF = MethodVariableAccess.REFERENCE.loadFrom(1);
+
+    private static final int PROT_FINAL = Opcodes.ACC_PROTECTED | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC;
+    private static final int PUB_FINAL = Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC;
+
+    private static final Builder<?> CDO;
+    private static final Builder<?> ACDO;
+
+    static {
+        final ByteBuddy bb = new ByteBuddy();
+        CDO = bb.subclass(CodecDataObject.class).visit(ByteBuddyUtils.computeFrames()).modifiers(PUB_FINAL);
+        ACDO = bb.subclass(AugmentableCodecDataObject.class).visit(ByteBuddyUtils.computeFrames()).modifiers(PUB_FINAL);
+    }
+
+    private final ImmutableMap<Method, NodeContextSupplier> properties;
+    private final Entry<Method, IdentifiableItemCodec> keyMethod;
+    private final Builder<?> template;
+
+    private CodecDataObjectGenerator(final Builder<?> template,
+            final ImmutableMap<Method, NodeContextSupplier> properties,
+            final @Nullable Entry<Method, IdentifiableItemCodec> keyMethod) {
+        this.template = requireNonNull(template);
+        this.properties = requireNonNull(properties);
+        this.keyMethod = keyMethod;
+    }
+
+    static <D extends DataObject, T extends CodecDataObject<T>> Class<T> generate(final CodecClassLoader loader,
+            final Class<D> bindingInterface, final ImmutableMap<Method, NodeContextSupplier> properties,
+            final Entry<Method, IdentifiableItemCodec> keyMethod) {
+        return loader.generateClass(bindingInterface, "codecImpl",
+            new CodecDataObjectGenerator<>(CDO, properties, keyMethod));
+    }
+
+    static <D extends DataObject, T extends CodecDataObject<T>> Class<T> generateAugmentable(
+            final CodecClassLoader loader, final Class<D> bindingInterface,
+            final ImmutableMap<Method, NodeContextSupplier> properties,
+            final Entry<Method, IdentifiableItemCodec> keyMethod) {
+        return loader.generateClass(bindingInterface, "codecImpl",
+            new CodecDataObjectGenerator<>(ACDO, properties, keyMethod));
+    }
+
+    @Override
+    public GeneratorResult<T> generateClass(final CodecClassLoader loeader, final String fqcn,
+            final Class<?> bindingInterface) {
+        LOG.trace("Generating class {}", fqcn);
+
+        @SuppressWarnings("unchecked")
+        Builder<T> builder = (Builder<T>) template.name(fqcn).implement(bindingInterface);
+
+        for (Method method : properties.keySet()) {
+            LOG.trace("Generating for method {}", method);
+            final String methodName = method.getName();
+            final TypeDescription retType = TypeDescription.ForLoadedType.of(method.getReturnType());
+            builder = builder.defineMethod(methodName, retType, PUB_FINAL)
+                    .intercept(new MethodImplementation(BB_NCS, BRIDGE_RESOLVE, CODEC_MEMBER, methodName, retType));
+        }
+
+        if (keyMethod != null) {
+            LOG.trace("Generating for key {}", keyMethod);
+            final Method method = keyMethod.getKey();
+            final String methodName = method.getName();
+            final TypeDescription retType = TypeDescription.ForLoadedType.of(method.getReturnType());
+            builder = builder.defineMethod(methodName, retType, PUB_FINAL)
+                    .intercept(new MethodImplementation(BB_IIC, BRIDGE_RESOLVE_KEY, CODEC_MEMBER_KEY, methodName,
+                        retType));
+        }
+
+        // Index all property methods, turning them into "getFoo()" invocations, retaining order. We will be using
+        // those invocations in each of the three methods. Note that we do not glue the invocations to 'this', as we
+        // will be invoking them on 'other' in codecEquals()
+        final ImmutableMap<StackManipulation, Method> methods = Maps.uniqueIndex(properties.keySet(),
+            ByteBuddyUtils::invokeMethod);
+
+        // Final bits:
+        return GeneratorResult.of(builder
+                // codecHashCode() ...
+                .defineMethod("codecHashCode", BB_INT, PROT_FINAL)
+                .intercept(new Implementation.Simple(new CodecHashCode(methods)))
+                // ... codecEquals() ...
+                .defineMethod("codecEquals", BB_BOOLEAN, PROT_FINAL).withParameter(BB_DATAOBJECT)
+                .intercept(codecEquals(methods))
+                // ... and codecFillToString() ...
+                .defineMethod("codecFillToString", BB_HELPER, PROT_FINAL).withParameter(BB_HELPER)
+                .intercept(codecFillToString(methods))
+                // ... and build it
+                .make());
+    }
+
+    @Override
+    public Class<T> customizeLoading(final @NonNull Supplier<Class<T>> loader) {
+        final CodecDataObjectGenerator<?> prev = CodecDataObjectBridge.setup(this);
+        try {
+            final Class<T> result = loader.get();
+
+            /*
+             * This a bit of magic to support NodeContextSupplier constants. These constants need to be resolved while
+             * we have the information needed to find them -- that information is being held in this instance and we
+             * leak it to a thread-local variable held by CodecDataObjectBridge.
+             *
+             * By default the JVM will defer class initialization to first use, which unfortunately is too late for
+             * us, and hence we need to force class to initialize.
+             */
+            try {
+                Class.forName(result.getName(), true, result.getClassLoader());
+            } catch (ClassNotFoundException e) {
+                throw new LinkageError("Failed to find newly-defined " + result, e);
+            }
+
+            return result;
+        } finally {
+            CodecDataObjectBridge.tearDown(prev);
+        }
+    }
+
+    @NonNull NodeContextSupplier resolve(final @NonNull String methodName) {
+        final Optional<Entry<Method, NodeContextSupplier>> found = properties.entrySet().stream()
+                .filter(entry -> methodName.equals(entry.getKey().getName())).findAny();
+        verify(found.isPresent(), "Failed to find property for %s in %s", methodName, this);
+        return verifyNotNull(found.get().getValue());
+    }
+
+    @NonNull IdentifiableItemCodec resolveKey(final @NonNull String methodName) {
+        return verifyNotNull(verifyNotNull(keyMethod, "No key method attached for %s in %s", methodName, this)
+            .getValue());
+    }
+
+    private static Implementation codecEquals(final ImmutableMap<StackManipulation, Method> properties) {
+        // Label for 'return false;'
+        final Label falseLabel = new Label();
+        // Condition for 'if (!...)'
+        final StackManipulation ifFalse = ByteBuddyUtils.ifEq(falseLabel);
+
+        final List<StackManipulation> manipulations = new ArrayList<>(properties.size() * 6 + 5);
+        for (Entry<StackManipulation, Method> entry : properties.entrySet()) {
+            // if (!java.util.(Objects|Arrays).equals(getFoo(), other.getFoo())) {
+            //     return false;
+            // }
+            manipulations.add(THIS);
+            manipulations.add(entry.getKey());
+            manipulations.add(FIRST_ARG_REF);
+            manipulations.add(entry.getKey());
+            manipulations.add(entry.getValue().getReturnType().isArray() ? ARRAYS_EQUALS : OBJECTS_EQUALS);
+            manipulations.add(ifFalse);
+        }
+
+        // return true;
+        manipulations.add(IntegerConstant.ONE);
+        manipulations.add(MethodReturn.INTEGER);
+        // L0: return false;
+        manipulations.add(ByteBuddyUtils.markLabel(falseLabel));
+        manipulations.add(IntegerConstant.ZERO);
+        manipulations.add(MethodReturn.INTEGER);
+
+        return new Implementation.Simple(manipulations.toArray(new StackManipulation[0]));
+    }
+
+    private static Implementation codecFillToString(final ImmutableMap<StackManipulation, Method> properties) {
+        final List<StackManipulation> manipulations = new ArrayList<>(properties.size() * 4 + 2);
+        // push 'return helper' to stack...
+        manipulations.add(FIRST_ARG_REF);
+        for (Entry<StackManipulation, Method> entry : properties.entrySet()) {
+            // .add("getFoo", getFoo())
+            manipulations.add(new TextConstant(entry.getValue().getName()));
+            manipulations.add(THIS);
+            manipulations.add(entry.getKey());
+            manipulations.add(HELPER_ADD);
+        }
+        // ... execute 'return helper'
+        manipulations.add(MethodReturn.REFERENCE);
+
+        return new Implementation.Simple(manipulations.toArray(new StackManipulation[0]));
+    }
+
+    private static final class MethodImplementation implements Implementation {
+        private static final Generic BB_ARFU = TypeDefinition.Sort.describe(AtomicReferenceFieldUpdater.class);
+        private static final Generic BB_OBJECT = TypeDefinition.Sort.describe(Object.class);
+        private static final StackManipulation OBJECT_CLASS = ClassConstant.of(TypeDescription.OBJECT);
+        private static final StackManipulation ARFU_NEWUPDATER = invokeMethod(AtomicReferenceFieldUpdater.class,
+            "newUpdater", Class.class, Class.class, String.class);
+
+        private static final int PRIV_CONST = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL
+                | Opcodes.ACC_SYNTHETIC;
+        private static final int PRIV_VOLATILE = Opcodes.ACC_PRIVATE | Opcodes.ACC_VOLATILE | Opcodes.ACC_SYNTHETIC;
+
+        private final Generic contextType;
+        private final StackManipulation resolveMethod;
+        private final StackManipulation codecMember;
+        private final TypeDescription retType;
+
+        // getFoo
+        private final String methodName;
+        // getFoo$$$A
+        private final String arfuName;
+        // getFoo$$$C
+        private final String contextName;
+
+        MethodImplementation(final Generic contextType, final StackManipulation resolveMethod,
+            final StackManipulation codecMember, final String methodName, final TypeDescription retType) {
+            this.contextType = requireNonNull(contextType);
+            this.resolveMethod = requireNonNull(resolveMethod);
+            this.codecMember = requireNonNull(codecMember);
+            this.methodName = requireNonNull(methodName);
+            this.retType = requireNonNull(retType);
+            this.arfuName = methodName + "$$$A";
+            this.contextName = methodName + "$$$C";
+        }
+
+        @Override
+        public InstrumentedType prepare(final InstrumentedType instrumentedType) {
+            final InstrumentedType tmp = instrumentedType
+                    // private static final AtomicReferenceFieldUpdater<This, Object> getFoo$$$A;
+                    .withField(new FieldDescription.Token(arfuName, PRIV_CONST, BB_ARFU))
+                    // private static final <CONTEXT_TYPE> getFoo$$$C;
+                    .withField(new FieldDescription.Token(contextName, PRIV_CONST, contextType))
+                    // private volatile Object getFoo;
+                    .withField(new FieldDescription.Token(methodName, PRIV_VOLATILE, BB_OBJECT));
+
+            // "getFoo"
+            final TextConstant methodNameText = new TextConstant(methodName);
+
+            return tmp
+                .withInitializer(new ByteCodeAppender.Simple(
+                    // getFoo$$$A = AtomicReferenceFieldUpdater.newUpdater(This.class, Object.class, "getFoo");
+                    ClassConstant.of(tmp),
+                    OBJECT_CLASS,
+                    methodNameText,
+                    ARFU_NEWUPDATER,
+                    putField(tmp, arfuName),
+                    // getFoo$$$C = CodecDataObjectBridge.<RESOLVE_METHOD>("getFoo");
+                    methodNameText,
+                    resolveMethod,
+                    putField(tmp, contextName)));
+        }
+
+        @Override
+        public ByteCodeAppender appender(final Target implementationTarget) {
+            final TypeDescription instrumentedType = implementationTarget.getInstrumentedType();
+            return new ByteCodeAppender.Simple(
+                // return (FooType) codecMember(getFoo$$$A, getFoo$$$C);
+                THIS,
+                getField(instrumentedType, arfuName),
+                getField(instrumentedType, contextName),
+                codecMember,
+                TypeCasting.to(retType),
+                MethodReturn.REFERENCE);
+        }
+    }
+
+    private static final class CodecHashCode implements ByteCodeAppender {
+        private static final StackManipulation THIRTY_ONE = IntegerConstant.forValue(31);
+        private static final StackManipulation LOAD_RESULT = MethodVariableAccess.INTEGER.loadFrom(1);
+        private static final StackManipulation STORE_RESULT = MethodVariableAccess.INTEGER.storeAt(1);
+        private static final StackManipulation ARRAYS_HASHCODE = invokeMethod(Arrays.class, "hashCode", byte[].class);
+        private static final StackManipulation OBJECTS_HASHCODE = invokeMethod(Objects.class, "hashCode", Object.class);
+
+        private final ImmutableMap<StackManipulation, Method> properties;
+
+        CodecHashCode(final ImmutableMap<StackManipulation, Method> properties) {
+            this.properties = requireNonNull(properties);
+        }
+
+        @Override
+        public Size apply(final MethodVisitor methodVisitor, final Context implementationContext,
+                final MethodDescription instrumentedMethod) {
+            final List<StackManipulation> manipulations = new ArrayList<>(properties.size() * 8 + 4);
+            // int result = 1;
+            manipulations.add(IntegerConstant.ONE);
+            manipulations.add(STORE_RESULT);
+
+            for (Entry<StackManipulation, Method> entry : properties.entrySet()) {
+                // result = 31 * result + java.util.(Objects,Arrays).hashCode(getFoo());
+                manipulations.add(THIRTY_ONE);
+                manipulations.add(LOAD_RESULT);
+                manipulations.add(Multiplication.INTEGER);
+                manipulations.add(THIS);
+                manipulations.add(entry.getKey());
+                manipulations.add(entry.getValue().getReturnType().isArray() ? ARRAYS_HASHCODE : OBJECTS_HASHCODE);
+                manipulations.add(Addition.INTEGER);
+                manipulations.add(STORE_RESULT);
+            }
+            // return result;
+            manipulations.add(LOAD_RESULT);
+            manipulations.add(MethodReturn.INTEGER);
+
+            StackManipulation.Size operandStackSize = new StackManipulation.Compound(manipulations)
+                    .apply(methodVisitor, implementationContext);
+            return new Size(operandStackSize.getMaximalSize(), instrumentedMethod.getStackSize() + 1);
+        }
+    }
+}
index cb9fa451fb7df236ab26f762e707561260f18f5b..6505b3afdb0a3a47deb017f4707e0928165c85c3 100644 (file)
@@ -12,10 +12,10 @@ import static com.google.common.base.Verify.verify;
 import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
-import java.io.IOException;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
@@ -29,12 +29,8 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
-import javassist.CannotCompileException;
-import javassist.CtClass;
-import javassist.NotFoundException;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.mdsal.binding.dom.codec.loader.StaticClassPool;
 import org.opendaylight.mdsal.binding.generator.api.ClassLoadingStrategy;
 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
 import org.opendaylight.mdsal.binding.model.api.Type;
@@ -64,7 +60,11 @@ import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-abstract class DataObjectCodecContext<D extends DataObject, T extends DataNodeContainer & WithStatus>
+/**
+ * This class is an implementation detail. It is public only due to technical reasons and may change at any time.
+ */
+@Beta
+public abstract class DataObjectCodecContext<D extends DataObject, T extends DataNodeContainer & WithStatus>
         extends DataContainerCodecContext<D, T> {
     private static final class Augmentations implements Immutable {
         final ImmutableMap<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYang;
@@ -87,9 +87,6 @@ abstract class DataObjectCodecContext<D extends DataObject, T extends DataNodeCo
         DataObjectCodecContext.class, NormalizedNodeContainer.class);
     private static final Comparator<Method> METHOD_BY_ALPHABET = Comparator.comparing(Method::getName);
     private static final Augmentations EMPTY_AUGMENTATIONS = new Augmentations(ImmutableMap.of(), ImmutableMap.of());
-    private static final CtClass SUPERCLASS = StaticClassPool.findClass(CodecDataObject.class);
-    private static final CtClass AUGMENTABLE_SUPERCLASS = StaticClassPool.findClass(
-        AugmentableCodecDataObject.class);
 
     private final ImmutableMap<String, ValueNodeCodecContext> leafChild;
     private final ImmutableMap<YangInstanceIdentifier.PathArgument, NodeContextSupplier> byYang;
@@ -168,27 +165,21 @@ abstract class DataObjectCodecContext<D extends DataObject, T extends DataNodeCo
         byBindingArgClassBuilder.putAll(byStreamClass);
         this.byBindingArgClass = ImmutableMap.copyOf(byBindingArgClassBuilder);
 
-        final CtClass superClass;
+        final Class<? extends CodecDataObject<?>> generatedClass;
         final MethodType ctorType;
         if (Augmentable.class.isAssignableFrom(bindingClass)) {
             this.possibleAugmentations = factory().getRuntimeContext().getAvailableAugmentationTypes(getSchema());
-            superClass = AUGMENTABLE_SUPERCLASS;
+            generatedClass = CodecDataObjectGenerator.generateAugmentable(prototype.getFactory().getLoader(),
+                bindingClass, propBuilder.build(), keyMethod);
             ctorType = AUGMENTABLE_CONSTRUCTOR_TYPE;
         } else {
             this.possibleAugmentations = ImmutableMap.of();
-            superClass = SUPERCLASS;
+            generatedClass = CodecDataObjectGenerator.generate(prototype.getFactory().getLoader(), bindingClass,
+                propBuilder.build(), keyMethod);
             ctorType = CONSTRUCTOR_TYPE;
         }
         reloadAllAugmentations();
 
-        final Class<?> generatedClass;
-        try {
-            generatedClass = prototype.getFactory().getLoader().generateSubclass(superClass, bindingClass, "codecImpl",
-                new CodecDataObjectCustomizer(propBuilder.build(), keyMethod));
-        } catch (CannotCompileException | IOException | NotFoundException e) {
-            throw new LinkageError("Failed to generated class for " + bindingClass, e);
-        }
-
         final MethodHandle ctor;
         try {
             ctor = MethodHandles.publicLookup().findConstructor(generatedClass, ctorType);
index f48ade1b2160e500e1cefecc5dbfee690d7dfb4d..dd42c1e6e142ca716b7ccf2a9b55ae192cb207fd 100644 (file)
@@ -164,7 +164,6 @@ public abstract class DataObjectStreamer<T extends DataObject> implements DataOb
         writer.endNode();
     }
 
-
     private static void commonStreamLeafset(final BindingStreamEventWriter writer, final List<?> value)
             throws IOException {
         for (Object entry : value) {
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerBridge.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerBridge.java
deleted file mode 100644 (file)
index e75ee77..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.mdsal.binding.dom.codec.impl;
-
-import static com.google.common.base.Verify.verifyNotNull;
-
-import com.google.common.annotations.Beta;
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * Bridge for initializing {@link DataObjectStreamer} instance constants during class loading time. This class is public
- * only due to implementation restrictions and can change at any time.
- */
-// FIXME: this bridge is only necessary to work around Javassist compiler resolution of dependencies. If we switch to
-//         a more bytecode-oriented framework, this bridge becomes superfluous.
-//
-@Beta
-public final class DataObjectStreamerBridge {
-    private static final ThreadLocal<DataObjectStreamerCustomizer> CURRENT_CUSTOMIZER = new ThreadLocal<>();
-
-    private DataObjectStreamerBridge() {
-
-    }
-
-    public static @NonNull DataObjectStreamer<?> resolve(final @NonNull String methodName) {
-        return verifyNotNull(CURRENT_CUSTOMIZER.get(), "No customizer attached").resolve(methodName);
-    }
-
-    static @Nullable DataObjectStreamerCustomizer setup(final @NonNull DataObjectStreamerCustomizer next) {
-        final DataObjectStreamerCustomizer prev = CURRENT_CUSTOMIZER.get();
-        CURRENT_CUSTOMIZER.set(verifyNotNull(next));
-        return prev;
-    }
-
-    static void tearDown(final @Nullable DataObjectStreamerCustomizer prev) {
-        if (prev == null) {
-            CURRENT_CUSTOMIZER.remove();
-        } else {
-            CURRENT_CUSTOMIZER.set(prev);
-        }
-    }
-}
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerCustomizer.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerCustomizer.java
deleted file mode 100644 (file)
index f958c15..0000000
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.mdsal.binding.dom.codec.impl;
-
-import static com.google.common.base.Verify.verify;
-import static com.google.common.base.Verify.verifyNotNull;
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Supplier;
-import com.google.common.collect.ImmutableMap;
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import javassist.CannotCompileException;
-import javassist.CtClass;
-import javassist.CtField;
-import javassist.CtMethod;
-import javassist.Modifier;
-import javassist.NotFoundException;
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
-import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader;
-import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.Customizer;
-import org.opendaylight.mdsal.binding.dom.codec.loader.StaticClassPool;
-import org.opendaylight.mdsal.binding.dom.codec.util.BindingSchemaMapping;
-import org.opendaylight.mdsal.binding.model.api.GeneratedType;
-import org.opendaylight.mdsal.binding.model.api.MethodSignature;
-import org.opendaylight.mdsal.binding.model.api.ParameterizedType;
-import org.opendaylight.mdsal.binding.model.api.Type;
-import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
-import org.opendaylight.yangtools.yang.binding.Augmentable;
-import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.DataObjectSerializerRegistry;
-import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Beta
-public final class DataObjectStreamerCustomizer implements Customizer {
-    static final CtClass CT_DOS = StaticClassPool.findClass(DataObjectStreamer.class);
-    static final String INSTANCE_FIELD = "INSTANCE";
-
-    private static final Logger LOG = LoggerFactory.getLogger(DataObjectStreamerCustomizer.class);
-    private static final String UNKNOWN_SIZE = BindingStreamEventWriter.class.getName() + ".UNKNOWN_SIZE";
-
-    private static final CtClass CT_VOID = StaticClassPool.findClass(void.class);
-    private static final CtClass[] SERIALIZE_ARGS = new CtClass[] {
-        StaticClassPool.findClass(DataObjectSerializerRegistry.class),
-        StaticClassPool.findClass(DataObject.class),
-        StaticClassPool.findClass(BindingStreamEventWriter.class)
-    };
-
-    private final Map<String, Class<? extends DataObject>> constants = new HashMap<>();
-    private final ImmutableMap<String, Type> props;
-    private final CodecContextFactory registry;
-    private final DataNodeContainer schema;
-    private final String startEvent;
-    private final Class<?> type;
-
-    DataObjectStreamerCustomizer(final CodecContextFactory registry, final GeneratedType genType,
-            final DataNodeContainer schema, final Class<?> type, final String startEvent) {
-        this.registry = requireNonNull(registry);
-        this.schema = requireNonNull(schema);
-        this.type = requireNonNull(type);
-        this.startEvent = requireNonNull(startEvent);
-        props = collectAllProperties(genType);
-    }
-
-    public static DataObjectStreamerCustomizer create(final CodecContextFactory registry, final Class<?> type) {
-        final Entry<GeneratedType, WithStatus> typeAndSchema = registry.getRuntimeContext().getTypeWithSchema(type);
-        final WithStatus schema = typeAndSchema.getValue();
-
-        final String startEvent;
-        if (schema instanceof ContainerSchemaNode || schema instanceof NotificationDefinition) {
-            startEvent = "startContainerNode(" + type.getName() + ".class," + UNKNOWN_SIZE;
-        } else if (schema instanceof ListSchemaNode) {
-            final ListSchemaNode casted = (ListSchemaNode) schema;
-            if (!casted.getKeyDefinition().isEmpty()) {
-                startEvent = "startMapEntryNode(obj." + BindingMapping.IDENTIFIABLE_KEY_NAME + "(), " + UNKNOWN_SIZE;
-            } else {
-                startEvent = "startUnkeyedListItem(" + UNKNOWN_SIZE;
-            }
-        } else if (schema instanceof AugmentationSchemaNode) {
-            startEvent = "startAugmentationNode(" + type.getName() + ".class";
-        } else if (schema instanceof CaseSchemaNode) {
-            startEvent = "startCase(" + type.getName() + ".class, " + UNKNOWN_SIZE;
-        } else {
-            throw new UnsupportedOperationException("Schema type " + schema.getClass() + " is not supported");
-        }
-
-        return new DataObjectStreamerCustomizer(registry, typeAndSchema.getKey(), (DataNodeContainer) schema, type,
-            startEvent);
-    }
-
-    @Override
-    public List<Class<?>> customize(final CodecClassLoader loader, final CtClass bindingClass, final CtClass generated)
-            throws CannotCompileException, NotFoundException, IOException {
-        LOG.trace("Definining streamer {}", generated.getName());
-
-        final CtField instanceField = new CtField(generated, INSTANCE_FIELD, generated);
-        instanceField.setModifiers(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
-        generated.addField(instanceField, "new " + generated.getName() + "()");
-
-        // This results in a body
-        final String objType = bindingClass.getName();
-        final StringBuilder sb = new StringBuilder()
-                .append("{\n")
-                .append("final ").append(objType).append(" obj = (").append(objType).append(") $2;\n")
-                .append("$3.").append(startEvent).append(");\n");
-
-        final List<Class<?>> dependencies = emitChildren(sb, loader, generated);
-        if (Augmentable.class.isAssignableFrom(type)) {
-            sb.append("streamAugmentations($1, $3, obj);\n");
-        }
-
-        sb.append("$3.endNode();\n")
-            .append('}');
-
-        final CtMethod serialize = new CtMethod(CT_VOID, "serialize", SERIALIZE_ARGS, generated);
-        serialize.setModifiers(Modifier.PUBLIC);
-        serialize.setBody(sb.toString());
-        generated.addMethod(serialize);
-
-        generated.setModifiers(Modifier.PUBLIC | Modifier.FINAL);
-        LOG.trace("Definition of {} done", generated.getName());
-
-        return dependencies;
-    }
-
-    @Override
-    public Class<?> customizeLoading(final @NonNull Supplier<Class<?>> loader) {
-        if (constants.isEmpty()) {
-            return loader.get();
-        }
-
-        final DataObjectStreamerCustomizer prev = DataObjectStreamerBridge.setup(this);
-        try {
-            final Class<?> result = loader.get();
-
-            /*
-             * This a bit of magic to support DataObjectStreamer constants. These constants need to be resolved while
-             * we have the information needed to find them -- that information is being held in this instance and we
-             * leak it to a thread-local variable held by DataObjectStreamerBridge.
-             *
-             * By default the JVM will defer class initialization to first use, which unfortunately is too late for
-             * us, and hence we need to force class to initialize.
-             */
-            try {
-                Class.forName(result.getName(), true, result.getClassLoader());
-            } catch (ClassNotFoundException e) {
-                throw new LinkageError("Failed to find newly-defined " + result, e);
-            }
-
-            return result;
-        } finally {
-            DataObjectStreamerBridge.tearDown(prev);
-        }
-    }
-
-    @NonNull DataObjectStreamer<?> resolve(final @NonNull String methodName) {
-        final Class<? extends DataObject> target = verifyNotNull(constants.get(methodName), "Cannot resolve type of %s",
-            methodName);
-        return verifyNotNull(registry.getDataObjectSerializer(target), "Cannot find serializer for %s", target);
-    }
-
-    private List<Class<?>> emitChildren(final StringBuilder sb, final CodecClassLoader loader,
-            final CtClass generated) throws CannotCompileException {
-        final List<Class<?>> dependencies = new ArrayList<>();
-
-        for (final DataSchemaNode schemaChild : schema.getChildNodes()) {
-            if (!schemaChild.isAugmenting()) {
-                final String getterName = BindingSchemaMapping.getGetterMethodName(schemaChild);
-                final Method getter;
-                try {
-                    getter = type.getMethod(getterName);
-                } catch (NoSuchMethodException e) {
-                    throw new IllegalStateException("Failed to find getter " + getterName, e);
-                }
-
-                final Class<?> dependency = emitChild(sb, loader, generated, getterName, getter.getReturnType(),
-                    schemaChild);
-                if (dependency != null) {
-                    LOG.trace("Require dependency {}", dependency);
-                    dependencies.add(dependency);
-                }
-            }
-        }
-
-        return dependencies;
-    }
-
-    private @Nullable Class<?> emitChild(final StringBuilder sb, final CodecClassLoader loader, final CtClass generated,
-            final String getterName, final Class<?> returnType, final DataSchemaNode child)
-                    throws CannotCompileException {
-        if (child instanceof LeafSchemaNode) {
-            sb.append("streamLeaf($3, \"").append(child.getQName().getLocalName()).append("\", obj.")
-            .append(getterName).append("());\n");
-            return null;
-        }
-        if (child instanceof ContainerSchemaNode) {
-            final Class<? extends DataObject> itemClass = returnType.asSubclass(DataObject.class);
-            final String constField = declareDependency(generated, getterName, itemClass);
-
-            sb.append("streamContainer(").append(constField).append(", $1, $3, obj.").append(getterName)
-            .append("());\n");
-            return registry.getDataObjectSerializer(itemClass).getClass();
-        }
-        if (child instanceof ListSchemaNode) {
-            final Type childType = props.get(getterName);
-            verify(childType instanceof ParameterizedType, "Unexpected type %s for %s", childType, getterName);
-            final Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
-            final Class<?> valueClass;
-            try {
-                valueClass = loader.loadClass(valueType.getFullyQualifiedName());
-            } catch (ClassNotFoundException e) {
-                throw new LinkageError("Failed to load " + valueType, e);
-            }
-
-            verify(DataObject.class.isAssignableFrom(valueClass), "Value type %s of %s is not a DataObject", valueClass,
-                returnType);
-            final Class<? extends DataObject> itemClass = valueClass.asSubclass(DataObject.class);
-            final ListSchemaNode casted = (ListSchemaNode) child;
-
-            sb.append("stream");
-            if (casted.getKeyDefinition().isEmpty()) {
-                sb.append("List");
-            } else {
-                if (casted.isUserOrdered()) {
-                    sb.append("Ordered");
-                }
-                sb.append("Map");
-            }
-
-            final String constField = declareDependency(generated, getterName, itemClass);
-            sb.append('(').append(valueClass.getName()).append(".class, ").append(constField).append(", $1, $3, obj.")
-            .append(getterName).append("());\n");
-            return registry.getDataObjectSerializer(itemClass).getClass();
-        }
-        if (child instanceof AnyXmlSchemaNode) {
-            sb.append("streamAnyxml($3, \"").append(child.getQName().getLocalName()).append("\", obj.")
-            .append(getterName).append("());\n");
-            return null;
-        }
-        if (child instanceof LeafListSchemaNode) {
-            sb.append("stream");
-            if (((LeafListSchemaNode) child).isUserOrdered()) {
-                sb.append("Ordered");
-            }
-            sb.append("LeafList($3, \"").append(child.getQName().getLocalName()).append("\", obj.")
-            .append(getterName).append("());\n");
-            return null;
-        }
-        if (child instanceof ChoiceSchemaNode) {
-            sb.append("streamChoice(").append(returnType.getName()).append(".class, $1, $3, obj.").append(getterName)
-            .append("());\n");
-            return null;
-        }
-
-        LOG.debug("Ignoring {} due to unhandled schema {}", getterName, child);
-        return null;
-    }
-
-    /*
-     * Javassist not quite helpful in our environment. We really want to output plain bytecode so that it links
-     * using normal ClassLoader mechanics (supported via CodecClassLoader), but Javassist's compiler really gets in
-     * the way of keeping things simple by requiring CtClass references to dependencies at the the time we set the
-     * implementation body. In order to side-step those requirements, we rely on defining references to our dependencies
-     * as constants and fill them up via customizeLoading().
-     *
-     * This method defines the constants for later use. Should we migrate to a more byte-code oriented framework
-     * (like ByteBuddy), we will pay some cost in assembling the method bodies, we can ditch the constants, as we
-     * provide INSTANCE_FIELD which can readily be reused and CodecClassLoader will resolve the dependencies without
-     * any problems.
-     */
-    private String declareDependency(final CtClass generated, final String getterName,
-            final Class<? extends DataObject> bindingClass) throws CannotCompileException {
-        final String fieldName = getterName + "_STREAMER";
-
-        final CtField instanceField = new CtField(CT_DOS, fieldName, generated);
-        instanceField.setModifiers(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL);
-        generated.addField(instanceField,
-            DataObjectStreamerBridge.class.getName() + ".resolve(\"" + getterName + "\")");
-
-        verify(constants.put(getterName, bindingClass) == null, "Duplicate dependency for %s", getterName);
-        return fieldName;
-    }
-
-    private static ImmutableMap<String, Type> collectAllProperties(final GeneratedType type) {
-        final Map<String, Type> props = new HashMap<>();
-        collectAllProperties(type, props);
-        return ImmutableMap.copyOf(props);
-    }
-
-    private static void collectAllProperties(final GeneratedType type, final Map<String, Type> hashMap) {
-        for (final MethodSignature definition : type.getMethodDefinitions()) {
-            hashMap.put(definition.getName(), definition.getReturnType());
-        }
-        for (final Type parent : type.getImplements()) {
-            if (parent instanceof GeneratedType) {
-                collectAllProperties((GeneratedType) parent, hashMap);
-            }
-        }
-    }
-}
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerGenerator.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerGenerator.java
new file mode 100644 (file)
index 0000000..fa7b0c7
--- /dev/null
@@ -0,0 +1,453 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.dom.codec.impl;
+
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.getField;
+import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.invokeMethod;
+import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.putField;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.description.field.FieldDescription;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.type.TypeDefinition;
+import net.bytebuddy.description.type.TypeDefinition.Sort;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.description.type.TypeDescription.Generic;
+import net.bytebuddy.dynamic.DynamicType.Builder;
+import net.bytebuddy.dynamic.scaffold.InstrumentedType;
+import net.bytebuddy.implementation.Implementation;
+import net.bytebuddy.implementation.Implementation.Context;
+import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
+import net.bytebuddy.implementation.bytecode.Duplication;
+import net.bytebuddy.implementation.bytecode.StackManipulation;
+import net.bytebuddy.implementation.bytecode.TypeCreation;
+import net.bytebuddy.implementation.bytecode.constant.ClassConstant;
+import net.bytebuddy.implementation.bytecode.constant.IntegerConstant;
+import net.bytebuddy.implementation.bytecode.constant.TextConstant;
+import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
+import net.bytebuddy.implementation.bytecode.member.MethodReturn;
+import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
+import net.bytebuddy.jar.asm.MethodVisitor;
+import net.bytebuddy.jar.asm.Opcodes;
+import net.bytebuddy.matcher.ElementMatchers;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
+import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader;
+import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.ClassGenerator;
+import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.GeneratorResult;
+import org.opendaylight.mdsal.binding.dom.codec.util.BindingSchemaMapping;
+import org.opendaylight.mdsal.binding.model.api.GeneratedType;
+import org.opendaylight.mdsal.binding.model.api.MethodSignature;
+import org.opendaylight.mdsal.binding.model.api.ParameterizedType;
+import org.opendaylight.mdsal.binding.model.api.Type;
+import org.opendaylight.yangtools.yang.binding.Augmentable;
+import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.DataObjectSerializerRegistry;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class DataObjectStreamerGenerator<T extends DataObjectStreamer<?>> implements ClassGenerator<T> {
+    static final String INSTANCE_FIELD = "INSTANCE";
+
+    private static final Logger LOG = LoggerFactory.getLogger(DataObjectStreamerGenerator.class);
+    private static final Generic BB_VOID = TypeDefinition.Sort.describe(void.class);
+    private static final Generic BB_DATAOBJECT = TypeDefinition.Sort.describe(DataObject.class);
+    private static final Generic BB_DOSR = TypeDefinition.Sort.describe(DataObjectSerializerRegistry.class);
+    private static final Generic BB_BESV = TypeDefinition.Sort.describe(BindingStreamEventWriter.class);
+    private static final Generic BB_IOX = TypeDefinition.Sort.describe(IOException.class);
+
+    private static final int PUB_FINAL = Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC;
+    private static final int PUB_CONST = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL
+            | Opcodes.ACC_SYNTHETIC;
+
+    private static final Builder<?> TEMPLATE = new ByteBuddy().subclass(DataObjectStreamer.class)
+            .modifiers(PUB_FINAL);
+
+    private static final StackManipulation REG = MethodVariableAccess.REFERENCE.loadFrom(1);
+    private static final StackManipulation OBJ = MethodVariableAccess.REFERENCE.loadFrom(2);
+    private static final StackManipulation STREAM = MethodVariableAccess.REFERENCE.loadFrom(3);
+    private static final StackManipulation UNKNOWN_SIZE = IntegerConstant.forValue(
+        BindingStreamEventWriter.UNKNOWN_SIZE);
+
+    private static final StackManipulation START_AUGMENTATION_NODE = invokeMethod(BindingStreamEventWriter.class,
+        "startAugmentationNode", Class.class);
+    private static final StackManipulation START_CASE = invokeMethod(BindingStreamEventWriter.class,
+        "startCase", Class.class, int.class);
+    private static final StackManipulation START_CONTAINER_NODE = invokeMethod(BindingStreamEventWriter.class,
+        "startContainerNode", Class.class, int.class);
+    private static final StackManipulation END_NODE = invokeMethod(BindingStreamEventWriter.class,
+        "endNode");
+
+    // startMapEntryNode(obj.key(), UNKNOWN_SIZE);
+    private static final StackManipulation START_MAP_ENTRY_NODE = new StackManipulation.Compound(
+        OBJ,
+        invokeMethod(Identifiable.class, "key"),
+        UNKNOWN_SIZE,
+        invokeMethod(BindingStreamEventWriter.class, "startMapEntryNode", Identifier.class, int.class));
+
+    // startUnkeyedListItem(UNKNOWN_SIZE);
+    private static final StackManipulation START_UNKEYED_LIST_ITEM = new StackManipulation.Compound(
+        UNKNOWN_SIZE,
+        invokeMethod(BindingStreamEventWriter.class, "startUnkeyedListItem", int.class));
+
+    private static final StackManipulation STREAM_ANYXML = invokeMethod(DataObjectStreamer.class,
+        "streamAnyxml", BindingStreamEventWriter.class, String.class, Object.class);
+    private static final StackManipulation STREAM_CHOICE = invokeMethod(DataObjectStreamer.class,
+        "streamChoice", Class.class, DataObjectSerializerRegistry.class, BindingStreamEventWriter.class,
+        DataContainer.class);
+    private static final StackManipulation STREAM_CONTAINER = invokeMethod(DataObjectStreamer.class,
+        "streamContainer", DataObjectStreamer.class, DataObjectSerializerRegistry.class, BindingStreamEventWriter.class,
+        DataObject.class);
+    private static final StackManipulation STREAM_LEAF = invokeMethod(DataObjectStreamer.class,
+        "streamLeaf", BindingStreamEventWriter.class, String.class, Object.class);
+    private static final StackManipulation STREAM_LEAF_LIST = invokeMethod(DataObjectStreamer.class,
+        "streamLeafList",
+        BindingStreamEventWriter.class, String.class, List.class);
+    private static final StackManipulation STREAM_ORDERED_LEAF_LIST = invokeMethod(DataObjectStreamer.class,
+        "streamOrderedLeafList", BindingStreamEventWriter.class, String.class, List.class);
+    private static final StackManipulation STREAM_LIST = invokeMethod(DataObjectStreamer.class,
+        "streamList", Class.class, DataObjectStreamer.class, DataObjectSerializerRegistry.class,
+        BindingStreamEventWriter.class, List.class);
+    private static final StackManipulation STREAM_MAP = invokeMethod(DataObjectStreamer.class,
+        "streamMap", Class.class, DataObjectStreamer.class, DataObjectSerializerRegistry.class,
+        BindingStreamEventWriter.class, List.class);
+    private static final StackManipulation STREAM_ORDERED_MAP = invokeMethod(DataObjectStreamer.class,
+        "streamOrderedMap", Class.class, DataObjectStreamer.class, DataObjectSerializerRegistry.class,
+        BindingStreamEventWriter.class, List.class);
+
+    // streamAugmentations(reg, stream, obj);
+    private static final StackManipulation STREAM_AUGMENTATIONS = new StackManipulation.Compound(
+        REG,
+        STREAM,
+        OBJ,
+        invokeMethod(DataObjectStreamer.class, "streamAugmentations", DataObjectSerializerRegistry.class,
+            BindingStreamEventWriter.class, Augmentable.class));
+
+    private final CodecContextFactory registry;
+    private final StackManipulation startEvent;
+    private final DataNodeContainer schema;
+    private final Class<?> type;
+    private final GeneratedType genType;
+
+    private DataObjectStreamerGenerator(final CodecContextFactory registry, final GeneratedType genType,
+            final DataNodeContainer schema, final Class<?> type, final StackManipulation startEvent) {
+        this.registry = requireNonNull(registry);
+        this.genType = requireNonNull(genType);
+        this.schema = requireNonNull(schema);
+        this.type = requireNonNull(type);
+        this.startEvent = requireNonNull(startEvent);
+    }
+
+    static Class<? extends DataObjectStreamer<?>> generateStreamer(final CodecClassLoader loader,
+            final CodecContextFactory registry, final Class<?> type) {
+
+        final Entry<GeneratedType, WithStatus> typeAndSchema = registry.getRuntimeContext().getTypeWithSchema(type);
+        final WithStatus schema = typeAndSchema.getValue();
+
+        final StackManipulation startEvent;
+        if (schema instanceof ContainerSchemaNode || schema instanceof NotificationDefinition) {
+            startEvent = classUnknownSizeMethod(START_CONTAINER_NODE, type);
+        } else if (schema instanceof ListSchemaNode) {
+            startEvent = ((ListSchemaNode) schema).getKeyDefinition().isEmpty() ? START_UNKEYED_LIST_ITEM
+                    : START_MAP_ENTRY_NODE;
+        } else if (schema instanceof AugmentationSchemaNode) {
+            // startAugmentationNode(Foo.class);
+            startEvent = new StackManipulation.Compound(
+                ClassConstant.of(Sort.describe(type).asErasure()),
+                START_AUGMENTATION_NODE);
+        } else if (schema instanceof CaseSchemaNode) {
+            startEvent = classUnknownSizeMethod(START_CASE, type);
+        } else {
+            throw new UnsupportedOperationException("Schema type " + schema.getClass() + " is not supported");
+        }
+
+        return loader.generateClass(type, "streamer",
+            new DataObjectStreamerGenerator<>(registry, typeAndSchema.getKey(), (DataNodeContainer) schema, type,
+                    startEvent));
+    }
+
+    @Override
+    public GeneratorResult<T> generateClass(final CodecClassLoader loader, final String fqcn,
+            final Class<?> bindingInterface) {
+        LOG.trace("Definining streamer {}", fqcn);
+
+        @SuppressWarnings("unchecked")
+        Builder<T> builder = (Builder<T>) TEMPLATE.name(fqcn);
+
+        final ImmutableMap<String, Type> props = collectAllProperties(genType);
+        final List<ChildStream> children = new ArrayList<>(props.size());
+        for (final DataSchemaNode schemaChild : schema.getChildNodes()) {
+            if (!schemaChild.isAugmenting()) {
+                final String getterName = BindingSchemaMapping.getGetterMethodName(schemaChild);
+                final Method getter;
+                try {
+                    getter = type.getMethod(getterName);
+                } catch (NoSuchMethodException e) {
+                    throw new IllegalStateException("Failed to find getter " + getterName, e);
+                }
+
+                final ChildStream child = createStream(loader, props, schemaChild, getter);
+                if (child != null) {
+                    children.add(child);
+                }
+            }
+        }
+
+        final ImmutableList.Builder<Class<?>> depBuilder = ImmutableList.builder();
+        for (ChildStream child : children) {
+            final Class<?> dependency = child.getDependency();
+            if (dependency != null) {
+                depBuilder.add(dependency);
+            }
+        }
+
+        final GeneratorResult<T> result = GeneratorResult.of(builder
+            .defineMethod("serialize", BB_VOID, PUB_FINAL)
+                .withParameters(BB_DOSR, BB_DATAOBJECT, BB_BESV)
+                .throwing(BB_IOX)
+            .intercept(new SerializeImplementation(bindingInterface, startEvent, children)).make(), depBuilder.build());
+
+        LOG.trace("Definition of {} done", fqcn);
+        return result;
+    }
+
+    private ChildStream createStream(final CodecClassLoader loader, final ImmutableMap<String, Type> props,
+            final DataSchemaNode childSchema, final Method getter) {
+        if (childSchema instanceof LeafSchemaNode) {
+            return qnameChildStream(STREAM_LEAF, getter, childSchema);
+        }
+        if (childSchema instanceof ContainerSchemaNode) {
+            return containerChildStream(getter);
+        }
+        if (childSchema instanceof ListSchemaNode) {
+            final String getterName = getter.getName();
+            final Type childType = props.get(getterName);
+            verify(childType instanceof ParameterizedType, "Unexpected type %s for %s", childType, getterName);
+            final Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
+            final Class<?> valueClass;
+            try {
+                valueClass = loader.loadClass(valueType.getFullyQualifiedName());
+            } catch (ClassNotFoundException e) {
+                throw new LinkageError("Failed to load " + valueType, e);
+            }
+            return listChildStream(getter, valueClass.asSubclass(DataObject.class), (ListSchemaNode) childSchema);
+        }
+        if (childSchema instanceof ChoiceSchemaNode) {
+            return choiceChildStream(getter);
+        }
+        if (childSchema instanceof AnyXmlSchemaNode) {
+            return qnameChildStream(STREAM_ANYXML, getter, childSchema);
+        }
+        if (childSchema instanceof LeafListSchemaNode) {
+            return qnameChildStream(((LeafListSchemaNode) childSchema).isUserOrdered() ? STREAM_ORDERED_LEAF_LIST
+                    : STREAM_LEAF_LIST, getter, childSchema);
+        }
+
+        LOG.debug("Ignoring {} due to unhandled schema {}", getter, childSchema);
+        return null;
+    }
+
+    private static ChildStream choiceChildStream(final Method getter) {
+        // streamChoice(Foo.class, reg, stream, obj.getFoo());
+        return new ChildStream(
+            ClassConstant.of(Sort.describe(getter.getReturnType()).asErasure()),
+            REG,
+            STREAM,
+            OBJ,
+            invokeMethod(getter),
+            STREAM_CHOICE);
+    }
+
+    private ChildStream containerChildStream(final Method getter) {
+        final Class<? extends DataObject> itemClass = getter.getReturnType().asSubclass(DataObject.class);
+        final DataObjectStreamer<?> streamer = registry.getDataObjectSerializer(itemClass);
+
+        // streamContainer(FooStreamer.INSTANCE, reg, stream, obj.getFoo());
+        return new ChildStream(streamer,
+            streamerInstance(streamer),
+            REG,
+            STREAM,
+            OBJ,
+            invokeMethod(getter),
+            STREAM_CONTAINER);
+    }
+
+    private ChildStream listChildStream(final Method getter, final Class<? extends DataObject> itemClass,
+            final ListSchemaNode childSchema) {
+        final DataObjectStreamer<?> streamer = registry.getDataObjectSerializer(itemClass);
+        final StackManipulation method;
+        if (childSchema.getKeyDefinition().isEmpty()) {
+            method = STREAM_LIST;
+        } else {
+            method = childSchema.isUserOrdered() ? STREAM_ORDERED_MAP : STREAM_MAP;
+        }
+
+        // <METHOD>(Foo.class, FooStreamer.INSTACE, reg, stream, obj.getFoo());
+        return new ChildStream(streamer,
+            ClassConstant.of(Sort.describe(itemClass).asErasure()),
+            streamerInstance(streamer),
+            REG,
+            STREAM,
+            OBJ,
+            invokeMethod(getter),
+            method);
+    }
+
+    private static ChildStream qnameChildStream(final StackManipulation method, final Method getter,
+            final DataSchemaNode schema) {
+        // <METHOD>(stream, "foo", obj.getFoo());
+        return new ChildStream(
+            STREAM,
+            new TextConstant(schema.getQName().getLocalName()),
+            OBJ,
+            invokeMethod(getter),
+            method);
+    }
+
+    private static StackManipulation streamerInstance(final DataObjectStreamer<?> streamer) {
+        try {
+            return getField(streamer.getClass().getDeclaredField(INSTANCE_FIELD));
+        } catch (NoSuchFieldException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private static StackManipulation classUnknownSizeMethod(final StackManipulation method, final Class<?> type) {
+        // <METHOD>(Foo.class, UNKNOWN_SIZE);
+        return new StackManipulation.Compound(
+                ClassConstant.of(Sort.describe(type).asErasure()),
+                UNKNOWN_SIZE,
+                method);
+    }
+
+    private static ImmutableMap<String, Type> collectAllProperties(final GeneratedType type) {
+        final Map<String, Type> props = new HashMap<>();
+        collectAllProperties(type, props);
+        return ImmutableMap.copyOf(props);
+    }
+
+    private static void collectAllProperties(final GeneratedType type, final Map<String, Type> hashMap) {
+        for (final MethodSignature definition : type.getMethodDefinitions()) {
+            hashMap.put(definition.getName(), definition.getReturnType());
+        }
+        for (final Type parent : type.getImplements()) {
+            if (parent instanceof GeneratedType) {
+                collectAllProperties((GeneratedType) parent, hashMap);
+            }
+        }
+    }
+
+    private static final class SerializeImplementation implements Implementation {
+        private final List<ChildStream> children;
+        private final StackManipulation startEvent;
+        private final Class<?> bindingInterface;
+
+        SerializeImplementation(final Class<?> bindingInterface, final StackManipulation startEvent,
+                final List<ChildStream> children) {
+            this.bindingInterface = requireNonNull(bindingInterface);
+            this.startEvent = requireNonNull(startEvent);
+            this.children = requireNonNull(children);
+        }
+
+        @Override
+        public InstrumentedType prepare(final InstrumentedType instrumentedType) {
+            return instrumentedType
+                    // private static final This INSTANCE = new This();
+                    .withField(new FieldDescription.Token(INSTANCE_FIELD, PUB_CONST, instrumentedType.asGenericType()))
+                    .withInitializer(InitializeInstanceField.INSTANCE);
+        }
+
+        @Override
+        public ByteCodeAppender appender(final Target implementationTarget) {
+            final List<StackManipulation> manipulations = new ArrayList<>(children.size() + 6);
+
+            // stream.<START_EVENT>(...);
+            manipulations.add(STREAM);
+            manipulations.add(startEvent);
+
+            // ... emit children ...
+            manipulations.addAll(children);
+
+            if (Augmentable.class.isAssignableFrom(bindingInterface)) {
+                // streamAugmentations(reg, stream, obj);
+                manipulations.add(STREAM_AUGMENTATIONS);
+            }
+
+            // stream.endNode();
+            manipulations.add(STREAM);
+            manipulations.add(END_NODE);
+            // return;
+            manipulations.add(MethodReturn.VOID);
+
+            return new ByteCodeAppender.Simple(manipulations);
+        }
+    }
+
+    private static final class ChildStream extends StackManipulation.Compound {
+        private final @Nullable Class<?> dependency;
+
+        ChildStream(final StackManipulation... stackManipulation) {
+            super(stackManipulation);
+            dependency = null;
+        }
+
+        ChildStream(final DataObjectStreamer<?> streamer, final StackManipulation... stackManipulation) {
+            super(stackManipulation);
+            dependency = streamer.getClass();
+        }
+
+        @Nullable Class<?> getDependency() {
+            return dependency;
+        }
+    }
+
+    private enum InitializeInstanceField implements ByteCodeAppender {
+        INSTANCE;
+
+        @Override
+        public Size apply(final MethodVisitor methodVisitor, final Context implementationContext,
+                final MethodDescription instrumentedMethod) {
+            final TypeDescription instrumentedType = implementationContext.getInstrumentedType();
+            StackManipulation.Size operandStackSize = new StackManipulation.Compound(
+                TypeCreation.of(instrumentedType),
+                Duplication.SINGLE,
+                MethodInvocation.invoke(instrumentedType.getDeclaredMethods()
+                    .filter(ElementMatchers.isDefaultConstructor()).getOnly().asDefined()),
+                putField(instrumentedType, INSTANCE_FIELD))
+                    .apply(methodVisitor, implementationContext);
+            return new Size(operandStackSize.getMaximalSize(), instrumentedMethod.getStackSize());
+        }
+    }
+}
index dbad1573d54a9ca83739eda9a5531c15fc211f6c..313e300ee8f00b1d019d3450753b6cb3267f83c4 100644 (file)
@@ -12,22 +12,19 @@ import static com.google.common.base.Verify.verify;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableList;
-import java.io.IOException;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
-import javassist.CannotCompileException;
-import javassist.CtClass;
-import javassist.Modifier;
-import javassist.NotFoundException;
 import javax.xml.transform.dom.DOMSource;
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.dynamic.DynamicType.Builder;
+import net.bytebuddy.jar.asm.Opcodes;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingOpaqueObjectCodecTreeNode;
 import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader;
-import org.opendaylight.mdsal.binding.dom.codec.loader.StaticClassPool;
+import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.GeneratorResult;
 import org.opendaylight.yangtools.concepts.Codec;
 import org.opendaylight.yangtools.yang.binding.OpaqueData;
 import org.opendaylight.yangtools.yang.binding.OpaqueObject;
@@ -55,9 +52,11 @@ abstract class OpaqueNodeCodecContext<T extends OpaqueObject<T>> extends ValueNo
         }
     }
 
-    private static final CtClass SUPERCLASS = StaticClassPool.findClass(CodecOpaqueObject.class);
     private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(OpaqueObject.class,
         OpaqueData.class);
+    @SuppressWarnings("rawtypes")
+    private static final Builder<CodecOpaqueObject> TEMPLATE = new ByteBuddy().subclass(CodecOpaqueObject.class)
+            .modifiers(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC);
 
     private final Codec<Object, Object> valueCodec = new Codec<Object, Object>() {
         @Override
@@ -132,17 +131,11 @@ abstract class OpaqueNodeCodecContext<T extends OpaqueObject<T>> extends ValueNo
     }
 
     private static MethodHandle createImpl(final CodecClassLoader rootLoader, final Class<?> bindingClass) {
-        final Class<?> proxyClass;
-        try {
-            proxyClass = rootLoader.generateSubclass(SUPERCLASS, bindingClass, "codecImpl",
-                (pool, binding, generated) -> {
-                    generated.addInterface(binding);
-                    generated.setModifiers(Modifier.PUBLIC | Modifier.FINAL);
-                    return ImmutableList.of();
-                });
-        } catch (CannotCompileException | IOException | NotFoundException e) {
-            throw new LinkageError("Failed to instantiate prototype for " + bindingClass, e);
-        }
+        final Class<?> proxyClass = rootLoader.generateClass(bindingClass, "codecImpl",
+            (loader, fqcn, bindingInterface) -> GeneratorResult.of(TEMPLATE
+                .name(fqcn)
+                .implement(bindingInterface)
+                .make()));
 
         Constructor<?> ctor;
         try {
index c8b2a60140ab512569a57bb2f5a4d05853414508..0a0c9480d719ff46f58bd55aa3346a39976c22bc 100644 (file)
@@ -7,25 +7,26 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.loader;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Verify.verify;
-import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Strings;
 import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.io.File;
 import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Collection;
 import java.util.HashSet;
-import java.util.List;
+import java.util.Map.Entry;
 import java.util.Set;
-import javassist.CannotCompileException;
-import javassist.ClassPool;
-import javassist.CtClass;
-import javassist.LoaderClassPath;
-import javassist.NotFoundException;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.dynamic.DynamicType.Unloaded;
+import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
 import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,32 +45,19 @@ import org.slf4j.LoggerFactory;
  * <p>In single-classloader environments, obviously, the root loader can load all binding classes, and hence no leaf
  * loader is created.
  *
- * <p>
- * Each {@link CodecClassLoader} has a {@link ClassPool} attached to it and can perform operations on it. Leaf loaders
- * specify the root loader's ClassPool as their parent, but are configured to lookup classes first in themselves.
- *
  * @author Robert Varga
  */
 @Beta
 public abstract class CodecClassLoader extends ClassLoader {
-    /**
-     * A customizer allowing a generated class to be modified before it is loader.
-     */
-    @FunctionalInterface
-    public interface Customizer {
+    public interface ClassGenerator<T> {
         /**
-         * Customize a generated class before it is instantiated in the loader.
+         * Generate a class.
          *
-         * @param loader CodecClassLoader which will hold the class. It can be used to lookup/instantiate other classes
-         * @param bindingClass Binding class for which the customized class is being generated
-         * @param generated The class being generated
-         * @return A set of generated classes the generated class depends on
-         * @throws CannotCompileException if the customizer cannot perform partial compilation
-         * @throws NotFoundException if the customizer cannot find a required class
-         * @throws IOException if the customizer cannot perform partial loading
+         * @param fqcn Generated class Fully-qualified class name
+         * @param bindingInterface Binding interface for which the class is being generated
+         * @return A result.
          */
-        @NonNull List<Class<?>> customize(@NonNull CodecClassLoader loader, @NonNull CtClass bindingClass,
-                @NonNull CtClass generated) throws CannotCompileException, NotFoundException, IOException;
+        GeneratorResult<T> generateClass(CodecClassLoader loader, String fqcn, Class<?> bindingInterface);
 
         /**
          * Run the specified loader in a customized environment. The environment customizations must be cleaned up by
@@ -78,75 +66,83 @@ public abstract class CodecClassLoader extends ClassLoader {
          * @param loader Class loader to execute
          * @return Class returned by the loader
          */
-        default Class<?> customizeLoading(final @NonNull Supplier<Class<?>> loader) {
+        default Class<T> customizeLoading(final @NonNull Supplier<Class<T>> loader) {
             return loader.get();
         }
     }
 
+    public static final class GeneratorResult<T> {
+        private final @NonNull ImmutableSet<Class<?>> dependecies;
+        private final @NonNull Unloaded<T> result;
+
+        GeneratorResult(final Unloaded<T> result, final ImmutableSet<Class<?>> dependecies) {
+            this.result = requireNonNull(result);
+            this.dependecies = requireNonNull(dependecies);
+        }
+
+        public static <T> @NonNull GeneratorResult<T> of(final Unloaded<T> result) {
+            return new GeneratorResult<>(result, ImmutableSet.of());
+        }
+
+        public static <T> @NonNull GeneratorResult<T> of(final Unloaded<T> result,
+                final Collection<Class<?>> dependencies) {
+            return dependencies.isEmpty() ? of(result) : new GeneratorResult<>(result,
+                    ImmutableSet.copyOf(dependencies));
+        }
+
+        @NonNull Unloaded<T> getResult() {
+            return result;
+        }
+
+        @NonNull ImmutableSet<Class<?>> getDependencies() {
+            return dependecies;
+        }
+    }
+
+    private static final ClassLoadingStrategy<CodecClassLoader> STRATEGY = (classLoader, types) -> {
+        verify(types.size() == 1, "Unexpected multiple types", types);
+        final Entry<TypeDescription, byte[]> entry = types.entrySet().iterator().next();
+        return ImmutableMap.of(entry.getKey(), classLoader.loadClass(entry.getKey().getName(), entry.getValue()));
+    };
+
     static {
         verify(ClassLoader.registerAsParallelCapable());
     }
 
     private static final Logger LOG = LoggerFactory.getLogger(CodecClassLoader.class);
+    private static final File BYTECODE_DIRECTORY;
 
-    private final ClassPool classPool;
-
-    private CodecClassLoader(final ClassLoader parentLoader, final ClassPool parentPool) {
-        super(parentLoader);
-        this.classPool = new ClassPool(parentPool);
-        this.classPool.childFirstLookup = true;
-        this.classPool.appendClassPath(new LoaderClassPath(this));
-    }
-
-    CodecClassLoader() {
-        this(StaticClassPool.LOADER, StaticClassPool.POOL);
+    static {
+        final String dir = System.getProperty("org.opendaylight.mdsal.binding.dom.codec.loader.bytecodeDumpDirectory");
+        BYTECODE_DIRECTORY = Strings.isNullOrEmpty(dir) ? null : new File(dir);
     }
 
-    CodecClassLoader(final CodecClassLoader parent) {
-        this(parent, parent.classPool);
+    CodecClassLoader(final ClassLoader parentLoader) {
+        super(parentLoader);
     }
 
     /**
-     * Turn a Class instance into a CtClass for referencing it in code generation. This method supports both
-     * generated- and non-generated classes.
+     * Instantiate a new CodecClassLoader, which serves as the root of generated code loading.
      *
-     * @param clazz Class to be looked up.
-     * @return A CtClass instance
-     * @throws NotFoundException if the class cannot be found
-     * @throws NullPointerException if {@code clazz} is null
+     * @return A new CodecClassLoader.
      */
-    public final @NonNull CtClass findClass(final @NonNull Class<?> clazz) throws NotFoundException {
-        return BindingReflections.isBindingClass(clazz) ? findClassLoader(clazz).getLocalFrozen(clazz.getName())
-                : StaticClassPool.findClass(clazz);
+    public static @NonNull CodecClassLoader create() {
+        return AccessController.doPrivileged((PrivilegedAction<CodecClassLoader>)() -> new RootCodecClassLoader());
     }
 
     /**
-     * Create a new class by subclassing specified class and running a customizer on it. The name of the target class
-     * is formed through concatenation of the name of a {@code bindingInterface} and specified {@code suffix}
+     * The name of the target class is formed through concatenation of the name of a {@code bindingInterface} and
+     * specified {@code suffix}.
      *
-     * @param superClass Superclass from which to derive
      * @param bindingInterface Binding compile-time-generated interface
      * @param suffix Suffix to use
-     * @param customizer Customizer to use to process the class
+     * @param generator Code generator to run
      * @return A generated class object
-     * @throws CannotCompileException if the resulting generated class cannot be compiled or customized
-     * @throws NotFoundException if the binding interface cannot be found or the generated class cannot be customized
-     * @throws IOException if the generated class cannot be turned into bytecode or the generator fails with IOException
      * @throws NullPointerException if any argument is null
      */
-    public final Class<?> generateSubclass(final CtClass superClass, final Class<?> bindingInterface,
-            final String suffix, final Customizer customizer) throws CannotCompileException, IOException,
-                NotFoundException {
-        return findClassLoader(requireNonNull(bindingInterface))
-                .doGenerateSubclass(superClass, bindingInterface, suffix, customizer);
-    }
-
-    final @NonNull CtClass getLocalFrozen(final String name) throws NotFoundException {
-        synchronized (getClassLoadingLock(name)) {
-            final CtClass result = classPool.get(name);
-            result.freeze();
-            return result;
-        }
+    public final <T> Class<T> generateClass(final Class<?> bindingInterface,
+            final String suffix, final ClassGenerator<T> generator)  {
+        return findClassLoader(requireNonNull(bindingInterface)).doGenerateClass(bindingInterface, suffix, generator);
     }
 
     /**
@@ -168,53 +164,37 @@ public abstract class CodecClassLoader extends ClassLoader {
      */
     abstract @NonNull CodecClassLoader findClassLoader(@NonNull Class<?> bindingClass);
 
-    private Class<?> doGenerateSubclass(final CtClass superClass, final Class<?> bindingInterface, final String suffix,
-            final Customizer customizer) throws CannotCompileException, IOException, NotFoundException {
-        checkArgument(!superClass.isInterface(), "%s must not be an interface", superClass);
-        checkArgument(bindingInterface.isInterface(), "%s is not an interface", bindingInterface);
-        checkArgument(!Strings.isNullOrEmpty(suffix));
+    private <T> Class<T> doGenerateClass(final Class<?> bindingInterface, final String suffix,
+            final ClassGenerator<T> generator)  {
+        final String fqcn = bindingInterface.getName() + "$$$" + suffix;
 
-        final String bindingName = bindingInterface.getName();
-        final String fqn = bindingName + "$$$" + suffix;
-        synchronized (getClassLoadingLock(fqn)) {
+        synchronized (getClassLoadingLock(fqcn)) {
             // Attempt to find a loaded class
-            final Class<?> loaded = findLoadedClass(fqn);
-            if (loaded != null) {
-                return loaded;
+            final Class<?> existing = findLoadedClass(fqcn);
+            if (existing != null) {
+                return (Class<T>) existing;
             }
 
-            // Get the interface
-            final CtClass bindingCt = getLocalFrozen(bindingName);
-            try {
-                final byte[] byteCode;
-                final CtClass generated = verifyNotNull(classPool.makeClass(fqn, superClass));
-                try {
-                    final List<Class<?>> deps = customizer.customize(this, bindingCt, generated);
-                    final String ctName = generated.getName();
-                    verify(fqn.equals(ctName), "Target class is %s returned result is %s", fqn, ctName);
-                    processDependencies(deps);
-
-                    byteCode = generated.toBytecode();
-                } finally {
-                    // Always detach the result, as we will never use it again
-                    generated.detach();
-                }
-
-                return customizer.customizeLoading(() -> {
-                    final Class<?> newClass = defineClass(fqn, byteCode, 0, byteCode.length);
-                    resolveClass(newClass);
-                    return newClass;
-                });
-            } finally {
-                // Binding interfaces are used only a few times, hence it does not make sense to cache them in the class
-                // pool.
-                // TODO: this hinders caching, hence we should re-think this
-                bindingCt.detach();
-            }
+            final GeneratorResult<T> result = generator.generateClass(this, fqcn, bindingInterface);
+            final Unloaded<T> unloaded = result.getResult();
+            verify(fqcn.equals(unloaded.getTypeDescription().getName()), "Unexpected class in %s", unloaded);
+            verify(unloaded.getAuxiliaryTypes().isEmpty(), "Auxiliary types present in %s", unloaded);
+            dumpBytecode(unloaded);
+
+            processDependencies(result.getDependencies());
+            return generator.customizeLoading(() -> (Class<T>) unloaded.load(this, STRATEGY).getLoaded());
         }
     }
 
-    private void processDependencies(final List<Class<?>> deps) {
+    final Class<?> loadClass(final String fqcn, final byte[] byteCode) {
+        synchronized (getClassLoadingLock(fqcn)) {
+            final Class<?> existing = findLoadedClass(fqcn);
+            verify(existing == null, "Attempted to load existing %s", existing);
+            return defineClass(fqcn, byteCode, 0, byteCode.length);
+        }
+    }
+
+    private void processDependencies(final Collection<Class<?>> deps) {
         final Set<LeafCodecClassLoader> depLoaders = new HashSet<>();
         for (Class<?> dep : deps) {
             final ClassLoader depLoader = dep.getClassLoader();
@@ -238,4 +218,14 @@ public abstract class CodecClassLoader extends ClassLoader {
             appendLoaders(depLoaders);
         }
     }
+
+    private static void dumpBytecode(final Unloaded<?> unloaded) {
+        if (BYTECODE_DIRECTORY != null) {
+            try {
+                unloaded.saveIn(BYTECODE_DIRECTORY);
+            } catch (IOException | IllegalArgumentException e) {
+                LOG.info("Failed to save {}", unloaded.getTypeDescription().getName(), e);
+            }
+        }
+    }
 }
index eebeb6c8184474738f618b89e29b7ba23618a7b2..ef2eed4052d092727bd91f019cbce88a6f6c9018 100644 (file)
@@ -48,10 +48,13 @@ final class LeafCodecClassLoader extends CodecClassLoader {
         } catch (ClassNotFoundException e) {
             LOG.trace("Class {} not found in target, looking through dependencies", name);
             for (LeafCodecClassLoader loader : dependencies) {
-                final Class<?> loaded = loader.findLoadedClass(name);
-                if (loaded != null) {
-                    LOG.trace("Class {} found in dependency {}", name, loader);
-                    return loaded;
+                // Careful: a loading operation may be underway, make sure that process has completed
+                synchronized (loader.getClassLoadingLock(name)) {
+                    final Class<?> loaded = loader.findLoadedClass(name);
+                    if (loaded != null) {
+                        LOG.trace("Class {} found in dependency {}", name, loader);
+                        return loaded;
+                    }
                 }
             }
 
index 1b2b8023d9e198307699767a329f63c85f3247f0..5465e83cacffad343fab0463ae573e005335da41 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.mdsal.binding.dom.codec.loader;
 
 import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
@@ -15,12 +16,14 @@ import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 // A root codec classloader, binding only whatever is available StaticClassPool
 final class RootCodecClassLoader extends CodecClassLoader {
     private static final Logger LOG = LoggerFactory.getLogger(RootCodecClassLoader.class);
+    private static final ClassLoader LOADER = verifyNotNull(RootCodecClassLoader.class.getClassLoader());
 
     static {
         verify(registerAsParallelCapable());
@@ -33,7 +36,7 @@ final class RootCodecClassLoader extends CodecClassLoader {
     private volatile ImmutableMap<ClassLoader, CodecClassLoader> loaders = ImmutableMap.of();
 
     RootCodecClassLoader() {
-        super();
+        super(LOADER);
     }
 
     @Override
@@ -55,7 +58,7 @@ final class RootCodecClassLoader extends CodecClassLoader {
         // ourselves) or we need to create a new Leaf.
         final CodecClassLoader found;
         if (!isOurClass(bindingClass)) {
-            StaticClassPool.verifyStaticLinkage(target);
+            verifyStaticLinkage(target);
             found = AccessController.doPrivileged(
                 (PrivilegedAction<CodecClassLoader>)() -> new LeafCodecClassLoader(this, target));
         } else {
@@ -99,4 +102,17 @@ final class RootCodecClassLoader extends CodecClassLoader {
         }
         return bindingClass.equals(ourClass);
     }
+
+    // Sanity check: target has to resolve yang-binding contents to the same class, otherwise we are in a pickle
+    private static void verifyStaticLinkage(final ClassLoader candidate) {
+        final Class<?> targetClazz;
+        try {
+            targetClazz = candidate.loadClass(DataContainer.class.getName());
+        } catch (ClassNotFoundException e) {
+            throw new IllegalStateException("ClassLoader " + candidate + " cannot load " + DataContainer.class, e);
+        }
+        verify(DataContainer.class.equals(targetClazz),
+            "Class mismatch on DataContainer. Ours is from %s, target %s has %s from %s",
+            DataContainer.class.getClassLoader(), candidate, targetClazz, targetClazz.getClassLoader());
+    }
 }
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/loader/StaticClassPool.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/loader/StaticClassPool.java
deleted file mode 100644 (file)
index a2b7ac6..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.mdsal.binding.dom.codec.loader;
-
-import static com.google.common.base.Verify.verify;
-import static com.google.common.base.Verify.verifyNotNull;
-
-import com.google.common.annotations.Beta;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import javassist.ClassPool;
-import javassist.CtClass;
-import javassist.LoaderClassPath;
-import javassist.NotFoundException;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.yangtools.yang.binding.DataContainer;
-
-/**
- * Static class pool, bound to the class loader of binding-dom-codec. It can be used to acquire CtClass instances that
- * reside within the binding-dom-codec artifact or any of its direct mandatory dependencies. It can also instantiate
- * {@link CodecClassLoader} instances for use with code generation.
- *
- * @author Robert Varga
- */
-@Beta
-public final class StaticClassPool {
-    static final ClassLoader LOADER = verifyNotNull(StaticClassPool.class.getClassLoader());
-    static final ClassPool POOL;
-
-    static {
-        final ClassPool pool = new ClassPool();
-        pool.appendClassPath(new LoaderClassPath(LOADER));
-        POOL = pool;
-    }
-
-    private StaticClassPool() {
-        // Utility class
-    }
-
-    /**
-     * Instantiate a new CodecClassLoader.
-     *
-     * @return A new CodecClassLoader.
-     */
-    public static @NonNull CodecClassLoader createLoader() {
-        return AccessController.doPrivileged((PrivilegedAction<CodecClassLoader>)() -> new RootCodecClassLoader());
-    }
-
-    /**
-     * Resolve a binding-dom-codec class to its {@link CtClass} counterpart.
-     *
-     * @param clazz Class to resolve
-     * @return A CtClass instance
-     * @throws IllegalStateException if the class cannot be resolved
-     * @throws NullPointerException if {@code clazz} is null
-     */
-    public static synchronized @NonNull CtClass findClass(final @NonNull Class<?> clazz) {
-        final CtClass ret;
-        try {
-            ret = POOL.get(clazz.getName());
-        } catch (NotFoundException e) {
-            throw new IllegalStateException("Failed to find " + clazz, e);
-        }
-        ret.freeze();
-        return ret;
-    }
-
-    // Sanity check: target has to resolve yang-binding contents to the same class, otherwise we are in a pickle
-    static void verifyStaticLinkage(final ClassLoader candidate) {
-        final Class<?> targetClazz;
-        try {
-            targetClazz = candidate.loadClass(DataContainer.class.getName());
-        } catch (ClassNotFoundException e) {
-            throw new IllegalStateException("ClassLoader " + candidate + " cannot load " + DataContainer.class, e);
-        }
-        verify(DataContainer.class.equals(targetClazz),
-            "Class mismatch on DataContainer. Ours is from %s, target %s has %s from %s",
-            DataContainer.class.getClassLoader(), candidate, targetClazz, targetClazz.getClassLoader());
-    }
-}
index 4b0a42d36c3414d06f018f9af61a1ac8ca3b3331..3747006bbccc53d17b4007591284d02c07b5e0f7 100644 (file)
@@ -6,16 +6,10 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 /**
- * {@link java.lang.ClassLoader} support for Binding/DOM codec translation code generators. This package provides two
- * core classes:
- * <ul>
- * <li>{@link org.opendaylight.mdsal.binding.dom.codec.loader.StaticClassPool}, which is allows lookup of classes within
- *     Binding/DOM codec for the purpose of referencing them within code generators.</li>
- * <li>{@link org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader}, which allows lookup of
- *     compile-time-generated Binding classes for the purpose of referencing them within code generators and which
- *     serves as the ClassLoader holding runtime-generated codecs.
- * </li>
- * </ul>
+ * {@link java.lang.ClassLoader} support for Binding/DOM codec translation code generators. This package provides one
+ * core class, {@link org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader}, which allows lookup of
+ * compile-time-generated Binding classes for the purpose of referencing them within code generators and which
+ * serves as the ClassLoader holding runtime-generated codecs.
  *
  * <p>
  * While the interfaces and classes in this package may be publicly accessible, they are an implementation detail and