/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.transform.trait; import groovy.lang.MetaProperty; import java.util.List; import org.codehaus.groovy.ast.ClassCodeExpressionTransformer; import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.InnerClassNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.expr.ArgumentListExpression; import org.codehaus.groovy.ast.expr.BinaryExpression; import org.codehaus.groovy.ast.expr.ClassExpression; import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.ast.expr.MethodCallExpression; import org.codehaus.groovy.ast.expr.PropertyExpression; import org.codehaus.groovy.ast.expr.TupleExpression; import org.codehaus.groovy.ast.expr.VariableExpression; import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.syntax.Types; import static org.objectweb.asm.Opcodes.ACC_ABSTRACT; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_STATIC; import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC; /** * This transformer is used to transform calls to <code>SomeTrait.super.foo()</code> into the appropriate trait call. * * @author Cédric Champeau * @since 2.3.0 */ class SuperCallTraitTransformer extends ClassCodeExpressionTransformer { static final String UNRESOLVED_HELPER_CLASS = "UNRESOLVED_HELPER_CLASS"; private final SourceUnit unit; SuperCallTraitTransformer(final SourceUnit unit) { this.unit = unit; } @Override protected SourceUnit getSourceUnit() { return unit; } @Override public Expression transform(final Expression exp) { if (exp instanceof MethodCallExpression) { return transformMethodCallExpression((MethodCallExpression)exp); } if (exp instanceof BinaryExpression) { return transformBinaryExpression((BinaryExpression) exp); } return super.transform(exp); } private Expression transformBinaryExpression(final BinaryExpression exp) { Expression trn = super.transform(exp); if (trn instanceof BinaryExpression) { BinaryExpression bin = (BinaryExpression) trn; Expression leftExpression = bin.getLeftExpression(); if (bin.getOperation().getType() == Types.EQUAL && leftExpression instanceof PropertyExpression) { ClassNode traitReceiver = null; PropertyExpression leftPropertyExpression = (PropertyExpression) leftExpression; if (isTraitSuperPropertyExpression(leftPropertyExpression.getObjectExpression())) { PropertyExpression pexp = (PropertyExpression) leftPropertyExpression.getObjectExpression(); traitReceiver = pexp.getObjectExpression().getType(); } if (traitReceiver!=null) { // A.super.foo = ... TraitHelpersTuple helpers = Traits.findHelpers(traitReceiver); ClassNode helper = helpers.getHelper(); String setterName = MetaProperty.getSetterName(leftPropertyExpression.getPropertyAsString()); List<MethodNode> methods = helper.getMethods(setterName); for (MethodNode method : methods) { Parameter[] parameters = method.getParameters(); if (parameters.length==2 && parameters[0].getType().equals(traitReceiver)) { ArgumentListExpression args = new ArgumentListExpression( new VariableExpression("this"), transform(exp.getRightExpression()) ); MethodCallExpression setterCall = new MethodCallExpression( new ClassExpression(helper), setterName, args ); setterCall.setMethodTarget(method); setterCall.setImplicitThis(false); return setterCall; } } return bin; } } } return trn; } private Expression transformMethodCallExpression(final MethodCallExpression exp) { if (isTraitSuperPropertyExpression(exp.getObjectExpression())) { Expression objectExpression = exp.getObjectExpression(); ClassNode traitReceiver = ((PropertyExpression) objectExpression).getObjectExpression().getType(); if (traitReceiver != null) { // (SomeTrait.super).foo() --> SomeTrait$Helper.foo(this) ClassExpression receiver = new ClassExpression( getHelper(traitReceiver) ); ArgumentListExpression newArgs = new ArgumentListExpression(); Expression arguments = exp.getArguments(); newArgs.addExpression(new VariableExpression("this")); if (arguments instanceof TupleExpression) { List<Expression> expressions = ((TupleExpression) arguments).getExpressions(); for (Expression expression : expressions) { newArgs.addExpression(transform(expression)); } } else { newArgs.addExpression(transform(arguments)); } MethodCallExpression result = new MethodCallExpression( receiver, transform(exp.getMethod()), newArgs ); result.setImplicitThis(false); result.setSpreadSafe(exp.isSpreadSafe()); result.setSafe(exp.isSafe()); result.setSourcePosition(exp); return result; } } return super.transform(exp); } private ClassNode getHelper(final ClassNode traitReceiver) { if (helperClassNotCreatedYet(traitReceiver)) { // GROOVY-7909 A Helper class in same compilation unit may have not been created when referenced // Here create a symbol as a "placeholder" and it will be resolved later. ClassNode ret = new InnerClassNode( traitReceiver, Traits.helperClassName(traitReceiver), ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE, ClassNode.EMPTY_ARRAY, null ).getPlainNodeReference(); ret.setRedirect(null); traitReceiver.redirect().setNodeMetaData(UNRESOLVED_HELPER_CLASS, ret); return ret; } else { TraitHelpersTuple helpers = Traits.findHelpers(traitReceiver); return helpers.getHelper(); } } private boolean helperClassNotCreatedYet(final ClassNode traitReceiver) { return !traitReceiver.redirect().getInnerClasses().hasNext() && this.unit.getAST().getClasses().contains(traitReceiver.redirect()); } private boolean isTraitSuperPropertyExpression(Expression exp) { if (exp instanceof PropertyExpression) { PropertyExpression pexp = (PropertyExpression) exp; Expression objectExpression = pexp.getObjectExpression(); if (objectExpression instanceof ClassExpression) { ClassNode type = objectExpression.getType(); if (Traits.isTrait(type) && "super".equals(pexp.getPropertyAsString())) { return true; } } } return false; } }