|
1 | 1 | package com.fasterxml.jackson.module.afterburner.deser; |
2 | 2 |
|
3 | | -import static org.objectweb.asm.Opcodes.*; |
4 | | - |
5 | | -import java.lang.reflect.AnnotatedElement; |
6 | | -import java.lang.reflect.Constructor; |
7 | | -import java.lang.reflect.Method; |
8 | | -import java.lang.reflect.Modifier; |
9 | | - |
10 | | -import com.fasterxml.jackson.databind.DeserializationContext; |
11 | 3 | import com.fasterxml.jackson.databind.deser.ValueInstantiator; |
12 | 4 | import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator; |
13 | 5 | import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams; |
14 | | - |
15 | | -import org.objectweb.asm.ClassWriter; |
16 | | -import org.objectweb.asm.Label; |
17 | | -import org.objectweb.asm.MethodVisitor; |
18 | | -import org.objectweb.asm.Type; |
19 | | - |
20 | 6 | import com.fasterxml.jackson.module.afterburner.util.ClassName; |
21 | 7 | import com.fasterxml.jackson.module.afterburner.util.DynamicPropertyAccessorBase; |
22 | 8 | import com.fasterxml.jackson.module.afterburner.util.MyClassLoader; |
| 9 | +import com.fasterxml.jackson.module.afterburner.util.bytebuddy.ConstructorCallStackManipulation; |
| 10 | +import com.fasterxml.jackson.module.afterburner.util.bytebuddy.SimpleExceptionHandler; |
| 11 | +import net.bytebuddy.ByteBuddy; |
| 12 | +import net.bytebuddy.ClassFileVersion; |
| 13 | +import net.bytebuddy.description.modifier.TypeManifestation; |
| 14 | +import net.bytebuddy.description.modifier.Visibility; |
| 15 | +import net.bytebuddy.description.type.TypeDescription; |
| 16 | +import net.bytebuddy.dynamic.DynamicType; |
| 17 | +import net.bytebuddy.dynamic.scaffold.TypeValidation; |
| 18 | +import net.bytebuddy.implementation.Implementation; |
| 19 | +import net.bytebuddy.implementation.bytecode.ByteCodeAppender; |
| 20 | +import net.bytebuddy.implementation.bytecode.StackManipulation; |
| 21 | +import net.bytebuddy.implementation.bytecode.member.MethodReturn; |
| 22 | + |
| 23 | +import java.lang.reflect.AnnotatedElement; |
| 24 | +import java.lang.reflect.Constructor; |
| 25 | +import java.lang.reflect.Method; |
| 26 | +import java.lang.reflect.Modifier; |
| 27 | + |
| 28 | +import static net.bytebuddy.description.method.MethodDescription.ForLoadedMethod; |
| 29 | +import static net.bytebuddy.description.method.MethodDescription.InDefinedShape; |
| 30 | +import static net.bytebuddy.description.type.TypeDescription.ForLoadedType; |
| 31 | +import static net.bytebuddy.implementation.bytecode.member.MethodInvocation.invoke; |
| 32 | +import static net.bytebuddy.implementation.bytecode.member.MethodVariableAccess.REFERENCE; |
| 33 | +import static net.bytebuddy.matcher.ElementMatchers.named; |
23 | 34 |
|
24 | 35 | /** |
25 | 36 | * Helper class that tries to generate {@link ValueInstantiator} class |
@@ -99,115 +110,65 @@ protected OptimizedValueInstantiator createSubclass(Constructor<?> ctor, Method |
99 | 110 | } |
100 | 111 | } |
101 | 112 |
|
102 | | - protected byte[] generateOptimized(ClassName baseName, Constructor<?> ctor, Method factory) |
103 | | - { |
104 | | - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); |
105 | | - String superClass = internalClassName(OptimizedValueInstantiator.class.getName()); |
| 113 | + protected byte[] generateOptimized(ClassName baseName, Constructor<?> ctor, Method factory) { |
106 | 114 | final String tmpClassName = baseName.getSlashedTemplate(); |
107 | | - |
108 | | - cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER + ACC_FINAL, tmpClassName, null, superClass, null); |
109 | | - cw.visitSource(baseName.getSourceFilename(), null); |
110 | | - |
111 | | - // First: must define 2 constructors: |
112 | | - // (a) default constructor, for creating bogus instance (just calls default instance) |
113 | | - // (b) copy-constructor which takes StdValueInstantiator instance, passes to superclass |
114 | | - final String optimizedValueInstDesc = Type.getDescriptor(OptimizedValueInstantiator.class); |
115 | | - final String stdValueInstDesc = Type.getDescriptor(StdValueInstantiator.class); |
116 | | - |
117 | | - // default (no-arg) constructor: |
118 | | - MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); |
119 | | - mv.visitCode(); |
120 | | - mv.visitVarInsn(ALOAD, 0); |
121 | | - mv.visitMethodInsn(INVOKESPECIAL, superClass, "<init>", "()V", false); |
122 | | - mv.visitInsn(RETURN); |
123 | | - mv.visitMaxs(0, 0); |
124 | | - mv.visitEnd(); |
125 | | - // then single-arg constructor |
126 | | - mv = cw.visitMethod(ACC_PUBLIC, "<init>", "("+stdValueInstDesc+")V", null, null); |
127 | | - mv.visitCode(); |
128 | | - mv.visitVarInsn(ALOAD, 0); |
129 | | - mv.visitVarInsn(ALOAD, 1); |
130 | | - mv.visitMethodInsn(INVOKESPECIAL, superClass, "<init>", "("+stdValueInstDesc+")V", false); |
131 | | - mv.visitInsn(RETURN); |
132 | | - mv.visitMaxs(0, 0); |
133 | | - mv.visitEnd(); |
134 | | - |
135 | | - // and then non-static factory method to use second constructor (implements base-class method) |
136 | | - // protected abstract OptimizedValueInstantiator with(StdValueInstantiator src); |
137 | | - mv = cw.visitMethod(ACC_PUBLIC, "with", "(" |
138 | | - +stdValueInstDesc+")"+optimizedValueInstDesc, null, null); |
139 | | - mv.visitCode(); |
140 | | - mv.visitTypeInsn(NEW, tmpClassName); |
141 | | - mv.visitInsn(DUP); |
142 | | - mv.visitVarInsn(ALOAD, 1); |
143 | | - mv.visitMethodInsn(INVOKESPECIAL, tmpClassName, "<init>", "("+stdValueInstDesc+")V", false); |
144 | | - mv.visitInsn(ARETURN); |
145 | | - mv.visitMaxs(0, 0); |
146 | | - mv.visitEnd(); |
147 | | - |
148 | | - // And then override: public Object createUsingDefault() |
149 | | - mv = cw.visitMethod(ACC_PUBLIC, "createUsingDefault", |
150 | | - "(" +Type.getDescriptor(DeserializationContext.class)+")Ljava/lang/Object;", null, null); |
151 | | - mv.visitCode(); |
152 | | - |
153 | | - // 19-Apr-2017, tatu: Need to take care to of try catch block... |
154 | | - Label startTryBlock = new Label(); |
155 | | - Label endTryBlock = new Label(); |
156 | | - Label startCatchBlock = new Label(); |
157 | | - |
158 | | - // Initiale try-catch block |
159 | | - mv.visitTryCatchBlock(startTryBlock, endTryBlock, startCatchBlock, "java/lang/Exception"); |
160 | | - mv.visitLabel(startTryBlock); |
161 | | - |
162 | | - // Then new/static-factory call |
163 | | - if (ctor != null) { |
164 | | - addCreator(mv, ctor); |
165 | | - } else { |
166 | | - addCreator(mv, factory); |
167 | | - } |
168 | | - mv.visitInsn(ARETURN); |
169 | | - |
170 | | - mv.visitLabel(endTryBlock); |
171 | | - // and then do catch block |
172 | | - mv.visitLabel(startCatchBlock); |
173 | | - mv.visitVarInsn(ASTORE, 2); // push Exception e |
174 | | - mv.visitVarInsn(ALOAD, 0); // this |
175 | | - mv.visitVarInsn(ALOAD, 1); // Arg #1 ("ctxt") |
176 | | - mv.visitVarInsn(ALOAD, 2); // caught exception |
177 | | - // 27-Jul-2017, tatu: as per [modules-base#27], need name not desc here. For reasons. |
178 | | - final String optimizedValueInstName = Type.getInternalName(OptimizedValueInstantiator.class); |
179 | | - mv.visitMethodInsn(INVOKEVIRTUAL, |
180 | | - optimizedValueInstName, "_handleInstantiationProblem", |
181 | | - String.format("(%s%s)Ljava/lang/Object;", |
182 | | - Type.getDescriptor(DeserializationContext.class), |
183 | | - "Ljava/lang/Exception;"), |
184 | | - false); |
185 | | - mv.visitInsn(ARETURN); |
186 | | - |
187 | | - // and call it all done |
188 | | - mv.visitMaxs(0, 0); |
189 | | - mv.visitEnd(); |
190 | | - |
191 | | - cw.visitEnd(); |
192 | | - return cw.toByteArray(); |
| 115 | + final DynamicType.Builder<?> builder = |
| 116 | + new ByteBuddy(ClassFileVersion.JAVA_V5) |
| 117 | + .with(TypeValidation.DISABLED) |
| 118 | + .subclass(OptimizedValueInstantiator.class) //default strategy ensures that all constructors are created |
| 119 | + .name(tmpClassName) |
| 120 | + .modifiers(Visibility.PUBLIC, TypeManifestation.FINAL) |
| 121 | + .method(named("with")) |
| 122 | + .intercept( |
| 123 | + //call the constructor of this method that takes a single StdValueInstantiator arg |
| 124 | + //the required arg is in the position 1 of the method's local variables |
| 125 | + new Implementation.Simple( |
| 126 | + new ByteCodeAppender.Simple( |
| 127 | + new ConstructorCallStackManipulation.OfInstrumentedType.OneArg( |
| 128 | + REFERENCE.loadFrom(1) |
| 129 | + ), |
| 130 | + MethodReturn.REFERENCE |
| 131 | + ) |
| 132 | + ) |
| 133 | + |
| 134 | + ) |
| 135 | + .method(named("createUsingDefault")) |
| 136 | + .intercept( |
| 137 | + new SimpleExceptionHandler( |
| 138 | + creatorInvokerStackManipulation(ctor, factory), |
| 139 | + creatorExceptionHandlerStackManipulation(), |
| 140 | + Exception.class, |
| 141 | + 1 //we added a new local variable in the catch block |
| 142 | + ) |
| 143 | + ); |
| 144 | + |
| 145 | + |
| 146 | + return builder.make().getBytes(); |
193 | 147 | } |
194 | 148 |
|
195 | | - protected void addCreator(MethodVisitor mv, Constructor<?> ctor) |
196 | | - { |
197 | | - Class<?> owner = ctor.getDeclaringClass(); |
198 | | - String valueClassInternal = Type.getInternalName(owner); |
199 | | - mv.visitTypeInsn(NEW, valueClassInternal); |
200 | | - mv.visitInsn(DUP); |
201 | | - mv.visitMethodInsn(INVOKESPECIAL, valueClassInternal, "<init>", "()V", |
202 | | - owner.isInterface()); |
| 149 | + private StackManipulation creatorInvokerStackManipulation(Constructor<?> ctor, Method factory) { |
| 150 | + final StackManipulation invokeManipulation = |
| 151 | + null == ctor ? |
| 152 | + invoke(new ForLoadedMethod(factory)) : |
| 153 | + new ConstructorCallStackManipulation.KnownConstructorOfExistingType(ctor); |
| 154 | + return new StackManipulation.Compound( |
| 155 | + invokeManipulation, |
| 156 | + MethodReturn.REFERENCE |
| 157 | + ); |
203 | 158 | } |
204 | 159 |
|
205 | | - protected void addCreator(MethodVisitor mv, Method factory) |
206 | | - { |
207 | | - Class<?> owner = factory.getDeclaringClass(); |
208 | | - Class<?> valueClass = factory.getReturnType(); |
209 | | - mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(owner), |
210 | | - factory.getName(), "()"+Type.getDescriptor(valueClass), |
211 | | - owner.isInterface()); |
| 160 | + private StackManipulation creatorExceptionHandlerStackManipulation() { |
| 161 | + final TypeDescription typeDescription = new ForLoadedType(OptimizedValueInstantiator.class); |
| 162 | + final InDefinedShape methodDescription = |
| 163 | + typeDescription.getDeclaredMethods().filter(named("_handleInstantiationProblem")).getOnly(); |
| 164 | + |
| 165 | + return new StackManipulation.Compound( |
| 166 | + REFERENCE.storeAt(2), //push exception to new local |
| 167 | + REFERENCE.loadFrom(0), //'this' |
| 168 | + REFERENCE.loadFrom(1), //Arg #1 ("ctxt") |
| 169 | + REFERENCE.loadFrom(2), //exception |
| 170 | + invoke(methodDescription), |
| 171 | + MethodReturn.REFERENCE |
| 172 | + ); |
212 | 173 | } |
213 | 174 | } |
0 commit comments