/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.ejb3.iiop.stub;
// because it calls some ProxyAssembler
// methods that currently are package
// accessible
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.rmi.RemoteException;
import org.jboss.as.jacorb.rmi.AttributeAnalysis;
import org.jboss.as.jacorb.rmi.ExceptionAnalysis;
import org.jboss.as.jacorb.rmi.InterfaceAnalysis;
import org.jboss.as.jacorb.rmi.OperationAnalysis;
import org.jboss.as.jacorb.rmi.RMIIIOPViolationException;
import org.jboss.as.jacorb.rmi.marshal.CDRStream;
import org.jboss.as.jacorb.rmi.marshal.strategy.StubStrategy;
import org.jboss.classfilewriter.ClassFile;
import org.jboss.classfilewriter.ClassMethod;
import org.jboss.classfilewriter.code.CodeAttribute;
import org.jboss.classfilewriter.util.Boxing;
import org.jboss.classfilewriter.util.DescriptorUtils;
/**
* Utility class responsible for the dynamic generation of bytecodes of
* IIOP stub classes.
*
* @author Stuart Douglas
* @author <a href="mailto:reverbel@ime.usp.br">Francisco Reverbel</a>
*/
public class IIOPStubCompiler {
public static final String ID_FIELD_NAME = "$ids";
/**
* Returns the name of the stub strategy field associated with the method
* whose index is <code>methodIndex</code>.
*/
private static String strategy(int methodIndex) {
return "$s" + methodIndex;
}
/**
* Returns the name of static initializer method associated with the method
* whose index is <code>methodIndex</code>.
*/
private static String init(int methodIndex) {
return "$i" + methodIndex;
}
/**
* Generates the code of a given method within a stub class.
*
* @param asm the <code>ProxyAssembler</code> used to assemble
* the method code
* @param superclass the superclass of the stub class within which the
* method will be generated
* @param m a <code>Method</code> instance describing the
* method declaration by an RMI/IDL interface
* @param idlName a string with the method name mapped to IDL
* @param strategyField a string with the name of the strategy field that
* will be associated with the generated method
* @param initMethod a string with the name of the static initialization
* method that will be associated with the generated
* method.
*/
private static void generateMethodCode(ClassFile asm,
Class superclass,
Method m,
String idlName,
String strategyField,
String initMethod) {
Class returnType = m.getReturnType();
Class[] paramTypes = m.getParameterTypes();
Class[] exceptions = m.getExceptionTypes();
// Generate a static field with the StubStrategy for the method
asm.addField(Modifier.PRIVATE + Modifier.STATIC, strategyField, StubStrategy.class);
// Generate the method code
final CodeAttribute ca = asm.addMethod(m).getCodeAttribute();
// The method code issues a call
// super.invoke*(idlName, strategyField, args)
ca.aload(0);
ca.ldc(idlName);
ca.getstatic(asm.getName(), strategyField, StubStrategy.class);
// Push args
if (paramTypes.length == 0) {
ca.iconst(0);
ca.anewarray(Object.class.getName());
//asm.pushField(Util.class, "NOARGS");
} else {
ca.iconst(paramTypes.length);
ca.anewarray(Object.class.getName());
int index = 1;
for (int j = 0; j < paramTypes.length; j++) {
Class type = paramTypes[j];
ca.dup();
ca.iconst(j);
if (!type.isPrimitive()) {
// object or array
ca.aload(index);
} else if (type.equals(double.class)) {
ca.dload(index);
Boxing.boxDouble(ca);
index++;
} else if (type.equals(long.class)) {
ca.lload(index);
Boxing.boxLong(ca);
index++;
} else if (type.equals(float.class)) {
ca.fload(index);
Boxing.boxFloat(ca);
} else {
ca.iload(index);
Boxing.boxIfNessesary(ca, DescriptorUtils.makeDescriptor(type));
}
index++;
ca.aastore();
}
}
// Generate the call to a invoke* method ot the superclass
String invoke = "invoke";
String ret = "Ljava/lang/Object;";
if (returnType.isPrimitive() && returnType != Void.TYPE) {
String typeName = returnType.getName();
invoke += (Character.toUpperCase(typeName.charAt(0))
+ typeName.substring(1));
ret = DescriptorUtils.makeDescriptor(returnType);
}
ca.invokevirtual(superclass.getName(), invoke, "(Ljava/lang/String;Lorg/jboss/as/jacorb/rmi/marshal/strategy/StubStrategy;[Ljava/lang/Object;)" + ret);
if (!returnType.isPrimitive() && returnType != Object.class) {
ca.checkcast(returnType);
}
ca.returnInstruction();
// Generate a static method that initializes the method's strategy field
final CodeAttribute init = asm.addMethod(Modifier.PRIVATE + Modifier.STATIC,initMethod, "V").getCodeAttribute();
int i;
int len;
// Push first argument for StubStrategy constructor:
// array with abbreviated names of the param marshallers
len = paramTypes.length;
init.iconst(len);
init.anewarray(String.class.getName());
for (i = 0; i < len; i++) {
init.dup();
init.iconst(i);
init.ldc(CDRStream.abbrevFor(paramTypes[i]));
init.aastore();
}
// Push second argument for StubStrategy constructor:
// array with exception repository ids
len = exceptions.length;
int n = 0;
for (i = 0; i < len; i++) {
if (!RemoteException.class.isAssignableFrom(exceptions[i])) {
n++;
}
}
init.iconst(n);
init.anewarray(String.class.getName());
try {
int j = 0;
for (i = 0; i < len; i++) {
if (!RemoteException.class.isAssignableFrom(exceptions[i])) {
init.dup();
init.iconst(j);
init.ldc(
ExceptionAnalysis.getExceptionAnalysis(exceptions[i])
.getExceptionRepositoryId());
init.aastore();
j++;
}
}
} catch (RMIIIOPViolationException e) {
throw new RuntimeException("Cannot obtain "
+ "exception repository id for "
+ exceptions[i].getName() + ":\n" + e);
}
// Push third argument for StubStrategy constructor:
// array with exception class names
init.iconst(n);
init.anewarray(String.class.getName());
int j = 0;
for (i = 0; i < len; i++) {
if (!RemoteException.class.isAssignableFrom(exceptions[i])) {
init.dup();
init.iconst(j);
init.ldc(exceptions[i].getName());
init.aastore();
j++;
}
}
// Push fourth argument for StubStrategy constructor:
// abbreviated name of the return value marshaller
init.ldc(CDRStream.abbrevFor(returnType));
// Push fifth argument for StubStrategy constructor:
// null (no ClassLoader specified)
init.aconstNull();
// Constructs the StubStrategy
init.invokestatic(StubStrategy.class.getName(), "forMethod", "([Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)Lorg/jboss/as/jacorb/rmi/marshal/strategy/StubStrategy;");
// Set the strategy field of this stub class
init.putstatic(asm.getName(), strategyField, StubStrategy.class);
init.returnInstruction();
}
/**
* Generates the bytecodes of a stub class for a given interface.
*
* @param interfaceAnalysis an <code>InterfaceAnalysis</code> instance
* describing the RMI/IIOP interface to be
* implemented by the stub class
* @param superclass the superclass of the stub class
* @param stubClassName the name of the stub class
* @return a byte array with the generated bytecodes.
*/
private static ClassFile generateCode(InterfaceAnalysis interfaceAnalysis,
Class superclass, String stubClassName) {
final ClassFile asm =
new ClassFile(stubClassName,
superclass.getName(),
interfaceAnalysis.getCls().getName());
int methodIndex = 0;
AttributeAnalysis[] attrs = interfaceAnalysis.getAttributes();
for (int i = 0; i < attrs.length; i++) {
OperationAnalysis op = attrs[i].getAccessorAnalysis();
generateMethodCode(asm, superclass, op.getMethod(), op.getIDLName(),
strategy(methodIndex), init(methodIndex));
methodIndex++;
op = attrs[i].getMutatorAnalysis();
if (op != null) {
generateMethodCode(asm, superclass,
op.getMethod(), op.getIDLName(),
strategy(methodIndex), init(methodIndex));
methodIndex++;
}
}
final OperationAnalysis[] ops = interfaceAnalysis.getOperations();
for (int i = 0; i < ops.length; i++) {
generateMethodCode(asm, superclass,
ops[i].getMethod(), ops[i].getIDLName(),
strategy(methodIndex), init(methodIndex));
methodIndex++;
}
// Generate the constructor
final ClassMethod ctor = asm.addMethod(Modifier.PUBLIC, "<init>", "V");
ctor.getCodeAttribute().aload(0);
ctor.getCodeAttribute().invokespecial(superclass.getName(), "<init>", "()V");
ctor.getCodeAttribute().returnInstruction();
// Generate the method _ids(), declared as abstract in ObjectImpl
final String[] ids = interfaceAnalysis.getAllTypeIds();
asm.addField(Modifier.PRIVATE + Modifier.STATIC, ID_FIELD_NAME, String[].class);
final CodeAttribute idMethod = asm.addMethod(Modifier.PUBLIC + Modifier.FINAL, "_ids", "[Ljava/lang/String;").getCodeAttribute();
idMethod.getstatic(stubClassName, ID_FIELD_NAME, "[Ljava/lang/String;");
idMethod.returnInstruction();
// Generate the static initializer
final CodeAttribute clinit = asm.addMethod(Modifier.STATIC, "<clinit>", "V").getCodeAttribute();
clinit.iconst(ids.length);
clinit.anewarray(String.class.getName());
for (int i = 0; i < ids.length; i++) {
clinit.dup();
clinit.iconst(i);
clinit.ldc(ids[i]);
clinit.aastore();
}
clinit.putstatic(stubClassName, ID_FIELD_NAME, "[Ljava/lang/String;");
int n = methodIndex; // last methodIndex + 1
for (methodIndex = 0; methodIndex < n; methodIndex++) {
clinit.invokestatic(stubClassName, init(methodIndex), "()V");
}
clinit.returnInstruction();
return asm;
}
/**
* Generates the bytecodes of a stub class for a given interface.
*
* @param interfaceAnalysis an <code>InterfaceAnalysis</code> instance
* describing the RMI/IIOP interface to be
* implemented by the stub class
* @param superclass the superclass of the stub class
* @param stubClassName the name of the stub class
* @return a byte array with the generated bytecodes.
*/
private static ClassFile makeCode(InterfaceAnalysis interfaceAnalysis,
Class superclass, String stubClassName) {
ClassFile code = generateCode(interfaceAnalysis, superclass, stubClassName);
//try {
// String fname = stubClassName;
// fname = fname.substring(1 + fname.lastIndexOf('.')) + ".class";
// fname = "/tmp/" + fname;
// java.io.OutputStream cf = new java.io.FileOutputStream(fname);
// cf.write(code);
// cf.close();
// System.err.println("wrote " + fname);
//}
//catch(java.io.IOException ee) {
//}
return code;
}
// Public method ----------------------------------------------------------
/**
* Generates the bytecodes of a stub class for a given interface.
*
* @param intf RMI/IIOP interface to be implemented by the
* stub class
* @param stubClassName the name of the stub class
* @return a byte array with the generated bytecodes;
*/
public static ClassFile compile(Class intf, String stubClassName) {
InterfaceAnalysis interfaceAnalysis = null;
try {
interfaceAnalysis = InterfaceAnalysis.getInterfaceAnalysis(intf);
} catch (RMIIIOPViolationException e) {
throw new RuntimeException("RMI/IIOP Violation:\n" + e);
}
return makeCode(interfaceAnalysis, DynamicIIOPStub.class, stubClassName);
}
public Class<?> compileToClass(Class intf, String stubClassName) {
return compile(intf, stubClassName).define(intf.getClassLoader(), intf.getProtectionDomain());
}
}