blob: 213fe2557fe12c8e0afb85df559a015a70fa574d [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.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.ast.*;
import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.ast.stmt.*;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.syntax.RuntimeParserException;
import org.codehaus.groovy.syntax.Token;
import org.codehaus.groovy.syntax.Types;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* Verifies the AST node and adds any defaulted AST code before
* bytecode generation occurs.
*
* @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
* @version $Revision$
*/
public class Verifier implements GroovyClassVisitor, Opcodes {
public static final String __TIMESTAMP = "__timeStamp";
public static final String __TIMESTAMP__ = "__timeStamp__239_neverHappen";
private ClassNode classNode;
private MethodNode methodNode;
public ClassNode getClassNode() {
return classNode;
}
public MethodNode getMethodNode() {
return methodNode;
}
/**
* add code to implement GroovyObject
* @param node
*/
public void visitClass(ClassNode node) {
this.classNode = node;
if ((classNode.getModifiers() & Opcodes.ACC_INTERFACE) >0) {
//interfaces have no construcotrs, but this code expects one,
//so create a dummy and don't add it to the class node
ConstructorNode dummy = new ConstructorNode(0,null);
addInitialization(node, dummy);
node.visitContents(this);
return;
}
addDefaultParameterMethods(node);
addDefaultParameterConstructors(node);
if (!node.isDerivedFromGroovyObject()) {
node.addInterface(ClassHelper.make(GroovyObject.class));
// lets add a new field for the metaclass
StaticMethodCallExpression initMetaClassCall =
new StaticMethodCallExpression(
ClassHelper.make(ScriptBytecodeAdapter.class),
"initMetaClass",
VariableExpression.THIS_EXPRESSION);
PropertyNode metaClassProperty =
node.addProperty("metaClass", ACC_PUBLIC, ClassHelper.make(MetaClass.class), initMetaClassCall, null, null);
metaClassProperty.setSynthetic(true);
FieldNode metaClassField = metaClassProperty.getField();
metaClassField.setModifiers(metaClassField.getModifiers() | ACC_TRANSIENT);
FieldExpression metaClassVar = new FieldExpression(metaClassField);
IfStatement initMetaClassField =
new IfStatement(
new BooleanExpression(
new BinaryExpression(metaClassVar, Token.newSymbol( Types.COMPARE_EQUAL, -1, -1), ConstantExpression.NULL)),
new ExpressionStatement(new BinaryExpression(metaClassVar, Token.newSymbol( Types.EQUAL, -1, -1), initMetaClassCall)),
EmptyStatement.INSTANCE);
node.addSyntheticMethod(
"getMetaClass",
ACC_PUBLIC,
ClassHelper.make(MetaClass.class),
Parameter.EMPTY_ARRAY,
ClassNode.EMPTY_ARRAY,
new BlockStatement(new Statement[] { initMetaClassField, new ReturnStatement(metaClassVar)}, new VariableScope())
);
// @todo we should check if the base class implements the invokeMethod method
// lets add the invokeMethod implementation
ClassNode superClass = node.getSuperClass();
boolean addDelegateObject =
(node instanceof InnerClassNode && superClass.equals(ClassHelper.CLOSURE_TYPE))
|| superClass.equals(ClassHelper.GSTRING_TYPE);
// don't do anything as the base class implements the invokeMethod
if (!addDelegateObject) {
VariableExpression vMethods = new VariableExpression("method");
VariableExpression vArguments = new VariableExpression("arguments");
VariableScope blockScope = new VariableScope();
blockScope.putReferencedLocalVariable(vMethods);
blockScope.putReferencedLocalVariable(vArguments);
node.addSyntheticMethod(
"invokeMethod",
ACC_PUBLIC,
ClassHelper.OBJECT_TYPE,
new Parameter[] {
new Parameter(ClassHelper.STRING_TYPE, "method"),
new Parameter(ClassHelper.OBJECT_TYPE, "arguments")
},
ClassNode.EMPTY_ARRAY,
new BlockStatement(
new Statement[] {
initMetaClassField,
new ReturnStatement(
new MethodCallExpression(
metaClassVar,
"invokeMethod",
new ArgumentListExpression(
VariableExpression.THIS_EXPRESSION,
vMethods,
vArguments
)
)
)
},
blockScope
)
);
if (!node.isScript()) {
node.addSyntheticMethod(
"getProperty",
ACC_PUBLIC,
ClassHelper.OBJECT_TYPE,
new Parameter[] { new Parameter(ClassHelper.STRING_TYPE, "property")},
ClassNode.EMPTY_ARRAY,
new BlockStatement(
new Statement[] {
initMetaClassField,
new ReturnStatement(
new MethodCallExpression(
metaClassVar,
"getProperty",
new ArgumentListExpression(
VariableExpression.THIS_EXPRESSION,
new VariableExpression("property"))))
},
new VariableScope()
));
VariableExpression vProp = new VariableExpression("property");
VariableExpression vValue = new VariableExpression("value");
blockScope = new VariableScope();
blockScope.putReferencedLocalVariable(vProp);
blockScope.putReferencedLocalVariable(vValue);
node.addSyntheticMethod(
"setProperty",
ACC_PUBLIC,
ClassHelper.VOID_TYPE,
new Parameter[] {
new Parameter(ClassHelper.STRING_TYPE, "property"),
new Parameter(ClassHelper.OBJECT_TYPE, "value")
},
ClassNode.EMPTY_ARRAY,
new BlockStatement(
new Statement[] {
initMetaClassField,
new ExpressionStatement(
new MethodCallExpression(
metaClassVar,
"setProperty",
new ArgumentListExpression(
VariableExpression.THIS_EXPRESSION,
vProp,
vValue)))
},
blockScope
));
}
}
}
if (node.getDeclaredConstructors().isEmpty()) {
ConstructorNode constructor = new ConstructorNode(ACC_PUBLIC, null);
constructor.setSynthetic(true);
node.addConstructor(constructor);
}
if (!(node instanceof InnerClassNode)) {// add a static timestamp field to the class
addTimeStamp(node);
}
addInitialization(node);
checkReturnInObjectInitializer(node.getObjectInitializerStatements());
node.getObjectInitializerStatements().clear();
addCovariantMethods(node);
node.visitContents(this);
}
protected void addTimeStamp(ClassNode node) {
FieldNode timeTagField = new FieldNode(
Verifier.__TIMESTAMP,
Modifier.PUBLIC | Modifier.STATIC,
ClassHelper.Long_TYPE,
//"",
node,
new ConstantExpression(new Long(System.currentTimeMillis())));
// alternatively , FieldNode timeTagField = SourceUnit.createFieldNode("public static final long __timeStamp = " + System.currentTimeMillis() + "L");
timeTagField.setSynthetic(true);
node.addField(timeTagField);
timeTagField = new FieldNode(
Verifier.__TIMESTAMP__ + String.valueOf(System.currentTimeMillis()),
Modifier.PUBLIC | Modifier.STATIC,
ClassHelper.Long_TYPE,
//"",
node,
new ConstantExpression(new Long(0)));
// alternatively , FieldNode timeTagField = SourceUnit.createFieldNode("public static final long __timeStamp = " + System.currentTimeMillis() + "L");
timeTagField.setSynthetic(true);
node.addField(timeTagField);
}
private void checkReturnInObjectInitializer(List init) {
CodeVisitorSupport cvs = new CodeVisitorSupport() {
public void visitReturnStatement(ReturnStatement statement) {
throw new RuntimeParserException("'return' is not allowed in object initializer",statement);
}
};
for (Iterator iterator = init.iterator(); iterator.hasNext();) {
Statement stm = (Statement) iterator.next();
stm.visit(cvs);
}
}
public void visitConstructor(ConstructorNode node) {
CodeVisitorSupport checkSuper = new CodeVisitorSupport() {
boolean firstMethodCall = true;
String type=null;
public void visitMethodCallExpression(MethodCallExpression call) {
if (!firstMethodCall) return;
firstMethodCall = false;
String name = call.getMethodAsString();
// the name might not be null if the method name is a GString for example
if (name==null) return;
if (!name.equals("super") && !name.equals("this")) return;
type=name;
call.getArguments().visit(this);
type=null;
}
public void visitVariableExpression(VariableExpression expression) {
if (type==null) return;
String name = expression.getName();
if (!name.equals("this") && !name.equals("super")) return;
throw new RuntimeParserException("cannot reference "+name+" inside of "+type+"(....) before supertype constructor has been called",expression);
}
};
Statement s = node.getCode();
//todo why can a statement can be null?
if (s == null) return;
s.visit(checkSuper);
}
public void visitMethod(MethodNode node) {
this.methodNode = node;
Statement statement = node.getCode();
if (!node.isVoidMethod()) {
if (statement instanceof ExpressionStatement) {
ExpressionStatement expStmt = (ExpressionStatement) statement;
node.setCode(new ReturnStatement(expStmt.getExpression()));
}
else if (statement instanceof BlockStatement) {
BlockStatement block = (BlockStatement) statement;
// lets copy the list so we create a new block
List list = new ArrayList(block.getStatements());
if (!list.isEmpty()) {
int idx = list.size() - 1;
Statement last = (Statement) list.get(idx);
if (last instanceof ExpressionStatement) {
ExpressionStatement expStmt = (ExpressionStatement) last;
list.set(idx, new ReturnStatement(expStmt));
}
else if (!(last instanceof ReturnStatement)) {
list.add(new ReturnStatement(ConstantExpression.NULL));
}
}
else {
list.add(new ReturnStatement(ConstantExpression.NULL));
}
node.setCode(new BlockStatement(filterStatements(list),block.getVariableScope()));
}
}
else if (!node.isAbstract()) {
BlockStatement newBlock = new BlockStatement();
if (statement instanceof BlockStatement) {
newBlock.addStatements(filterStatements(((BlockStatement)statement).getStatements()));
}
else {
newBlock.addStatement(filterStatement(statement));
}
newBlock.addStatement(ReturnStatement.RETURN_NULL_OR_VOID);
node.setCode(newBlock);
}
if (node.getName().equals("main") && node.isStatic()) {
Parameter[] params = node.getParameters();
if (params.length == 1) {
Parameter param = params[0];
if (param.getType() == null || param.getType()==ClassHelper.OBJECT_TYPE) {
param.setType(ClassHelper.STRING_TYPE.makeArray());
}
}
}
statement = node.getCode();
if (statement!=null) statement.visit(new VerifierCodeVisitor(this));
}
public void visitField(FieldNode node) {
}
public void visitProperty(PropertyNode node) {
String name = node.getName();
FieldNode field = node.getField();
String getterName = "get" + capitalize(name);
String setterName = "set" + capitalize(name);
Statement getterBlock = node.getGetterBlock();
if (getterBlock == null) {
if (!node.isPrivate() && classNode.getGetterMethod(getterName) == null) {
getterBlock = createGetterBlock(node, field);
}
}
Statement setterBlock = node.getSetterBlock();
if (setterBlock == null) {
if (!node.isPrivate() && (node.getModifiers()&ACC_FINAL)==0 && classNode.getSetterMethod(setterName) == null) {
setterBlock = createSetterBlock(node, field);
}
}
if (getterBlock != null) {
MethodNode getter =
new MethodNode(getterName, node.getModifiers(), node.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock);
getter.setSynthetic(true);
classNode.addMethod(getter);
visitMethod(getter);
if (ClassHelper.boolean_TYPE==node.getType() || ClassHelper.Boolean_TYPE==node.getType()) {
String secondGetterName = "is" + capitalize(name);
MethodNode secondGetter =
new MethodNode(secondGetterName, node.getModifiers(), node.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock);
secondGetter.setSynthetic(true);
classNode.addMethod(secondGetter);
visitMethod(secondGetter);
}
}
if (setterBlock != null) {
Parameter[] setterParameterTypes = { new Parameter(node.getType(), "value")};
MethodNode setter =
new MethodNode(setterName, node.getModifiers(), ClassHelper.VOID_TYPE, setterParameterTypes, ClassNode.EMPTY_ARRAY, setterBlock);
setter.setSynthetic(true);
classNode.addMethod(setter);
visitMethod(setter);
}
}
// Implementation methods
//-------------------------------------------------------------------------
private interface DefaultArgsAction {
void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method);
}
/**
* Creates a new helper method for each combination of default parameter expressions
*/
protected void addDefaultParameterMethods(final ClassNode node) {
List methods = new ArrayList(node.getMethods());
addDefaultParameters(methods, new DefaultArgsAction(){
public void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method) {
MethodCallExpression expression = new MethodCallExpression(VariableExpression.THIS_EXPRESSION, method.getName(), arguments);
expression.setImplicitThis(true);
Statement code = null;
if (method.isVoidMethod()) {
code = new ExpressionStatement(expression);
} else {
code = new ReturnStatement(expression);
}
node.addMethod(method.getName(), method.getModifiers(), method.getReturnType(), newParams, method.getExceptions(), code);
}
});
}
protected void addDefaultParameterConstructors(final ClassNode node) {
List methods = new ArrayList(node.getDeclaredConstructors());
addDefaultParameters(methods, new DefaultArgsAction(){
public void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method) {
ConstructorNode ctor = (ConstructorNode) method;
ConstructorCallExpression expression = new ConstructorCallExpression(ClassNode.THIS, arguments);
Statement code = new ExpressionStatement(expression);
node.addConstructor(ctor.getModifiers(), newParams, ctor.getExceptions(), code);
}
});
}
/**
* Creates a new helper method for each combination of default parameter expressions
*/
protected void addDefaultParameters(List methods, DefaultArgsAction action) {
for (Iterator iter = methods.iterator(); iter.hasNext();) {
MethodNode method = (MethodNode) iter.next();
if (method.hasDefaultValue()) {
Parameter[] parameters = method.getParameters();
int counter = 0;
List paramValues = new ArrayList();
int size = parameters.length;
for (int i = size - 1; i >= 0; i--) {
Parameter parameter = parameters[i];
if (parameter != null && parameter.hasInitialExpression()) {
paramValues.add(new Integer(i));
paramValues.add(parameter.getInitialExpression());
counter++;
}
}
for (int j = 1; j <= counter; j++) {
Parameter[] newParams = new Parameter[parameters.length - j];
ArgumentListExpression arguments = new ArgumentListExpression();
int index = 0;
int k = 1;
for (int i = 0; i < parameters.length; i++) {
if (k > counter - j && parameters[i] != null && parameters[i].hasInitialExpression()) {
arguments.addExpression(parameters[i].getInitialExpression());
k++;
}
else if (parameters[i] != null && parameters[i].hasInitialExpression()) {
newParams[index++] = parameters[i];
arguments.addExpression(new VariableExpression(parameters[i].getName()));
k++;
}
else {
newParams[index++] = parameters[i];
arguments.addExpression(new VariableExpression(parameters[i].getName()));
}
}
if (parameters.length>0 && parameters[parameters.length-1].getType().isArray()) {
// vargs call... better expand the argument:
Expression exp = arguments.getExpression(parameters.length-1);
SpreadExpression se = new SpreadExpression(exp);
arguments.getExpressions().set(parameters.length-1, se);
}
action.call(arguments,newParams,method);
}
for (int i = 0; i < parameters.length; i++) {
// remove default expression
parameters[i].setInitialExpression(null);
}
}
}
}
protected void addClosureCode(InnerClassNode node) {
// add a new invoke
}
protected void addInitialization(ClassNode node) {
for (Iterator iter = node.getDeclaredConstructors().iterator(); iter.hasNext();) {
addInitialization(node, (ConstructorNode) iter.next());
}
}
protected void addInitialization(ClassNode node, ConstructorNode constructorNode) {
Statement firstStatement = constructorNode.getFirstStatement();
ConstructorCallExpression first = getFirstIfSpecialConstructorCall(firstStatement);
// in case of this(...) let the other constructor do the intit
if (first!=null && first.isThisCall()) return;
List statements = new ArrayList();
List staticStatements = new ArrayList();
for (Iterator iter = node.getFields().iterator(); iter.hasNext();) {
addFieldInitialization(statements, staticStatements, (FieldNode) iter.next());
}
statements.addAll(node.getObjectInitializerStatements());
if (!statements.isEmpty()) {
Statement code = constructorNode.getCode();
BlockStatement block = new BlockStatement();
List otherStatements = block.getStatements();
if (code instanceof BlockStatement) {
block = (BlockStatement) code;
otherStatements=block.getStatements();
}
else if (code != null) {
otherStatements.add(code);
}
if (!otherStatements.isEmpty()) {
if (first!=null) {
// it is super(..) since this(..) is already covered
otherStatements.remove(0);
statements.add(0, firstStatement);
}
statements.addAll(otherStatements);
}
constructorNode.setCode(new BlockStatement(statements, block.getVariableScope()));
}
if (!staticStatements.isEmpty()) {
node.addStaticInitializerStatements(staticStatements,true);
}
}
private ConstructorCallExpression getFirstIfSpecialConstructorCall(Statement code) {
if (code == null || !(code instanceof ExpressionStatement)) return null;
Expression expression = ((ExpressionStatement)code).getExpression();
if (!(expression instanceof ConstructorCallExpression)) return null;
ConstructorCallExpression cce = (ConstructorCallExpression) expression;
if (cce.isSpecialCall()) return cce;
return null;
}
protected void addFieldInitialization(
List list,
List staticList,
FieldNode fieldNode) {
Expression expression = fieldNode.getInitialExpression();
if (expression != null) {
ExpressionStatement statement =
new ExpressionStatement(
new BinaryExpression(
new FieldExpression(fieldNode),
Token.newSymbol(Types.EQUAL, fieldNode.getLineNumber(), fieldNode.getColumnNumber()),
expression));
if (fieldNode.isStatic()) {
staticList.add(statement);
}
else {
list.add(statement);
}
}
}
/**
* Capitalizes the start of the given bean property name
*/
public static String capitalize(String name) {
return name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
}
protected Statement createGetterBlock(PropertyNode propertyNode, FieldNode field) {
Expression expression = new FieldExpression(field);
return new ReturnStatement(expression);
}
protected Statement createSetterBlock(PropertyNode propertyNode, FieldNode field) {
Expression expression = new FieldExpression(field);
return new ExpressionStatement(
new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("value")));
}
/**
* Filters the given statements
*/
protected List filterStatements(List list) {
List answer = new ArrayList(list.size());
for (Iterator iter = list.iterator(); iter.hasNext();) {
answer.add(filterStatement((Statement) iter.next()));
}
return answer;
}
protected Statement filterStatement(Statement statement) {
if (statement instanceof ExpressionStatement) {
ExpressionStatement expStmt = (ExpressionStatement) statement;
Expression expression = expStmt.getExpression();
if (expression instanceof ClosureExpression) {
ClosureExpression closureExp = (ClosureExpression) expression;
if (!closureExp.isParameterSpecified()) {
return closureExp.getCode();
}
}
}
return statement;
}
public void visitGenericType(GenericsType genericsType) {
}
public static long getTimestamp (Class clazz) {
final Field[] fields = clazz.getFields();
for (int i = 0; i != fields.length; ++i ) {
if (Modifier.isStatic(fields[i].getModifiers())) {
final String name = fields[i].getName();
if (name.startsWith(__TIMESTAMP__)) {
try {
return Long.decode(name.substring(__TIMESTAMP__.length())).longValue();
}
catch (NumberFormatException e) {
return Long.MAX_VALUE;
}
}
}
}
return Long.MAX_VALUE;
}
protected void addCovariantMethods(ClassNode classNode) {
Map methodsToAdd = new HashMap();
List declaredMethods = new ArrayList(classNode.getMethods());
Map genericsSpec = new HashMap();
// remove staic methods from declaredMethods
for (Iterator methodsIterator = declaredMethods.iterator(); methodsIterator.hasNext();) {
MethodNode m = (MethodNode) methodsIterator.next();
if (m.isStatic()) methodsIterator.remove();
}
addCovariantMethods(classNode, declaredMethods, methodsToAdd, genericsSpec);
for (Iterator it = methodsToAdd.values().iterator(); it.hasNext();) {
MethodNode method = (MethodNode) it.next();
classNode.addMethod(method);
}
}
private void addCovariantMethods(ClassNode classNode, List declaredMethods, Map methodsToAdd, Map oldGenericsSpec) {
ClassNode sn = classNode.getUnresolvedSuperClass(false);
if (sn!=null && sn.redirect()!=ClassHelper.OBJECT_TYPE) {
Map genericsSpec = createGenericsSpec(sn,oldGenericsSpec);
for (Iterator it = declaredMethods.iterator(); it.hasNext();) {
MethodNode method = (MethodNode) it.next();
if (method.isStatic()) continue;
storeMissingCovariantMethods(sn,method,methodsToAdd,genericsSpec);
addCovariantMethods(sn,declaredMethods,methodsToAdd,genericsSpec);
}
}
ClassNode[] interfaces = classNode.getInterfaces();
for (int i=0; i<interfaces.length; i++) {
Map genericsSpec = createGenericsSpec(interfaces[i],oldGenericsSpec);
for (Iterator it = declaredMethods.iterator(); it.hasNext();) {
MethodNode method = (MethodNode) it.next();
if (method.isStatic()) continue;
storeMissingCovariantMethods(interfaces[i],method,methodsToAdd,genericsSpec);
addCovariantMethods(sn,declaredMethods,methodsToAdd,genericsSpec);
}
}
}
private MethodNode getCovariantImplementation(final MethodNode oldMethod, final MethodNode overridingMethod, Map genericsSpec) {
if (!oldMethod.getName().equals(overridingMethod.getName())) return null;
if (!equalParameters(overridingMethod,oldMethod,genericsSpec)) return null;
ClassNode mr = overridingMethod.getReturnType();
ClassNode omr = oldMethod.getReturnType();
if (mr.equals(omr)) return null;
ClassNode testmr = correctToGenericsSpec(genericsSpec,omr);
if (!mr.isDerivedFrom(testmr)) {
throw new RuntimeParserException(
"the return type is incompatible with "+
oldMethod.getTypeDescriptor()+
" in "+oldMethod.getDeclaringClass().getName(),
overridingMethod);
}
if ((oldMethod.getModifiers()&ACC_FINAL)!=0) {
throw new RuntimeParserException(
"cannot override final method "+
oldMethod.getTypeDescriptor()+
" in "+oldMethod.getDeclaringClass().getName(),
overridingMethod);
}
if (oldMethod.isStatic() != overridingMethod.isStatic()){
throw new RuntimeParserException(
"cannot override method "+
oldMethod.getTypeDescriptor()+
" in "+oldMethod.getDeclaringClass().getName()+
" with disparate static modifier",
overridingMethod);
}
MethodNode newMethod = new MethodNode(
oldMethod.getName(),
overridingMethod.getModifiers() | ACC_SYNTHETIC | ACC_BRIDGE,
oldMethod.getReturnType().getPlainNodeReference(),
cleanParameters(oldMethod.getParameters()),
oldMethod.getExceptions(),
null
);
List instructions = new ArrayList(1);
instructions.add (
new BytecodeInstruction() {
public void visit(MethodVisitor mv) {
BytecodeHelper helper = new BytecodeHelper(mv);
mv.visitVarInsn(ALOAD,0);
Parameter[] para = oldMethod.getParameters();
Parameter[] goal = overridingMethod.getParameters();
for (int i = 0; i < para.length; i++) {
helper.load(para[i].getType(), i+1);
if (!para[i].getType().equals(goal[i].getType())) {
helper.doCast(goal[i].getType());
}
}
mv.visitMethodInsn(
INVOKEVIRTUAL,
BytecodeHelper.getClassInternalName(classNode),
overridingMethod.getName(),
BytecodeHelper.getMethodDescriptor(overridingMethod.getReturnType(), overridingMethod.getParameters()));
helper.doReturn(oldMethod.getReturnType());
}
}
);
newMethod.setCode(new BytecodeSequence(instructions));
return newMethod;
}
private Parameter[] cleanParameters(Parameter[] parameters) {
Parameter[] params = new Parameter[parameters.length];
for (int i = 0; i < params.length; i++) {
params[i] = new Parameter(parameters[i].getType().getPlainNodeReference(),parameters[i].getName());
}
return params;
}
private void storeMissingCovariantMethods(ClassNode current, MethodNode method, Map methodsToAdd, Map genericsSpec) {
List methods = current.getMethods();
for (Iterator sit = methods.iterator(); sit.hasNext();) {
MethodNode toOverride = (MethodNode) sit.next();
MethodNode bridgeMethod = getCovariantImplementation(toOverride,method,genericsSpec);
if (bridgeMethod==null) continue;
methodsToAdd.put (bridgeMethod.getTypeDescriptor(),bridgeMethod);
return;
}
}
private ClassNode correctToGenericsSpec(Map genericsSpec, GenericsType type) {
ClassNode ret = null;
if (type.isPlaceholder()){
String name = type.getName();
ret = (ClassNode) genericsSpec.get(name);
}
if (ret==null) ret = type.getType();
return ret;
}
private ClassNode correctToGenericsSpec(Map genericsSpec, ClassNode type) {
if (type.isGenericsPlaceHolder()){
String name = type.getGenericsTypes()[0].getName();
type = (ClassNode) genericsSpec.get(name);
}
if (type==null) type = ClassHelper.OBJECT_TYPE;
return type;
}
private boolean equalParameters(MethodNode m1, MethodNode m2, Map genericsSpec) {
Parameter[] p1 = m1.getParameters();
Parameter[] p2 = m2.getParameters();
if (p1.length!=p2.length) return false;
for (int i = 0; i < p2.length; i++) {
ClassNode type = p2[i].getType();
type = correctToGenericsSpec(genericsSpec,type);
if (!p1[i].getType().equals(type)) return false;
}
return true;
}
private Map createGenericsSpec(ClassNode current, Map oldSpec) {
Map ret = new HashMap(oldSpec);
// ret contains the type specs, what we now need is the type spec for the
// current class. To get that we first apply the type parameters to the
// current class and then use the type names of the current class to reset
// the map. Example:
// class A<V,W,X>{}
// class B<T extends Number> extends A<T,Long,String> {}
// first we have: T->Number
// we apply it to A<T,Long,String> -> A<Number,Long,String>
// resulting in: V->Number,W->Long,X->String
GenericsType[] sgts = current.getGenericsTypes();
if (sgts!=null) {
ClassNode[] spec = new ClassNode[sgts.length];
for (int i = 0; i < spec.length; i++) {
spec[i]=correctToGenericsSpec(ret, sgts[i]);
}
GenericsType[] newGts = current.redirect().getGenericsTypes();
ret.clear();
for (int i = 0; i < spec.length; i++) {
ret.put(newGts[i].getName(), spec[i]);
}
}
return ret;
}
}