/* * Copyright 2009-2010 MBTE Sweden AB. * * 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.mbte.groovypp.compiler; import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.expr.ArgumentListExpression; import org.codehaus.groovy.ast.expr.MethodCallExpression; import org.codehaus.groovy.ast.expr.VariableExpression; import org.codehaus.groovy.ast.stmt.ExpressionStatement; import org.codehaus.groovy.ast.stmt.Statement; import java.util.ArrayList; import java.util.List; public class ClosureMethodNode extends MethodNode { private ClosureMethodNode owner; private List<Dependent> dependentMethods; public ClosureMethodNode(String name, int modifiers, ClassNode returnType, Parameter[] parameters, Statement code) { super(name, modifiers, returnType, parameters, ClassNode.EMPTY_ARRAY, code); } public ClosureMethodNode getOwner() { return owner; } public void setOwner(ClosureMethodNode owner) { this.owner = owner; } public List<Dependent> getDependentMethods() { return dependentMethods; } public void setDependentMethods(List<Dependent> dependentMethods) { this.dependentMethods = dependentMethods; } public void createDependentMethods(final ClassNode newType) { if (!hasDefaultValue()) return; ArrayList<Dependent> results = new ArrayList<Dependent> (); Parameter[] parameters = getParameters(); ArrayList<Integer> mutable = new ArrayList<Integer> (); for (int i = parameters.length-1; i >= 0; i--) { if (parameters [i].hasInitialExpression()) { mutable.add(i); } } for (int k = 0; k != mutable.size(); k++) { Parameter[] newParams = new Parameter[parameters.length - k - 1]; ArgumentListExpression args = new ArgumentListExpression(); for (int i = 0, l = 0; i != parameters.length; i++) { int mk = mutable.get(k); if (!parameters [i].hasInitialExpression() || i < mk) { newParams [l++] = parameters [i]; args.addExpression(new VariableExpression(parameters[i])); } else { args.addExpression(parameters[i].getInitialExpression()); } } MethodCallExpression call = new MethodCallExpression(VariableExpression.THIS_EXPRESSION, getName(), args); call.setSourcePosition(this); ExpressionStatement code = new ExpressionStatement(call); code.setSourcePosition(call); Dependent _doCallMethodDef = new Dependent( this, getName(), ACC_PUBLIC, ClassHelper.OBJECT_TYPE, newParams, code ){ }; newType.addMethod(_doCallMethodDef); results.add(_doCallMethodDef); } setDependentMethods(results); for (Parameter parameter : parameters) { parameter.setInitialExpression(null); } } public boolean checkOverride(MethodNode baseMethod, ClassNode baseType) { class Mutation { final Parameter p; final ClassNode t; public Mutation(ClassNode t, Parameter p) { this.t = t; this.p = p; } void mutate () { p.setType(t); } } List<Mutation> mutations = null; Parameter[] baseMethodParameters = baseMethod.getParameters(); Parameter[] closureParameters = getParameters(); if (closureParameters.length == baseMethodParameters.length) { for (int i = 0; i < closureParameters.length; i++) { Parameter closureParameter = closureParameters[i]; Parameter missingMethodParameter = baseMethodParameters[i]; ClassNode parameterType = missingMethodParameter.getType(); if (!parameterType.redirect().equals(closureParameter.getType().redirect())) { parameterType = TypeUtil.getSubstitutedType(parameterType, baseType.redirect(), baseType); if (parameterType.redirect().equals(closureParameter.getType().redirect()) || closureParameter.isDynamicTyped()) { parameterType = TypeUtil.withGenericTypes(parameterType, (GenericsType[]) null); if (mutations == null) mutations = new ArrayList<Mutation>(); mutations.add(new Mutation(parameterType, closureParameter)); } else { return false; } } } if (mutations != null) for (Mutation mutation : mutations) { mutation.mutate(); } ClassNode returnType = TypeUtil.getSubstitutedType(baseMethod.getReturnType(), baseType.redirect(), baseType); setReturnType(returnType); return true; } if (dependentMethods != null) { for (Dependent dependentMethod : dependentMethods) { if(dependentMethod.checkOverride(baseMethod, baseType)) return true; } } return false; } public static class Dependent extends ClosureMethodNode { private ClosureMethodNode master; public Dependent(ClosureMethodNode master, String name, int modifiers, ClassNode returnType, Parameter[] parameters, Statement code) { super(name, modifiers, returnType, parameters, code); this.master = master; } public ClosureMethodNode getMaster() { return master; } public String getTypeDescriptor() { StringBuilder buf = new StringBuilder(master.getName().length()+20); buf.append(master.getReturnType().getName()); buf.append(' '); buf.append(master.getName()); buf.append("("); for (int i = 0; i < getParameters().length; i++) { if (i > 0) { buf.append(", "); } Parameter param = getParameters()[i]; buf.append(param.getType().getName()); } buf.append(")"); return buf.toString(); } @Override public ClassNode getReturnType() { return master.getReturnType(); } @Override public void setReturnType(ClassNode type) { if (master != null) master.setReturnType(type); else super.setReturnType(type); } } }