Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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
*
* http://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.util.ArrayList;
import java.util.List;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
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.Utils.ReturnType;
/**
* Builder that creates the dispatcher. The dispatcher is the implementation of the interface extracted for a type which
* then delegates to the executor. A new dispatcher (and executor) is built for each class reload.
*
* @author Andy Clement
* @since 0.5.0
*/
public class DispatcherBuilder {
/**
* Factory method that builds the dispatcher for a specified reloadabletype.
*
* @param rtype the reloadable type
* @param newVersionTypeDescriptor the descriptor of the new version (the executor will be generated according to
* this)
* @param versionstamp the suffix that should be appended to the generated dispatcher
* @return the bytecode for the new dispatcher
*/
public static byte[] createFor(ReloadableType rtype, IncrementalTypeDescriptor newVersionTypeDescriptor,
String versionstamp) {
ClassReader fileReader = new ClassReader(rtype.interfaceBytes);
DispatcherBuilderVisitor dispatcherVisitor = new DispatcherBuilderVisitor(rtype, newVersionTypeDescriptor,
versionstamp);
fileReader.accept(dispatcherVisitor, 0);
return dispatcherVisitor.getBytes();
}
/**
* Whilst visiting the interface, the implementation is created.
*/
static class DispatcherBuilderVisitor extends ClassVisitor implements Opcodes, Constants {
private ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
private String classname;
private String executorClassName;
private String suffix;
private ReloadableType rtype;
private IncrementalTypeDescriptor typeDescriptor;
public DispatcherBuilderVisitor(ReloadableType rtype, IncrementalTypeDescriptor typeDescriptor, String suffix) {
super(ASM5);
this.classname = rtype.getSlashedName();
this.typeDescriptor = typeDescriptor;
this.suffix = suffix;
this.rtype = rtype;
this.executorClassName = Utils.getExecutorName(classname, suffix);
}
public byte[] getBytes() {
return cw.toByteArray();
}
public void visit(int version, int flags, String name, String signature, String superclassName,
String[] interfaceNames) {
String dispatcherName = Utils.getDispatcherName(classname, suffix);
cw.visit(version, Opcodes.ACC_PUBLIC, dispatcherName, null, "java/lang/Object",
new String[] { Utils.getInterfaceName(classname),
"org/springsource/loaded/__DynamicallyDispatchable" });
generateDefaultConstructor();
}
private void generateDefaultConstructor() {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
private void generateClinitDispatcher() {
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, mStaticInitializerName, "()V", null, null);
mv.visitCode();
mv.visitMethodInsn(INVOKESTATIC, executorClassName, mStaticInitializerName, "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
public AnnotationVisitor visitAnnotation(String arg0, boolean arg1) {
return null;
}
public void visitAttribute(Attribute arg0) {
}
public void visitEnd() {
}
public FieldVisitor visitField(int arg0, String arg1, String arg2, String arg3, Object arg4) {
return null;
}
public void visitInnerClass(String arg0, String arg1, String arg2, int arg3) {
}
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
String[] exceptions) {
if (name.equals(mDynamicDispatchName)) {
generateDynamicDispatchMethod(name, descriptor, signature, exceptions);
}
else if (!name.equals("")) {
generateRegularMethod(name, descriptor, signature, exceptions);
}
return null;
}
/**
* Generate the body of the dynamic dispatcher method. This method is responsible for calling all the methods
* that are added to a type after the first time it is defined.
*/
private void generateDynamicDispatchMethod(String name, String descriptor, String signature, String[] exceptions) {
final int indexDispatcherInstance = 0;
final int indexArgs = 1;
final int indexTarget = 2;
final int indexNameAndDescriptor = 3;
// Should be generating the code for each additional method in
// the executor (new version) that wasn't in the original.
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, name, descriptor, signature, exceptions);
mv.visitCode();
// Entries required here for all methods that exist in the new version but didn't exist in the original version
// There should be no entries for catchers
int maxStack = 0;
// Basically generate a long if..else sequence for each method
List methods = new ArrayList(typeDescriptor.getNewOrChangedMethods());
// these are added because we may be calling through the dynamic dispatcher if calling from an invokeinterface - the invokeinterface
// will call __execute on the interface, which is then implemented by the real class - but it may be that the
// actual type implementing the interface already implements that method - if the dispatcher doesn't recognize
// it then we may go bang
// System.out.println("Generating __execute in type " + classname);
for (MethodMember m : typeDescriptor.getOriginal().getMethods()) {
methods.add(m);
}
for (MethodMember method : methods) {
if (MethodMember.isCatcher(method) || MethodMember.isSuperDispatcher(method)) { // for reason above, may also need to consider catchers here - what if an interface is changed to add a toString() method, for example
continue;
// would the implementation for a catcher call the super catcher?
}
// System.out.println("Generating handler for " + method.name);
String nameWithDescriptor = new StringBuilder(method.name).append(method.descriptor).toString();
// 2. Load the input name+descriptor and compare it with this method:
mv.visitVarInsn(ALOAD, 3);
mv.visitLdcInsn(nameWithDescriptor);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
Label label = new Label();
mv.visitJumpInsn(IFEQ, label); // means if false
// 3. Generate the code that will call the method on the executor:
if (!method.isStatic()) {
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, classname);
}
String callDescriptor = method.isStatic() ? method.descriptor : Utils.insertExtraParameter(classname,
method.descriptor);
int pcount = Utils.getParameterCount(method.descriptor);
if (pcount > maxStack) {
pcount = maxStack;
}
// 4. Unpack parameter array to fit the descriptor for that method
Utils.generateInstructionsToUnpackArrayAccordingToDescriptor(mv, method.descriptor, 1);
ReturnType returnType = Utils.getReturnTypeDescriptor(method.descriptor);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, executorClassName, method.name, callDescriptor, false);
if (returnType.isVoid()) {
mv.visitInsn(ACONST_NULL);
}
else if (returnType.isPrimitive()) {
Utils.insertBoxInsns(mv, returnType.descriptor);
}
mv.visitInsn(Opcodes.ARETURN);
mv.visitLabel(label);
}
for (MethodMember ctor : typeDescriptor.getLatestTypeDescriptor().getConstructors()) {
String nameWithDescriptor = new StringBuilder(ctor.name).append(ctor.descriptor).toString();
// 2. Load the input name+descriptor and compare it with this method:
// if (nameAndDescriptor.equals(xxx)) {
mv.visitVarInsn(ALOAD, indexNameAndDescriptor);
mv.visitLdcInsn(nameWithDescriptor);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
Label label = new Label();
mv.visitJumpInsn(IFEQ, label); // means if false
// 3. Generate the code that will call the method on the executor:
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, classname);
String callDescriptor = Utils.insertExtraParameter(classname, ctor.descriptor);
int pcount = Utils.getParameterCount(ctor.descriptor);
if (pcount > maxStack) {
pcount = maxStack;
}
// 4. Unpack parameter array to fit the descriptor for that method
Utils.generateInstructionsToUnpackArrayAccordingToDescriptor(mv, ctor.descriptor, 1);
// ReturnType returnType = Utils.getReturnTypeDescriptor(method.descriptor);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, executorClassName, "___init___", callDescriptor, false);
// if (returnType.isVoid()) {
mv.visitInsn(ACONST_NULL);
// } else if (returnType.isPrimitive()) {
// Utils.insertBoxInsns(mv, returnType.descriptor);
// }
mv.visitInsn(Opcodes.ARETURN);
mv.visitLabel(label);
}
// 5. Throw exception as dynamic dispatcher has been called for something it shouldn't have
// At this point we failed to find it as a method we can dispatch to our executor, so we want
// to pass it 'up' to our supertype. We need to get the dispatcher for our superclass
// and then call the __execute() on it, assuming that it will be able to handle this request.
// alternative 1: use the dispatcher for the superclass
// Determine the supertype
String slashedSupertypeName = rtype.getTypeDescriptor().getSupertypeName();
// getDispatcher will give us the dispatcher for the supertype
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedSupertypeName, fReloadableTypeFieldName, lReloadableType);
mv.visitMethodInsn(INVOKEVIRTUAL, tReloadableType, "getDispatcher",
"()Lorg/springsource/loaded/__DynamicallyDispatchable;", false);
// alternative 2: find the right dispatcher - i.e. who in the super hierarchy provides that nameAndDescriptor
// now invoke the dynamic dispatch call on that dispatcher
mv.visitVarInsn(ALOAD, indexArgs);
mv.visitVarInsn(ALOAD, indexTarget);
mv.visitVarInsn(ALOAD, indexNameAndDescriptor);
mv.visitMethodInsn(INVOKEINTERFACE, tDynamicallyDispatchable, mDynamicDispatchName,
mDynamicDispatchDescriptor, false);
mv.visitInsn(ARETURN);
// mv.visitTypeInsn(NEW, "java/lang/IllegalStateException");
// mv.visitInsn(DUP);
// mv.visitVarInsn(ALOAD, 3);
// mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalStateException", "", "(Ljava/lang/String;)V");
// mv.visitInsn(ATHROW);
mv.visitMaxs(maxStack, 6);
mv.visitEnd();
}
/**
* Called to generate the implementation of a normal method on the interface - a normal method is one that did
* exist when the type was first defined. Might be a catcher.
*/
private void generateRegularMethod(String name, String descriptor, String signature, String[] exceptions) {
// The original descriptor is how it was defined on the original type and how it is defined in the executor class.
// The original descriptor is this descriptor with the first parameter trimmed off.
boolean isClinit = name.equals("___clinit___");
String originalDescriptor = isClinit ? descriptor : Utils.stripFirstParameter(descriptor);
MethodMember method = null;
// Detect if the name has been modified for clash avoidance reasons
if (name.equals("___init___")) {
// it is a ctor
method = rtype.getConstructor(originalDescriptor);
}
else {
if (isClinit) {
generateClinitDispatcher();
return;
}
else {
// TODO need a better solution that these __
if (name.startsWith("__") && !name.equals("__$swapInit")) { // __$swapInit is the groovy reset method
// clash avoidance name
method = rtype.getMethod(name.substring(2), originalDescriptor);
}
else {
method = rtype.getMethod(name, originalDescriptor);
}
}
}
boolean isStatic = method.isStatic();
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, name, descriptor, signature,
exceptions);
mv.visitCode();
// The input descriptor will include the extra initial parameter (the instance, or null for static methods)
ReturnType returnTypeDescriptor = Utils.getReturnTypeDescriptor(descriptor);
// For a static method the first parameter can be ignored
int params = Utils.getParameterCount(descriptor);
String callDescriptor = isStatic ? originalDescriptor : descriptor;
Utils.createLoadsBasedOnDescriptor(mv, callDescriptor, isStatic ? 2 : 1);
mv.visitMethodInsn(INVOKESTATIC, executorClassName, name, callDescriptor, false);
Utils.addCorrectReturnInstruction(mv, returnTypeDescriptor, false);
mv.visitMaxs(params, params + 1);
mv.visitEnd();
}
public void visitOuterClass(String arg0, String arg1, String arg2) {
}
public void visitSource(String arg0, String arg1) {
}
}
}