diff --git a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index a21ea8a50dc..64340ceb16a 100644 --- a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -229,7 +229,7 @@ public class ExpressionCodegen extends JetVisitor { final JetType expressionType = bindingContext.getExpressionType(loopRange); Type loopRangeType = typeMapper.mapType(expressionType); if (loopRangeType.getSort() == Type.ARRAY) { - generateForInArray(expression, loopRangeType); + new ForInArrayLoopGenerator(expression, loopRangeType).invoke(); } else { final DeclarationDescriptor descriptor = expressionType.getConstructor().getDeclarationDescriptor(); @@ -242,57 +242,14 @@ public class ExpressionCodegen extends JetVisitor { 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 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); @@ -335,6 +292,140 @@ public class ExpressionCodegen extends JetVisitor { 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(myMap.getIndex(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(myMap.getIndex(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(myMap.getIndex(parameterDescriptor), Type.INT_TYPE); + v.load(myEndVar, Type.INT_TYPE); + v.ificmpgt(end); + } + + @Override + protected void generateIncrement() { + v.iinc(myMap.getIndex(parameterDescriptor), 1); // TODO support decreasing order + } + + @Override + protected void cleanupTemp() { + myMap.leaveTemp(2); + } + } + @Override public void visitBreakExpression(JetBreakExpression expression) { JetSimpleNameExpression labelElement = expression.getTargetLabel(); @@ -444,8 +535,8 @@ public class ExpressionCodegen extends JetVisitor { @Override public void visitSimpleNameExpression(JetSimpleNameExpression expression) { final DeclarationDescriptor descriptor = bindingContext.resolveReferenceExpression(expression); + final DeclarationDescriptor container = descriptor.getContainingDeclaration(); if (descriptor instanceof VariableDescriptor) { - final DeclarationDescriptor container = descriptor.getContainingDeclaration(); if (isClass(container, "Number")) { Type castType = getCastType(expression.getReferencedName()); if (castType != null) { @@ -492,13 +583,20 @@ public class ExpressionCodegen extends JetVisitor { } } - boolean isStatic = descriptor.getContainingDeclaration() instanceof NamespaceDescriptorImpl; - final boolean directToField = expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER; - final StackValue iValue = intermediateValueForProperty(propertyDescriptor, directToField); - if (!isStatic) { + if (isClass(container, "Array") && propertyDescriptor.getName().equals("size")) { ensureReceiverOnStack(expression); + v.arraylength(); + myStack.push(StackValue.onStack(Type.INT_TYPE)); + } + else { + boolean isStatic = container instanceof NamespaceDescriptorImpl; + final boolean directToField = expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER; + final StackValue iValue = intermediateValueForProperty(propertyDescriptor, directToField); + if (!isStatic) { + ensureReceiverOnStack(expression); + } + myStack.push(iValue); } - myStack.push(iValue); } else { throw new UnsupportedOperationException("don't know how to generate reference " + descriptor); @@ -1153,43 +1251,61 @@ public class ExpressionCodegen extends JetVisitor { @Override public void visitNewExpression(JetNewExpression expression) { - final JetUserType constructorType = (JetUserType) expression.getTypeReference().getTypeElement(); + JetTypeReference typeReference = expression.getTypeReference(); + final JetUserType constructorType = (JetUserType) typeReference.getTypeElement(); final JetSimpleNameExpression constructorReference = constructorType.getReferenceExpression(); DeclarationDescriptor constructorDescriptor = bindingContext.resolveReferenceExpression(constructorReference); final PsiElement declaration = bindingContext.getDeclarationPsiElement(constructorDescriptor); + Type type; 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), "", jvmConstructor.getDescriptor()); - myStack.push(StackValue.onStack(type)); - return; + type = generateJavaConstructorCall(expression, (PsiMethod) declaration); } 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))); + type = typeMapper.mapType(bindingContext.resolveTypeReference(typeReference), OwnerKind.IMPLEMENTATION); + if (type.getSort() == Type.ARRAY) { + generateNewArray(expression, type); } + else { + v.anew(type); + v.dup(); - v.invokespecial(JetTypeMapper.jvmNameForImplementation(classDecl), "", method.getDescriptor()); - myStack.push(StackValue.onStack(type)); - return; + 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))); + } + + ClassDescriptor classDecl = (ClassDescriptor) constructorDescriptor.getContainingDeclaration(); + v.invokespecial(JetTypeMapper.jvmNameForImplementation(classDecl), "", method.getDescriptor()); + } } + else { + throw new UnsupportedOperationException("don't know how to generate this new expression"); + } + myStack.push(StackValue.onStack(type)); + } - throw new UnsupportedOperationException("don't know how to generate this new expression"); + private Type generateJavaConstructorCall(JetNewExpression 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), "", jvmConstructor.getDescriptor()); + return type; + } + + private void generateNewArray(JetNewExpression expression, Type type) { + List 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 @@ -1308,7 +1424,7 @@ public class ExpressionCodegen extends JetVisitor { 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); + Type type = typeMapper.mapType(jetType, OwnerKind.INTERFACE); gen(expression.getLeft(), OBJECT_TYPE); if (opToken == JetTokens.AS_SAFE) { generateInstanceOf(new Runnable() { @@ -1380,7 +1496,7 @@ public class ExpressionCodegen extends JetVisitor { if (leaveExpressionOnStack) { v.dup(); } - Type type = typeMapper.jvmType((ClassDescriptor) descriptor, OwnerKind.INTERFACE); + Type type = typeMapper.mapType(jetType, OwnerKind.INTERFACE); v.instanceOf(type); } } @@ -1487,5 +1603,11 @@ public class ExpressionCodegen extends JetVisitor { } private static class CompilationException extends RuntimeException { + private CompilationException() { + } + + private CompilationException(String message) { + super(message); + } } } diff --git a/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java b/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java index c82d00dbb52..2414093a025 100644 --- a/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java +++ b/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java @@ -3,6 +3,7 @@ package org.jetbrains.jet.codegen; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import jet.typeinfo.TypeInfo; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; @@ -118,6 +119,10 @@ public class JetTypeMapper { } public Type mapType(final JetType jetType) { + return mapType(jetType, OwnerKind.INTERFACE); + } + + public Type mapType(final JetType jetType, OwnerKind kind) { if (jetType.equals(JetStandardClasses.getUnitType()) || jetType.equals(JetStandardClasses.getNothingType())) { return Type.VOID_TYPE; } @@ -191,7 +196,7 @@ public class JetTypeMapper { if (declaration instanceof PsiClass) { return psiClassType((PsiClass) declaration); } - return Type.getObjectType(jvmNameForInterface((ClassDescriptor) descriptor)); + return Type.getObjectType(jetJvmName((ClassDescriptor) descriptor, kind)); } throw new UnsupportedOperationException("Unknown type " + jetType); diff --git a/idea/tests/org/jetbrains/jet/codegen/ControlStructuresTest.java b/idea/tests/org/jetbrains/jet/codegen/ControlStructuresTest.java index 8d7e06b291c..41949f40385 100644 --- a/idea/tests/org/jetbrains/jet/codegen/ControlStructuresTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/ControlStructuresTest.java @@ -92,6 +92,14 @@ public class ControlStructuresTest extends CodegenTestCase { assertEquals("IntelliJ IDEA", main.invoke(null, new Object[] { args })); } + public void testForInRange() throws Exception { + loadText("fun foo(sb: StringBuilder) { for(x in 1..4) sb.append(x) }"); + final Method main = generateFunction(); + StringBuilder stringBuilder = new StringBuilder(); + main.invoke(null, stringBuilder); + assertEquals("1234", stringBuilder.toString()); + } + public void testThrowCheckedException() throws Exception { loadText("fun foo() { throw new Exception(); }"); final Method main = generateFunction(); diff --git a/idea/tests/org/jetbrains/jet/codegen/NamespaceGenTest.java b/idea/tests/org/jetbrains/jet/codegen/NamespaceGenTest.java index fd74ebf0f0e..675ec2ee193 100644 --- a/idea/tests/org/jetbrains/jet/codegen/NamespaceGenTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/NamespaceGenTest.java @@ -360,6 +360,23 @@ public class NamespaceGenTest extends CodegenTestCase { assertEquals(10, data[0]); } + public void testArrayNew() throws Exception { + loadText("fun foo() = new Array(4)"); + final Method main = generateFunction(); + int[] result = (int[]) main.invoke(null); + assertEquals(4, result.length); + } + + public void testArraySize() throws Exception { + loadText("fun foo(a: Array) = a.size"); + System.out.println(generateToText()); + final Method main = generateFunction(); + Object[] args = new Object[] { new int[4] }; + int result = (Integer) main.invoke(null, args); + assertEquals(4, result); + + } + public void testIntRange() throws Exception { loadText("fun foo() = 1..10"); final Method main = generateFunction(); diff --git a/stdlib/src/jet/IntRange.java b/stdlib/src/jet/IntRange.java index b816414f65b..dc538cddc4b 100644 --- a/stdlib/src/jet/IntRange.java +++ b/stdlib/src/jet/IntRange.java @@ -17,4 +17,12 @@ public class IntRange implements Range { } return item <= startValue && item >= endValue; } + + public int getStartValue() { + return startValue; + } + + public int getEndValue() { + return endValue; + } }