Update byte-buddy integration
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / NotificationCodecContext.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.binding.dom.codec.impl;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.getField;
12 import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.loadThis;
13 import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.putField;
14
15 import com.google.common.base.Throwables;
16 import java.lang.invoke.MethodHandle;
17 import java.lang.invoke.MethodHandles;
18 import java.lang.invoke.MethodType;
19 import java.time.Instant;
20 import net.bytebuddy.ByteBuddy;
21 import net.bytebuddy.description.method.MethodDescription;
22 import net.bytebuddy.description.method.MethodDescription.InGenericShape;
23 import net.bytebuddy.description.type.TypeDefinition;
24 import net.bytebuddy.description.type.TypeDescription;
25 import net.bytebuddy.description.type.TypeDescription.Generic;
26 import net.bytebuddy.dynamic.scaffold.InstrumentedType;
27 import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
28 import net.bytebuddy.implementation.Implementation;
29 import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
30 import net.bytebuddy.implementation.bytecode.StackManipulation;
31 import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
32 import net.bytebuddy.implementation.bytecode.member.MethodReturn;
33 import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
34 import net.bytebuddy.jar.asm.Opcodes;
35 import net.bytebuddy.matcher.ElementMatchers;
36 import org.eclipse.jdt.annotation.NonNull;
37 import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.GeneratorResult;
38 import org.opendaylight.yangtools.yang.binding.DataObject;
39 import org.opendaylight.yangtools.yang.binding.EventInstantAware;
40 import org.opendaylight.yangtools.yang.binding.Notification;
41 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
42 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
44 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
45
46 final class NotificationCodecContext<D extends DataObject & Notification>
47         extends DataObjectCodecContext<D, NotificationDefinition> {
48     private static final Generic EVENT_INSTANT_AWARE = TypeDefinition.Sort.describe(EventInstantAware.class);
49
50     private static final String EVENT_INSTANT_NAME;
51     private static final Generic EVENT_INSTANT_RETTYPE;
52
53     static {
54         final MethodDescription eventInstance = EVENT_INSTANT_AWARE.getDeclaredMethods().getOnly();
55         EVENT_INSTANT_NAME = eventInstance.getName();
56         EVENT_INSTANT_RETTYPE = eventInstance.getReturnType();
57     }
58
59     private static final Generic BB_DOCC = TypeDefinition.Sort.describe(DataObjectCodecContext.class);
60     private static final Generic BB_NNC = TypeDefinition.Sort.describe(NormalizedNodeContainer.class);
61     private static final Generic BB_I = TypeDefinition.Sort.describe(Instant.class);
62
63     private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, DataObjectCodecContext.class,
64         NormalizedNodeContainer.class, Instant.class);
65     private static final MethodType NOTIFICATION_TYPE = MethodType.methodType(Notification.class,
66         NotificationCodecContext.class, ContainerNode.class, Instant.class);
67     private static final String INSTANT_FIELD = "instant";
68
69     private final MethodHandle eventProxy;
70
71     NotificationCodecContext(final Class<?> key, final NotificationDefinition schema,
72             final CodecContextFactory factory) {
73         super(DataContainerCodecPrototype.from(key, schema, factory));
74         final Class<D> bindingClass = getBindingClass();
75
76         final Class<?> awareClass = factory().getLoader().generateClass(bindingClass, "eventInstantAware",
77             (loader, fqcn, bindingInterface) -> {
78                 final Class<?> codecImpl = loader.getGeneratedClass(bindingClass, "codecImpl");
79
80                 return GeneratorResult.of(new ByteBuddy()
81                     .subclass(codecImpl, ConstructorStrategy.Default.NO_CONSTRUCTORS)
82                     .implement(EVENT_INSTANT_AWARE)
83                     .name(fqcn)
84                     .defineField(INSTANT_FIELD, BB_I, Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC)
85                     .defineConstructor(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC)
86                         .withParameters(BB_DOCC, BB_NNC, BB_I)
87                         .intercept(ConstructorImplementation.INSTANCE)
88                     .defineMethod(EVENT_INSTANT_NAME, EVENT_INSTANT_RETTYPE, Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC)
89                         .intercept(EventInstantImplementation.INSTANCE)
90                     .make());
91             });
92
93         final MethodHandle ctor;
94         try {
95             ctor = MethodHandles.publicLookup().findConstructor(awareClass, CONSTRUCTOR_TYPE);
96         } catch (IllegalAccessException | NoSuchMethodException e) {
97             throw new LinkageError("Failed to acquire constructor", e);
98         }
99         eventProxy = ctor.asType(NOTIFICATION_TYPE);
100     }
101
102     @Override
103     public D deserialize(final NormalizedNode<?, ?> data) {
104         checkState(data instanceof ContainerNode);
105         return createBindingProxy((ContainerNode) data);
106     }
107
108     @SuppressWarnings("checkstyle:illegalCatch")
109     Notification deserialize(final @NonNull ContainerNode data, final @NonNull Instant eventInstant) {
110         try {
111             return (Notification) eventProxy.invokeExact(this, data, eventInstant);
112         } catch (final Throwable e) {
113             Throwables.throwIfUnchecked(e);
114             throw new IllegalStateException(e);
115         }
116     }
117
118     @Override
119     protected Object deserializeObject(final NormalizedNode<?, ?> normalizedNode) {
120         return deserialize(normalizedNode);
121     }
122
123     private enum ConstructorImplementation implements Implementation {
124         INSTANCE;
125
126         private static final StackManipulation LOAD_INSTANT_ARG = MethodVariableAccess.REFERENCE.loadFrom(3);
127         private static final StackManipulation LOAD_CTOR_ARGS;
128
129         static {
130             try {
131                 LOAD_CTOR_ARGS = MethodVariableAccess.allArgumentsOf(new MethodDescription.ForLoadedConstructor(
132                     AugmentableCodecDataObject.class.getDeclaredConstructor(DataObjectCodecContext.class,
133                         NormalizedNodeContainer.class)));
134             } catch (NoSuchMethodException e) {
135                 throw new ExceptionInInitializerError(e);
136             }
137         }
138
139         @Override
140         public InstrumentedType prepare(final InstrumentedType instrumentedType) {
141             return instrumentedType;
142         }
143
144         @Override
145         public ByteCodeAppender appender(final Target implementationTarget) {
146             final TypeDescription instrumentedType = implementationTarget.getInstrumentedType();
147             final InGenericShape superCtor = instrumentedType.getSuperClass().getDeclaredMethods()
148                     .filter(ElementMatchers.isConstructor()).getOnly();
149
150             return new ByteCodeAppender.Simple(
151                 loadThis(),
152                 LOAD_CTOR_ARGS,
153                 MethodInvocation.invoke(superCtor),
154                 loadThis(),
155                 LOAD_INSTANT_ARG,
156                 putField(instrumentedType, INSTANT_FIELD),
157                 MethodReturn.VOID);
158         }
159     }
160
161     private enum EventInstantImplementation implements Implementation {
162         INSTANCE;
163
164         @Override
165         public InstrumentedType prepare(final InstrumentedType instrumentedType) {
166             return instrumentedType;
167         }
168
169         @Override
170         public ByteCodeAppender appender(final Target implementationTarget) {
171             return new ByteCodeAppender.Simple(
172               loadThis(),
173               getField(implementationTarget.getInstrumentedType(), INSTANT_FIELD),
174               MethodReturn.REFERENCE);
175         }
176     }
177 }