/*
* 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.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.InnerClassNode;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.mbte.groovypp.compiler.CompilerTransformer;
import org.mbte.groovypp.compiler.Register;
import org.mbte.groovypp.compiler.TypeUtil;
import org.mbte.groovypp.compiler.bytecode.BytecodeExpr;
import org.mbte.groovypp.compiler.bytecode.ResolvedVarBytecodeExpr;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class VariableExpressionTransformer extends ExprTransformer<VariableExpression> {
public Expression transform(final VariableExpression exp, final CompilerTransformer compiler) {
if (exp.isThisExpression()) {
if (!compiler.methodNode.isStatic())
return new This(exp, compiler);
else {
if (compiler.methodNode.getName().equals("$doCall")) {
return new Self(exp, compiler);
} else {
// compiler.addError("Cannot use 'this' in static method", exp);
return ClassExpressionTransformer.newExpr(exp, compiler.classNode);
}
}
}
if (exp.isSuperExpression()) {
if (!compiler.methodNode.isStatic())
return new Super(exp, compiler);
else {
if (compiler.methodNode.getName().equals("$doCall")) {
return new Self(exp, compiler);
} else {
// compiler.addError("Cannot use 'this' in static method", exp);
return ClassExpressionTransformer.newExpr(exp, compiler.classNode);
}
}
}
final Register var = compiler.compileStack.getRegister(exp.getName(), false);
if (var == null) {
if (exp.isClosureSharedVariable()) {
// we are in closure
final VariableExpression ve = VariableExpression.THIS_EXPRESSION;
final PropertyExpression pe = new PropertyExpression(ve, exp.getName());
pe.setType(exp.getType());
pe.setSourcePosition(exp);
return compiler.transform(pe);
} else if (exp.getAccessedVariable() != null) {
String name = exp.getName();
if (exp.getAccessedVariable() instanceof VariableExpression && DeclarationExpressionTransformer.hasFieldAnnotation((VariableExpression)exp.getAccessedVariable())) {
// @Field
FieldNode fieldNode = (FieldNode) ((VariableExpression)exp.getAccessedVariable()).getAccessedVariable();
name = fieldNode.getName();
}
PropertyExpression pe = new PropertyExpression(VariableExpression.THIS_EXPRESSION, name);
pe.setImplicitThis(true);
pe.setSourcePosition(exp);
return compiler.transform(pe);
}
} else {
ClassNode vtype = compiler.getLocalVarInferenceTypes().get(exp);
if (vtype == null)
vtype = var.getType();
if (var.getIndex() == 0 && var.getName().equals("$self"))
return new Self(exp, compiler);
else
return new ResolvedVarBytecodeExpr(vtype, exp, compiler);
}
compiler.addError("Cannot find variable " + exp.getName(), exp);
return null;
}
private static class ThisBase extends BytecodeExpr {
public ThisBase(VariableExpression exp, ClassNode type) {
super(exp, type);
}
public void compile(MethodVisitor mv) {
mv.visitVarInsn(ALOAD, 0);
}
}
public static class This extends ThisBase {
public This(VariableExpression exp, CompilerTransformer compiler) {
super(exp, getThisType(compiler));
}
private static ClassNode getThisType(CompilerTransformer compiler) {
final ClassNode classNode = compiler.classNode;
if (hasChoiceOfThis(classNode)) {
InnerClassNode newType = new InnerClassNode(classNode, compiler.getNextClosureName(),
ACC_PUBLIC|ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE);
newType.setInterfaces(new ClassNode[] {TypeUtil.TTHIS});
return newType;
} else {
return classNode;
}
}
private static boolean hasChoiceOfThis(ClassNode classNode) {
return classNode instanceof InnerClassNode && (classNode.getModifiers() & Opcodes.ACC_STATIC) == 0;
}
@Override
public boolean isThis() {
return true;
}
}
public static class ThisSpecial extends This {
public ThisSpecial(VariableExpression exp, CompilerTransformer compiler) {
super(exp, compiler);
}
}
public static class Super extends ThisBase {
public Super(VariableExpression exp, CompilerTransformer compiler) {
super(exp, compiler.classNode.getSuperClass());
}
}
public static class Self extends ThisBase {
public Self(VariableExpression exp, CompilerTransformer compiler) {
super(exp, compiler.methodNode.getParameters()[0].getType());
}
@Override
public boolean isThis() {
return true;
}
}
}