Update byte-buddy integration
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / ByteBuddyUtils.java
1 /*
2  * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.binding.dom.codec.impl;
9
10 import static java.util.Objects.requireNonNull;
11
12 import java.lang.reflect.Field;
13 import java.lang.reflect.Method;
14 import net.bytebuddy.asm.AsmVisitorWrapper;
15 import net.bytebuddy.description.field.FieldDescription;
16 import net.bytebuddy.description.field.FieldDescription.ForLoadedField;
17 import net.bytebuddy.description.field.FieldList;
18 import net.bytebuddy.description.method.MethodDescription.ForLoadedMethod;
19 import net.bytebuddy.description.method.MethodList;
20 import net.bytebuddy.description.type.TypeDescription;
21 import net.bytebuddy.implementation.Implementation;
22 import net.bytebuddy.implementation.bytecode.StackManipulation;
23 import net.bytebuddy.implementation.bytecode.member.FieldAccess;
24 import net.bytebuddy.implementation.bytecode.member.FieldAccess.Defined;
25 import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
26 import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
27 import net.bytebuddy.jar.asm.ClassVisitor;
28 import net.bytebuddy.jar.asm.ClassWriter;
29 import net.bytebuddy.jar.asm.Label;
30 import net.bytebuddy.jar.asm.MethodVisitor;
31 import net.bytebuddy.jar.asm.Opcodes;
32 import net.bytebuddy.matcher.ElementMatchers;
33 import net.bytebuddy.pool.TypePool;
34
35 final class ByteBuddyUtils {
36     private static final StackManipulation LOAD_THIS = MethodVariableAccess.loadThis();
37
38     private ByteBuddyUtils() {
39
40     }
41
42     // TODO: eliminate this method once MethodVariableAccess.loadThis() returns a singleton
43     static StackManipulation loadThis() {
44         return LOAD_THIS;
45     }
46
47     static StackManipulation invokeMethod(final Method method) {
48         return MethodInvocation.invoke(describe(method));
49     }
50
51     static StackManipulation invokeMethod(final Class<?> clazz, final String name, final Class<?>... args) {
52         return MethodInvocation.invoke(describe(clazz, name, args));
53     }
54
55     static AsmVisitorWrapper computeFrames() {
56         return ComputeFrames.INSTANCE;
57     }
58
59     static StackManipulation ifEq(final Label label) {
60         return new IfEq(label);
61     }
62
63     static StackManipulation markLabel(final Label label) {
64         return new Mark(label);
65     }
66
67     static StackManipulation getField(final Field field) {
68         return FieldAccess.forField(new ForLoadedField(field).asDefined()).read();
69     }
70
71     static StackManipulation getField(final TypeDescription instrumentedType, final String fieldName) {
72         return fieldAccess(instrumentedType, fieldName).read();
73     }
74
75     static StackManipulation putField(final TypeDescription instrumentedType, final String fieldName) {
76         return fieldAccess(instrumentedType, fieldName).write();
77     }
78
79     private static ForLoadedMethod describe(final Method method) {
80         return new ForLoadedMethod(method);
81     }
82
83     private static ForLoadedMethod describe(final Class<?> clazz, final String name, final Class<?>... args) {
84         return describe(getMethod(clazz, name, args));
85     }
86
87     private static Defined fieldAccess(final TypeDescription instrumentedType, final String fieldName) {
88         return FieldAccess.forField(instrumentedType.getDeclaredFields().filter(ElementMatchers.named(fieldName))
89             .getOnly());
90     }
91
92     /**
93      * Utility wrapper to force ASM to compute frames.
94      */
95     private enum ComputeFrames implements AsmVisitorWrapper {
96         INSTANCE;
97
98         @Override
99         public int mergeWriter(final int flags) {
100             return flags | ClassWriter.COMPUTE_FRAMES;
101         }
102
103         @Override
104         public int mergeReader(final int flags) {
105             return flags | ClassWriter.COMPUTE_FRAMES;
106         }
107
108         @Override
109         public ClassVisitor wrap(final TypeDescription td, final ClassVisitor cv, final Implementation.Context ctx,
110                 final TypePool tp, final FieldList<FieldDescription.InDefinedShape> fields, final MethodList<?> methods,
111                 final int wflags, final int rflags) {
112             return cv;
113         }
114     }
115
116     /**
117      * IFEQ opcode invocation, jumping to a particular label.
118      */
119     private static final class IfEq implements StackManipulation {
120         private static final StackManipulation.Size SIZE = new StackManipulation.Size(-1, 0);
121
122         private final Label label;
123
124         IfEq(final Label label) {
125             this.label = requireNonNull(label);
126         }
127
128         @Override
129         public boolean isValid() {
130             return true;
131         }
132
133         @Override
134         public StackManipulation.Size apply(final MethodVisitor mv, final Implementation.Context ctx) {
135             mv.visitJumpInsn(Opcodes.IFEQ, label);
136             return SIZE;
137         }
138     }
139
140     /**
141      * A label definition, marking the spot where IfEq should jump.
142      */
143     private static final class Mark implements StackManipulation {
144         private static final StackManipulation.Size SIZE = new StackManipulation.Size(0, 0);
145
146         private final Label label;
147
148         Mark(final Label label) {
149             this.label = requireNonNull(label);
150         }
151
152         @Override
153         public boolean isValid() {
154             return true;
155         }
156
157         @Override
158         public StackManipulation.Size apply(final MethodVisitor mv, final Implementation.Context ctx) {
159             mv.visitLabel(label);
160             return SIZE;
161         }
162     }
163
164     private static Method getMethod(final Class<?> clazz, final String name, final Class<?>... args) {
165         try {
166             return clazz.getDeclaredMethod(name, args);
167         } catch (NoSuchMethodException e) {
168             throw new IllegalStateException(e);
169         }
170     }
171 }