Files
kotlin-fork/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java
T
2011-05-19 16:30:23 +02:00

1445 lines
60 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.NoPatternMatchedException;
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.Iterator;
import java.util.List;
import java.util.Stack;
/**
* @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 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 ITERATOR_TYPE = Type.getType(Iterator.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 static final Type NO_PATTERN_MATCHED_EXCEPTION_TYPE = Type.getType(NoPatternMatchedException.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 Type returnType;
private final DeclarationDescriptor contextType;
private final OwnerKind contextKind;
private final BindingContext bindingContext;
public ExpressionCodegen(MethodVisitor v,
BindingContext bindingContext,
FrameMap myMap,
JetTypeMapper typeMapper,
Type returnType,
DeclarationDescriptor contextType,
OwnerKind contextKind) {
this.myMap = myMap;
this.typeMapper = typeMapper;
this.returnType = returnType;
this.contextType = contextType;
this.contextKind = contextKind;
this.v = new InstructionAdapter(v);
this.bindingContext = bindingContext;
}
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) {
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) {
generateForInArray(expression, loopRangeType);
}
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;
}
}
throw new UnsupportedOperationException("for/in loop currently only supported for arrays and Iterable instances");
}
}
private void generateForInArray(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 lengthVar = myMap.enterTemp();
gen(expression.getLoopRange(), loopRangeType);
v.arraylength();
v.store(lengthVar, Type.INT_TYPE);
int indexVar = myMap.enterTemp();
v.aconst(0);
v.store(indexVar, Type.INT_TYPE);
myMap.enter(parameterDescriptor, asmParamType.getSize());
Label condition = new Label();
Label increment = new Label();
Label end = new Label();
v.mark(condition);
myContinueTargets.push(increment);
myBreakTargets.push(end);
v.load(indexVar, Type.INT_TYPE);
v.load(lengthVar, Type.INT_TYPE);
v.ificmpge(end);
gen(expression.getLoopRange(), loopRangeType); // array
v.load(indexVar, Type.INT_TYPE);
v.aload(loopRangeType.getElementType());
StackValue.onStack(loopRangeType.getElementType()).put(asmParamType, v);
v.store(myMap.getIndex(parameterDescriptor), asmParamType);
gen(expression.getBody(), Type.VOID_TYPE);
v.mark(increment);
v.iinc(indexVar, 1);
v.goTo(condition);
v.mark(end);
final int paramIndex = myMap.leave(parameterDescriptor);
v.visitLocalVariable(loopParameter.getName(), asmParamType.getDescriptor(), null, condition, end, paramIndex);
myMap.leaveTemp();
myMap.leaveTemp();
myBreakTargets.pop();
myContinueTargets.pop();
}
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(myMap.getIndex(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();
}
@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.getBody());
}
else {
throw new UnsupportedOperationException("don't know how to generate non-block function literals");
}
}
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) {
StackValue value = myStack.pop();
value.put(returnType, v);
v.areturn(returnType);
}
}
@Override
public void visitSimpleNameExpression(JetSimpleNameExpression expression) {
final DeclarationDescriptor descriptor = bindingContext.resolveReferenceExpression(expression);
if (descriptor instanceof VariableDescriptor) {
final DeclarationDescriptor container = descriptor.getContainingDeclaration();
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);
}
myStack.push(StackValue.field(fieldType, owner, psiField.getName(), isStatic));
}
else {
int index = myMap.getIndex(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(myMap.getIndex(parameter), typeMapper.mapType(outType)));
return;
}
}
}
}
boolean isStatic = descriptor.getContainingDeclaration() instanceof NamespaceDescriptorImpl;
final boolean directToField = expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER;
final StackValue iValue = intermediateValueForProperty(propertyDescriptor, directToField);
if (!isStatic) {
ensureReceiverOnStack(expression);
}
myStack.push(iValue);
}
else {
throw new UnsupportedOperationException("don't know how to generate reference " + descriptor);
}
}
}
public StackValue intermediateValueForProperty(PropertyDescriptor propertyDescriptor, final boolean directToField) {
boolean isStatic = propertyDescriptor.getContainingDeclaration() instanceof NamespaceDescriptorImpl;
final JetType outType = propertyDescriptor.getOutType();
boolean isInsideClass = propertyDescriptor.getContainingDeclaration() == contextType;
Method getter;
Method setter;
if (directToField) {
getter = null;
setter = null;
}
else {
getter = isInsideClass && propertyDescriptor.getGetter() == null ? null : typeMapper.mapGetterSignature(propertyDescriptor);
setter = isInsideClass && propertyDescriptor.getSetter() == null ? null : typeMapper.mapSetterSignature(propertyDescriptor);
}
String fieldOwner;
String interfaceOwner;
if (isInsideClass || isStatic) {
fieldOwner = interfaceOwner = typeMapper.getOwner(propertyDescriptor, contextKind);
}
else {
fieldOwner = null;
interfaceOwner = typeMapper.getOwner(propertyDescriptor, OwnerKind.INTERFACE);
}
return StackValue.property(propertyDescriptor.getName(), fieldOwner, interfaceOwner, typeMapper.mapType(outType), isStatic, 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 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);
Method methodDescriptor;
if (declarationPsiElement instanceof PsiMethod) {
PsiMethod psiMethod = (PsiMethod) declarationPsiElement;
methodDescriptor = getMethodDescriptor(psiMethod);
final boolean isStatic = psiMethod.hasModifierProperty(PsiModifier.STATIC);
if (!isStatic) {
ensureReceiverOnStack(expression);
}
pushMethodArguments(expression, methodDescriptor);
v.visitMethodInsn(isStatic ? Opcodes.INVOKESTATIC : Opcodes.INVOKEVIRTUAL,
JetTypeMapper.jvmName(psiMethod.getContainingClass()),
methodDescriptor.getName(),
methodDescriptor.getDescriptor());
}
else {
methodDescriptor = typeMapper.mapSignature((JetFunction) declarationPsiElement);
if (functionParent instanceof NamespaceDescriptorImpl && declarationPsiElement instanceof JetFunction) {
pushMethodArguments(expression, methodDescriptor);
final String owner = NamespaceCodegen.getJVMClassName(DescriptorRenderer.getFQName(functionParent));
v.invokestatic(owner, methodDescriptor.getName(), methodDescriptor.getDescriptor());
}
else if (functionParent instanceof ClassDescriptor && declarationPsiElement instanceof JetFunction) {
ensureReceiverOnStack(expression);
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);
}
}
if (methodDescriptor.getReturnType() != Type.VOID_TYPE) {
myStack.push(StackValue.onStack(methodDescriptor.getReturnType()));
}
}
else {
throw new CompilationException();
}
}
else {
throw new UnsupportedOperationException("Don't know how to generate a call");
}
}
private void ensureReceiverOnStack(JetElement expression) {
if (expression.getParent() instanceof JetDotQualifiedExpression) {
final JetDotQualifiedExpression parent = (JetDotQualifiedExpression) expression.getParent();
if (!resolvesToClassOrPackage(parent.getReceiverExpression())) {
// we have a receiver on stack
myStack.pop().put(JetTypeMapper.TYPE_OBJECT, v);
}
}
else if (!(expression.getParent() instanceof JetSafeQualifiedExpression)) {
v.load(0, JetTypeMapper.TYPE_OBJECT); // TODO hope it works; really need more checks here :)
}
}
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 myMap.getIndex(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 == 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 (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 isIntPrimitive(type) || type == Type.FLOAT_TYPE || type == Type.DOUBLE_TYPE || type == Type.LONG_TYPE;
}
private static boolean isIntPrimitive(Type type) {
return type == Type.INT_TYPE || type == Type.SHORT_TYPE || type == Type.BYTE_TYPE || type == Type.CHAR_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 && 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 = myMap.getIndex(variableDescriptor);
assert index >= 0;
JetExpression initializer = property.getInitializer();
if (initializer != null) {
Type type = typeMapper.mapType(variableDescriptor.getOutType());
gen(initializer, type);
v.store(index, type);
}
}
@Override
public void visitNewExpression(JetNewExpression expression) {
final JetUserType constructorType = (JetUserType) expression.getTypeReference().getTypeElement();
final JetSimpleNameExpression constructorReference = constructorType.getReferenceExpression();
DeclarationDescriptor constructorDescriptor = bindingContext.resolveReferenceExpression(constructorReference);
final PsiElement declaration = bindingContext.getDeclarationPsiElement(constructorDescriptor);
if (declaration instanceof PsiMethod) {
final PsiMethod constructor = (PsiMethod) declaration;
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());
myStack.push(StackValue.onStack(type));
return;
}
else if (constructorDescriptor instanceof ConstructorDescriptor) {
ClassDescriptor classDecl = (ClassDescriptor) constructorDescriptor.getContainingDeclaration();
Type type = JetTypeMapper.jetImplementationType(classDecl);
v.anew(type);
v.dup();
Method method = typeMapper.mapConstructorSignature((ConstructorDescriptor) constructorDescriptor, OwnerKind.IMPLEMENTATION);
pushMethodArguments(expression, method);
for (JetTypeReference typeArgumentReference : constructorType.getTypeArgumentsAsTypes()) {
JetType typeArgument = bindingContext.resolveTypeReference(typeArgumentReference);
// TODO is the makeNullable() call correct here?
ClassCodegen.newTypeInfo(v, typeMapper.mapType(TypeUtils.makeNullable(typeArgument)));
}
v.invokespecial(JetTypeMapper.jvmNameForImplementation(classDecl), "<init>", method.getDescriptor());
myStack.push(StackValue.onStack(type));
return;
}
throw new UnsupportedOperationException("don't know how to generate this new expression");
}
@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) {
thisToStack();
}
public void thisToStack() {
if (contextKind == OwnerKind.NAMESPACE) {
throw new UnsupportedOperationException("Cannot generate this expression in top level context");
}
ClassDescriptor contextClass = (ClassDescriptor) contextType;
if (contextKind == OwnerKind.IMPLEMENTATION) {
v.load(0, JetTypeMapper.jetImplementationType(contextClass));
}
else if (contextKind == OwnerKind.DELEGATING_IMPLEMENTATION) {
v.getfield(typeMapper.jvmName(contextClass, contextKind), "$this", JetTypeMapper.jetInterfaceType(contextClass).getDescriptor());
}
else {
throw new UnsupportedOperationException("Unknown kind: " + contextKind);
}
}
@Override
public void visitTryExpression(JetTryExpression expression) {
if (expression.getFinallyBlock() != null) {
throw new UnsupportedOperationException("finally block in try/catch not yet supported");
}
Label tryStart = new Label();
v.mark(tryStart);
gen(expression.getTryBlock(), Type.VOID_TYPE);
Label tryEnd = new Label();
v.mark(tryEnd);
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 = myMap.getIndex(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());
}
v.mark(end);
}
@Override
public void visitBinaryWithTypeRHSExpression(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.jvmType((ClassDescriptor) descriptor, OwnerKind.INTERFACE);
gen(expression.getLeft(), OBJECT_TYPE);
if (opToken == JetTokens.AS_SAFE) {
generateInstanceOf(expression.getLeft(), jetType, true);
Label isInstance = new Label();
v.ifne(isInstance);
v.pop();
v.aconst(null);
v.mark(isInstance);
myStack.push(StackValue.onStack(type));
}
else {
throw new UnsupportedOperationException("'as' not yet implemented");
}
}
}
@Override
public void visitTypeofExpression(JetTypeofExpression expression) {
gen(expression.getBaseExpression(), JET_OBJECT_TYPE);
v.invokeinterface("jet/JetObject", "getTypeInfo", "()Ljet/typeinfo/TypeInfo;");
}
@Override
public void visitIsExpression(JetIsExpression expression) {
JetPattern pattern = expression.getPattern();
if (!(pattern instanceof JetTypePattern)) {
throw new UnsupportedOperationException("can only generate a type pattern with 'is'");
}
JetTypeReference typeReference = ((JetTypePattern) pattern).getTypeReference();
JetType jetType = bindingContext.resolveTypeReference(typeReference);
generateInstanceOf(expression.getLeftHandSide(), jetType, false);
StackValue value = StackValue.onStack(Type.BOOLEAN_TYPE);
myStack.push(expression.isNot() ? StackValue.not(value) : value);
}
private void generateInstanceOf(JetExpression expression, 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);
gen(expression, OBJECT_TYPE);
if (leaveExpressionOnStack) {
v.dupX1();
}
v.invokevirtual("jet/typeinfo/TypeInfo", "isInstance", "(Ljava/lang/Object;)Z");
}
else {
gen(expression, OBJECT_TYPE);
if (leaveExpressionOnStack) {
v.dup();
}
Type type = typeMapper.jvmType((ClassDescriptor) descriptor, 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();
Type subjectType = expressionType(expr);
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);
myStack.pop().condJump(nextEntry, true, v);
}
else if (condition instanceof JetWhenConditionInRange) {
JetExpression range = ((JetWhenConditionInRange) condition).getRangeExpression();
gen(range, RANGE_TYPE);
new StackValue.Local(subjectLocal, subjectType).put(OBJECT_TYPE, v);
v.invokeinterface(CLASS_RANGE, "contains", "(Ljava/lang/Comparable;)Z");
new StackValue.OnStack(Type.BOOLEAN_TYPE).condJump(nextEntry, true, v);
}
else {
throw new UnsupportedOperationException("unsupported kind of when condition");
}
}
else {
hasElse = true;
}
genToJVMStack(whenEntry.getExpression());
v.goTo(end);
}
if (!hasElse && nextEntry != null) {
v.mark(nextEntry);
v.anew(NO_PATTERN_MATCHED_EXCEPTION_TYPE);
v.dup();
v.invokespecial(CLASS_NO_PATTERN_MATCHED_EXCEPTION, "<init>", "()V");
v.athrow();
}
v.mark(end);
myStack.push(StackValue.onStack(expressionType(expression)));
myMap.leaveTemp(subjectType.getSize());
}
private static class CompilationException extends RuntimeException {
}
}