/*
* Copyright 2003-2012 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.asm.sc;
import static org.codehaus.groovy.ast.ClassHelper.BigDecimal_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.BigInteger_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.CLASS_Type;
import static org.codehaus.groovy.ast.ClassHelper.GROOVY_OBJECT_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.Integer_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.Iterator_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.LIST_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.Long_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.MAP_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.Number_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.STRING_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.getWrapper;
import static org.codehaus.groovy.ast.ClassHelper.int_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.make;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.chooseBestMethod;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf;
import groovyjarjarasm.asm.Label;
import groovyjarjarasm.asm.MethodVisitor;
import groovyjarjarasm.asm.Opcodes;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.InnerClassNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.PropertyNode;
import org.codehaus.groovy.ast.Variable;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
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.VariableExpression;
import org.codehaus.groovy.ast.stmt.EmptyStatement;
import org.codehaus.groovy.classgen.BytecodeExpression;
import org.codehaus.groovy.classgen.asm.BytecodeHelper;
import org.codehaus.groovy.classgen.asm.CallSiteWriter;
import org.codehaus.groovy.classgen.asm.CompileStack;
import org.codehaus.groovy.classgen.asm.OperandStack;
import org.codehaus.groovy.classgen.asm.TypeChooser;
import org.codehaus.groovy.classgen.asm.WriterController;
import org.codehaus.groovy.runtime.MetaClassHelper;
import org.codehaus.groovy.syntax.SyntaxException;
import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
import org.codehaus.groovy.transform.stc.StaticTypesMarker;
/**
* A call site writer which replaces call site caching with static calls. This means that the generated code
* looks more like Java code than dynamic Groovy code. Best effort is made to use JVM instructions instead of
* calls to helper methods.
*
* @author Cedric Champeau
*/
public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes {
private static final MethodNode GROOVYOBJECT_GETPROPERTY_METHOD = GROOVY_OBJECT_TYPE.getMethod("getProperty", new Parameter[]{new Parameter(STRING_TYPE, "propertyName")});
private static final ClassNode COLLECTION_TYPE = make(Collection.class);
private static final MethodNode COLLECTION_SIZE_METHOD = COLLECTION_TYPE.getMethod("size", Parameter.EMPTY_ARRAY);
private static final MethodNode MAP_GET_METHOD = MAP_TYPE.getMethod("get", new Parameter[] { new Parameter(OBJECT_TYPE, "key")});
private WriterController controller;
public StaticTypesCallSiteWriter(final StaticTypesWriterController controller) {
super(controller);
this.controller = controller;
}
@Override
public void generateCallSiteArray() {
if (controller instanceof StaticTypesWriterController) {
((StaticTypesWriterController)controller).getRegularCallSiteWriter().generateCallSiteArray();
} else {
super.generateCallSiteArray();
}
}
@Override
public void makeCallSite(final Expression receiver, final String message, final Expression arguments, final boolean safe, final boolean implicitThis, final boolean callCurrent, final boolean callStatic) {
}
@Override
public void makeGetPropertySite(Expression receiver, final String methodName, final boolean safe, final boolean implicitThis) {
TypeChooser typeChooser = controller.getTypeChooser();
ClassNode classNode = controller.getClassNode();
ClassNode receiverType = typeChooser.resolveType(receiver, classNode);
Object type = receiver.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
if (type==null && receiver instanceof VariableExpression) {
Variable variable = ((VariableExpression) receiver).getAccessedVariable();
if (variable instanceof Expression) {
type = ((Expression) variable).getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
}
}
if (type!=null) {
// in case a "flow type" is found, it is preferred to use it instead of
// the declaration type
receiverType = (ClassNode) type;
}
boolean isClassReceiver = false;
if (receiverType.equals(CLASS_Type)
&& receiverType.getGenericsTypes()!=null
&& !receiverType.getGenericsTypes()[0].isPlaceholder()) {
isClassReceiver = true;
receiverType = receiverType.getGenericsTypes()[0].getType();
}
MethodVisitor mv = controller.getMethodVisitor();
if (receiverType.isArray() && methodName.equals("length")) {
receiver.visit(controller.getAcg());
ClassNode arrayGetReturnType = typeChooser.resolveType(receiver, classNode);
controller.getOperandStack().doGroovyCast(arrayGetReturnType);
mv.visitInsn(ARRAYLENGTH);
controller.getOperandStack().replace(int_TYPE);
return;
} else if (
(receiverType.implementsInterface(COLLECTION_TYPE)
|| COLLECTION_TYPE.equals(receiverType)) && ("size".equals(methodName) || "length".equals(methodName))) {
MethodCallExpression expr = new MethodCallExpression(
receiver,
"size",
ArgumentListExpression.EMPTY_ARGUMENTS
);
expr.setMethodTarget(COLLECTION_SIZE_METHOD);
expr.setImplicitThis(implicitThis);
expr.setSafe(safe);
expr.visit(controller.getAcg());
return;
}
if (makeGetPropertyWithGetter(receiver, receiverType, methodName, safe, implicitThis)) return;
if (makeGetField(receiver, receiverType, methodName, implicitThis, samePackages(receiverType.getPackageName(), classNode.getPackageName()))) return;
if (receiverType.isEnum()) {
mv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(receiverType), methodName, BytecodeHelper.getTypeDescription(receiverType));
controller.getOperandStack().push(receiverType);
return;
}
if (receiver instanceof ClassExpression) {
if (makeGetField(receiver, receiver.getType(), methodName, implicitThis, samePackages(receiver.getType().getPackageName(), classNode.getPackageName()))) return;
if (makeGetPropertyWithGetter(receiver, receiver.getType(), methodName, safe, implicitThis)) return;
}
if (isClassReceiver) {
// we are probably looking for a property of the class
if (makeGetPropertyWithGetter(receiver, CLASS_Type, methodName, safe, implicitThis)) return;
if (makeGetField(receiver, CLASS_Type, methodName, false, true)) return;
}
if (makeGetPrivateFieldWithBridgeMethod(receiver, receiverType, methodName, safe, implicitThis)) return;
// GROOVY-5580, it is still possible that we're calling a superinterface property
String getterName = "get" + MetaClassHelper.capitalize(methodName);
if (receiverType.isInterface()) {
Set<ClassNode> allInterfaces = receiverType.getAllInterfaces();
MethodNode getterMethod = null;
for (ClassNode anInterface : allInterfaces) {
getterMethod = anInterface.getGetterMethod(getterName);
if (getterMethod!=null) break;
}
// GROOVY-5585
if (getterMethod==null) {
getterMethod = OBJECT_TYPE.getGetterMethod(getterName);
}
if (getterMethod!=null) {
MethodCallExpression call = new MethodCallExpression(
receiver,
getterName,
ArgumentListExpression.EMPTY_ARGUMENTS
);
call.setMethodTarget(getterMethod);
call.setImplicitThis(false);
call.setSourcePosition(receiver);
call.visit(controller.getAcg());
return;
}
}
// GROOVY-5568, we would be facing a DGM call, but instead of foo.getText(), have foo.text
List<MethodNode> methods = findDGMMethodsByNameAndArguments(receiverType, getterName, ClassNode.EMPTY_ARRAY);
if (!methods.isEmpty()) {
List<MethodNode> methodNodes = chooseBestMethod(receiverType, methods, ClassNode.EMPTY_ARRAY);
if (methodNodes.size()==1) {
MethodNode getter = methodNodes.get(0);
MethodCallExpression call = new MethodCallExpression(
receiver,
getterName,
ArgumentListExpression.EMPTY_ARGUMENTS
);
call.setMethodTarget(getter);
call.setImplicitThis(false);
call.setSourcePosition(receiver);
call.visit(controller.getAcg());
return;
}
}
boolean isStaticProperty = receiver instanceof ClassExpression
&& (receiverType.isDerivedFrom(receiver.getType()) || receiverType.implementsInterface(receiver.getType()));
if (!isStaticProperty) {
if (receiverType.implementsInterface(MAP_TYPE) || MAP_TYPE.equals(receiverType)) {
// for maps, replace map.foo with map.get('foo')
writeMapDotProperty(receiver, methodName, mv);
return;
}
if (receiverType.implementsInterface(LIST_TYPE) || LIST_TYPE.equals(receiverType)) {
writeListDotProperty(receiver, methodName, mv);
return;
}
}
controller.getSourceUnit().addError(
new SyntaxException("Access to "+
(receiver instanceof ClassExpression?receiver.getType():receiverType).toString(false)
+"#"+methodName+" is forbidden", receiver.getLineNumber(), receiver.getColumnNumber(), receiver.getLastLineNumber(), receiver.getLastColumnNumber())
);
controller.getMethodVisitor().visitInsn(ACONST_NULL);
controller.getOperandStack().push(OBJECT_TYPE);
}
private void writeMapDotProperty(final Expression receiver, final String methodName, final MethodVisitor mv) {
receiver.visit(controller.getAcg()); // load receiver
mv.visitLdcInsn(methodName); // load property name
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
controller.getOperandStack().replace(OBJECT_TYPE);
}
private void writeListDotProperty(final Expression receiver, final String methodName, final MethodVisitor mv) {
ClassNode componentType = (ClassNode) receiver.getNodeMetaData(StaticCompilationMetadataKeys.COMPONENT_TYPE);
if (componentType==null) {
componentType = OBJECT_TYPE;
}
// for lists, replace list.foo with:
// def result = new ArrayList(list.size())
// for (e in list) { result.add (e.foo) }
// result
CompileStack compileStack = controller.getCompileStack();
Variable tmpList = new VariableExpression("tmpList", make(ArrayList.class));
int var = compileStack.defineTemporaryVariable(tmpList, false);
Variable iterator = new VariableExpression("iterator", Iterator_TYPE);
int it = compileStack.defineTemporaryVariable(iterator, false);
Variable nextVar = new VariableExpression("next", componentType);
final int next = compileStack.defineTemporaryVariable(nextVar, false);
mv.visitTypeInsn(NEW, "java/util/ArrayList");
mv.visitInsn(DUP);
receiver.visit(controller.getAcg());
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "size", "()I");
controller.getOperandStack().remove(1);
mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>", "(I)V");
mv.visitVarInsn(ASTORE, var);
Label l1 = new Label();
mv.visitLabel(l1);
receiver.visit(controller.getAcg());
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "iterator", "()Ljava/util/Iterator;");
controller.getOperandStack().remove(1);
mv.visitVarInsn(ASTORE, it);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitVarInsn(ALOAD, it);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z");
Label l3 = new Label();
mv.visitJumpInsn(IFEQ, l3);
mv.visitVarInsn(ALOAD, it);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(componentType));
mv.visitVarInsn(ASTORE, next);
Label l4 = new Label();
mv.visitLabel(l4);
mv.visitVarInsn(ALOAD, var);
final ClassNode finalComponentType = componentType;
PropertyExpression pexp = new PropertyExpression(new BytecodeExpression() {
@Override
public void visit(final MethodVisitor mv) {
mv.visitVarInsn(ALOAD, next);
}
@Override
public ClassNode getType() {
return finalComponentType;
}
}, methodName);
pexp.visit(controller.getAcg());
controller.getOperandStack().box();
controller.getOperandStack().remove(1);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z");
mv.visitInsn(POP);
Label l5 = new Label();
mv.visitLabel(l5);
mv.visitJumpInsn(GOTO, l2);
mv.visitLabel(l3);
mv.visitVarInsn(ALOAD, var);
controller.getOperandStack().push(make(ArrayList.class));
controller.getCompileStack().removeVar(next);
controller.getCompileStack().removeVar(it);
controller.getCompileStack().removeVar(var);
}
private boolean makeGetPrivateFieldWithBridgeMethod(final Expression receiver, final ClassNode receiverType, final String fieldName, final boolean safe, final boolean implicitThis) {
FieldNode field = receiverType.getField(fieldName);
ClassNode classNode = controller.getClassNode();
if (field!=null && Modifier.isPrivate(field.getModifiers())
&& (StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(receiverType, classNode) || StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(classNode,receiverType))
&& !receiverType.equals(classNode)) {
Map<String, MethodNode> accessors = (Map<String, MethodNode>) receiverType.redirect().getNodeMetaData(StaticCompilationMetadataKeys.PRIVATE_FIELDS_ACCESSORS);
if (accessors!=null) {
MethodNode methodNode = accessors.get(fieldName);
if (methodNode!=null) {
MethodCallExpression mce = new MethodCallExpression(receiver, methodNode.getName(),
new ArgumentListExpression(field.isStatic()?new ConstantExpression(null):receiver));
mce.setMethodTarget(methodNode);
mce.setSafe(safe);
mce.setImplicitThis(implicitThis);
mce.visit(controller.getAcg());
return true;
}
}
}
return false;
}
@Override
public void makeGroovyObjectGetPropertySite(final Expression receiver, final String methodName, final boolean safe, final boolean implicitThis) {
TypeChooser typeChooser = controller.getTypeChooser();
ClassNode classNode = controller.getClassNode();
ClassNode receiverType = typeChooser.resolveType(receiver, classNode);
if (receiver instanceof VariableExpression && ((VariableExpression) receiver).isThisExpression() && !controller.isInClosure()) {
receiverType = classNode;
}
String property = methodName;
if (classNode.getNodeMetaData(StaticCompilationMetadataKeys.WITH_CLOSURE)!=null && "owner".equals(property)) {
// the current class node is a closure used in a "with"
property = "delegate";
}
if (makeGetPropertyWithGetter(receiver, receiverType, property, safe, implicitThis)) return;
if (makeGetField(receiver, receiverType, property, implicitThis, samePackages(receiverType.getPackageName(), classNode.getPackageName()))) return;
MethodCallExpression call = new MethodCallExpression(
receiver,
"getProperty",
new ArgumentListExpression(new ConstantExpression(property))
);
call.setImplicitThis(implicitThis);
call.setSafe(safe);
call.setMethodTarget(GROOVYOBJECT_GETPROPERTY_METHOD);
call.visit(controller.getAcg());
return;
}
@Override
public void makeCallSiteArrayInitializer() {
}
private boolean makeGetPropertyWithGetter(final Expression receiver, final ClassNode receiverType, final String methodName, final boolean safe, final boolean implicitThis) {
// does a getter exists ?
String getterName = "get" + MetaClassHelper.capitalize(methodName);
MethodNode getterNode = receiverType.getGetterMethod(getterName);
if (getterNode==null) {
getterName = "is" + MetaClassHelper.capitalize(methodName);
getterNode = receiverType.getGetterMethod(getterName);
}
if (getterNode!=null && receiver instanceof ClassExpression && !CLASS_Type.equals(receiverType) && !getterNode.isStatic()) {
return false;
}
// GROOVY-5561: if two files are compiled in the same source unit
// and that one references the other, the getters for properties have not been
// generated by the compiler yet (generated by the Verifier)
PropertyNode propertyNode = receiverType.getProperty(methodName);
if (propertyNode!=null) {
// it is possible to use a getter
String prefix = "get";
if (boolean_TYPE.equals(propertyNode.getOriginType())) {
prefix = "is";
}
getterName = prefix + MetaClassHelper.capitalize(methodName);
getterNode = new MethodNode(
getterName,
ACC_PUBLIC,
propertyNode.getOriginType(),
Parameter.EMPTY_ARRAY,
ClassNode.EMPTY_ARRAY,
EmptyStatement.INSTANCE);
getterNode.setDeclaringClass(receiverType);
if (propertyNode.isStatic()) getterNode.setModifiers(ACC_PUBLIC + ACC_STATIC);
}
if (getterNode!=null) {
MethodCallExpression call = new MethodCallExpression(
receiver,
getterName,
ArgumentListExpression.EMPTY_ARGUMENTS
);
call.setSourcePosition(receiver);
call.setMethodTarget(getterNode);
call.setImplicitThis(implicitThis);
call.setSafe(safe);
call.visit(controller.getAcg());
return true;
}
// go upper level
ClassNode superClass = receiverType.getSuperClass();
if (superClass !=null) {
return makeGetPropertyWithGetter(receiver, superClass, methodName, safe, implicitThis);
}
return false;
}
boolean makeGetField(final Expression receiver, final ClassNode receiverType, final String fieldName, final boolean implicitThis, final boolean samePackage) {
FieldNode field = receiverType.getField(fieldName);
// direct access is allowed if we are in the same class as the declaring class
// or we are in an inner class
if (field !=null
&& isDirectAccessAllowed(field, controller.getClassNode(), samePackage)) {
CompileStack compileStack = controller.getCompileStack();
MethodVisitor mv = controller.getMethodVisitor();
if (field.isStatic()) {
mv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(field.getOwner()), fieldName, BytecodeHelper.getTypeDescription(field.getOriginType()));
controller.getOperandStack().push(field.getOriginType());
} else {
if (implicitThis) {
compileStack.pushImplicitThis(implicitThis);
}
receiver.visit(controller.getAcg());
if (implicitThis) compileStack.popImplicitThis();
if (!controller.getOperandStack().getTopOperand().isDerivedFrom(field.getOwner())) {
mv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(field.getOwner()));
}
mv.visitFieldInsn(GETFIELD, BytecodeHelper.getClassInternalName(field.getOwner()), fieldName, BytecodeHelper.getTypeDescription(field.getOriginType()));
}
controller.getOperandStack().replace(field.getOriginType());
return true;
}
ClassNode superClass = receiverType.getSuperClass();
if (superClass !=null) {
return makeGetField(receiver, superClass, fieldName, implicitThis, false);
}
return false;
}
private static boolean samePackages(final String pkg1, final String pkg2) {
return (
(pkg1 ==null && pkg2 ==null)
|| pkg1 !=null && pkg1.equals(pkg2)
);
}
private static boolean isDirectAccessAllowed(FieldNode a, ClassNode receiver, boolean isSamePackage) {
ClassNode declaringClass = a.getDeclaringClass().redirect();
ClassNode receiverType = receiver.redirect();
// first, direct access from within the class or inner class nodes
if (declaringClass.equals(receiverType)) return true;
if (receiverType instanceof InnerClassNode) {
while (receiverType!=null && receiverType instanceof InnerClassNode) {
if (declaringClass.equals(receiverType)) return true;
receiverType = receiverType.getOuterClass();
}
}
// no getter
return a.isPublic() || (a.isProtected() && isSamePackage);
}
@Override
public void makeSiteEntry() {
}
@Override
public void prepareCallSite(final String message) {
}
@Override
public void makeSingleArgumentCall(final Expression receiver, final String message, final Expression arguments) {
TypeChooser typeChooser = controller.getTypeChooser();
ClassNode classNode = controller.getClassNode();
ClassNode rType = typeChooser.resolveType(receiver, classNode);
ClassNode aType = typeChooser.resolveType(arguments, classNode);
if (getWrapper(rType).isDerivedFrom(Number_TYPE)
&& getWrapper(aType).isDerivedFrom(Number_TYPE)) {
if ("plus".equals(message) || "minus".equals(message) || "multiply".equals(message) || "div".equals(message)) {
writeNumberNumberCall(receiver, message, arguments);
return;
} else if ("power".equals(message)) {
writePowerCall(receiver, arguments, rType, aType);
return;
}
} else if (STRING_TYPE.equals(rType) && "plus".equals(message)) {
writeStringPlusCall(receiver, message, arguments);
return;
} else if (rType.isArray() && "getAt".equals(message)) {
writeArrayGet(receiver, arguments, rType, aType);
return;
}
// check if a getAt method can be found on the receiver
ClassNode current = rType;
MethodNode getAtNode = null;
while (current!=null && getAtNode==null) {
getAtNode = current.getMethod("getAt", new Parameter[]{new Parameter(aType, "index")});
current = current.getSuperClass();
}
if (getAtNode!=null) {
MethodCallExpression call = new MethodCallExpression(
receiver,
"getAt",
arguments
);
call.setSourcePosition(arguments);
call.setImplicitThis(false);
call.setMethodTarget(getAtNode);
call.visit(controller.getAcg());
return;
}
// make sure Map#getAt() and List#getAt handled with the bracket syntax are properly compiled
ClassNode[] args = {aType};
boolean acceptAnyMethod =
MAP_TYPE.equals(rType) || rType.implementsInterface(MAP_TYPE)
|| LIST_TYPE.equals(rType) || rType.implementsInterface(LIST_TYPE);
List<MethodNode> nodes = StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments(rType, message, args);
nodes = StaticTypeCheckingSupport.chooseBestMethod(rType, nodes, args);
if (nodes.size()==1 || nodes.size()>1 && acceptAnyMethod) {
MethodNode methodNode = nodes.get(0);
MethodCallExpression call = new MethodCallExpression(
receiver,
message,
arguments
);
call.setSourcePosition(arguments);
call.setImplicitThis(false);
call.setMethodTarget(methodNode);
call.visit(controller.getAcg());
return;
}
if (implementsInterfaceOrIsSubclassOf(rType, MAP_TYPE)) {
// fallback to Map#get
MethodCallExpression call = new MethodCallExpression(
receiver,
"get",
arguments
);
call.setMethodTarget(MAP_GET_METHOD);
call.setSourcePosition(arguments);
call.setImplicitThis(false);
call.visit(controller.getAcg());
return;
}
// todo: more cases
throw new GroovyBugError(
"At line "+receiver.getLineNumber() + " column " + receiver.getColumnNumber() + "\n" +
"On receiver: "+receiver.getText() + " with message: "+message+" and arguments: "+arguments.getText()+"\n"+
"This method should not have been called. Please try to create a simple example reproducing this error and file" +
"a bug report at http://jira.codehaus.org/browse/GROOVY");
}
private void writeArrayGet(final Expression receiver, final Expression arguments, final ClassNode rType, final ClassNode aType) {
OperandStack operandStack = controller.getOperandStack();
int m1 = operandStack.getStackLength();
// visit receiver
receiver.visit(controller.getAcg());
// visit arguments as array index
arguments.visit(controller.getAcg());
operandStack.doGroovyCast(int_TYPE);
int m2 = operandStack.getStackLength();
// array access
controller.getMethodVisitor().visitInsn(AALOAD);
operandStack.replace(rType.getComponentType(), m2-m1);
}
private void writePowerCall(Expression receiver, Expression arguments, final ClassNode rType, ClassNode aType) {
OperandStack operandStack = controller.getOperandStack();
int m1 = operandStack.getStackLength();
//slow Path
prepareSiteAndReceiver(receiver, "power", false, controller.getCompileStack().isLHS());
visitBoxedArgument(arguments);
int m2 = operandStack.getStackLength();
MethodVisitor mv = controller.getMethodVisitor();
if (BigDecimal_TYPE.equals(rType) && Integer_TYPE.equals(getWrapper(aType))) {
mv.visitMethodInsn(INVOKESTATIC,
"org/codehaus/groovy/runtime/DefaultGroovyMethods",
"power",
"(Ljava/math/BigDecimal;Ljava/lang/Integer;)Ljava/lang/Number;");
} else if (BigInteger_TYPE.equals(rType) && Integer_TYPE.equals(getWrapper(aType))) {
mv.visitMethodInsn(INVOKESTATIC,
"org/codehaus/groovy/runtime/DefaultGroovyMethods",
"power",
"(Ljava/math/BigInteger;Ljava/lang/Integer;)Ljava/lang/Number;");
} else if (Long_TYPE.equals(getWrapper(rType)) && Integer_TYPE.equals(getWrapper(aType))) {
mv.visitMethodInsn(INVOKESTATIC,
"org/codehaus/groovy/runtime/DefaultGroovyMethods",
"power",
"(Ljava/lang/Long;Ljava/lang/Integer;)Ljava/lang/Number;");
} else if (Integer_TYPE.equals(getWrapper(rType)) && Integer_TYPE.equals(getWrapper(aType))) {
mv.visitMethodInsn(INVOKESTATIC,
"org/codehaus/groovy/runtime/DefaultGroovyMethods",
"power",
"(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Number;");
} else {
mv.visitMethodInsn(INVOKESTATIC,
"org/codehaus/groovy/runtime/DefaultGroovyMethods",
"power",
"(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
}
controller.getOperandStack().replace(Number_TYPE, m2 - m1);
}
private void writeStringPlusCall(final Expression receiver, final String message, final Expression arguments) {
// todo: performance would be better if we created a StringBuilder
OperandStack operandStack = controller.getOperandStack();
int m1 = operandStack.getStackLength();
//slow Path
prepareSiteAndReceiver(receiver, message, false, controller.getCompileStack().isLHS());
visitBoxedArgument(arguments);
int m2 = operandStack.getStackLength();
MethodVisitor mv = controller.getMethodVisitor();
mv.visitMethodInsn(INVOKESTATIC,
"org/codehaus/groovy/runtime/DefaultGroovyMethods",
"plus",
"(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;");
controller.getOperandStack().replace(STRING_TYPE, m2-m1);
}
private void writeNumberNumberCall(final Expression receiver, final String message, final Expression arguments) {
OperandStack operandStack = controller.getOperandStack();
int m1 = operandStack.getStackLength();
//slow Path
prepareSiteAndReceiver(receiver, message, false, controller.getCompileStack().isLHS());
controller.getOperandStack().doGroovyCast(Number_TYPE);
visitBoxedArgument(arguments);
controller.getOperandStack().doGroovyCast(Number_TYPE);
int m2 = operandStack.getStackLength();
MethodVisitor mv = controller.getMethodVisitor();
mv.visitMethodInsn(INVOKESTATIC,
"org/codehaus/groovy/runtime/dgmimpl/NumberNumber" + MetaClassHelper.capitalize(message),
message,
"(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
controller.getOperandStack().replace(Number_TYPE, m2 - m1);
}
}