/*
* 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 groovy.lang.TypePolicy;
import org.codehaus.groovy.ast.*;
import org.codehaus.groovy.ast.stmt.*;
import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.classgen.BytecodeHelper;
import org.codehaus.groovy.syntax.Token;
import org.codehaus.groovy.syntax.Types;
import org.mbte.groovypp.compiler.*;
import org.mbte.groovypp.compiler.bytecode.BytecodeExpr;
import org.mbte.groovypp.compiler.bytecode.ResolvedMethodBytecodeExpr;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
public class ConstructorCallExpressionTransformer extends ExprTransformer<ConstructorCallExpression> {
private static final ClassNode[] MAP_ARGS = new ClassNode[]{TypeUtil.LINKED_HASH_MAP_TYPE};
public Expression transform(ConstructorCallExpression exp, final CompilerTransformer compiler) {
if (exp.isSuperCall() || exp.isThisCall())
return transformSpecial (exp, compiler);
MethodNode constructor;
ClassNode type = exp.getType();
if((type.getModifiers() & Opcodes.ACC_ABSTRACT) != 0) {
compiler.addError("Can't instantiate abstract type " + type.getName(), exp);
return null;
}
if (type instanceof InnerClassNode) {
InnerClassNode innerClassNode = (InnerClassNode) type;
if (innerClassNode.isAnonymous()) {
if(!improveWhatInnerClassVisitorDid(innerClassNode, exp, compiler))
return null;
}
}
rewriteThis0 (exp, compiler);
if (exp.getArguments() instanceof TupleExpression && ((TupleExpression)exp.getArguments()).getExpressions().size() == 1 && ((TupleExpression)exp.getArguments()).getExpressions().get(0) instanceof MapExpression) {
MapExpression me = (MapExpression) ((TupleExpression)exp.getArguments()).getExpressions().get(0);
constructor = compiler.findConstructor(type, MAP_ARGS, null);
if (constructor == null) {
final ArrayList<BytecodeExpr> propSetters = new ArrayList<BytecodeExpr> ();
constructor = compiler.findConstructor(type, ClassNode.EMPTY_ARRAY, null);
if (constructor != null) {
for (MapEntryExpression mee : me.getMapEntryExpressions()) {
BytecodeExpr obj = new BytecodeExpr(mee, type) {
protected void compile(MethodVisitor mv) {
mv.visitInsn(DUP);
}
};
propSetters.add(
(BytecodeExpr) compiler.transform(
new BinaryExpression(
new PropertyExpression(
obj,
mee.getKeyExpression()
),
Token.newSymbol(Types.ASSIGN, -1, -1),
mee.getValueExpression()
)
)
);
}
return new BytecodeExpr(exp, type) {
protected void compile(MethodVisitor mv) {
final String classInternalName = BytecodeHelper.getClassInternalName(getType());
mv.visitTypeInsn(NEW, classInternalName);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, classInternalName, "<init>", "()V");
for (BytecodeExpr prop : propSetters) {
prop.visit(mv);
if (!ClassHelper.VOID_TYPE.equals(prop.getType()))
pop(prop.getType(), mv);
}
}
};
}
}
}
final TupleExpression newArgs = (TupleExpression) compiler.transform(exp.getArguments());
final ClassNode[] argTypes = compiler.exprToTypeArray(newArgs);
constructor = findConstructorWithClosureCoercion(type, argTypes, compiler, null);
if (constructor != null) {
if (!AccessibilityCheck.isAccessible(constructor.getModifiers(), constructor.getDeclaringClass(), compiler.classNode, null)) {
compiler.addError("Cannot access constructor", exp);
return null;
}
final Parameter[] params = constructor.getParameters();
int base = 0;
// if ((type.getModifiers() & ACC_STATIC) == 0 && type.redirect() instanceof InnerClassNode) {
// base = 1;
// }
final ArgumentListExpression finalArgs = wrapArgumentsForVarargs(newArgs, params, base);
if ((constructor.getModifiers() & Opcodes.ACC_PRIVATE) != 0 && constructor.getDeclaringClass() != compiler.classNode) {
MethodNode delegate = compiler.context.getConstructorDelegate(constructor);
return ResolvedMethodBytecodeExpr.create(exp, delegate, null, finalArgs, compiler);
}
// Improve type.
GenericsType[] generics = type.redirect().getGenericsTypes();
// We don't support inference if the method itself is parameterized.
if (generics != null && constructor.getGenericsTypes() == null) {
ClassNode[] paramTypes = new ClassNode[params.length];
for (int i = 0; i < paramTypes.length; i++) {
paramTypes[i] = params[i].getType();
}
ClassNode[] unified = TypeUnification.inferTypeArguments(generics, paramTypes, argTypes);
if (TypeUnification.totalInference(unified)) {
type = TypeUtil.withGenericTypes(type, unified);
}
}
for (int i = 0; i != finalArgs.getExpressions().size(); ++i)
finalArgs.getExpressions().set(i, compiler.cast(finalArgs.getExpressions().get(i), constructor.getParameters()[i+base].getType()));
final MethodNode constructor1 = constructor;
final ClassNode compilerClass = compiler.classNode;
return new BytecodeExpr(exp, type) {
protected void compile(MethodVisitor mv) {
final String classInternalName = BytecodeHelper.getClassInternalName(getType());
mv.visitTypeInsn(NEW, classInternalName);
mv.visitInsn(DUP);
int first = 0;
// if ((getType().getModifiers() & ACC_STATIC) == 0 && getType().redirect() instanceof InnerClassNode) {
// mv.visitVarInsn(ALOAD, 0);
// for (ClassNode tp = compilerClass ; tp != getType().redirect().getOuterClass(); ) {
// compiler.context.setOuterClassInstanceUsed(tp);
// final ClassNode outerTp = tp.redirect().getOuterClass();
// mv.visitFieldInsn(GETFIELD, BytecodeHelper.getClassInternalName(tp), "this$0", BytecodeHelper.getTypeDescription(outerTp));
// tp = outerTp;
// }
// first = 1;
// }
for (int i = 0; i != finalArgs.getExpressions().size(); ++i) {
BytecodeExpr be = (BytecodeExpr) finalArgs.getExpressions().get(i);
be.visit(mv);
final ClassNode paramType = constructor1.getParameters()[i+first].getType();
final ClassNode type = be.getType();
box(type, mv);
cast(TypeUtil.wrapSafely(type), TypeUtil.wrapSafely(paramType), mv);
unbox(paramType, mv);
}
mv.visitMethodInsn(INVOKESPECIAL, classInternalName, "<init>", BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, constructor1.getParameters()));
}
};
}
if (compiler.policy == TypePolicy.STATIC) {
compiler.addError("Cannot find constructor of " + exp.getType().getName(), exp);
return null;
} else
return createDynamicCall(exp, compiler, newArgs);
}
private Expression createDynamicCall(final ConstructorCallExpression exp, CompilerTransformer compiler, TupleExpression newArgs) {
final List<Expression> args = newArgs.getExpressions();
for (int i = 0; i != args.size(); ++i) {
BytecodeExpr arg = compiler.transformSynthetic((BytecodeExpr) args.get(i));
if (arg instanceof CompiledClosureBytecodeExpr) {
compiler.processPendingClosure((CompiledClosureBytecodeExpr) arg);
}
args.set(i, arg);
}
return new BytecodeExpr(exp, exp.getType()) {
protected void compile(MethodVisitor mv) {
mv.visitLdcInsn(BytecodeHelper.getClassLoadingTypeDescription(getType()));
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
mv.visitLdcInsn(args.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
for (int j = 0; j != args.size(); ++j) {
mv.visitInsn(DUP);
mv.visitLdcInsn(j);
BytecodeExpr arg = (BytecodeExpr) args.get(j);
arg.visit(mv);
box(arg.getType(), mv);
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "org/codehaus/groovy/runtime/InvokerHelper", "invokeConstructorOf", "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitTypeInsn(Opcodes.CHECKCAST, BytecodeHelper.getClassInternalName(getType()));
}
};
}
private boolean improveWhatInnerClassVisitorDid(InnerClassNode innerClassNode, ConstructorCallExpression exp, CompilerTransformer compiler) {
// here we should fix constructor parameter types InnerClassVisitor did with constructor
// and add compile class itself
final ClassNode superClass = innerClassNode.getSuperClass();
final ConstructorNode constructorNode = innerClassNode.getDeclaredConstructors().get(0);
final VariableScope scope = innerClassNode.getVariableScope();
final boolean isStatic = !(superClass.redirect() instanceof InnerClassNode) || (superClass.getModifiers() & ACC_STATIC) != 0;
if (!isStatic && compiler.methodNode.getVariableScope().isInStaticContext()) {
compiler.addError("Can not instantiate non-static inner class " + superClass.getName() + " in static context", exp);
return false;
}
int additional = 1 + scope.getReferencedLocalVariablesCount();
TupleExpression te = (TupleExpression) exp.getArguments();
// remove all added parameters
for (int i = 0; i != additional; ++i) {
te.getExpressions().remove(0);
}
if (!isStatic) {
te.getExpressions().add(0, VariableExpression.THIS_EXPRESSION);
}
final TupleExpression newArgs = (TupleExpression) compiler.transform(exp.getArguments());
final ClassNode[] argTypes = compiler.exprToTypeArray(newArgs);
final MethodNode constr = findConstructorWithClosureCoercion(superClass, argTypes, compiler, innerClassNode);
if (constr == null) {
compiler.addError("Cannot find constructor of " + exp.getType().getName(), exp);
return false;
}
final ArgumentListExpression superArgs = new ArgumentListExpression();
if (!isStatic) {
superArgs.getExpressions().add(new VariableExpression("p$0"));
}
for(int i = 0; i != newArgs.getExpressions().size(); ++i) {
superArgs.getExpressions().add(new VariableExpression("p$"+(i+additional)));
}
BlockStatement code = (BlockStatement) constructorNode.getCode();
code.getStatements().remove(0);
code.getStatements().add(0,
new ExpressionStatement(
new ConstructorCallExpression(ClassNode.SUPER, superArgs)
)
);
final Parameter[] params = constructorNode.getParameters();
if (isStatic) {
te.getExpressions().add(0, VariableExpression.THIS_EXPRESSION);
}
int pCount = 0;
for (Iterator it=scope.getReferencedLocalVariablesIterator(); it.hasNext();) {
pCount++;
org.codehaus.groovy.ast.Variable var = (org.codehaus.groovy.ast.Variable) it.next();
final ClassNode vtype = compiler.transform(new VariableExpression(var)).getType();
params[pCount].setType(vtype);
innerClassNode.getField(var.getName()).setType(vtype);
te.getExpressions().add(pCount, new VariableExpression(var));
}
final Parameter[] superParams = constr.getParameters();
for(int i = 0; i != newArgs.getExpressions().size(); ++i) {
params[additional+i] = new Parameter(newArgs.getExpression(i).getType(), "p$" + (i+additional));
// params[additional+i] = new Parameter(i < superParams.length-1 ? superParams[i].getType() :
// (superParams[superParams.length-1].getType().isArray() ?
// superParams[superParams.length-1].getType().getComponentType()
// : superParams[superParams.length-1].getType()), "p$" + (i+additional));
}
ClassNodeCache.clearCache(innerClassNode);
return true;
}
private void rewriteThis0(ConstructorCallExpression exp, CompilerTransformer compiler) {
if (!(exp.getType().redirect() instanceof InnerClassNode)) return;
InnerClassNode inner = (InnerClassNode) exp.getType().redirect();
if ((inner.getModifiers() & ACC_STATIC) != 0) return;
Expression this0 = VariableExpression.THIS_EXPRESSION;
ClassNode tp = compiler.classNode;
for ( ; tp != null && tp != inner.redirect().getOuterClass(); ) {
compiler.context.setOuterClassInstanceUsed(tp);
tp = tp.redirect().getOuterClass();
this0 = new PropertyExpression(this0, "this$0");
}
if (tp == null)
return;
((TupleExpression)exp.getArguments()).getExpressions().set(0, this0);
}
// Insert array creation for varargs methods.
// Precondition: isApplicable.
private static ArgumentListExpression wrapArgumentsForVarargs(TupleExpression args, Parameter[] params, int base) {
List<Expression> unwrapped = args.getExpressions();
List<Expression> wrapped = new ArrayList<Expression>();
int nparams = params.length - base;
for (int i = 0; i < nparams - 1; i++) {
wrapped.add(args.getExpression(i));
}
int diff = unwrapped.size() - nparams;
assert diff >= -1;
if (diff > 0) {
List<Expression> add = new ArrayList<Expression>(diff);
for (int i = -1; i < diff; i++) {
add.add(args.getExpression(nparams + i));
}
wrapped.add(new ListExpression(add));
} else if (diff == 0) {
if (nparams > 0) {
wrapped.add(args.getExpression(nparams - 1));
}
} else if (diff == -1) {
wrapped.add(new ListExpression(new ArrayList<Expression>()));
}
return new ArgumentListExpression(wrapped);
}
private Expression transformSpecial(ConstructorCallExpression exp, CompilerTransformer compiler) {
final ClassNode type = exp.isSuperCall() ? compiler.classNode.getSuperClass() : compiler.classNode;
Expression args = exp.getArguments();
// if (type instanceof InnerClassNode) {
// if((type.getModifiers() & ACC_STATIC) == 0) {
// if (!(args instanceof TupleExpression)) {
// args = new TupleExpression(args);
// }
// ((TupleExpression)args).getExpressions().add(0, new VariableExpression(compiler.methodNode.getParameters()[0]));
// }
// }
final Expression newArgs = compiler.transform(args);
final ClassNode[] argTypes = compiler.exprToTypeArray(newArgs);
MethodNode constructor = compiler.findConstructor(type, argTypes, null);
if (constructor != null) {
return ResolvedMethodBytecodeExpr.create(exp, constructor,
exp.isSuperCall() ?
new VariableExpressionTransformer.Super(VariableExpression.SUPER_EXPRESSION, compiler)
: new VariableExpressionTransformer.ThisSpecial(VariableExpression.THIS_EXPRESSION, compiler),
(TupleExpression) newArgs, compiler);
}
compiler.addError("Cannot find constructor of " + (exp.isSuperCall() ? exp.getType().getSuperClass().getName() : exp.getType().getName()), exp);
return null;
}
private static MethodNode findConstructorVariatingArgs(ClassNode type, ClassNode[] argTypes, CompilerTransformer compiler, int firstNonVariating, ClassNode contextClass) {
MethodNode foundMethod = compiler.findConstructor(type, argTypes, contextClass);
if (foundMethod != null) {
return foundMethod;
}
if (argTypes.length > 0) {
for (int i=firstNonVariating+1; i < argTypes.length; ++i) {
final ClassNode oarg = argTypes[i];
if (oarg == null)
continue;
if (oarg.implementsInterface(TypeUtil.TCLOSURE)) {
foundMethod = findConstructorVariatingArgs(type, argTypes, compiler, i, contextClass);
if (foundMethod != null) {
Parameter p[] = foundMethod.getParameters();
if (p.length == argTypes.length) {
return foundMethod;
}
}
argTypes[i] = null;
foundMethod = findConstructorVariatingArgs(type, argTypes, compiler, i, contextClass);
if (foundMethod != null) {
Parameter p[] = foundMethod.getParameters();
if (p.length == argTypes.length) {
ClassNode argType = p[i].getType();
if (argType.equals(ClassHelper.CLOSURE_TYPE)) {
ClosureUtil.improveClosureType(oarg, ClassHelper.CLOSURE_TYPE);
StaticMethodBytecode.replaceMethodCode(compiler.su, compiler.context, ((ClosureClassNode)oarg).getDoCallMethod(), compiler.compileStack, compiler.debug == -1 ? -1 : compiler.debug+1, compiler.policy, oarg.getName());
}
else {
List<MethodNode> one = ClosureUtil.isOneMethodAbstract(argType);
GenericsType[] methodTypeVars = foundMethod.getGenericsTypes();
if (methodTypeVars != null && methodTypeVars.length > 0) {
ArrayList<ClassNode> formals = new ArrayList<ClassNode> (2);
ArrayList<ClassNode> instantiateds = new ArrayList<ClassNode> (2);
if (!foundMethod.isStatic()) {
formals.add(foundMethod.getDeclaringClass());
instantiateds.add(type);
}
for (int j = 0; j != i; j++) {
formals.add(foundMethod.getParameters()[j].getType());
instantiateds.add(argTypes[j]);
}
ClassNode[] unified = TypeUnification.inferTypeArguments(methodTypeVars,
formals.toArray(new ClassNode[formals.size()]),
instantiateds.toArray(new ClassNode[instantiateds.size()]));
argType = TypeUtil.getSubstitutedType(argType, foundMethod, unified);
}
MethodNode doCall = ClosureUtil.isMatch(one, (ClosureClassNode) oarg, argType, compiler);
if (doCall == null) {
foundMethod = null;
}
}
}
}
argTypes[i] = oarg;
return foundMethod;
}
else {
if (oarg.implementsInterface(TypeUtil.TMAP) || oarg.implementsInterface(TypeUtil.TLIST)|| oarg.implementsInterface(TypeUtil.TTERNARY)) {
return structural(type, argTypes, compiler, contextClass, i, oarg);
}
else {
if (oarg.implementsInterface(TypeUtil.TTHIS)) {
ClassNode run = oarg.getOuterClass();
while (run != null) {
argTypes[i] = run;
foundMethod = findConstructorVariatingArgs(type, argTypes, compiler, i, contextClass);
if (foundMethod != null) return foundMethod;
if ((run.getModifiers() & Opcodes.ACC_STATIC) != 0) break;
run = run.getOuterClass();
}
argTypes[i] = oarg.getOuterClass();
}
}
}
}
}
return null;
}
private static MethodNode structural(ClassNode type, ClassNode[] argTypes, CompilerTransformer compiler, ClassNode contextClass, int i, ClassNode oarg) {
MethodNode foundMethod;
foundMethod = findConstructorVariatingArgs(type, argTypes, compiler, i, contextClass);
if (foundMethod != null) {
Parameter p[] = foundMethod.getParameters();
if (p.length == argTypes.length) {
return foundMethod;
}
}
argTypes[i] = null;
foundMethod = findConstructorVariatingArgs(type, argTypes, compiler, i, contextClass);
if (foundMethod != null) {
Parameter p[] = foundMethod.getParameters();
if (p.length == argTypes.length) {
ClassNode argType = p[i].getType();
GenericsType[] methodTypeVars = foundMethod.getGenericsTypes();
if (methodTypeVars != null && methodTypeVars.length > 0) {
ArrayList<ClassNode> formals = new ArrayList<ClassNode> (2);
ArrayList<ClassNode> instantiateds = new ArrayList<ClassNode> (2);
if (!foundMethod.isStatic()) {
formals.add(foundMethod.getDeclaringClass());
instantiateds.add(type);
}
for (int j = 0; j != i; j++) {
formals.add(foundMethod.getParameters()[j].getType());
instantiateds.add(argTypes[j]);
}
ClassNode[] unified = TypeUnification.inferTypeArguments(methodTypeVars,
formals.toArray(new ClassNode[formals.size()]),
instantiateds.toArray(new ClassNode[instantiateds.size()]));
argType = TypeUtil.getSubstitutedType(argType, foundMethod, unified);
}
}
}
argTypes[i] = oarg;
return foundMethod;
}
public static MethodNode findConstructorWithClosureCoercion(ClassNode type, ClassNode[] argTypes, CompilerTransformer compiler, ClassNode contextClass) {
return findConstructorVariatingArgs(type, argTypes, compiler, -1, contextClass);
}
}