Files
kotlin-fork/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java
T
2011-06-24 17:56:46 +04:00

1797 lines
75 KiB
Java

package org.jetbrains.jet.codegen;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.ProjectScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import jet.IntRange;
import jet.JetObject;
import jet.Range;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.JetStandardClasses;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeProjection;
import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.jet.resolve.DescriptorRenderer;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import org.objectweb.asm.commons.Method;
import java.util.*;
/**
* @author max
*/
public class ExpressionCodegen extends JetVisitor {
private static final String CLASS_OBJECT = "java/lang/Object";
private static final String CLASS_STRING = "java/lang/String";
private static final String CLASS_STRING_BUILDER = "java/lang/StringBuilder";
private static final String CLASS_COMPARABLE = "java/lang/Comparable";
private static final String CLASS_ITERABLE = "java/lang/Iterable";
private static final String CLASS_ITERATOR = "java/util/Iterator";
private static final String CLASS_RANGE = "jet/Range";
private static final String CLASS_INT_RANGE = "jet/IntRange";
private static final String CLASS_NO_PATTERN_MATCHED_EXCEPTION = "jet/NoPatternMatchedException";
private static final String CLASS_TYPE_CAST_EXCEPTION = "jet/TypeCastException";
private static final String ITERABLE_ITERATOR_DESCRIPTOR = "()Ljava/util/Iterator;";
private static final String ITERATOR_HASNEXT_DESCRIPTOR = "()Z";
private static final String ITERATOR_NEXT_DESCRIPTOR = "()Ljava/lang/Object;";
private static final String INT_RANGE_CONSTRUCTOR_DESCRIPTOR = "(II)V";
private static final Type OBJECT_TYPE = Type.getType(Object.class);
private static final Type INTEGER_TYPE = Type.getType(Integer.class);
private static final Type ITERATOR_TYPE = Type.getType(Iterator.class);
private static final Type THROWABLE_TYPE = Type.getType(Throwable.class);
private static final Type RANGE_TYPE = Type.getType(Range.class);
private static final Type INT_RANGE_TYPE = Type.getType(IntRange.class);
private static final Type JET_OBJECT_TYPE = Type.getType(JetObject.class);
private final Stack<Label> myContinueTargets = new Stack<Label>();
private final Stack<Label> myBreakTargets = new Stack<Label>();
private final Stack<StackValue> myStack = new Stack<StackValue>();
private final InstructionAdapter v;
private final FrameMap myMap;
private final JetTypeMapper typeMapper;
private final GenerationState state;
private final Type returnType;
private final DeclarationDescriptor contextType;
private final OwnerKind contextKind;
private final BindingContext bindingContext;
private final StackValue thisExpression;
private final Map<ClassDescriptor, StackValue> outerThisExpressions = new HashMap<ClassDescriptor, StackValue>();
public ExpressionCodegen(MethodVisitor v,
FrameMap myMap,
Type returnType,
DeclarationDescriptor contextType,
OwnerKind contextKind,
@Nullable StackValue thisExpression,
GenerationState state) {
this.myMap = myMap;
this.typeMapper = state.getTypeMapper();
this.returnType = returnType;
this.contextType = contextType;
this.contextKind = contextKind;
this.state = state;
this.v = new InstructionAdapter(v);
this.bindingContext = state.getBindingContext();
this.thisExpression = thisExpression;
}
public void addOuterThis(ClassDescriptor outer, StackValue expression) {
outerThisExpressions.put(outer, expression);
}
static void loadTypeInfo(ClassDescriptor descriptor, InstructionAdapter v) {
String owner = JetTypeMapper.jvmNameForImplementation(descriptor);
if (descriptor.getTypeConstructor().getParameters().size() > 0) {
v.load(0, JetTypeMapper.TYPE_OBJECT);
v.getfield(owner, "$typeInfo", "Ljet/typeinfo/TypeInfo;");
}
else {
v.getstatic(owner, "$typeInfo", "Ljet/typeinfo/TypeInfo;");
}
}
private void gen(JetElement expr) {
if (expr == null) throw new CompilationException();
expr.accept(this);
}
public void gen(JetElement expr, Type type) {
int oldStackDepth = myStack.size();
gen(expr);
if (myStack.size() == oldStackDepth+1) {
putTopOfStack(type);
}
}
private void putTopOfStack(Type type) {
StackValue value = myStack.pop();
value.put(type, v);
}
public void genToJVMStack(JetExpression expr) {
gen(expr, expressionType(expr));
}
private StackValue generateIntermediateValue(final JetExpression baseExpression) {
int oldStackSize = myStack.size();
gen(baseExpression);
if (myStack.size() != oldStackSize+1) {
throw new UnsupportedOperationException("intermediate value expected");
}
return myStack.pop();
}
@Override
public void visitExpression(JetExpression expression) {
throw new UnsupportedOperationException("Codegen for " + expression + " is not yet implemented");
}
@Override
public void visitParenthesizedExpression(JetParenthesizedExpression expression) {
gen(expression.getExpression());
}
@Override
public void visitAnnotatedExpression(JetAnnotatedExpression expression) {
gen(expression.getBaseExpression());
}
@Override
public void visitIfExpression(JetIfExpression expression) {
JetType expressionType = bindingContext.getExpressionType(expression);
Type asmType = typeMapper.mapType(expressionType);
int oldStackDepth = myStack.size();
gen(expression.getCondition());
assert myStack.size() == oldStackDepth+1;
JetExpression thenExpression = expression.getThen();
JetExpression elseExpression = expression.getElse();
if (thenExpression == null && elseExpression == null) {
throw new CompilationException();
}
if (thenExpression == null) {
generateSingleBranchIf(elseExpression, false);
return;
}
if (elseExpression == null) {
generateSingleBranchIf(thenExpression, true);
return;
}
Label elseLabel = new Label();
myStack.pop().condJump(elseLabel, true, v); // == 0, i.e. false
gen(thenExpression, asmType);
Label endLabel = new Label();
v.goTo(endLabel);
v.mark(elseLabel);
gen(elseExpression, asmType);
v.mark(endLabel);
if (asmType != Type.VOID_TYPE) {
myStack.push(StackValue.onStack(asmType));
}
}
@Override
public void visitWhileExpression(JetWhileExpression expression) {
Label condition = new Label();
myContinueTargets.push(condition);
v.mark(condition);
Label end = new Label();
myBreakTargets.push(end);
gen(expression.getCondition());
myStack.pop().condJump(end, true, v);
gen(expression.getBody(), Type.VOID_TYPE);
v.goTo(condition);
v.mark(end);
myBreakTargets.pop();
myContinueTargets.pop();
}
@Override
public void visitDoWhileExpression(JetDoWhileExpression expression) {
Label condition = new Label();
v.mark(condition);
myContinueTargets.push(condition);
Label end = new Label();
myBreakTargets.push(end);
gen(expression.getBody(), Type.VOID_TYPE);
gen(expression.getCondition());
myStack.pop().condJump(condition, false, v);
v.mark(end);
myBreakTargets.pop();
myContinueTargets.pop();
}
@Override
public void visitForExpression(JetForExpression expression) {
final JetExpression loopRange = expression.getLoopRange();
final JetType expressionType = bindingContext.getExpressionType(loopRange);
Type loopRangeType = typeMapper.mapType(expressionType);
if (loopRangeType.getSort() == Type.ARRAY) {
new ForInArrayLoopGenerator(expression, loopRangeType).invoke();
}
else {
final DeclarationDescriptor descriptor = expressionType.getConstructor().getDeclarationDescriptor();
final PsiElement declaration = bindingContext.getDeclarationPsiElement(descriptor);
if (declaration instanceof PsiClass) {
final Project project = declaration.getProject();
final PsiClass iterable = JavaPsiFacade.getInstance(project).findClass("java.lang.Iterable", ProjectScope.getAllScope(project));
if (((PsiClass) declaration).isInheritor(iterable, true)) {
generateForInIterable(expression, loopRangeType);
return;
}
}
if (isClass(descriptor, "IntRange")) { // TODO IntRange subclasses
new ForInRangeLoopGenerator(expression, loopRangeType).invoke();
return;
}
throw new UnsupportedOperationException("for/in loop currently only supported for arrays and Iterable instances");
}
}
private void generateForInIterable(JetForExpression expression, Type loopRangeType) {
final JetParameter loopParameter = expression.getLoopParameter();
final VariableDescriptor parameterDescriptor = bindingContext.getVariableDescriptor(loopParameter);
JetType paramType = parameterDescriptor.getOutType();
Type asmParamType = typeMapper.mapType(paramType);
int iteratorVar = myMap.enterTemp();
gen(expression.getLoopRange(), loopRangeType);
v.invokeinterface(CLASS_ITERABLE, "iterator", ITERABLE_ITERATOR_DESCRIPTOR);
v.store(iteratorVar, ITERATOR_TYPE);
Label begin = new Label();
Label end = new Label();
myContinueTargets.push(begin);
myBreakTargets.push(end);
v.mark(begin);
v.load(iteratorVar, ITERATOR_TYPE);
v.invokeinterface(CLASS_ITERATOR, "hasNext", ITERATOR_HASNEXT_DESCRIPTOR);
v.ifeq(end);
myMap.enter(parameterDescriptor, asmParamType.getSize());
v.load(iteratorVar, ITERATOR_TYPE);
v.invokeinterface(CLASS_ITERATOR, "next", ITERATOR_NEXT_DESCRIPTOR);
// TODO checkcast should be generated via StackValue
if (asmParamType.getSort() == Type.OBJECT && !"java.lang.Object".equals(asmParamType.getClassName())) {
v.checkcast(asmParamType);
}
v.store(lookupLocal(parameterDescriptor), asmParamType);
gen(expression.getBody(), Type.VOID_TYPE);
v.goTo(begin);
v.mark(end);
int paramIndex = myMap.leave(parameterDescriptor);
v.visitLocalVariable(loopParameter.getName(), asmParamType.getDescriptor(), null, begin, end, paramIndex);
myMap.leaveTemp();
myBreakTargets.pop();
myContinueTargets.pop();
}
private abstract class ForLoopGenerator {
protected JetForExpression expression;
protected Type loopRangeType;
protected VariableDescriptor parameterDescriptor;
public ForLoopGenerator(JetForExpression expression, Type loopRangeType) {
this.expression = expression;
this.loopRangeType = loopRangeType;
final JetParameter loopParameter = expression.getLoopParameter();
this.parameterDescriptor = bindingContext.getVariableDescriptor(loopParameter);
}
public void invoke() {
JetType paramType = parameterDescriptor.getOutType();
Type asmParamType = typeMapper.mapType(paramType);
myMap.enter(parameterDescriptor, asmParamType.getSize());
generatePrologue();
Label condition = new Label();
Label increment = new Label();
Label end = new Label();
v.mark(condition);
myContinueTargets.push(increment);
myBreakTargets.push(end);
generateCondition(asmParamType, end);
gen(expression.getBody(), Type.VOID_TYPE);
v.mark(increment);
generateIncrement();
v.goTo(condition);
v.mark(end);
cleanupTemp();
final int paramIndex = myMap.leave(parameterDescriptor);
v.visitLocalVariable(expression.getLoopParameter().getName(), asmParamType.getDescriptor(), null, condition, end, paramIndex);
myBreakTargets.pop();
myContinueTargets.pop();
}
protected void generatePrologue() {
}
protected abstract void generateCondition(Type asmParamType, Label end);
protected abstract void generateIncrement();
protected void cleanupTemp() {
}
}
private class ForInArrayLoopGenerator extends ForLoopGenerator {
private int myLengthVar;
private int myIndexVar;
public ForInArrayLoopGenerator(JetForExpression expression, Type loopRangeType) {
super(expression, loopRangeType);
}
@Override
protected void generatePrologue() {
myLengthVar = myMap.enterTemp();
gen(expression.getLoopRange(), loopRangeType);
v.arraylength();
v.store(myLengthVar, Type.INT_TYPE);
myIndexVar = myMap.enterTemp();
v.aconst(0);
v.store(myIndexVar, Type.INT_TYPE);
}
protected void generateCondition(Type asmParamType, Label end) {
v.load(myIndexVar, Type.INT_TYPE);
v.load(myLengthVar, Type.INT_TYPE);
v.ificmpge(end);
gen(expression.getLoopRange(), loopRangeType); // array
v.load(myIndexVar, Type.INT_TYPE);
v.aload(loopRangeType.getElementType());
StackValue.onStack(loopRangeType.getElementType()).put(asmParamType, v);
v.store(lookupLocal(parameterDescriptor), asmParamType);
}
protected void generateIncrement() {
v.iinc(myIndexVar, 1);
}
protected void cleanupTemp() {
myMap.leaveTemp(2);
}
}
private class ForInRangeLoopGenerator extends ForLoopGenerator {
private int myRangeVar;
private int myEndVar;
public ForInRangeLoopGenerator(JetForExpression expression, Type loopRangeType) {
super(expression, loopRangeType);
}
@Override
protected void generatePrologue() {
myRangeVar = myMap.enterTemp();
myEndVar = myMap.enterTemp();
gen(expression.getLoopRange(), loopRangeType);
v.dup();
v.dup();
v.store(myRangeVar, loopRangeType);
v.invokevirtual("jet/IntRange", "getStartValue", "()I");
v.store(lookupLocal(parameterDescriptor), Type.INT_TYPE);
v.invokevirtual("jet/IntRange", "getEndValue", "()I");
v.store(myEndVar, Type.INT_TYPE);
}
@Override
protected void generateCondition(Type asmParamType, Label end) {
v.load(lookupLocal(parameterDescriptor), Type.INT_TYPE);
v.load(myEndVar, Type.INT_TYPE);
v.ificmpgt(end);
}
@Override
protected void generateIncrement() {
v.iinc(lookupLocal(parameterDescriptor), 1); // TODO support decreasing order
}
@Override
protected void cleanupTemp() {
myMap.leaveTemp(2);
}
}
@Override
public void visitBreakExpression(JetBreakExpression expression) {
JetSimpleNameExpression labelElement = expression.getTargetLabel();
Label label = labelElement == null ? myBreakTargets.peek() : null; // TODO:
v.goTo(label);
}
@Override
public void visitContinueExpression(JetContinueExpression expression) {
String labelName = expression.getLabelName();
Label label = labelName == null ? myContinueTargets.peek() : null; // TODO:
v.goTo(label);
}
private void generateSingleBranchIf(JetExpression expression, boolean inverse) {
Label endLabel = new Label();
myStack.pop().condJump(endLabel, inverse, v);
gen(expression, Type.VOID_TYPE);
v.mark(endLabel);
}
@Override
public void visitConstantExpression(JetConstantExpression expression) {
myStack.push(StackValue.constant(expression.getValue(), expressionType(expression)));
}
@Override
public void visitBlockExpression(JetBlockExpression expression) {
List<JetElement> statements = expression.getStatements();
generateBlock(statements);
}
@Override
public void visitFunctionLiteralExpression(JetFunctionLiteralExpression expression) {
if (bindingContext.isBlock(expression)) {
generateBlock(expression.getFunctionLiteral().getBodyExpression().getStatements());
}
else {
final GeneratedClosureDescriptor closure = state.generateClosure(expression, this);
v.anew(Type.getObjectType(closure.getClassname()));
v.dup();
final Method cons = closure.getConstructor();
for (int i = 0; i < closure.getArgs().size(); i++) {
StackValue arg = closure.getArgs().get(i);
arg.put(cons.getArgumentTypes()[i], v);
}
v.invokespecial(closure.getClassname(), "<init>", cons.getDescriptor());
}
}
private void generateBlock(List<JetElement> statements) {
Label blockStart = new Label();
v.mark(blockStart);
for (JetElement statement : statements) {
if (statement instanceof JetProperty) {
final VariableDescriptor variableDescriptor = bindingContext.getVariableDescriptor((JetProperty) statement);
final Type type = typeMapper.mapType(variableDescriptor.getOutType());
myMap.enter(variableDescriptor, type.getSize());
}
}
for (int i = 0, statementsSize = statements.size(); i < statementsSize; i++) {
JetElement statement = statements.get(i);
if (i == statements.size() - 1) {
gen(statement);
}
else {
gen(statement, Type.VOID_TYPE);
}
}
Label blockEnd = new Label();
v.mark(blockEnd);
for (JetElement statement : statements) {
if (statement instanceof JetProperty) {
JetProperty var = (JetProperty) statement;
VariableDescriptor variableDescriptor = bindingContext.getVariableDescriptor(var);
Type outType = typeMapper.mapType(variableDescriptor.getOutType());
int index = myMap.leave(variableDescriptor);
v.visitLocalVariable(var.getName(), outType.getDescriptor(), null, blockStart, blockEnd, index);
}
}
}
@Override
public void visitReturnExpression(JetReturnExpression expression) {
final JetExpression returnedExpression = expression.getReturnedExpression();
if (returnedExpression != null) {
gen(returnedExpression, returnType);
v.areturn(returnType);
}
else {
v.visitInsn(Opcodes.RETURN);
}
}
public void returnTopOfStack() {
if (myStack.size() > 0) {
putTopOfStack(returnType);
v.areturn(returnType);
}
}
@Override
public void visitSimpleNameExpression(JetSimpleNameExpression expression) {
DeclarationDescriptor descriptor = bindingContext.resolveReferenceExpression(expression);
if (descriptor instanceof NamespaceDescriptor) return; // No code to generate
if (descriptor instanceof VariableAsFunctionDescriptor) {
descriptor = ((VariableAsFunctionDescriptor) descriptor).getVariableDescriptor();
}
final DeclarationDescriptor container = descriptor.getContainingDeclaration();
if (descriptor instanceof VariableDescriptor) {
if (isClass(container, "Number")) {
Type castType = getCastType(expression.getReferencedName());
if (castType != null) {
final StackValue value = myStack.pop();
value.put(castType, v);
myStack.push(StackValue.onStack(castType));
return;
}
}
}
PsiElement declaration = bindingContext.getDeclarationPsiElement(descriptor);
if (declaration instanceof PsiField) {
PsiField psiField = (PsiField) declaration;
final String owner = JetTypeMapper.jvmName(psiField.getContainingClass());
final Type fieldType = psiTypeToAsm(psiField.getType());
final boolean isStatic = psiField.hasModifierProperty(PsiModifier.STATIC);
if (!isStatic) {
ensureReceiverOnStack(expression, null);
}
myStack.push(StackValue.field(fieldType, owner, psiField.getName(), isStatic));
}
else {
int index = lookupLocal(descriptor);
if (index >= 0) {
final JetType outType = ((VariableDescriptor) descriptor).getOutType();
myStack.push(StackValue.local(index, typeMapper.mapType(outType)));
}
else if (descriptor instanceof PropertyDescriptor) {
final PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
//TODO: hack, will not need if resolve goes to right descriptor itself
if (declaration instanceof JetParameter) {
if (PsiTreeUtil.getParentOfType(expression, JetDelegationSpecifier.class) != null) {
JetClass aClass = PsiTreeUtil.getParentOfType(expression, JetClass.class);
ConstructorDescriptor constructorDescriptor = bindingContext.getConstructorDescriptor(aClass);
List<ValueParameterDescriptor> parameters = constructorDescriptor.getUnsubstitutedValueParameters();
for (ValueParameterDescriptor parameter : parameters) {
if (parameter.getName().equals(descriptor.getName())) {
final JetType outType = ((VariableDescriptor) descriptor).getOutType();
myStack.push(StackValue.local(lookupLocal(parameter), typeMapper.mapType(outType)));
return;
}
}
}
}
if (isClass(container, "Array") && propertyDescriptor.getName().equals("size")) {
ensureReceiverOnStack(expression, null);
v.arraylength();
myStack.push(StackValue.onStack(Type.INT_TYPE));
}
else if (declaration instanceof JetObjectDeclarationName) {
JetObjectDeclaration objectDeclaration = PsiTreeUtil.getParentOfType(declaration, JetObjectDeclaration.class);
ClassDescriptor classDescriptor = bindingContext.getClassDescriptor(objectDeclaration);
myStack.push(StackValue.field(typeMapper.jvmType(classDescriptor, OwnerKind.IMPLEMENTATION),
typeMapper.jvmName(classDescriptor, OwnerKind.IMPLEMENTATION),
"$instance",
true));
}
else {
boolean isStatic = container instanceof NamespaceDescriptorImpl;
final boolean directToField = expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER;
JetExpression receiver = getReceiverForSelector(expression);
final boolean forceInterface = receiver != null && !(receiver instanceof JetThisExpression);
final StackValue iValue = intermediateValueForProperty(propertyDescriptor, directToField, forceInterface);
if (!isStatic) {
ensureReceiverOnStack(expression, container instanceof ClassDescriptor ? (ClassDescriptor) container : null);
}
myStack.push(iValue);
}
}
else {
final StackValue value = state.lookupInContext(descriptor);
if (value == null) {
throw new UnsupportedOperationException("don't know how to generate reference " + descriptor);
}
// receiver
StackValue.local(0, JetTypeMapper.TYPE_OBJECT).put(JetTypeMapper.TYPE_OBJECT, v);
myStack.push(value);
}
}
}
public int lookupLocal(DeclarationDescriptor descriptor) {
return myMap.getIndex(descriptor);
}
public StackValue intermediateValueForProperty(PropertyDescriptor propertyDescriptor, final boolean forceField, boolean forceInterface) {
DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
boolean isStatic = containingDeclaration instanceof NamespaceDescriptorImpl;
final JetType outType = propertyDescriptor.getOutType();
boolean isInsideClass = !forceInterface && containingDeclaration == contextType;
Method getter;
Method setter;
if (forceField) {
getter = null;
setter = null;
}
else {
getter = isInsideClass && propertyDescriptor.getGetter() == null ? null : typeMapper.mapGetterSignature(propertyDescriptor);
setter = isInsideClass && propertyDescriptor.getSetter() == null ? null : typeMapper.mapSetterSignature(propertyDescriptor);
}
String owner;
boolean isInterface;
if (isInsideClass || isStatic) {
owner = typeMapper.getOwner(propertyDescriptor, contextKind);
isInterface = false;
}
else {
owner = typeMapper.getOwner(propertyDescriptor, OwnerKind.INTERFACE);
isInterface = !(containingDeclaration instanceof ClassDescriptor && ((ClassDescriptor) containingDeclaration).isObject());
}
return StackValue.property(propertyDescriptor.getName(), owner, typeMapper.mapType(outType), isStatic, isInterface, getter, setter);
}
@Nullable
private static Type getCastType(String castMethodName) {
if ("dbl".equals(castMethodName)) {
return Type.DOUBLE_TYPE;
}
if ("flt".equals(castMethodName)) {
return Type.FLOAT_TYPE;
}
if ("lng".equals(castMethodName)) {
return Type.LONG_TYPE;
}
if ("int".equals(castMethodName)) {
return Type.INT_TYPE;
}
if ("chr".equals(castMethodName)) {
return Type.CHAR_TYPE;
}
if ("sht".equals(castMethodName)) {
return Type.SHORT_TYPE;
}
if ("byt".equals(castMethodName)) {
return Type.BYTE_TYPE;
}
return null;
}
@Override
public void visitCallExpression(JetCallExpression expression) {
JetExpression callee = expression.getCalleeExpression();
if (callee instanceof JetSimpleNameExpression) {
DeclarationDescriptor funDescriptor = bindingContext.resolveReferenceExpression((JetSimpleNameExpression) callee);
if (funDescriptor == null) {
throw new CompilationException("Cannot resolve: " + callee.getText());
}
if (funDescriptor instanceof ConstructorDescriptor) {
generateConstructorCall(expression, (JetSimpleNameExpression) callee);
}
else if (funDescriptor instanceof FunctionDescriptor) {
final DeclarationDescriptor functionParent = funDescriptor.getContainingDeclaration();
if (isNumberPrimitive(functionParent)) {
if (funDescriptor.getName().equals("inv")) {
final StackValue value = myStack.pop(); // HACK we rely on the dot reference handler to put it on the stack
final Type asmType = expressionType(expression);
value.put(asmType, v);
generateInv(asmType);
return;
}
}
PsiElement declarationPsiElement = bindingContext.getDeclarationPsiElement(funDescriptor);
final FunctionDescriptor fd = (FunctionDescriptor) funDescriptor;
if (declarationPsiElement == null && isClass(functionParent, "String")) {
final Project project = expression.getProject();
PsiClass jlString = JavaPsiFacade.getInstance(project).findClass("java.lang.String",
ProjectScope.getAllScope(project));
// TODO better overload mapping
final PsiMethod[] methods = jlString.findMethodsByName(funDescriptor.getName(), false);
final int arity = fd.getUnsubstitutedValueParameters().size();
for (PsiMethod method : methods) {
if (method.getParameterList().getParametersCount() == arity) {
declarationPsiElement = method;
}
}
}
if (declarationPsiElement == null) {
throw new UnsupportedOperationException("couldn't find declaration for " + funDescriptor);
}
Method methodDescriptor;
if (declarationPsiElement instanceof PsiMethod) {
PsiMethod psiMethod = (PsiMethod) declarationPsiElement;
String owner = JetTypeMapper.jvmName(psiMethod.getContainingClass());
methodDescriptor = getMethodDescriptor(psiMethod);
final boolean isStatic = psiMethod.hasModifierProperty(PsiModifier.STATIC);
if (!isStatic) {
ensureReceiverOnStack(expression, null);
if (expression.getParent() instanceof JetQualifiedExpression) {
final JetExpression receiver = ((JetQualifiedExpression) expression.getParent()).getReceiverExpression();
JetType expressionType = bindingContext.getExpressionType(receiver);
DeclarationDescriptor declarationDescriptor = expressionType.getConstructor().getDeclarationDescriptor();
PsiElement ownerDeclaration = bindingContext.getDeclarationPsiElement(declarationDescriptor);
if (ownerDeclaration instanceof PsiClass) {
owner = typeMapper.mapType(expressionType).getInternalName();
}
}
}
pushMethodArguments(expression, methodDescriptor);
v.visitMethodInsn(isStatic ? Opcodes.INVOKESTATIC : Opcodes.INVOKEVIRTUAL,
owner,
methodDescriptor.getName(),
methodDescriptor.getDescriptor());
}
else if(declarationPsiElement instanceof JetNamedFunction) {
final JetNamedFunction jetFunction = (JetNamedFunction) declarationPsiElement;
methodDescriptor = typeMapper.mapSignature(jetFunction);
if (functionParent instanceof NamespaceDescriptorImpl) {
if (jetFunction.getReceiverTypeRef() != null) {
ensureReceiverOnStack(expression, null);
}
pushMethodArguments(expression, methodDescriptor);
final String owner = NamespaceCodegen.getJVMClassName(DescriptorRenderer.getFQName(functionParent));
v.invokestatic(owner, methodDescriptor.getName(), methodDescriptor.getDescriptor());
}
else if (functionParent instanceof ClassDescriptor) {
ensureReceiverOnStack(expression, (ClassDescriptor) functionParent);
pushMethodArguments(expression, methodDescriptor);
final String owner = JetTypeMapper.jvmNameForInterface((ClassDescriptor) functionParent);
v.invokeinterface(owner, methodDescriptor.getName(), methodDescriptor.getDescriptor());
}
else {
throw new UnsupportedOperationException("don't know how to generate call to " + declarationPsiElement);
}
}
else {
gen(callee, Type.getObjectType(ClosureCodegen.getInternalClassName(fd)));
boolean isExtensionFunction = fd.getReceiverType() != null;
int paramCount = fd.getUnsubstitutedValueParameters().size();
if (isExtensionFunction) {
ensureReceiverOnStack(expression, null);
paramCount++;
}
methodDescriptor = ClosureCodegen.erasedInvokeSignature(fd);
pushMethodArguments(expression, methodDescriptor);
v.invokevirtual(ClosureCodegen.getInternalClassName(fd), "invoke", methodDescriptor.getDescriptor());
}
if (methodDescriptor.getReturnType() != Type.VOID_TYPE) {
final Type retType = typeMapper.mapType(fd.getUnsubstitutedReturnType());
StackValue.onStack(methodDescriptor.getReturnType()).upcast(retType, v);
myStack.push(StackValue.onStack(retType));
}
}
else {
throw new CompilationException();
}
}
else {
throw new UnsupportedOperationException("Don't know how to generate a call");
}
}
private JetExpression getReceiverForSelector(JetElement expression) {
if (expression.getParent() instanceof JetDotQualifiedExpression && !isReceiver(expression)) {
final JetDotQualifiedExpression parent = (JetDotQualifiedExpression) expression.getParent();
return parent.getReceiverExpression();
}
return null;
}
private void ensureReceiverOnStack(JetElement expression, @Nullable ClassDescriptor calleeContainingClass) {
JetExpression receiver = getReceiverForSelector(expression);
if (receiver != null) {
if (!resolvesToClassOrPackage(receiver)) {
// we have a receiver on stack
if (myStack.isEmpty()) {
throw new IllegalStateException("expected receiver on stack but it's not there: " + receiver.getText());
}
myStack.pop().put(JetTypeMapper.TYPE_OBJECT, v);
}
}
else if (!(expression.getParent() instanceof JetSafeQualifiedExpression)) {
final StackValue value = generateThisOrOuter(calleeContainingClass);
value.put(value.type, v);
}
}
public StackValue generateThisOrOuter(ClassDescriptor calleeContainingClass) {
final StackValue value = outerThisExpressions.get(calleeContainingClass);
if (value != null) {
return value;
}
else {
// TODO hope it works; really need more checks here :)
if (calleeContainingClass != null && contextType instanceof ClassDescriptor &&
calleeContainingClass == contextType.getContainingDeclaration()) {
v.load(0, JetTypeMapper.TYPE_OBJECT);
return StackValue.field(typeMapper.jvmType(calleeContainingClass, OwnerKind.IMPLEMENTATION),
typeMapper.jvmName((ClassDescriptor) contextType, OwnerKind.IMPLEMENTATION),
"this$0",
false);
// TODO handle more levels of class nestng
}
else {
return thisExpression != null ? thisExpression : StackValue.local(0, JetTypeMapper.TYPE_OBJECT);
}
}
}
private static boolean isReceiver(JetElement expression) {
final PsiElement parent = expression.getParent();
if (parent instanceof JetQualifiedExpression) {
final JetExpression receiverExpression = ((JetQualifiedExpression) parent).getReceiverExpression();
return expression == receiverExpression;
}
return false;
}
private void pushMethodArguments(JetCall expression, Method method) {
final Type[] argTypes = method.getArgumentTypes();
List<JetArgument> args = expression.getValueArguments();
for (int i = 0, argsSize = args.size(); i < argsSize; i++) {
JetArgument arg = args.get(i);
gen(arg.getArgumentExpression(), argTypes[i]);
}
}
private static Method getMethodDescriptor(PsiMethod method) {
Type returnType = method.isConstructor() ? Type.VOID_TYPE : psiTypeToAsm(method.getReturnType());
PsiParameter[] parameters = method.getParameterList().getParameters();
Type[] parameterTypes = new Type[parameters.length];
for (int i = 0; i < parameters.length; i++) {
parameterTypes[i] = psiTypeToAsm(parameters [i].getType());
}
return new Method(method.getName(), Type.getMethodDescriptor(returnType, parameterTypes));
}
private Type expressionType(JetExpression expr) {
return typeMapper.mapType(bindingContext.getExpressionType(expr));
}
private int indexOfLocal(JetReferenceExpression lhs) {
final DeclarationDescriptor declarationDescriptor = bindingContext.resolveReferenceExpression(lhs);
return lookupLocal(declarationDescriptor);
}
private static Type psiTypeToAsm(PsiType type) {
if (type instanceof PsiPrimitiveType) {
if (type == PsiType.VOID) {
return Type.VOID_TYPE;
}
if (type == PsiType.INT) {
return Type.INT_TYPE;
}
if (type == PsiType.LONG) {
return Type.LONG_TYPE;
}
if (type == PsiType.BOOLEAN) {
return Type.BOOLEAN_TYPE;
}
if (type == PsiType.BYTE) {
return Type.BYTE_TYPE;
}
if (type == PsiType.SHORT) {
return Type.SHORT_TYPE;
}
if (type == PsiType.CHAR) {
return Type.CHAR_TYPE;
}
if (type == PsiType.FLOAT) {
return Type.FLOAT_TYPE;
}
if (type == PsiType.DOUBLE) {
return Type.DOUBLE_TYPE;
}
}
if (type instanceof PsiClassType) {
PsiClass psiClass = ((PsiClassType) type).resolve();
if (psiClass instanceof PsiTypeParameter) {
final PsiClassType[] extendsListTypes = psiClass.getExtendsListTypes();
if (extendsListTypes.length > 0) {
throw new UnsupportedOperationException("should return common supertype");
}
return OBJECT_TYPE;
}
if (psiClass == null) {
throw new UnsupportedOperationException("unresolved PsiClassType: " + type);
}
return JetTypeMapper.psiClassType(psiClass);
}
throw new UnsupportedOperationException("don't know how to map type " + type + " to ASM");
}
@Override
public void visitDotQualifiedExpression(JetDotQualifiedExpression expression) {
JetExpression receiver = expression.getReceiverExpression();
if (!resolvesToClassOrPackage(receiver)) {
gen(expression.getReceiverExpression());
}
gen(expression.getSelectorExpression());
}
private boolean resolvesToClassOrPackage(JetExpression receiver) {
if (receiver instanceof JetReferenceExpression) {
DeclarationDescriptor declaration = bindingContext.resolveReferenceExpression((JetReferenceExpression) receiver);
PsiElement declarationElement = bindingContext.getDeclarationPsiElement(declaration);
if (declarationElement instanceof PsiClass) {
return true;
}
}
return false;
}
@Override
public void visitSafeQualifiedExpression(JetSafeQualifiedExpression expression) {
genToJVMStack(expression.getReceiverExpression());
Label ifnull = new Label();
Label end = new Label();
v.dup();
v.ifnull(ifnull);
gen(expression.getSelectorExpression());
v.goTo(end);
v.mark(ifnull);
// null is already on stack here after the dup
JetType expressionType = bindingContext.getExpressionType(expression);
if (expressionType.equals(JetStandardClasses.getUnitType())) {
v.pop();
}
v.mark(end);
}
@Override
public void visitBinaryExpression(JetBinaryExpression expression) {
final IElementType opToken = expression.getOperationReference().getReferencedNameElementType();
if (opToken == JetTokens.EQ) {
generateAssignmentExpression(expression);
}
else if (JetTokens.AUGMENTED_ASSIGNMENTS.contains(opToken)) {
generateAugmentedAssignment(expression);
}
else if (opToken == JetTokens.ANDAND) {
generateBooleanAnd(expression);
}
else if (opToken == JetTokens.OROR) {
generateBooleanOr(expression);
}
else if (opToken == JetTokens.EQEQ || opToken == JetTokens.EXCLEQ ||
opToken == JetTokens.EQEQEQ || opToken == JetTokens.EXCLEQEQEQ) {
generateEquals(expression.getLeft(), expression.getRight(), opToken);
}
else if (opToken == JetTokens.LT || opToken == JetTokens.LTEQ ||
opToken == JetTokens.GT || opToken == JetTokens.GTEQ) {
generateCompareOp(expression.getLeft(), expression.getRight(), opToken, expressionType(expression.getLeft()));
}
else if (opToken == JetTokens.ELVIS) {
generateElvis(expression);
}
else if (opToken == JetTokens.RANGE) {
generateRange(expression);
}
else {
DeclarationDescriptor op = bindingContext.resolveReferenceExpression(expression.getOperationReference());
if (op instanceof FunctionDescriptor) {
DeclarationDescriptor cls = op.getContainingDeclaration();
if (isNumberPrimitive(cls)) {
int opcode = opcodeForMethod(op.getName());
generateBinaryOp(expression, (FunctionDescriptor) op, opcode);
return;
}
else if (isClass(cls, "String") && op.getName().equals("plus")) {
generateConcatenation(expression);
return;
}
}
throw new UnsupportedOperationException("Don't know how to generate binary op " + expression);
}
}
private void generateBooleanAnd(JetBinaryExpression expression) {
gen(expression.getLeft(), Type.BOOLEAN_TYPE);
Label ifFalse = new Label();
v.ifeq(ifFalse);
gen(expression.getRight(), Type.BOOLEAN_TYPE);
Label end = new Label();
v.goTo(end);
v.mark(ifFalse);
v.aconst(false);
v.mark(end);
myStack.push(StackValue.onStack(Type.BOOLEAN_TYPE));
}
private void generateBooleanOr(JetBinaryExpression expression) {
gen(expression.getLeft(), Type.BOOLEAN_TYPE);
Label ifTrue = new Label();
v.ifne(ifTrue);
gen(expression.getRight(), Type.BOOLEAN_TYPE);
Label end = new Label();
v.goTo(end);
v.mark(ifTrue);
v.aconst(true);
v.mark(end);
myStack.push(StackValue.onStack(Type.BOOLEAN_TYPE));
}
private void generateEquals(JetExpression left, JetExpression right, IElementType opToken) {
final Type leftType = expressionType(left);
final Type rightType = expressionType(right);
gen(left, leftType);
gen(right, rightType);
generateEqualsForExpressionsOnStack(opToken, leftType, rightType);
}
private void generateEqualsForExpressionsOnStack(IElementType opToken, Type leftType, Type rightType) {
if (isNumberPrimitive(leftType) && leftType == rightType) {
compareExpressionsOnStack(opToken, leftType);
}
else {
if (opToken == JetTokens.EQEQEQ || opToken == JetTokens.EXCLEQEQEQ) {
myStack.push(StackValue.cmp(opToken, leftType));
}
else {
generateNullSafeEquals(opToken);
}
}
}
private void generateNullSafeEquals(IElementType opToken) {
v.dup2(); // left right left right
Label rightNull = new Label();
v.ifnull(rightNull);
Label leftNull = new Label();
v.ifnull(leftNull);
v.invokevirtual(CLASS_OBJECT, "equals", "(Ljava/lang/Object;)Z");
Label end = new Label();
v.goTo(end);
v.mark(rightNull);
// left right left
Label bothNull = new Label();
v.ifnull(bothNull);
v.mark(leftNull);
v.pop2();
v.aconst(Boolean.FALSE);
v.goTo(end);
v.mark(bothNull);
v.pop2();
v.aconst(Boolean.TRUE);
v.mark(end);
final StackValue onStack = StackValue.onStack(Type.BOOLEAN_TYPE);
if (opToken == JetTokens.EXCLEQ) {
myStack.push(StackValue.not(onStack));
}
else {
myStack.push(onStack);
}
}
private void generateElvis(JetBinaryExpression expression) {
final Type exprType = expressionType(expression);
final Type leftType = expressionType(expression.getLeft());
gen(expression.getLeft(), leftType);
v.dup();
Label end = new Label();
Label ifNull = new Label();
v.ifnull(ifNull);
StackValue.onStack(leftType).put(exprType, v);
v.goTo(end);
v.mark(ifNull);
v.pop();
gen(expression.getRight(), exprType);
v.mark(end);
myStack.push(StackValue.onStack(exprType));
}
private void generateRange(JetBinaryExpression expression) {
final Type leftType = expressionType(expression.getLeft());
if (JetTypeMapper.isIntPrimitive(leftType)) {
v.anew(INT_RANGE_TYPE);
v.dup();
gen(expression.getLeft(), Type.INT_TYPE);
gen(expression.getRight(), Type.INT_TYPE);
v.invokespecial(CLASS_INT_RANGE, "<init>", INT_RANGE_CONSTRUCTOR_DESCRIPTOR);
myStack.push(StackValue.onStack(INT_RANGE_TYPE));
}
else {
throw new UnsupportedOperationException("ranges are only supported for int objects");
}
}
private static boolean isNumberPrimitive(DeclarationDescriptor descriptor) {
if (!(descriptor instanceof ClassDescriptor)) {
return false;
}
String className = descriptor.getName();
return className.equals("Int") || className.equals("Long") || className.equals("Short") ||
className.equals("Byte") || className.equals("Char") || className.equals("Float") ||
className.equals("Double");
}
private static boolean isClass(DeclarationDescriptor descriptor, String name) {
if (!(descriptor instanceof ClassDescriptor)) {
return false;
}
String className = descriptor.getName();
return className.equals(name);
}
private static boolean isNumberPrimitive(Type type) {
return JetTypeMapper.isIntPrimitive(type) || type == Type.FLOAT_TYPE || type == Type.DOUBLE_TYPE || type == Type.LONG_TYPE;
}
private static int opcodeForMethod(final String name) {
if (name.equals("plus")) return Opcodes.IADD;
if (name.equals("minus")) return Opcodes.ISUB;
if (name.equals("times")) return Opcodes.IMUL;
if (name.equals("div")) return Opcodes.IDIV;
if (name.equals("mod")) return Opcodes.IREM;
if (name.equals("shl")) return Opcodes.ISHL;
if (name.equals("shr")) return Opcodes.ISHR;
if (name.equals("ushr")) return Opcodes.IUSHR;
if (name.equals("and")) return Opcodes.IAND;
if (name.equals("or")) return Opcodes.IOR;
if (name.equals("xor")) return Opcodes.IXOR;
throw new UnsupportedOperationException("Don't know how to generate binary op method " + name);
}
private void generateBinaryOp(JetBinaryExpression expression, FunctionDescriptor op, int opcode) {
JetType returnType = op.getUnsubstitutedReturnType();
final Type asmType = typeMapper.mapType(returnType);
if (asmType == Type.INT_TYPE || asmType == Type.LONG_TYPE ||
asmType == Type.FLOAT_TYPE || asmType == Type.DOUBLE_TYPE) {
gen(expression.getLeft(), asmType);
gen(expression.getRight(), asmType);
v.visitInsn(asmType.getOpcode(opcode));
myStack.push(StackValue.onStack(asmType));
}
else {
throw new UnsupportedOperationException("Don't know how to generate binary op with return type " + returnType);
}
}
private void generateCompareOp(JetExpression left, JetExpression right, IElementType opToken, Type operandType) {
gen(left, operandType);
gen(right, operandType);
compareExpressionsOnStack(opToken, operandType);
}
private void compareExpressionsOnStack(IElementType opToken, Type operandType) {
if (operandType.getSort() == Type.OBJECT) {
v.invokeinterface(CLASS_COMPARABLE, "compareTo", "(Ljava/lang/Object;)I");
v.aconst(0);
operandType = Type.INT_TYPE;
}
myStack.push(StackValue.cmp(opToken, operandType));
}
private void generateAssignmentExpression(JetBinaryExpression expression) {
StackValue stackValue = generateIntermediateValue(expression.getLeft());
genToJVMStack(expression.getRight());
stackValue.store(v);
}
private void generateAugmentedAssignment(JetBinaryExpression expression) {
DeclarationDescriptor op = bindingContext.resolveReferenceExpression(expression.getOperationReference());
final JetExpression lhs = expression.getLeft();
Type lhsType = expressionType(lhs);
if (isNumberPrimitive(lhsType)) {
StackValue value = generateIntermediateValue(lhs); // receiver
value.dupReceiver(v, 0); // receiver receiver
value.put(lhsType, v); // receiver lhs
genToJVMStack(expression.getRight()); // receiver lhs rhs
v.visitInsn(lhsType.getOpcode(opcodeForMethod(op.getName()))); // receiver result
value.store(v);
}
else if ("java.lang.String".equals(lhsType.getClassName()) && op.getName().equals("plus")) {
generateStringBuilderConstructor(); // StringBuilder
StackValue value = generateIntermediateValue(lhs); // StringBuilder receiver
value.dupReceiver(v, 1); // receiver StringBuilder receiver
value.put(lhsType, v); // receiver StringBuilder value
invokeAppendMethod(lhsType); // receiver StringBuilder
invokeAppend(expression.getRight()); // receiver StringBuilder
v.invokevirtual(CLASS_STRING_BUILDER, "toString", "()Ljava/lang/String;");
value.store(v);
}
else {
throw new UnsupportedOperationException("Augmented assignment for non-primitive types not yet implemented");
}
}
private void generateConcatenation(JetBinaryExpression expression) {
generateStringBuilderConstructor();
invokeAppend(expression.getLeft());
invokeAppend(expression.getRight());
v.invokevirtual(CLASS_STRING_BUILDER, "toString", "()Ljava/lang/String;");
myStack.push(StackValue.onStack(Type.getObjectType(CLASS_STRING)));
}
private void generateStringBuilderConstructor() {
Type type = Type.getObjectType(CLASS_STRING_BUILDER);
v.anew(type);
v.dup();
Method method = new Method("<init>", Type.VOID_TYPE, new Type[0]);
v.invokespecial(CLASS_STRING_BUILDER, method.getName(), method.getDescriptor());
}
private void invokeAppend(final JetExpression expr) {
if (expr instanceof JetBinaryExpression) {
final JetBinaryExpression binaryExpression = (JetBinaryExpression) expr;
if (binaryExpression.getOperationToken() == JetTokens.PLUS) {
invokeAppend(binaryExpression.getLeft());
invokeAppend(binaryExpression.getRight());
return;
}
}
Type exprType = expressionType(expr);
gen(expr, exprType);
invokeAppendMethod(exprType);
}
private void invokeAppendMethod(Type exprType) {
Method appendDescriptor = new Method("append", Type.getObjectType(CLASS_STRING_BUILDER),
new Type[] { exprType.getSort() == Type.OBJECT ? JetTypeMapper.TYPE_OBJECT : exprType});
v.invokevirtual(CLASS_STRING_BUILDER, "append", appendDescriptor.getDescriptor());
}
@Override
public void visitPrefixExpression(JetPrefixExpression expression) {
DeclarationDescriptor op = bindingContext.resolveReferenceExpression(expression.getOperationSign());
if (op instanceof FunctionDescriptor) {
final Type asmType = expressionType(expression);
DeclarationDescriptor cls = op.getContainingDeclaration();
if (isNumberPrimitive(cls)) {
if (generateUnaryOp(op, asmType, expression.getBaseExpression())) return;
}
else if (isClass(cls, "Boolean") && op.getName().equals("not")) {
generateNot(expression);
return;
}
}
throw new UnsupportedOperationException("Don't know how to generate this prefix expression");
}
@Override
public void visitPostfixExpression(JetPostfixExpression expression) {
DeclarationDescriptor op = bindingContext.resolveReferenceExpression(expression.getOperationSign());
if (op instanceof FunctionDescriptor) {
final Type asmType = expressionType(expression);
DeclarationDescriptor cls = op.getContainingDeclaration();
if (isNumberPrimitive(cls) && (op.getName().equals("inc") || op.getName().equals("dec"))) {
if (bindingContext.isStatement(expression)) {
generateIncrement(op, asmType, expression.getBaseExpression());
}
else {
int oldStackSize = myStack.size();
gen(expression.getBaseExpression(), asmType);
generateIncrement(op, asmType, expression.getBaseExpression());
myStack.push(StackValue.onStack(asmType));
assert myStack.size() == oldStackSize+1;
}
return;
}
}
throw new UnsupportedOperationException("Don't know how to generate this prefix expression");
}
private boolean generateUnaryOp(DeclarationDescriptor op, Type asmType, final JetExpression operand) {
if (op.getName().equals("minus")) {
gen(operand, asmType);
v.neg(asmType);
myStack.push(StackValue.onStack(asmType));
return true;
}
else if (op.getName().equals("inc") || op.getName().equals("dec")) {
myStack.push(generateIncrement(op, asmType, operand));
return true;
}
return false;
}
private void generateNot(JetPrefixExpression expression) {
final StackValue stackValue = generateIntermediateValue(expression.getBaseExpression());
myStack.push(StackValue.not(stackValue));
}
private StackValue generateIncrement(DeclarationDescriptor op, Type asmType, JetExpression operand) {
int increment = op.getName().equals("inc") ? 1 : -1;
if (operand instanceof JetReferenceExpression) {
final int index = indexOfLocal((JetReferenceExpression) operand);
if (index >= 0 && JetTypeMapper.isIntPrimitive(asmType)) {
v.iinc(index, increment);
return StackValue.local(index, asmType);
}
}
StackValue value = generateIntermediateValue(operand);
value.dupReceiver(v, 0);
value.put(asmType, v);
if (asmType == Type.LONG_TYPE) {
v.aconst(Long.valueOf(increment));
}
else if (asmType == Type.FLOAT_TYPE) {
v.aconst(Float.valueOf(increment));
}
else if (asmType == Type.DOUBLE_TYPE) {
v.aconst(Double.valueOf(increment));
}
else {
v.aconst(increment);
}
v.add(asmType);
value.store(v);
return value;
}
private void generateInv(Type asmType) {
v.aconst(-1);
v.xor(asmType);
myStack.push(StackValue.onStack(asmType));
}
@Override
public void visitProperty(JetProperty property) {
VariableDescriptor variableDescriptor = bindingContext.getVariableDescriptor(property);
int index = lookupLocal(variableDescriptor);
assert index >= 0;
JetExpression initializer = property.getInitializer();
if (initializer != null) {
Type type = typeMapper.mapType(variableDescriptor.getOutType());
gen(initializer, type);
v.store(index, type);
}
}
private void generateConstructorCall(JetCallExpression expression, JetSimpleNameExpression constructorReference) {
DeclarationDescriptor constructorDescriptor = bindingContext.resolveReferenceExpression(constructorReference);
final PsiElement declaration = bindingContext.getDeclarationPsiElement(constructorDescriptor);
Type type;
if (declaration instanceof PsiMethod) {
type = generateJavaConstructorCall(expression, (PsiMethod) declaration);
}
else if (constructorDescriptor instanceof ConstructorDescriptor) {
type = typeMapper.mapType(bindingContext.getExpressionType(expression), OwnerKind.IMPLEMENTATION);
if (type.getSort() == Type.ARRAY) {
generateNewArray(expression, type);
}
else {
ClassDescriptor classDecl = (ClassDescriptor) constructorDescriptor.getContainingDeclaration();
v.anew(type);
v.dup();
// TODO typechecker must verify that we're the outer class of the instance being created
pushOuterClassArguments(classDecl);
Method method = typeMapper.mapConstructorSignature((ConstructorDescriptor) constructorDescriptor, OwnerKind.IMPLEMENTATION);
pushMethodArguments(expression, method);
for (JetTypeProjection jetTypeArgument : expression.getTypeArguments()) {
JetType typeArgument = bindingContext.resolveTypeReference(jetTypeArgument.getTypeReference());
// TODO is the makeNullable() call correct here?
ClassCodegen.newTypeInfo(v, typeMapper.mapType(TypeUtils.makeNullable(typeArgument)));
}
v.invokespecial(JetTypeMapper.jvmNameForImplementation(classDecl), "<init>", method.getDescriptor());
}
}
else {
throw new UnsupportedOperationException("don't know how to generate this new expression");
}
myStack.push(StackValue.onStack(type));
}
private void pushOuterClassArguments(ClassDescriptor classDecl) {
if (classDecl.getContainingDeclaration() instanceof ClassDescriptor) {
v.load(0, JetTypeMapper.jetImplementationType(classDecl));
}
}
private Type generateJavaConstructorCall(JetCallExpression expression, PsiMethod constructor) {
PsiClass javaClass = constructor.getContainingClass();
Type type = JetTypeMapper.psiClassType(javaClass);
v.anew(type);
v.dup();
final Method jvmConstructor = getMethodDescriptor(constructor);
pushMethodArguments(expression, jvmConstructor);
v.invokespecial(JetTypeMapper.jvmName(javaClass), "<init>", jvmConstructor.getDescriptor());
return type;
}
private void generateNewArray(JetCallExpression expression, Type type) {
List<JetArgument> args = expression.getValueArguments();
if (args.size() != 1) {
throw new CompilationException("array constructor requires one value argument");
}
gen(args.get(0).getArgumentExpression(), Type.INT_TYPE);
v.newarray(type.getElementType());
}
@Override
public void visitArrayAccessExpression(JetArrayAccessExpression expression) {
final JetExpression array = expression.getArrayExpression();
final Type arrayType = expressionType(array);
if (arrayType.getSort() == Type.ARRAY) {
gen(array, arrayType);
generateArrayIndex(expression);
final Type elementType = arrayType.getElementType();
myStack.push(StackValue.arrayElement(elementType));
}
else {
throw new UnsupportedOperationException("array access to non-Java arrays is not supported");
}
}
private void generateArrayIndex(JetArrayAccessExpression expression) {
final List<JetExpression> indices = expression.getIndexExpressions();
if (indices.size() != 1) {
throw new UnsupportedOperationException("array access with more than one index is not supported");
}
if (!expressionType(indices.get(0)).equals(Type.INT_TYPE)) {
throw new UnsupportedOperationException("array access with non-integer is not supported");
}
gen(indices.get(0), Type.INT_TYPE);
}
@Override
public void visitThrowExpression(JetThrowExpression expression) {
gen(expression.getThrownExpression(), JetTypeMapper.TYPE_OBJECT);
v.athrow();
}
@Override
public void visitThisExpression(JetThisExpression expression) {
final DeclarationDescriptor descriptor = bindingContext.resolveReferenceExpression(expression.getThisReference());
if (descriptor instanceof ClassDescriptor) {
myStack.push(generateThisOrOuter((ClassDescriptor) descriptor));
}
else {
generateThis();
}
}
public void thisToStack() {
generateThis();
putTopOfStack(JetTypeMapper.TYPE_OBJECT);
}
private void generateThis() {
if (thisExpression != null) {
myStack.push(thisExpression);
return;
}
if (contextKind != OwnerKind.NAMESPACE) {
ClassDescriptor contextClass = (ClassDescriptor) contextType;
final Type thisType = typeMapper.jvmType(contextClass, contextKind);
if (contextKind == OwnerKind.IMPLEMENTATION) {
myStack.push(StackValue.local(0, thisType));
return;
}
else if (contextKind == OwnerKind.DELEGATING_IMPLEMENTATION) {
v.load(0, thisType);
myStack.push(StackValue.field(JetTypeMapper.jetInterfaceType(contextClass),
thisType.getInternalName(),
"$this",
false));
return;
}
}
throw new UnsupportedOperationException("'this' expression is not defined in the context");
}
@Override
public void visitTryExpression(JetTryExpression expression) {
Label tryStart = new Label();
v.mark(tryStart);
gen(expression.getTryBlock(), Type.VOID_TYPE);
Label tryEnd = new Label();
v.mark(tryEnd);
JetFinallySection finallyBlock = expression.getFinallyBlock();
if (finallyBlock != null) {
gen(finallyBlock.getFinalExpression(), Type.VOID_TYPE);
}
Label end = new Label();
v.goTo(end); // TODO don't generate goto if there's no code following try/catch
for (JetCatchClause clause : expression.getCatchClauses()) {
Label clauseStart = new Label();
v.mark(clauseStart);
VariableDescriptor descriptor = bindingContext.getVariableDescriptor(clause.getCatchParameter());
Type descriptorType = typeMapper.mapType(descriptor.getOutType());
myMap.enter(descriptor, 1);
int index = lookupLocal(descriptor);
v.store(index, descriptorType);
gen(clause.getCatchBody(), Type.VOID_TYPE);
v.goTo(end); // TODO don't generate goto if there's no code following try/catch
myMap.leave(descriptor);
v.visitTryCatchBlock(tryStart, tryEnd, clauseStart, descriptorType.getInternalName());
}
if (finallyBlock != null) {
Label finallyStart = new Label();
v.mark(finallyStart);
int index = myMap.enterTemp();
v.store(index, THROWABLE_TYPE);
gen(finallyBlock.getFinalExpression(), Type.VOID_TYPE);
v.load(index, THROWABLE_TYPE);
v.athrow();
myMap.leaveTemp();
v.visitTryCatchBlock(tryStart, tryEnd, finallyStart, null);
}
v.mark(end);
}
@Override
public void visitBinaryWithTypeRHSExpression(final JetBinaryExpressionWithTypeRHS expression) {
JetSimpleNameExpression operationSign = expression.getOperationSign();
IElementType opToken = operationSign.getReferencedNameElementType();
if (opToken == JetTokens.COLON) {
gen(expression.getLeft());
}
else {
JetTypeReference typeReference = expression.getRight();
JetType jetType = bindingContext.resolveTypeReference(typeReference);
DeclarationDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor();
if (!(descriptor instanceof ClassDescriptor)) {
throw new UnsupportedOperationException("don't know how to handle non-class types in as/as?");
}
Type type = typeMapper.mapType(jetType, OwnerKind.INTERFACE);
generateInstanceOf(new Runnable() {
@Override
public void run() {
gen(expression.getLeft(), OBJECT_TYPE);
}
}, jetType, true);
Label isInstance = new Label();
v.ifne(isInstance);
v.pop();
if (opToken == JetTokens.AS_SAFE) {
v.aconst(null);
}
else {
throwNewException(CLASS_TYPE_CAST_EXCEPTION);
}
v.mark(isInstance);
v.checkcast(type);
myStack.push(StackValue.onStack(type));
}
}
@Override
public void visitTypeofExpression(JetTypeofExpression expression) {
gen(expression.getBaseExpression(), JET_OBJECT_TYPE);
v.invokeinterface("jet/JetObject", "getTypeInfo", "()Ljet/typeinfo/TypeInfo;");
}
@Override
public void visitIsExpression(final JetIsExpression expression) {
generatePatternMatch(expression.getPattern(), expression.isNegated(), new Runnable() {
@Override
public void run() {
gen(expression.getLeftHandSide(), OBJECT_TYPE);
}
});
}
private void generatePatternMatch(JetPattern pattern, boolean negated, Runnable expressionGen) {
if (pattern instanceof JetTypePattern) {
JetTypeReference typeReference = ((JetTypePattern) pattern).getTypeReference();
JetType jetType = bindingContext.resolveTypeReference(typeReference);
generateInstanceOf(expressionGen, jetType, false);
StackValue value = StackValue.onStack(Type.BOOLEAN_TYPE);
myStack.push(negated ? StackValue.not(value) : value);
}
else if (pattern instanceof JetWildcardPattern) {
myStack.push(StackValue.constant(!negated, Type.BOOLEAN_TYPE));
}
else {
throw new UnsupportedOperationException("Unsupported pattern type: " + pattern);
}
}
private void generateInstanceOf(Runnable expressionGen, JetType jetType, boolean leaveExpressionOnStack) {
DeclarationDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor();
if (!(descriptor instanceof ClassDescriptor)) {
throw new UnsupportedOperationException("don't know how to handle non-class types");
}
if (jetType.getArguments().size() > 0) {
generateTypeInfo(jetType);
expressionGen.run();
if (leaveExpressionOnStack) {
v.dupX1();
}
v.invokevirtual("jet/typeinfo/TypeInfo", "isInstance", "(Ljava/lang/Object;)Z");
}
else {
expressionGen.run();
if (leaveExpressionOnStack) {
v.dup();
}
Type type = typeMapper.mapType(jetType, OwnerKind.INTERFACE);
v.instanceOf(type);
}
}
private void generateTypeInfo(JetType jetType) {
DeclarationDescriptor declarationDescriptor = jetType.getConstructor().getDeclarationDescriptor();
if (declarationDescriptor instanceof TypeParameterDescriptor) {
DeclarationDescriptor containingDeclaration = declarationDescriptor.getContainingDeclaration();
if (containingDeclaration == contextType && contextType instanceof ClassDescriptor) {
loadTypeInfo((ClassDescriptor) contextType, v);
v.iconst(((TypeParameterDescriptor) declarationDescriptor).getIndex());
v.invokevirtual("jet/typeinfo/TypeInfo", "getTypeParameter", "(I)Ljet/typeinfo/TypeInfo;");
return;
}
throw new UnsupportedOperationException("don't know what this type parameter resolves to");
}
v.anew(JetTypeMapper.TYPE_TYPEINFO);
v.dup();
v.aconst(typeMapper.jvmType((ClassDescriptor) declarationDescriptor, OwnerKind.INTERFACE));
List<TypeProjection> arguments = jetType.getArguments();
if (arguments.size() > 0) {
v.iconst(arguments.size());
v.newarray(JetTypeMapper.TYPE_TYPEINFO);
for (int i = 0, argumentsSize = arguments.size(); i < argumentsSize; i++) {
TypeProjection argument = arguments.get(i);
v.dup();
v.iconst(i);
generateTypeInfo(argument.getType());
v.astore(JetTypeMapper.TYPE_OBJECT);
}
v.invokespecial("jet/typeinfo/TypeInfo", "<init>", "(Ljava/lang/Class;[Ljet/typeinfo/TypeInfo;)V");
}
else {
v.invokespecial("jet/typeinfo/TypeInfo", "<init>", "(Ljava/lang/Class;)V");
}
}
@Override
public void visitWhenExpression(JetWhenExpression expression) {
JetExpression expr = expression.getSubjectExpression();
final Type subjectType = expressionType(expr);
final int subjectLocal = myMap.enterTemp(subjectType.getSize());
gen(expr, subjectType);
v.store(subjectLocal, subjectType);
Label end = new Label();
Label nextEntry = null;
boolean hasElse = false;
for (JetWhenEntry whenEntry : expression.getEntries()) {
if (nextEntry != null) {
v.mark(nextEntry);
}
nextEntry = new Label();
if (!whenEntry.isElse()) {
JetWhenCondition condition = whenEntry.getCondition();
if (condition instanceof JetWhenConditionWithExpression) {
v.load(subjectLocal, subjectType);
JetExpression condExpression = ((JetWhenConditionWithExpression) condition).getExpression();
Type condType = expressionType(condExpression);
gen(condExpression, condType);
generateEqualsForExpressionsOnStack(JetTokens.EQEQ, subjectType, condType);
}
else if (condition instanceof JetWhenConditionInRange) {
JetExpression range = ((JetWhenConditionInRange) condition).getRangeExpression();
gen(range, RANGE_TYPE);
new StackValue.Local(subjectLocal, subjectType).put(INTEGER_TYPE, v);
v.invokeinterface(CLASS_RANGE, "contains", "(Ljava/lang/Comparable;)Z");
myStack.push(new StackValue.OnStack(Type.BOOLEAN_TYPE));
}
else if (condition instanceof JetWhenConditionIsPattern) {
JetWhenConditionIsPattern patternCondition = (JetWhenConditionIsPattern) condition;
JetPattern pattern = patternCondition.getPattern();
generatePatternMatch(pattern, patternCondition.isNegated(), new Runnable() {
@Override
public void run() {
new StackValue.Local(subjectLocal, subjectType).put(OBJECT_TYPE, v);
}
});
}
else {
throw new UnsupportedOperationException("unsupported kind of when condition");
}
myStack.pop().condJump(nextEntry, true, v);
}
else {
hasElse = true;
}
genToJVMStack(whenEntry.getExpression());
v.goTo(end);
}
if (!hasElse && nextEntry != null) {
v.mark(nextEntry);
throwNewException(CLASS_NO_PATTERN_MATCHED_EXCEPTION);
}
v.mark(end);
final Type type = expressionType(expression);
if (type.getSort() != Type.VOID) {
myStack.push(StackValue.onStack(type));
}
myMap.leaveTemp(subjectType.getSize());
}
private void throwNewException(final String className) {
v.anew(Type.getObjectType(className));
v.dup();
v.invokespecial(className, "<init>", "()V");
v.athrow();
}
private static class CompilationException extends RuntimeException {
private CompilationException() {
}
private CompilationException(String message) {
super(message);
}
}
}