/* * 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.transformers; import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.expr.*; import org.codehaus.groovy.ast.stmt.ExpressionStatement; import org.mbte.groovypp.compiler.ClassNodeCache; import org.mbte.groovypp.compiler.CompilerTransformer; import org.mbte.groovypp.compiler.TypeUtil; import org.mbte.groovypp.compiler.bytecode.BytecodeExpr; import org.objectweb.asm.MethodVisitor; import java.util.ArrayList; import java.util.List; public class MethodPointerExpressionTransformer extends ExprTransformer<MethodPointerExpression> { public Expression transform(final MethodPointerExpression exp, final CompilerTransformer compiler) { final String methodName; if (!(exp.getMethodName() instanceof ConstantExpression) || !(((ConstantExpression) exp.getMethodName()).getValue() instanceof String)) { compiler.addError("Non-static method name", exp); return null; } else { methodName = (String) ((ConstantExpression) exp.getMethodName()).getValue(); } final ClassNode type; final BytecodeExpr object; if (exp.getExpression() instanceof ClassExpression) { object = null; type = TypeUtil.wrapSafely(exp.getExpression().getType()); } else { object = (BytecodeExpr) compiler.transform(exp.getExpression()); type = object.getType(); } final Object methods = ClassNodeCache.getMethods(type, methodName); // todo: dynamic dispatch if (methods == null) { compiler.addError("Cannot find method '" + methodName + "'", exp); } else if (!(methods instanceof MethodNode)) { compiler.addError("Multiple methods '" + methodName + "' referenced. Cannot take the pointer", exp); } final MethodNode method = (MethodNode) methods; final Parameter[] methodParameters = method.getParameters(); final Parameter[] closureParameters = new Parameter[methodParameters.length]; final GenericsType[] generics = method.getGenericsTypes(); final ClassNode[] erasureBindings = generics == null ? null : new ClassNode[generics.length]; // All nulls. for (int i = 0; i < closureParameters.length; i++) { ClassNode t = methodParameters[i].getType(); if (erasureBindings != null) t = TypeUtil.getSubstitutedType(t, method, erasureBindings); t = TypeUtil.getSubstitutedType(t, method.getDeclaringClass(), type); closureParameters[i] = new Parameter(t, methodParameters[i].getName()); } final List<Expression> args = new ArrayList<Expression>(); for (int i = 0; i < closureParameters.length; i++) { args.add(new VariableExpression(closureParameters[i].getName())); } return new BytecodeExpr(exp, ClassHelper.CLOSURE_TYPE) { protected void compile(MethodVisitor mv) { final VariableScope scope = new VariableScope(compiler.compileStack.getScope()); final Expression receiver; if (object != null) { object.visit(mv); final String receiverName = compiler.context.getNextTempVarName(); final VariableExpression var = new VariableExpression(receiverName, type); var.setClosureSharedVariable(true); compiler.compileStack.defineVariable(var, true); scope.putReferencedLocalVariable(var); receiver = var; } else { receiver = exp.getExpression(); } final ExpressionStatement statement = new ExpressionStatement( new MethodCallExpression(receiver, methodName, new ArgumentListExpression(args))); final ClosureExpression closure = new ClosureExpression(closureParameters.length == 0 ? null : closureParameters, statement); closure.setVariableScope(scope); closure.setSourcePosition(exp); ((BytecodeExpr) compiler.transform(closure)).visit(mv); } }; } }