2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.dom.codec.impl;
10 import static com.google.common.base.Verify.verifyNotNull;
11 import static net.bytebuddy.implementation.bytecode.member.MethodVariableAccess.loadThis;
12 import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.getField;
13 import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.putField;
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.loader.BindingClassLoader.GeneratorResult;
38 import org.opendaylight.mdsal.binding.runtime.api.NotificationRuntimeType;
39 import org.opendaylight.yangtools.yang.binding.BaseNotification;
40 import org.opendaylight.yangtools.yang.binding.DataObject;
41 import org.opendaylight.yangtools.yang.binding.EventInstantAware;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
43 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
47 final class NotificationCodecContext<D extends DataObject & BaseNotification>
48 extends DataObjectCodecContext<D, NotificationRuntimeType> {
49 private static final Generic EVENT_INSTANT_AWARE = TypeDefinition.Sort.describe(EventInstantAware.class);
51 private static final String EVENT_INSTANT_NAME;
52 private static final Generic EVENT_INSTANT_RETTYPE;
55 final MethodDescription eventInstance = EVENT_INSTANT_AWARE.getDeclaredMethods().getOnly();
56 EVENT_INSTANT_NAME = eventInstance.getName();
57 EVENT_INSTANT_RETTYPE = eventInstance.getReturnType();
60 private static final Generic BB_DOCC = TypeDefinition.Sort.describe(DataObjectCodecContext.class);
61 private static final Generic BB_DCN = TypeDefinition.Sort.describe(DataContainerNode.class);
62 private static final Generic BB_I = TypeDefinition.Sort.describe(Instant.class);
64 private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, DataObjectCodecContext.class,
65 DataContainerNode.class, Instant.class);
66 private static final MethodType NOTIFICATION_TYPE = MethodType.methodType(BaseNotification.class,
67 NotificationCodecContext.class, ContainerNode.class, Instant.class);
68 private static final String INSTANT_FIELD = "instant";
70 private final MethodHandle eventProxy;
72 NotificationCodecContext(final Class<?> notificationClass, final NotificationRuntimeType type,
73 final CodecContextFactory factory) {
74 super(new Prototype<>(notificationClass, type, factory));
75 final var bindingClass = getBindingClass();
77 final var eventAwareClass = CodecPackage.EVENT_AWARE.generateClass(prototype().contextFactory().getLoader(),
78 bindingClass, (loader, fqcn, bindingInterface) -> {
79 final var codecImpl = CodecPackage.CODEC.getGeneratedClass(loader, bindingClass);
81 return GeneratorResult.of(new ByteBuddy()
82 .subclass(codecImpl, ConstructorStrategy.Default.NO_CONSTRUCTORS)
83 .implement(EVENT_INSTANT_AWARE)
85 .defineField(INSTANT_FIELD, BB_I, Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC)
86 .defineConstructor(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC)
87 .withParameters(BB_DOCC, BB_DCN, BB_I)
88 .intercept(ConstructorImplementation.INSTANCE)
89 .defineMethod(EVENT_INSTANT_NAME, EVENT_INSTANT_RETTYPE, Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC)
90 .intercept(EventInstantImplementation.INSTANCE)
94 final MethodHandle ctor;
96 ctor = MethodHandles.publicLookup().findConstructor(eventAwareClass, CONSTRUCTOR_TYPE);
97 } catch (IllegalAccessException | NoSuchMethodException e) {
98 throw new LinkageError("Failed to acquire constructor", e);
100 eventProxy = ctor.asType(NOTIFICATION_TYPE);
104 public D deserialize(final NormalizedNode data) {
105 return createBindingProxy(checkDataArgument(ContainerNode.class, data));
108 @SuppressWarnings("checkstyle:illegalCatch")
109 @NonNull BaseNotification deserialize(final @NonNull ContainerNode data, final @NonNull Instant eventInstant) {
110 final BaseNotification ret;
112 ret = (BaseNotification) eventProxy.invokeExact(this, data, eventInstant);
113 } catch (final Throwable e) {
114 Throwables.throwIfUnchecked(e);
115 throw new LinkageError("Failed to instantiate notification", e);
117 return verifyNotNull(ret);
121 protected Object deserializeObject(final NormalizedNode normalizedNode) {
122 return deserialize(normalizedNode);
126 * Prototype for a {@code notification}. This class only exists because DataContainerCodecContext requires a
129 static final class Prototype<D extends DataObject & BaseNotification>
130 extends DataObjectCodecPrototype<NotificationRuntimeType> {
131 private Prototype(final Class<?> cls, final NotificationRuntimeType type, final CodecContextFactory factory) {
132 super(cls, NodeIdentifier.create(type.statement().argument()), type, factory);
136 NotificationCodecContext<?> createInstance() {
137 throw new UnsupportedOperationException("Should never be invoked");
141 private enum ConstructorImplementation implements Implementation {
144 private static final StackManipulation LOAD_INSTANT_ARG = MethodVariableAccess.REFERENCE.loadFrom(3);
145 private static final StackManipulation LOAD_CTOR_ARGS;
149 LOAD_CTOR_ARGS = MethodVariableAccess.allArgumentsOf(new MethodDescription.ForLoadedConstructor(
150 AugmentableCodecDataObject.class.getDeclaredConstructor(AbstractDataObjectCodecContext.class,
151 DataContainerNode.class)));
152 } catch (NoSuchMethodException e) {
153 throw new ExceptionInInitializerError(e);
158 public InstrumentedType prepare(final InstrumentedType instrumentedType) {
159 return instrumentedType;
163 public ByteCodeAppender appender(final Target implementationTarget) {
164 final TypeDescription instrumentedType = implementationTarget.getInstrumentedType();
165 final InGenericShape superCtor = verifyNotNull(instrumentedType.getSuperClass()).getDeclaredMethods()
166 .filter(ElementMatchers.isConstructor()).getOnly();
168 return new ByteCodeAppender.Simple(
171 MethodInvocation.invoke(superCtor),
174 putField(instrumentedType, INSTANT_FIELD),
179 private enum EventInstantImplementation implements Implementation {
183 public InstrumentedType prepare(final InstrumentedType instrumentedType) {
184 return instrumentedType;
188 public ByteCodeAppender appender(final Target implementationTarget) {
189 return new ByteCodeAppender.Simple(
191 getField(implementationTarget.getInstrumentedType(), INSTANT_FIELD),
192 MethodReturn.REFERENCE);