spring-loaded TypeRewriter 源码
spring-loaded TypeRewriter 代码
文件路径:/springloaded/src/main/java/org/springsource/loaded/TypeRewriter.java
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.lang.reflect.Modifier;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.springsource.loaded.TypeRegistry.CouldBeReloadableDecision;
import org.springsource.loaded.TypeRegistry.ReloadableTypeNameDecision;
import org.springsource.loaded.Utils.ReturnType;
/**
* Rewrites a class such that it is amenable to reloading. This involves:
* <ul>
* <li>In every method, introduce logic to check it it the latest version of that method - if it isn't dispatch to the
* latest
* <li>Creates additional methods to aid with field setting/getting
* <li>Creates additional fields to help reloading (reloadable type instance, new field value holders)
* <li>Creates catchers for inherited methods. Catchers are simply passed through unless a new version of the class
* provides an implementation
* </ul>
*
* @author Andy Clement
* @since 0.5.0
*/
public class TypeRewriter implements Constants {
private static Logger log = Logger.getLogger(TypeRewriter.class.getName());
public static byte[] rewrite(ReloadableType rtype, byte[] bytes) {
ClassReader fileReader = new ClassReader(bytes);
RewriteClassAdaptor classAdaptor = new RewriteClassAdaptor(rtype);
fileReader.accept(classAdaptor, 0);
return classAdaptor.getBytes();
}
static class RewriteClassAdaptor extends ClassVisitor implements Constants {
private ClassWriter cw;
private String slashedname;
private ReloadableType rtype;
private TypeDescriptor typeDescriptor;
private boolean clinitDone = false;
private String supertypeName;
private boolean isInterface;
private int isTopmostReloadable = -1; // -1 = not computed. 0=false, 1=true
private boolean isEnum;
private boolean isGroovy;
public RewriteClassAdaptor(ReloadableType rtype) {
this(rtype, new ClassWriter(ClassWriter.COMPUTE_MAXS));
}
public RewriteClassAdaptor(ReloadableType rtype, ClassWriter classWriter) {
super(ASM5, classWriter);
this.rtype = rtype;
this.slashedname = rtype.getSlashedName();
this.cw = (ClassWriter) cv;
this.typeDescriptor = rtype.getTypeDescriptor();
this.isInterface = typeDescriptor.isInterface();
this.isEnum = typeDescriptor.isEnum();
this.isGroovy = typeDescriptor.isGroovyType();
}
public byte[] getBytes() {
return cw.toByteArray();
}
public ClassVisitor getClassVisitor() {
return cw;
}
@Override
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
access = Utils.promoteDefaultOrPrivateOrProtectedToPublic(access);
super.visit(version, access, name, signature, superName, interfaces);
this.supertypeName = superName;
// Extra members in a reloadable type
createReloadableTypeField();
if (GlobalConfiguration.fieldRewriting) {
if (!isInterface) {
if (isTopmostReloadable()) {
createInstanceStateManagerInstance();
}
createInstanceFieldGetterMethod();
createInstanceFieldSetterMethod();
createStaticFieldSetterMethod();
createStaticFieldGetterMethod();
createStaticInitializerForwarderMethod();
}
if (isTopmostReloadable()) {
createStaticStateManagerInstance();
}
}
if (!isInterface) {
createManagedConstructors();
createDispatcherCallingInitCtors();
}
}
/**
* Determine if this type is the top most reloadable type. Some state is only inserted into the top most
* reloadable type in a hierarchy, rather than into every reloadable type. Typically this type will be the top
* most reloadable if the supertype comes from a jar or is in a package we know is not reloadable (e.g.
* java.lang).
*
* @return true if top most reloadable
*/
private boolean isTopmostReloadable() {
if (isTopmostReloadable > -1) {
return isTopmostReloadable == 1;
}
boolean result = false;
TypeRegistry typeRegistry = rtype.getTypeRegistry();
String supertypeName = typeDescriptor.getSupertypeName();
ReloadableTypeNameDecision rtnd = typeRegistry.isReloadableTypeName(supertypeName, null, null);
if (rtnd.isReloadable) {
// If splitPackages option is turned ON or super type is being explicitly included via pattern,
// dig a bit deeper. It may still be excluded due to being in a non watched jar. (So effectively the
// inclusion pattern does not apply to jar contents).
if (GlobalConfiguration.allowSplitPackages || rtnd.explicitlyIncluded) {
CouldBeReloadableDecision cbrd = rtnd.cbrd;
if (cbrd == null) {
cbrd = typeRegistry.couldBeReloadable(supertypeName, false);
}
result = !cbrd.couldBeReloadable;
}
else {
result = false;
}
}
else {
result = true;
}
isTopmostReloadable = result ? 1 : 0;
return result;
// this would work if the supertype was always guaranteed to be dealt with before this type
// return typeRegistry.getReloadableType(typeDescriptor.getSupertypeName(), false) == null;
// original decision:
// This makes a mistake for split packages
// if (!typeRegistry.isReloadableTypeName(supertypeName)) {
// return true;
// }
// else {
// return false;
// }
}
private void createStaticInitializerForwarderMethod() {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC_STATIC, mStaticInitializerName, "()V", null, null);
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitMethodInsn(INVOKEVIRTUAL, tReloadableType, "fetchLatest", "()Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, Utils.getInterfaceName(slashedname));
mv.visitMethodInsn(INVOKEINTERFACE, Utils.getInterfaceName(slashedname), mStaticInitializerName, "()V");
mv.visitInsn(RETURN);
mv.visitMaxs(1, 0);
mv.visitEnd();
}
// TODO review whether we get these up and down the hierarchy or just at the top?
/**
* Create the static field getter method which ensures the static state manager is initialized.
*/
private void createStaticFieldGetterMethod() {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC_STATIC, mStaticFieldGetterName,
"(Ljava/lang/String;)Ljava/lang/Object;",
null, null);
mv.visitFieldInsn(GETSTATIC, slashedname, fStaticFieldsName, lStaticStateManager);
Label l2 = new Label();
mv.visitJumpInsn(IFNONNULL, l2);
mv.visitTypeInsn(NEW, tStaticStateManager);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, tStaticStateManager, "<init>", "()V");
mv.visitFieldInsn(PUTSTATIC, slashedname, fStaticFieldsName, lStaticStateManager);
mv.visitLabel(l2);
mv.visitFieldInsn(GETSTATIC, slashedname, fStaticFieldsName, lStaticStateManager);
mv.visitFieldInsn(GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, tStaticStateManager, "getValue", "(" + lReloadableType
+ "Ljava/lang/String;)Ljava/lang/Object;");
mv.visitInsn(ARETURN);
mv.visitMaxs(3, 2);
mv.visitEnd();
}
private void createDispatcherCallingInitCtors() {
MethodMember[] ctors = typeDescriptor.getConstructors();
for (MethodMember ctor : ctors) {
String desc = ctor.getDescriptor();
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "___init___", desc, null, null);
mv.visitFieldInsn(GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitInsn(ICONST_1);
mv.visitMethodInsn(INVOKEVIRTUAL, tReloadableType, "getLatestDispatcherInstance",
"(Z)Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, Utils.getInterfaceName(slashedname));
String desc2 = new StringBuffer("(L").append(slashedname).append(";").append(
desc.substring(1)).toString();
mv.visitVarInsn(ALOAD, 0);
Utils.createLoadsBasedOnDescriptor(mv, desc, 1);
mv.visitMethodInsn(INVOKEINTERFACE, Utils.getInterfaceName(slashedname), "___init___", desc2);
mv.visitInsn(RETURN);
mv.visitMaxs(3, Utils.getParameterCount(desc) + 1);
mv.visitEnd();
}
}
private void createManagedConstructors() {
String slashedSupertypeName = typeDescriptor.getSupertypeName();
if (slashedSupertypeName.equals("java/lang/Enum")) { // assert isEnum
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>",
"(Ljava/lang/String;ILorg/springsource/loaded/C;)V", null,
null);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ILOAD, 2);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Enum", "<init>", "(Ljava/lang/String;I)V");
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
return;
}
if (slashedSupertypeName.equals("groovy/lang/Closure")) { // assert this is a closure
// create the special constructor we want to use, not the one below
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>",
"(Ljava/lang/Object;Ljava/lang/Object;Lorg/springsource/loaded/C;)V", null, null);
TypeDescriptor superDescriptor = rtype.getTypeRegistry().getDescriptorFor(supertypeName);
MethodMember ctor = superDescriptor.getConstructor("(Ljava/lang/Object;Ljava/lang/Object;)V");
if (ctor == null) {
throw new IllegalStateException("Unable to find expected constructor on Closure type");
}
mv.visitVarInsn(ALOAD, 0); // this (uninitialized)
mv.visitVarInsn(ALOAD, 1); // 'owner'
mv.visitVarInsn(ALOAD, 2); // 'this'
mv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Closure", "<init>",
"(Ljava/lang/Object;Ljava/lang/Object;)V");
mv.visitInsn(RETURN);
mv.visitMaxs(3, 4);
mv.visitEnd();
}
else {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Lorg/springsource/loaded/C;)V", null, null);
mv.visitVarInsn(ALOAD, 0);
if (slashedSupertypeName.equals("java/lang/Object")) {
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
mv.visitInsn(RETURN);
mv.visitMaxs(1, 2);
}
else if (slashedSupertypeName.equals("java/lang/Enum")) { // assert isEnum
// Call Enum.<init>(null,0)
mv.visitInsn(ACONST_NULL);
mv.visitLdcInsn(0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Enum", "<init>", "(Ljava/lang/String;I)V");
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
}
else {
ReloadableType superRtype = rtype.getTypeRegistry().getReloadableType(slashedSupertypeName);
if (superRtype == null) {
// This means we are crossing a reloadable boundary (this type is reloadable, the supertype is not).
// At this point we may be in trouble. The only time we'll be OK is if the supertype declares a no-arg lructor
// we can see from here
TypeDescriptor superDescriptor = rtype.getTypeRegistry().getDescriptorFor(supertypeName);
MethodMember ctor = superDescriptor.getConstructor("()V");
if (ctor != null) {
mv.visitMethodInsn(INVOKESPECIAL, slashedSupertypeName, "<init>", "()V");
}
else {
String warningMessage = "SERIOUS WARNING (current limitation): At reloadable boundary of "
+ typeDescriptor.getDottedName()
+ " supertype="
+ supertypeName
+ " - no available default ctor for that supertype, problems may occur on reload if constructor changes";
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.SEVERE)) {
log.log(Level.SEVERE, warningMessage);
}
// suppress for closure subtypes
if (!(supertypeName.equals("groovy/lang/Closure")
|| supertypeName.startsWith("org/codehaus/groovy/runtime/callsite"))) {
if (GlobalConfiguration.verboseMode) {
System.out.println(warningMessage);
}
}
// TODO [verify] running enumtests we see that there is an issue here with leaving the special constructor without a super call in it.
// this will fail verification.
// throw new IllegalStateException("at reloadable boundary, not sure how to construct " + supertypeName);
}
}
else {
mv.visitInsn(ACONST_NULL);
mv.visitMethodInsn(INVOKESPECIAL, slashedSupertypeName, "<init>",
"(Lorg/springsource/loaded/C;)V");
}
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
}
mv.visitEnd();
}
}
/**
* Create the static field setter method. Looks a bit like this:
* <p>
* <code><pre>
* public static r$sets(Object newValue, String name) {
* if (r$fields==null) {
* r$fields = new SSMgr();
* }
* r$fields.setValue(reloadableTypeInstance, newValue, name);
* }
* </pre></code>
*/
private void createStaticFieldSetterMethod() {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC_STATIC, mStaticFieldSetterName, mStaticFieldSetterDescriptor,
null, null);
mv.visitFieldInsn(GETSTATIC, slashedname, fStaticFieldsName, lStaticStateManager);
Label l2 = new Label();
mv.visitJumpInsn(IFNONNULL, l2);
mv.visitTypeInsn(NEW, tStaticStateManager);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, tStaticStateManager, "<init>", "()V", false);
mv.visitFieldInsn(PUTSTATIC, slashedname, fStaticFieldsName, lStaticStateManager);
mv.visitLabel(l2);
mv.visitFieldInsn(GETSTATIC, slashedname, fStaticFieldsName, lStaticStateManager);
mv.visitFieldInsn(GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, tStaticStateManager, "setValue", "(" + lReloadableType
+ "Ljava/lang/Object;Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(4, 2);
mv.visitEnd();
}
/**
* Create the instance field getter method. Looks a bit like this:
* <p>
* <code><pre>
* public Object r$get(Object instance, String name) {
* if (this.instanceStateManager==null) {
* this.instanceStateManager = new InstanceStateManager();
* }
* return this.instanceStateManager.getValue(reloadableType,instance,name);
* }
* </pre></code>
*/
private void createInstanceFieldGetterMethod() {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, mInstanceFieldGetterName, mInstanceFieldGetterDescriptor,
null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, slashedname, fInstanceFieldsName, lInstanceStateManager);
Label l1 = new Label();
mv.visitJumpInsn(IFNONNULL, l1);
// instance state manager will only get initialised here if the constructor did not do it
mv.visitVarInsn(ALOAD, 0);
mv.visitTypeInsn(NEW, tInstanceStateManager);
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitMethodInsn(INVOKESPECIAL, tInstanceStateManager, "<init>",
"(Ljava/lang/Object;Lorg/springsource/loaded/ReloadableType;)V");
mv.visitFieldInsn(PUTFIELD, slashedname, fInstanceFieldsName, lInstanceStateManager);
mv.visitLabel(l1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, slashedname, fInstanceFieldsName, lInstanceStateManager);
mv.visitFieldInsn(GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, tInstanceStateManager, "getValue", "(" + lReloadableType
+ "Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;");
mv.visitInsn(ARETURN);
mv.visitMaxs(4, 3);
mv.visitEnd();
}
private final static int lvThis = 0;
// TODO [perf] shuffle ordering of value/instance passed in here to speed up things? (see also rewritePUTFIELD) can we reduce/remove swap
/**
* Create a field setter for instance fields, signature of: public void r$set(Object,Object,String)
*/
private void createInstanceFieldSetterMethod() {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, mInstanceFieldSetterName, mInstanceFieldSetterDescriptor,
null, null);
final int lvNewValue = 1;
final int lvInstance = 2;
final int lvName = 3;
mv.visitVarInsn(ALOAD, lvThis);
mv.visitFieldInsn(GETFIELD, slashedname, fInstanceFieldsName, lInstanceStateManager);
// Will only get initialised here if the constructor did not do it
Label l1 = new Label();
mv.visitJumpInsn(IFNONNULL, l1);
// Initialise the Instance State Manager
mv.visitVarInsn(ALOAD, lvThis);
mv.visitTypeInsn(NEW, tInstanceStateManager);
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, lvThis);
mv.visitFieldInsn(GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitMethodInsn(INVOKESPECIAL, tInstanceStateManager, "<init>",
"(Ljava/lang/Object;Lorg/springsource/loaded/ReloadableType;)V");
mv.visitFieldInsn(PUTFIELD, slashedname, fInstanceFieldsName, lInstanceStateManager);
mv.visitLabel(l1);
// get the instance state manager object
mv.visitVarInsn(ALOAD, lvThis);
mv.visitFieldInsn(GETFIELD, slashedname, fInstanceFieldsName, lInstanceStateManager);
// get the reloadable type
mv.visitFieldInsn(GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
// TODO [perf] do we need to pass reloadabletype? Shouldn't the ISMgr instance know it!
// call setValue
mv.visitVarInsn(ALOAD, lvInstance);
mv.visitVarInsn(ALOAD, lvNewValue);
mv.visitVarInsn(ALOAD, lvName);
// setValue(ReloadableType rtype, Object instance, Object value, String name)
mv.visitMethodInsn(INVOKEVIRTUAL, tInstanceStateManager, "setValue", "(" + lReloadableType
+ "Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)V");
mv.visitInsn(RETURN);
mv.visitMaxs(4, 4);
mv.visitEnd();
}
private void createInstanceStateManagerInstance() {
FieldVisitor f = cw.visitField(ACC_PUBLIC | ACC_TRANSIENT, fInstanceFieldsName, lInstanceStateManager,
null, null);
f.visitEnd();
}
private void createStaticStateManagerInstance() {
FieldVisitor f = cw.visitField(ACC_PUBLIC_STATIC /*| ACC_TRANSIENT*/ | ACC_FINAL, fStaticFieldsName,
lStaticStateManager, null, null);
f.visitEnd();
}
/**
* Create the reloadable type field, which can later answer questions about changes or be used to access the
* latest version of a type/method.
*/
private void createReloadableTypeField() {
int acc = isInterface ? ACC_PUBLIC_STATIC_FINAL : ACC_PUBLIC_STATIC; //ACC_PRIVATE_STATIC;
FieldVisitor fv = cw.visitField(acc, fReloadableTypeFieldName, lReloadableType, null, null);
fv.visitEnd();
}
@Override
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
String[] exceptions) {
MethodVisitor mv = super.visitMethod(promoteIfNecessary(flags, name), name, descriptor, signature,
exceptions);
MethodVisitor newMethodVisitor = getMethodVisitor(name, descriptor, mv);
return newMethodVisitor;
}
public MethodVisitor getMethodVisitor(String name, String descriptor, MethodVisitor mv) {
MethodVisitor newMethodVisitor = null;
if (name.charAt(0) == '<') {
if (name.charAt(1) == 'c') { // <clinit>
clinitDone = true;
newMethodVisitor = new MethodPrepender(mv, new ClinitPrepender(mv));
}
else { // <init>
newMethodVisitor = new AugmentingConstructorAdapter(mv, descriptor, slashedname,
isTopmostReloadable());
// want to create a copy of the constructor called ___init___ so it is reachable from the executor of the subtype.
// All it really needs to do is call through the dispatcher to the executor for the relevant constructor.
// this will force a reload of the supertype (to create the super executor) - that is something we can address later
}
}
else {
// what about just copying if isgroovy and name.startsWith("$get");
// Can't do this for $getStaticMetaClass so let us use $get$$ for now, until we discover why that lets us down...
// if (isGroovy && name.startsWith("$get$$")) {
// newMethodVisitor = new MethodAdapter(mv);
// } else {
newMethodVisitor = new AugmentingMethodAdapter(mv, name, descriptor);
// }
}
return newMethodVisitor;
}
// Default visibility elements need promotion to public so that they can be seen from the executor
private int promoteIfNecessary(int flags, String name) {
int newflags = Utils.promoteDefaultOrProtectedToPublic(flags, isEnum, name);
return newflags;
}
@Override
public FieldVisitor visitField(final int access, final String name, final String desc, final String signature,
final Object value) {
// Special casing here for serialVersionUID - not removing the final modifier. This enables
// the code in java.io.ObjectStreamClass to access it.
int modAccess = 0;
if ((access & ACC_FINAL) != 0) {
if (name.equals("serialVersionUID")) {
modAccess = (access & ~(ACC_PRIVATE | ACC_PROTECTED)) | ACC_PUBLIC;
}
else {
// remove final
modAccess = (access & ~(ACC_PRIVATE | ACC_PROTECTED)) | ACC_PUBLIC;
modAccess = modAccess & ~ACC_FINAL;
}
}
else {
// remove final
modAccess = (access & ~(ACC_PRIVATE | ACC_PROTECTED)) | ACC_PUBLIC;
modAccess = modAccess & ~ACC_FINAL;
}
return cv.visitField(modAccess, name, desc, signature, value);
}
static class FieldHolder {
final int access;
final String name;
final String desc;
public FieldHolder(int access, String name, String desc) {
this.access = access;
this.name = name;
this.desc = desc;
}
}
@Override
public void visitEnd() {
if (!clinitDone) {
// Need to add a static initializer to initialize the reloadable type field
MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
new ClinitPrepender(mv).prepend();
mv.visitInsn(RETURN);
mv.visitMaxs(3, 0);
mv.visitEnd();
}
generateCatchers();
generateDynamicDispatchHandler();
cv.visitEnd();
}
/**
* Create a basic dynamic dispatch handler. To support changes to interfaces, a new method is added to all
* reloadable interfaces and this needs an implementation. This method generates the implementation which
* delegates to the reloadable interface for the type. As interfaces can't get static methods we only have to
* worry about instance methods here.
*/
private void generateDynamicDispatchHandler() {
final int indexThis = 0;
final int indexArgs = 1;
final int indexTargetInstance = 2;
final int indexNameAndDescriptor = 3;
if (isInterface) {
cw.visitMethod(ACC_PUBLIC_ABSTRACT, mDynamicDispatchName, mDynamicDispatchDescriptor, null, null);
}
else {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, mDynamicDispatchName, mDynamicDispatchDescriptor, null,
null);
// Sometimes we come into the dynamic dispatcher because we are handling an INVOKEINTERFACE for a method
// not defined on the original interface. In these cases we will find that fetchLatest() returns null because
// the interface was reloaded, but the implementation was not (necessarily). Should we force it to reload
// the implementation? That would generate the correct dispatcher...
// 1. Ask the reloadable type for the latest version of the interface
// __DynamicallyDispatchable dispatchable = r$type.determineDispatcher(this,nameAndDescriptor)
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitVarInsn(ALOAD, indexThis);
mv.visitVarInsn(ALOAD, indexNameAndDescriptor);
mv.visitMethodInsn(INVOKEVIRTUAL, tReloadableType, "determineDispatcher",
"(Ljava/lang/Object;Ljava/lang/String;)Lorg/springsource/loaded/__DynamicallyDispatchable;");
mv.visitInsn(DUP);
Label l1 = new Label();
mv.visitJumpInsn(IFNULL, l1);
{
// 2. package up the parameters
// can I assert that 0==2 - ie. the instance being called upon is the same as the parameter passed in
mv.visitVarInsn(ALOAD, indexArgs);
mv.visitVarInsn(ALOAD, indexThis);
mv.visitVarInsn(ALOAD, indexNameAndDescriptor);
// 3. call it
// return dispatchable.__execute(parameters,this,nameAndDescriptor)
mv.visitMethodInsn(INVOKEINTERFACE, "org/springsource/loaded/__DynamicallyDispatchable",
mDynamicDispatchName,
mDynamicDispatchDescriptor);
mv.visitInsn(ARETURN);
}
mv.visitLabel(l1);
mv.visitInsn(POP); // POPNULL
// delegate to the superclass dynamic dispatch method
// mv.visitVarInsn(ALOAD, 0);
// mv.visitVarInsn(ALOAD, 1);
// mv.visitVarInsn(ALOAD, 2);
// mv.visitVarInsn(ALOAD, 3);
// mv.visitMethodInsn(INVOKESPECIAL, supertypeName, mDynamicDispatchName, mDynamicDispatchDescriptor);
// mv.visitInsn(ARETURN);
// 4. throw NoSuchMethodError
genThrowNoSuchMethodError(mv, indexNameAndDescriptor);
mv.visitMaxs(5, 4);
mv.visitEnd();
}
}
/**
* @param mv where to append the instructions
* @param index the index of the String message to load and use in the exception
*/
private final void genThrowNoSuchMethodError(MethodVisitor mv, int index) {
mv.visitTypeInsn(NEW, "java/lang/NoSuchMethodError");
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, index);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/NoSuchMethodError", "<init>", "(Ljava/lang/String;)V");
mv.visitInsn(ATHROW);
}
/**
* Catcher methods are 'empty' methods added to subtypes to 'catch' any virtual dispatch calls that would
* otherwise be missed. Catchers then check to see if the type on which they are defined now provides an
* implementation of the method in question - if it does then it is called, otherwise the catcher simply calls
* the supertype.
* <p>
* Catchers typically have the same visibility as the methods for which they exist, unless those methods are
* protected/default, in which case the catcher is made public. This enables them to be seen from the executor.
*/
private void generateCatchers() {
if (!GlobalConfiguration.catchersOn || isInterface) {
return;
}
FieldMember[] fms = typeDescriptor.getFieldsRequiringAccessors();
for (FieldMember field : fms) {
createProtectedFieldGetterSetter(field);
}
MethodMember[] methods = typeDescriptor.getMethods();
for (MethodMember method : methods) {
if (!MethodMember.isSuperDispatcher(method)) {
continue;
}
// TODO topmost test?
String name = method.getName();
String descriptor = method.getDescriptor();
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.FINEST)) {
log.finest("Creating super dispatcher for method " + name + descriptor + " in type " + slashedname);
}
// Create a superdispatcher for this method
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, method.getName(), method.getDescriptor(), null,
method.getExceptions());
int ps = Utils.getParameterCount(method.getDescriptor());
ReturnType methodReturnType = Utils.getReturnTypeDescriptor(method.getDescriptor());
int lvarIndex = 0;
mv.visitVarInsn(ALOAD, lvarIndex++); // load this
Utils.createLoadsBasedOnDescriptor(mv, descriptor, lvarIndex);
String targetMethod = method.getName().substring(0, method.getName().lastIndexOf("_$"));
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, typeDescriptor.getSupertypeName(), targetMethod,
method.getDescriptor());
Utils.addCorrectReturnInstruction(mv, methodReturnType, false);
int maxs = ps + 1;
if (methodReturnType.isDoubleSlot()) {
maxs++;
}
mv.visitMaxs(maxs, maxs);
mv.visitEnd();
}
for (MethodMember method : methods) {
if (!MethodMember.isCatcher(method)) {
continue;
}
String name = method.getName();
String descriptor = method.getDescriptor();
ReturnType returnType = Utils.getReturnTypeDescriptor(descriptor);
// 1. Create the method signature
int flags = method.getModifiers();
if (Modifier.isProtected(flags)) {
flags = flags - Modifier.PROTECTED + Modifier.PUBLIC;
}
MethodVisitor mv = cw.visitMethod(flags, method.getName(), method.getDescriptor(), null,
method.getExceptions());
// 2. Ask the type if anything has changed from first load
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitLdcInsn(method.getId());
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tReloadableType, "fetchLatestIfExists",
"(I)Ljava/lang/Object;");
// If the return value is null, there is no implementation
mv.visitInsn(DUP);
// 3. create the if statement
Label l1 = new Label();
mv.visitJumpInsn(Opcodes.IFNULL, l1);
// 4. if changed then call the interface to run whatever version has been added
mv.visitTypeInsn(CHECKCAST, Utils.getInterfaceName(slashedname));
int lvarIndex = 0;
mv.visitVarInsn(ALOAD, lvarIndex++); // load this
Utils.createLoadsBasedOnDescriptor(mv, descriptor, lvarIndex);
String desc = new StringBuffer("(L").append(slashedname).append(";").append(
descriptor.substring(1)).toString();
mv.visitMethodInsn(INVOKEINTERFACE, Utils.getInterfaceName(slashedname), name, desc);
Utils.addCorrectReturnInstruction(mv, returnType, true);
// 5. if unchanged just run the supertype version (could be another catcher...)
mv.visitLabel(l1);
mv.visitInsn(POP);
int ps = Utils.getParameterCount(method.getDescriptor());
ReturnType methodReturnType = Utils.getReturnTypeDescriptor(method.getDescriptor());
// A catcher for an interface method is inserted into abstract classes. These should never be reached unless
// they now provide an implementation (on a reload) and the subtype has deleted the implementation it had.
// This means there is never a need to call 'super' in the logic below and getting here when there isn't
// something to run in the executor is a bug (so we throw AbstractMethodError)
if (MethodMember.isCatcherForInterfaceMethod(method)) {
mv.visitTypeInsn(NEW, "java/lang/IllegalStateException");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/AbstractMethodError", "<init>", "()V", false);
mv.visitInsn(ATHROW);
}
else {
mv.visitVarInsn(ALOAD, 0); // load this
Utils.createLoadsBasedOnDescriptor(mv, method.getDescriptor(), 1);
mv.visitMethodInsn(INVOKESPECIAL, supertypeName, method.getName(), method.getDescriptor(), false);
Utils.addCorrectReturnInstruction(mv, methodReturnType, false);
}
int maxs = ps + 1;
if (methodReturnType.isDoubleSlot()) {
maxs++;
}
mv.visitMaxs(maxs, maxs);
mv.visitEnd();
}
}
private void insertCorrectLoad(MethodVisitor mv, ReturnType rt, int slot) {
if (rt.isPrimitive()) {
switch (rt.descriptor.charAt(0)) {
case 'Z':
case 'S':
case 'I':
case 'B':
case 'C':
mv.visitVarInsn(ILOAD, slot);
break;
case 'F':
mv.visitVarInsn(FLOAD, slot);
break;
case 'J':
mv.visitVarInsn(LLOAD, slot);
break;
case 'D':
mv.visitVarInsn(DLOAD, slot);
break;
default:
throw new IllegalStateException(rt.descriptor);
}
}
else {
mv.visitVarInsn(ALOAD, slot);
}
}
/**
* For the fields that need it (protected fields from a non-reloadable supertype), create the getters and
* setters so that the executor can read/write them.
*
*/
private void createProtectedFieldGetterSetter(FieldMember field) {
String descriptor = field.descriptor;
String name = field.name;
ReturnType rt = ReturnType.getReturnType(descriptor);
if (field.isStatic()) {
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC | Modifier.STATIC,
Utils.getProtectedFieldGetterName(name), "()"
+ descriptor,
null, null);
mv.visitFieldInsn(GETSTATIC, slashedname, name, descriptor);
Utils.addCorrectReturnInstruction(mv, rt, false);
mv.visitMaxs(rt.isDoubleSlot() ? 2 : 1, 0);
mv.visitEnd();
mv = cw.visitMethod(Modifier.PUBLIC | Modifier.STATIC, Utils.getProtectedFieldSetterName(name), "("
+ descriptor
+ ")V", null, null);
insertCorrectLoad(mv, rt, 0);
mv.visitFieldInsn(PUTSTATIC, slashedname, name, descriptor);
mv.visitInsn(RETURN);
mv.visitMaxs(rt.isDoubleSlot() ? 2 : 1, 1);
mv.visitEnd();
}
else {
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, Utils.getProtectedFieldGetterName(name), "()"
+ descriptor,
null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, slashedname, name, descriptor);
Utils.addCorrectReturnInstruction(mv, rt, false);
mv.visitMaxs(rt.isDoubleSlot() ? 2 : 1, 1);
mv.visitEnd();
mv = cw.visitMethod(Modifier.PUBLIC, Utils.getProtectedFieldSetterName(name), "(" + descriptor + ")V",
null, null);
mv.visitVarInsn(ALOAD, 0);
insertCorrectLoad(mv, rt, 1);
mv.visitFieldInsn(PUTFIELD, slashedname, name, descriptor);
mv.visitInsn(RETURN);
mv.visitMaxs(rt.isDoubleSlot() ? 3 : 2, 2);
mv.visitEnd();
}
}
/**
* The ClinitPrepender adds the code to initialize the reloadable type field at class load
*/
class ClinitPrepender implements Prepender, Constants {
MethodVisitor mv;
private final static String descriptorFor_getReloadableType = "(II)" + lReloadableType;
private final static String descriptorFor_associateReloadableType = "(" + lReloadableType
+ "Ljava/lang/Class;)V";
ClinitPrepender(MethodVisitor mv) {
this.mv = mv;
}
public void prepend() {
// Discover the ReloadableType object and store it here
mv.visitCode();
// TODO optimization: could collapse ints into one but this snippet isn't put in many places
mv.visitLdcInsn(rtype.getTypeRegistryId());
mv.visitLdcInsn(rtype.getId());
mv.visitMethodInsn(INVOKESTATIC, tRegistryType, "getReloadableType", descriptorFor_getReloadableType,
false);
mv.visitFieldInsn(PUTSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
// mv.visitFieldInsn(GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
// mv.visitLdcInsn(Type.getObjectType(rtype.getSlashedSupertypeName()));//Type("L" + rtype.getSlashedSupertypeName() + ";")); // faster way?
// mv.visitMethodInsn(INVOKEVIRTUAL, tReloadableType, "setSuperclass", "(Ljava/lang/Class;)V");
// only in the top most type - what about interfaces??
if (GlobalConfiguration.fieldRewriting) {
mv.visitFieldInsn(GETSTATIC, slashedname, fStaticFieldsName, lStaticStateManager);
Label l1 = new Label();
mv.visitJumpInsn(IFNONNULL, l1);
mv.visitTypeInsn(NEW, tStaticStateManager);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, tStaticStateManager, "<init>", "()V", false);
mv.visitFieldInsn(PUTSTATIC, slashedname, fStaticFieldsName, lStaticStateManager);
mv.visitLabel(l1);
}
// If the static initializer has changed, call the new version through the ___clinit___ method
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tReloadableType, "clinitchanged", "()I", false);
// 2. Create the if statement
Label wasZero = new Label();
mv.visitJumpInsn(Opcodes.IFEQ, wasZero); // if == 0, jump to where we can do the original thing
// 3. grab the latest dispatcher and call it through the interface
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitMethodInsn(INVOKEVIRTUAL, tReloadableType, "fetchLatest", "()Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, Utils.getInterfaceName(slashedname));
mv.visitMethodInsn(INVOKEINTERFACE, Utils.getInterfaceName(slashedname), mStaticInitializerName, "()V");
mv.visitInsn(RETURN);
// 4. do what you were going to do anyway
mv.visitLabel(wasZero);
}
}
/**
* Rewrites a method to include the extra checks to verify it is the most up to date version.
*/
class AugmentingMethodAdapter extends MethodVisitor implements Opcodes {
int methodId;
String name;
String descriptor;
MethodMember method;
ReturnType returnType;
public AugmentingMethodAdapter(MethodVisitor mv, String name, String descriptor) {
super(ASM5, mv);
this.name = name;
this.method = rtype.getMethod(name, descriptor);
this.methodId = method.getId();
this.descriptor = descriptor;
this.returnType = Utils.getReturnTypeDescriptor(descriptor);
}
@Override
public void visitCode() {
super.visitCode();
boolean isStaticMethod = method.isStatic();
// 1. ask the reloadable type if anything has changed since initial load by
// calling 'int changed(int)' passing in the method ID
// 0 if the method cannot have changed
// 1 if the method has changed
// 2 if the method has been deleted in a new version
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitLdcInsn(methodId);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tReloadableType, "changed", "(I)I");
// 2. Create the if statement
Label wasZero = new Label();
if (!isStaticMethod) {
mv.visitInsn(DUP);
}
mv.visitJumpInsn(Opcodes.IFEQ, wasZero); // if == 0, jump to where we can do the original thing
// TODO if it isStatic then the invoke side should be pointing at the right version - do we need to cope with it not?
if (!isStaticMethod) {
Label wasOne = new Label();
mv.visitInsn(ICONST_1);
mv.visitJumpInsn(IF_ICMPEQ, wasOne); // if == 1, method has changed
// If here, == 2, so method has been deleted
// either try an invokespecial on a super or throw a NoSuchmethodError
// Only worth including super.xxx() call if the supertype does define it or the supertype is reloadable
// Otherwise we will generate invokespecial 'Object.somethingThatCantBeThere' in some cases
TypeDescriptor superDescriptor = rtype.getTypeRegistry().getDescriptorFor(supertypeName);
if (!superDescriptor.definesNonPrivate(name + descriptor)) {
insertThrowNoSuchMethodError();
}
else {
insertInvokeSpecialToCallSuperMethod();
}
mv.visitLabel(wasOne);
}
// 3. grab the latest dispatcher and call it through the interface
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitMethodInsn(INVOKEVIRTUAL, tReloadableType, "fetchLatest", "()Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, Utils.getInterfaceName(slashedname));
int lvarIndex = 0;
if (!isStaticMethod) {
mv.visitVarInsn(ALOAD, lvarIndex++);
}
else {
mv.visitInsn(ACONST_NULL);
}
Utils.createLoadsBasedOnDescriptor(mv, descriptor, lvarIndex);
String desc = new StringBuilder("(L").append(slashedname).append(";").append(
descriptor.substring(1)).toString();
if (method.isStatic() && MethodMember.isClash(method)) {
name = "__" + name;
}
mv.visitMethodInsn(INVOKEINTERFACE, Utils.getInterfaceName(slashedname), name, desc);
Utils.addCorrectReturnInstruction(mv, returnType, true);
// 4. do what you were going to do anyway
mv.visitLabel(wasZero);
if (!isStaticMethod) {
mv.visitInsn(POP);
}
}
private void insertInvokeSpecialToCallSuperMethod() {
int lvarIndex = 0;
if (!Modifier.isStatic(method.getModifiers())) {
mv.visitVarInsn(ALOAD, lvarIndex++); // load this
}
Utils.createLoadsBasedOnDescriptor(mv, descriptor, lvarIndex);
mv.visitMethodInsn(INVOKESPECIAL, supertypeName, name, descriptor);
Utils.addCorrectReturnInstruction(mv, Utils.getReturnTypeDescriptor(descriptor), false);
}
private void insertThrowNoSuchMethodError() {
// exception text should look like: a.b.c.B.foo()V
String text = rtype.getName() + "." + name.replace('/', '.') + descriptor;
mv.visitTypeInsn(NEW, "java/lang/NoSuchMethodError");
mv.visitInsn(DUP);
mv.visitLdcInsn(text);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/NoSuchMethodError", "<init>", "(Ljava/lang/String;)V");
mv.visitInsn(ATHROW);
}
}
class AugmentingConstructorAdapter extends MethodVisitor implements Opcodes {
int ctorId;
String name;
String descriptor;
MethodMember method;
String type;
boolean isTopMost;
public AugmentingConstructorAdapter(MethodVisitor mv, String descriptor, String type, boolean isTopMost) {
super(ASM5, mv);
this.descriptor = descriptor;
this.type = type;
this.isTopMost = isTopMost;
}
@Override
public void visitCode() {
super.visitCode();
// 1. Quick check if anything has changed
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitLdcInsn(ctorId);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tReloadableType, "cchanged", "(I)Ljava/lang/Object;");
// return value of that call is the dispatcher to call, or null if we shouldn't do anything different
// TODO throw exception if no longer defined? (Shouldn't really be called if not defined though...)
// 2. if nothing has changed jump to the end and run the original code
Label wasNull = new Label();
mv.visitInsn(DUP);
mv.visitJumpInsn(Opcodes.IFNULL, wasNull); // if == null
mv.visitTypeInsn(CHECKCAST, Utils.getInterfaceName(slashedname));
// mv.visitInsn(SWAP);
mv.visitVarInsn(ALOAD, 0);
// mv.visitInsn(DUP);
// Now we have the dispatcher on the stack for our type, we can't pass 'this' (aload_0) on a call yet because it is not initialized
// have to initialize it now before we can pass it on
// mv.visitMethodInsn(INVOKESPECIAL, slashedname, "<init>", "(Lorg/springsource/loaded/ReloadableType;)V");
// mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
// mv.visitMethodInsn(INVOKESPECIAL, owner, name, desc)
if (isEnum) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ILOAD, 2);
mv.visitInsn(ACONST_NULL);
mv.visitMethodInsn(INVOKESPECIAL, slashedname, "<init>",
"(Ljava/lang/String;ILorg/springsource/loaded/C;)V");
}
else {
mv.visitInsn(ACONST_NULL);
mv.visitMethodInsn(INVOKESPECIAL, slashedname, "<init>", "(Lorg/springsource/loaded/C;)V");
}
// initialize the field instance manager
// not conditional on isTopMost because entering object construction through this route won't ever call the initialization logic in
// the super ctor, because all that is bypassed. We could put this initialization logic into the topmost 'special' constructor
// that we create, that is an alternative
if (GlobalConfiguration.fieldRewriting) {// && isTopMost) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, slashedname, fInstanceFieldsName, lInstanceStateManager);
Label l1 = new Label();
mv.visitJumpInsn(IFNONNULL, l1);
mv.visitVarInsn(ALOAD, 0);
mv.visitTypeInsn(NEW, tInstanceStateManager);
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitMethodInsn(INVOKESPECIAL, tInstanceStateManager, "<init>",
"(Ljava/lang/Object;Lorg/springsource/loaded/ReloadableType;)V");
mv.visitFieldInsn(PUTFIELD, slashedname, fInstanceFieldsName, lInstanceStateManager);
mv.visitLabel(l1);
}
mv.visitVarInsn(ALOAD, 0);
Utils.createLoadsBasedOnDescriptor(mv, descriptor, 1);
// from "(Ljava/lang/String;J)V" to "(Ljava/lang/String;J)Lcom/foo/A;"
// String desc = new StringBuilder().append(descriptor, 0, descriptor.length() - 1).append("L").append(slashedname)
// .append(";").toString();
// mv.visitMethodInsn(INVOKEVIRTUAL,"")
String desc = new StringBuilder("(L").append(slashedname).append(";").append(
descriptor.substring(1)).toString();
// mv.visitVarInsn(ALOAD, 0);
// mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
mv.visitMethodInsn(INVOKEINTERFACE, Utils.getInterfaceName(slashedname), "___init___", desc);
mv.visitInsn(RETURN);
// mv.visitTypeInsn(CHECKCAST, type);
// 4. do what you were going to do anyway
mv.visitLabel(wasNull);
mv.visitInsn(POP);
}
int unitializedObjectsCount = 0;
@Override
public void visitTypeInsn(final int opcode, final String type) {
if (opcode == NEW) {
unitializedObjectsCount++;
}
super.visitTypeInsn(opcode, type);
}
@Override
public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc,
final boolean itf) {
super.visitMethodInsn(opcode, owner, name, desc, itf);
if (opcode == INVOKESPECIAL) {
unitializedObjectsCount--;
}
if (unitializedObjectsCount == -1 && isTopMost) {
// Need to insert this after the relevant invokespecial
if (GlobalConfiguration.fieldRewriting) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, slashedname, fInstanceFieldsName, lInstanceStateManager);
Label l1 = new Label();
mv.visitJumpInsn(IFNONNULL, l1);
mv.visitVarInsn(ALOAD, 0);
mv.visitTypeInsn(NEW, tInstanceStateManager);
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType);
mv.visitMethodInsn(INVOKESPECIAL, tInstanceStateManager, "<init>",
"(Ljava/lang/Object;Lorg/springsource/loaded/ReloadableType;)V", false);
// mv.visitMethodInsn(INVOKESPECIAL, tInstanceStateManager, "<init>", "(Ljava/lang/Object;)V");
mv.visitFieldInsn(PUTFIELD, slashedname, fInstanceFieldsName, lInstanceStateManager);
mv.visitLabel(l1);
}
}
}
}
interface Prepender {
void prepend();
}
class MethodPrepender extends MethodVisitor implements Opcodes {
Prepender appender;
public MethodPrepender(MethodVisitor mv, Prepender appender) {
super(ASM5, mv);
this.appender = appender;
}
@Override
public void visitCode() {
super.visitCode();
appender.prepend();
}
}
}
}
相关信息
相关文章
spring-loaded AbstractMember 源码
spring-loaded AnyTypePattern 源码
spring-loaded ChildClassLoader 源码
spring-loaded ConstantPoolChecker 源码
spring-loaded ConstantPoolChecker2 源码
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦