blob: 3b00cb7f9e49b48c1af5f1700b6b104cdf8e4876 [file] [log] [blame]
/*
* Copyright 2003-2007 the original author or authors.
*
* 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.codehaus.groovy.classgen;
import groovy.lang.MetaMethod;
import org.objectweb.asm.*;
import org.codehaus.groovy.reflection.CachedClass;
import java.util.ArrayList;
import java.util.List;
/**
* Code generates a Reflector
*
* @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
* @version $Revision$
*/
public class ReflectorGenerator implements Opcodes {
private List methods;
private ClassVisitor cv;
private BytecodeHelper helper = new BytecodeHelper(null);
private String classInternalName;
private static List m_names = new ArrayList();
private static String get_m_name (int i) {
while (i >= m_names.size()) {
m_names.add("m" + m_names.size());
}
return (String) m_names.get(i);
}
public ReflectorGenerator(List methods) {
this.methods = methods;
}
public void generate(ClassVisitor cv, String className) {
this.cv = cv;
classInternalName = BytecodeHelper.getClassInternalName(className);
cv.visit(ClassGenerator.asmJDKVersion, ACC_PUBLIC + ACC_SUPER, classInternalName, null, "org/codehaus/groovy/runtime/Reflector", null);
cv.visitField(ACC_PUBLIC + ACC_STATIC, "accessor", "Ljava/lang/Object;", null, null);
MethodVisitor mvInit = cv.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mvInit.visitVarInsn(ALOAD, 0);
mvInit.visitMethodInsn(INVOKESPECIAL, "org/codehaus/groovy/runtime/Reflector", "<init>", "()V");
mvInit.visitInsn(RETURN);
mvInit.visitMaxs(1, 1);
MethodVisitor mvClinit = cv.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
mvClinit.visitTypeInsn(NEW, classInternalName);
mvClinit.visitInsn(DUP);
mvClinit.visitMethodInsn(INVOKESPECIAL, classInternalName, "<init>", "()V");
mvClinit.visitFieldInsn(PUTSTATIC, classInternalName, "accessor", "Ljava/lang/Object;");
mvClinit.visitInsn(RETURN);
mvClinit.visitMaxs(1, 1);
generateInvokeMethod();
cv.visitEnd();
}
protected void generateInvokeMethod() {
int methodCount = methods.size();
MethodVisitor mv = cv.visitMethod(
ACC_PUBLIC,
"invoke",
"(Lgroovy/lang/MetaMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
null,
null);
// load parameters for the helper method call
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitVarInsn(ALOAD, 3);
// get method number for switch
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/MetaMethod", "getMethodIndex", "()I");
// init meta methods with number
Label defaultLabel = new Label();
Label[] labels = new Label[methodCount];
int[] indices = new int[methodCount];
for (int i = 0; i < methodCount; i++) {
labels[i] = new Label();
MetaMethod method = (MetaMethod) methods.get(i);
method.setMethodIndex(i + 1);
indices[i] = method.getMethodIndex();
}
// do switch
mv.visitLookupSwitchInsn(defaultLabel, indices, labels);
// create switch cases
for (int i = 0; i < methodCount; i++) {
// call helper for invocation
mv.visitLabel(labels[i]);
mv.visitMethodInsn(
INVOKESPECIAL,
classInternalName,
get_m_name(i),
"(Lgroovy/lang/MetaMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitInsn(ARETURN);
}
// call helper for error
mv.visitLabel(defaultLabel);
mv.visitMethodInsn(
INVOKEVIRTUAL,
classInternalName,
"noSuchMethod",
"(Lgroovy/lang/MetaMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitInsn(ARETURN);
// end method
mv.visitMaxs(4, 4);
mv.visitEnd();
// create helper methods m*
for (int i = 0; i < methodCount; i++) {
mv = cv.visitMethod(
ACC_PRIVATE,
get_m_name(i),
"(Lgroovy/lang/MetaMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
null,
null);
helper = new BytecodeHelper(mv);
MetaMethod method = (MetaMethod) methods.get(i);
invokeMethod(method, mv);
if (method.getReturnType() == void.class) {
mv.visitInsn(ACONST_NULL);
}
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
}
protected void invokeMethod(MetaMethod method, MethodVisitor mv) {
// compute class to make the call on
Class callClass = method.getInterfaceClass();
boolean useInterface = false;
if (callClass == null) {
callClass = method.getCallClass();
} else {
useInterface = true;
}
// get bytecode information
String type = BytecodeHelper.getClassInternalName(callClass.getName());
String descriptor = BytecodeHelper.getMethodDescriptor(method.getReturnType(), method.getNativeParameterTypes());
// make call
if (method.isStatic()) {
loadParameters(method, 3, mv);
mv.visitMethodInsn(INVOKESTATIC, type, method.getName(), descriptor);
} else {
mv.visitVarInsn(ALOAD, 2);
helper.doCast(callClass);
loadParameters(method, 3, mv);
mv.visitMethodInsn((useInterface) ? INVOKEINTERFACE : INVOKEVIRTUAL, type, method.getName(), descriptor);
}
helper.box(method.getReturnType());
}
protected void loadParameters(MetaMethod method, int argumentIndex, MethodVisitor mv) {
CachedClass[] parameters = method.getParameterTypes();
int size = parameters.length;
for (int i = 0; i < size; i++) {
// unpack argument from Object[]
mv.visitVarInsn(ALOAD, argumentIndex);
helper.pushConstant(i);
mv.visitInsn(AALOAD);
// cast argument to parameter class, inclusive unboxing
// for methods with primitive types
Class type = parameters[i].cachedClass;
if (type.isPrimitive()) {
helper.unbox(type);
} else {
helper.doCast(type);
}
}
}
}