diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index d4ae9304e3d..fc158df1888 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -39,6 +39,7 @@ import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegen; import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt; import org.jetbrains.kotlin.codegen.coroutines.ResolvedCallWithRealDescriptor; import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension; +import org.jetbrains.kotlin.codegen.forLoop.*; import org.jetbrains.kotlin.codegen.inline.*; import org.jetbrains.kotlin.codegen.intrinsics.*; import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsKt; @@ -53,9 +54,7 @@ import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor; import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor; -import org.jetbrains.kotlin.diagnostics.DiagnosticUtils; import org.jetbrains.kotlin.diagnostics.Errors; -import org.jetbrains.kotlin.incremental.components.NoLookupLocation; import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor; @@ -422,7 +421,7 @@ public class ExpressionCodegen extends KtVisitor impleme } @NotNull - private Type asmType(@NotNull KotlinType type) { + public Type asmType(@NotNull KotlinType type) { return typeMapper.mapType(type); } @@ -605,68 +604,18 @@ public class ExpressionCodegen extends KtVisitor impleme } private void generateFor(@NotNull KtForExpression forExpression) { - ResolvedCall loopRangeCall = RangeCodegenUtil.getLoopRangeResolvedCall(forExpression, bindingContext); - if (loopRangeCall != null) { - AbstractForLoopGenerator optimizedForLoopGenerator = createOptimizedForLoopGeneratorOrNull(forExpression, loopRangeCall); - if (optimizedForLoopGenerator != null) { - generateForLoop(optimizedForLoopGenerator); - return; - } - } - - KtExpression loopRange = forExpression.getLoopRange(); - assert loopRange != null; - KotlinType loopRangeType = bindingContext.getType(loopRange); - assert loopRangeType != null; - Type asmLoopRangeType = asmType(loopRangeType); - if (asmLoopRangeType.getSort() == Type.ARRAY) { - generateForLoop(new ForInArrayLoopGenerator(forExpression)); - } - else if (RangeCodegenUtil.isRange(loopRangeType)) { - generateForLoop(new ForInRangeInstanceLoopGenerator(forExpression)); - } - else if (RangeCodegenUtil.isProgression(loopRangeType)) { - generateForLoop(new ForInProgressionExpressionLoopGenerator(forExpression)); - } - else { - generateForLoop(new IteratorForLoopGenerator(forExpression)); - } - } - - @Nullable - private AbstractForLoopGenerator createOptimizedForLoopGeneratorOrNull( - @NotNull KtForExpression forExpression, - @NotNull ResolvedCall loopRangeCall - ) { - CallableDescriptor loopRangeCallee = loopRangeCall.getResultingDescriptor(); - if (RangeCodegenUtil.isPrimitiveNumberRangeTo(loopRangeCallee)) { - return new ForInRangeLiteralLoopGenerator(forExpression, loopRangeCall); - } - else if (RangeCodegenUtil.isPrimitiveNumberDownTo(loopRangeCallee)) { - return new ForInDownToProgressionLoopGenerator(forExpression, loopRangeCall); - } - else if (RangeCodegenUtil.isArrayOrPrimitiveArrayIndices(loopRangeCallee)) { - return new ForInArrayIndicesRangeLoopGenerator(forExpression, loopRangeCall); - } - else if (RangeCodegenUtil.isCollectionIndices(loopRangeCallee)) { - return new ForInCollectionIndicesRangeLoopGenerator(forExpression, loopRangeCall); - } - else if (RangeCodegenUtil.isCharSequenceIndices(loopRangeCallee)) { - return new ForInCharSequenceIndicesRangeLoopGenerator(forExpression, loopRangeCall); - } - - return null; + generateForLoop(ForLoopGeneratorsKt.getForLoopGenerator(this, forExpression)); } @NotNull - private static KotlinType getExpectedReceiverType(@NotNull ResolvedCall resolvedCall) { + public static KotlinType getExpectedReceiverType(@NotNull ResolvedCall resolvedCall) { ReceiverParameterDescriptor extensionReceiver = resolvedCall.getResultingDescriptor().getExtensionReceiverParameter(); assert extensionReceiver != null : "Extension receiver should be non-null"; return extensionReceiver.getType(); } @Nullable - private static KtExpression getSingleArgumentExpression(@NotNull ResolvedCall resolvedCall) { + public static KtExpression getSingleArgumentExpression(@NotNull ResolvedCall resolvedCall) { List resolvedValueArguments = resolvedCall.getValueArgumentsByIndex(); if (resolvedValueArguments == null) return null; if (resolvedValueArguments.size() != 1) return null; @@ -694,7 +643,7 @@ public class ExpressionCodegen extends KtVisitor impleme PseudoInsnsKt.fakeAlwaysFalseIfeq(v, continueLabel); generator.beforeBody(); - blockStackElements.push(new LoopBlockStackElement(loopExit, continueLabel, targetLabel(generator.forExpression))); + blockStackElements.push(new LoopBlockStackElement(loopExit, continueLabel, targetLabel(generator.getForExpression()))); generator.body(); blockStackElements.pop(); v.mark(continueLabel); @@ -706,616 +655,12 @@ public class ExpressionCodegen extends KtVisitor impleme generator.afterLoop(); } - private abstract class AbstractForLoopGenerator { - - // for (e : E in c) {...} - protected final KtForExpression forExpression; - private final Label loopParameterStartLabel = new Label(); - private final Label bodyEnd = new Label(); - private final List leaveVariableTasks = Lists.newArrayList(); - - protected final KotlinType elementType; - protected final Type asmElementType; - - protected int loopParameterVar; - protected Type loopParameterType; - - private AbstractForLoopGenerator(@NotNull KtForExpression forExpression) { - this.forExpression = forExpression; - this.elementType = getElementType(forExpression); - this.asmElementType = asmType(elementType); - } - - @NotNull - private KotlinType getElementType(KtForExpression forExpression) { - KtExpression loopRange = forExpression.getLoopRange(); - assert loopRange != null; - ResolvedCall nextCall = getNotNull(bindingContext, - LOOP_RANGE_NEXT_RESOLVED_CALL, loopRange, - "No next() function " + DiagnosticUtils.atLocation(loopRange)); - //noinspection ConstantConditions - return nextCall.getResultingDescriptor().getReturnType(); - } - - public void beforeLoop() { - KtParameter loopParameter = forExpression.getLoopParameter(); - if (loopParameter == null) return; - KtDestructuringDeclaration multiParameter = loopParameter.getDestructuringDeclaration(); - if (multiParameter != null) { - // E tmp = tmp.next() - loopParameterType = asmElementType; - loopParameterVar = createLoopTempVariable(asmElementType); - } - else { - // E e = tmp.next() - VariableDescriptor parameterDescriptor = bindingContext.get(VALUE_PARAMETER, loopParameter); - loopParameterType = asmType(parameterDescriptor.getType()); - loopParameterVar = myFrameMap.enter(parameterDescriptor, loopParameterType); - scheduleLeaveVariable(() -> { - myFrameMap.leave(parameterDescriptor); - v.visitLocalVariable(parameterDescriptor.getName().asString(), - loopParameterType.getDescriptor(), null, - loopParameterStartLabel, bodyEnd, - loopParameterVar); - }); - } - } - - public abstract void checkEmptyLoop(@NotNull Label loopExit); - - public abstract void checkPreCondition(@NotNull Label loopExit); - - public void beforeBody() { - assignToLoopParameter(); - v.mark(loopParameterStartLabel); - - KtDestructuringDeclaration destructuringDeclaration = forExpression.getDestructuringDeclaration(); - if (destructuringDeclaration != null) { - generateDestructuringDeclaration(destructuringDeclaration); - } - } - - private void generateDestructuringDeclaration(@NotNull KtDestructuringDeclaration destructuringDeclaration) { - Label destructuringStartLabel = new Label(); - - List componentDescriptors = - CollectionsKt.map(destructuringDeclaration.getEntries(), ExpressionCodegen.this::getVariableDescriptorNotNull); - - for (VariableDescriptor componentDescriptor : CodegenUtilKt.filterOutDescriptorsWithSpecialNames(componentDescriptors)) { - @SuppressWarnings("ConstantConditions") Type componentAsmType = asmType(componentDescriptor.getReturnType()); - int componentVarIndex = myFrameMap.enter(componentDescriptor, componentAsmType); - scheduleLeaveVariable(() -> { - myFrameMap.leave(componentDescriptor); - v.visitLocalVariable(componentDescriptor.getName().asString(), - componentAsmType.getDescriptor(), null, - destructuringStartLabel, bodyEnd, - componentVarIndex); - }); - } - - v.visitLabel(destructuringStartLabel); - - initializeDestructuringDeclarationVariables( - destructuringDeclaration, - new TransientReceiver(elementType), - StackValue.local(loopParameterVar, asmElementType)); - } - - protected abstract void assignToLoopParameter(); - - protected abstract void increment(@NotNull Label loopExit); - - public void body() { - generateLoopBody(forExpression.getBody()); - } - - private void scheduleLeaveVariable(Runnable runnable) { - leaveVariableTasks.add(runnable); - } - - protected int createLoopTempVariable(Type type) { - int varIndex = myFrameMap.enterTemp(type); - scheduleLeaveVariable(() -> myFrameMap.leaveTemp(type)); - return varIndex; - } - - public void afterBody(@NotNull Label loopExit) { - markStartLineNumber(forExpression); - - increment(loopExit); - - v.mark(bodyEnd); - } - - public void afterLoop() { - for (Runnable task : Lists.reverse(leaveVariableTasks)) { - task.run(); - } - } - - // This method consumes range/progression from stack - // The result is stored to local variable - protected void generateRangeOrProgressionProperty( - @NotNull Type loopRangeType, - @NotNull String getterName, - @NotNull Type getterReturnType, - @NotNull Type varType, - int varToStore - ) { - v.invokevirtual(loopRangeType.getInternalName(), getterName, "()" + getterReturnType.getDescriptor(), false); - StackValue.local(varToStore, varType).store(StackValue.onStack(getterReturnType), v); - } - } - - private void generateLoopBody(@Nullable KtExpression body) { + public void generateLoopBody(@Nullable KtExpression body) { if (body != null) { gen(body, Type.VOID_TYPE); } } - private class IteratorForLoopGenerator extends AbstractForLoopGenerator { - - private int iteratorVarIndex; - private final ResolvedCall iteratorCall; - private final ResolvedCall nextCall; - private final Type asmTypeForIterator; - - private IteratorForLoopGenerator(@NotNull KtForExpression forExpression) { - super(forExpression); - - KtExpression loopRange = forExpression.getLoopRange(); - assert loopRange != null; - this.iteratorCall = getNotNull(bindingContext, - LOOP_RANGE_ITERATOR_RESOLVED_CALL, loopRange, - "No .iterator() function " + DiagnosticUtils.atLocation(loopRange)); - - KotlinType iteratorType = iteratorCall.getResultingDescriptor().getReturnType(); - assert iteratorType != null; - this.asmTypeForIterator = asmType(iteratorType); - - this.nextCall = getNotNull(bindingContext, - LOOP_RANGE_NEXT_RESOLVED_CALL, loopRange, - "No next() function " + DiagnosticUtils.atLocation(loopRange)); - } - - @Override - public void beforeLoop() { - super.beforeLoop(); - - // Iterator tmp = c.iterator() - - iteratorVarIndex = createLoopTempVariable(asmTypeForIterator); - - StackValue.local(iteratorVarIndex, asmTypeForIterator).store(invokeFunction(iteratorCall, StackValue.none()), v); - } - - @Override - public void checkEmptyLoop(@NotNull Label loopExit) { - } - - @Override - public void checkPreCondition(@NotNull Label loopExit) { - // tmp.hasNext() - - KtExpression loopRange = forExpression.getLoopRange(); - @SuppressWarnings("ConstantConditions") ResolvedCall hasNextCall = getNotNull(bindingContext, - LOOP_RANGE_HAS_NEXT_RESOLVED_CALL, loopRange, - "No hasNext() function " + DiagnosticUtils.atLocation(loopRange)); - @SuppressWarnings("ConstantConditions") Call fakeCall = makeFakeCall(new TransientReceiver(iteratorCall.getResultingDescriptor().getReturnType())); - StackValue result = invokeFunction(fakeCall, hasNextCall, StackValue.local(iteratorVarIndex, asmTypeForIterator)); - result.put(Type.BOOLEAN_TYPE, v); - - v.ifeq(loopExit); - } - - @Override - protected void assignToLoopParameter() { - @SuppressWarnings("ConstantConditions") Call fakeCall = - makeFakeCall(new TransientReceiver(iteratorCall.getResultingDescriptor().getReturnType())); - StackValue value = invokeFunction(fakeCall, nextCall, StackValue.local(iteratorVarIndex, asmTypeForIterator)); - //noinspection ConstantConditions - StackValue.local(loopParameterVar, loopParameterType).store(value, v); - } - - @Override - protected void increment(@NotNull Label loopExit) { - } - } - - private class ForInArrayLoopGenerator extends AbstractForLoopGenerator { - private int indexVar; - private int arrayVar; - private final KotlinType loopRangeType; - - private ForInArrayLoopGenerator(@NotNull KtForExpression forExpression) { - super(forExpression); - loopRangeType = bindingContext.getType(forExpression.getLoopRange()); - } - - @Override - public void beforeLoop() { - super.beforeLoop(); - - indexVar = createLoopTempVariable(Type.INT_TYPE); - - KtExpression loopRange = forExpression.getLoopRange(); - StackValue value = gen(loopRange); - Type asmLoopRangeType = asmType(loopRangeType); - if (value instanceof StackValue.Local && value.type.equals(asmLoopRangeType)) { - arrayVar = ((StackValue.Local) value).index; // no need to copy local variable into another variable - } - else { - arrayVar = createLoopTempVariable(OBJECT_TYPE); - value.put(asmLoopRangeType, v); - v.store(arrayVar, OBJECT_TYPE); - } - - v.iconst(0); - v.store(indexVar, Type.INT_TYPE); - } - - @Override - public void checkEmptyLoop(@NotNull Label loopExit) { - } - - @Override - public void checkPreCondition(@NotNull Label loopExit) { - v.load(indexVar, Type.INT_TYPE); - v.load(arrayVar, OBJECT_TYPE); - v.arraylength(); - v.ificmpge(loopExit); - } - - @Override - protected void assignToLoopParameter() { - Type arrayElParamType; - if (KotlinBuiltIns.isArray(loopRangeType)) { - arrayElParamType = boxType(asmElementType); - } - else { - arrayElParamType = asmElementType; - } - - v.load(arrayVar, OBJECT_TYPE); - v.load(indexVar, Type.INT_TYPE); - v.aload(arrayElParamType); - StackValue.onStack(arrayElParamType).put(asmElementType, v); - v.store(loopParameterVar, asmElementType); - } - - @Override - protected void increment(@NotNull Label loopExit) { - v.iinc(indexVar, 1); - } - } - - private abstract class AbstractForInProgressionOrRangeLoopGenerator extends AbstractForLoopGenerator { - protected int endVar; - - private StackValue loopParameter; - - private AbstractForInProgressionOrRangeLoopGenerator(@NotNull KtForExpression forExpression) { - super(forExpression); - - switch (asmElementType.getSort()) { - case Type.INT: - case Type.BYTE: - case Type.SHORT: - case Type.CHAR: - case Type.LONG: - break; - - default: - throw new IllegalStateException("Unexpected range element type: " + asmElementType); - } - } - - @Override - public void beforeLoop() { - super.beforeLoop(); - - endVar = createLoopTempVariable(asmElementType); - } - - protected void checkPostCondition(@NotNull Label loopExit) { - assert endVar != -1 : - "endVar must be allocated, endVar = " + endVar; - loopParameter().put(asmElementType, v); - v.load(endVar, asmElementType); - if (asmElementType.getSort() == Type.LONG) { - v.lcmp(); - v.ifeq(loopExit); - } - else { - v.ificmpeq(loopExit); - } - } - - @Override - public void checkPreCondition(@NotNull Label loopExit) { - } - - @NotNull - protected StackValue loopParameter() { - if (loopParameter == null) { - loopParameter = StackValue.local(loopParameterVar, loopParameterType); - } - return loopParameter; - } - } - - private abstract class AbstractForInRangeLoopGenerator extends AbstractForInProgressionOrRangeLoopGenerator { - private final int step; - - private AbstractForInRangeLoopGenerator(@NotNull KtForExpression forExpression, int step) { - super(forExpression); - this.step = step; - assert step == 1 || step == -1 : "'step' should be either 1 or -1: " + step; - } - - private AbstractForInRangeLoopGenerator(@NotNull KtForExpression forExpression) { - this(forExpression, 1); - } - - @Override - public void beforeLoop() { - super.beforeLoop(); - - storeRangeStartAndEnd(); - } - - protected abstract void storeRangeStartAndEnd(); - - @Override - public void checkEmptyLoop(@NotNull Label loopExit) { - loopParameter().put(asmElementType, v); - v.load(endVar, asmElementType); - if (asmElementType.getSort() == Type.LONG) { - v.lcmp(); - if (step > 0) { - v.ifgt(loopExit); - } - else { - v.iflt(loopExit); - } - } - else { - if (step > 0) { - v.ificmpgt(loopExit); - } - else { - v.ificmplt(loopExit); - } - } - } - - @Override - protected void assignToLoopParameter() { - } - - @Override - protected void increment(@NotNull Label loopExit) { - checkPostCondition(loopExit); - - if (loopParameterType == Type.INT_TYPE) { - v.iinc(loopParameterVar, step); - } - else { - StackValue loopParameter = loopParameter(); - loopParameter.put(asmElementType, v); - genIncrement(asmElementType, step, v); - loopParameter.store(StackValue.onStack(asmElementType), v); - } - } - } - - private class ForInRangeLiteralLoopGenerator extends AbstractForInRangeLoopGenerator { - private final ReceiverValue from; - private final KtExpression to; - - private ForInRangeLiteralLoopGenerator(@NotNull KtForExpression forExpression, @NotNull ResolvedCall loopRangeCall) { - super(forExpression); - this.from = loopRangeCall.getDispatchReceiver(); - this.to = getSingleArgumentExpression(loopRangeCall); - } - - @Override - protected void storeRangeStartAndEnd() { - loopParameter().store(generateReceiverValue(from, false), v); - StackValue.local(endVar, asmElementType).store(gen(to), v); - } - } - - private class ForInDownToProgressionLoopGenerator extends AbstractForInRangeLoopGenerator { - private final ReceiverValue from; - private final KtExpression to; - - private ForInDownToProgressionLoopGenerator(@NotNull KtForExpression forExpression, @NotNull ResolvedCall loopRangeCall) { - super(forExpression, -1); - this.from = loopRangeCall.getExtensionReceiver(); - this.to = getSingleArgumentExpression(loopRangeCall); - } - - @Override - protected void storeRangeStartAndEnd() { - loopParameter().store(generateReceiverValue(from, false), v); - StackValue.local(endVar, asmElementType).store(gen(to), v); - } - } - - private class ForInRangeInstanceLoopGenerator extends AbstractForInRangeLoopGenerator { - private ForInRangeInstanceLoopGenerator(@NotNull KtForExpression forExpression) { - super(forExpression); - } - - @Override - protected void storeRangeStartAndEnd() { - KotlinType loopRangeType = bindingContext.getType(forExpression.getLoopRange()); - assert loopRangeType != null; - Type asmLoopRangeType = asmType(loopRangeType); - gen(forExpression.getLoopRange(), asmLoopRangeType); - v.dup(); - - // ranges inherit first and last from corresponding progressions - generateRangeOrProgressionProperty(asmLoopRangeType, "getFirst", asmElementType, loopParameterType, loopParameterVar); - generateRangeOrProgressionProperty(asmLoopRangeType, "getLast", asmElementType, asmElementType, endVar); - } - } - - private abstract class ForInOptimizedIndicesLoopGenerator extends AbstractForInRangeLoopGenerator { - protected final ReceiverValue receiverValue; - protected final KotlinType expectedReceiverType; - - private ForInOptimizedIndicesLoopGenerator(@NotNull KtForExpression forExpression, @NotNull ResolvedCall loopRangeCall) { - super(forExpression); - this.receiverValue = loopRangeCall.getExtensionReceiver(); - this.expectedReceiverType = getExpectedReceiverType(loopRangeCall); - } - - @Override - protected void storeRangeStartAndEnd() { - loopParameter().store(StackValue.constant(0, asmElementType), v); - - StackValue receiver = generateReceiverValue(receiverValue, false); - Type receiverType = asmType(expectedReceiverType); - receiver.put(receiverType, v); - getReceiverSizeAsInt(); - v.iconst(1); - v.sub(Type.INT_TYPE); - StackValue.local(endVar, asmElementType).store(StackValue.onStack(Type.INT_TYPE), v); - } - - /** - * (receiver -> size:I) - */ - protected abstract void getReceiverSizeAsInt(); - } - - private class ForInCollectionIndicesRangeLoopGenerator extends ForInOptimizedIndicesLoopGenerator { - private ForInCollectionIndicesRangeLoopGenerator(@NotNull KtForExpression forExpression, @NotNull ResolvedCall loopRangeCall) { - super(forExpression, loopRangeCall); - } - - @Override - protected void getReceiverSizeAsInt() { - v.invokeinterface("java/util/Collection", "size", "()I"); - } - } - - private class ForInArrayIndicesRangeLoopGenerator extends ForInOptimizedIndicesLoopGenerator { - private ForInArrayIndicesRangeLoopGenerator(@NotNull KtForExpression forExpression, @NotNull ResolvedCall loopRangeCall) { - super(forExpression, loopRangeCall); - } - - @Override - protected void getReceiverSizeAsInt() { - v.arraylength(); - } - } - - private class ForInCharSequenceIndicesRangeLoopGenerator extends ForInOptimizedIndicesLoopGenerator { - private ForInCharSequenceIndicesRangeLoopGenerator(@NotNull KtForExpression forExpression, @NotNull ResolvedCall loopRangeCall) { - super(forExpression, loopRangeCall); - } - - @Override - protected void getReceiverSizeAsInt() { - v.invokeinterface("java/lang/CharSequence", "length", "()I"); - } - } - - private class ForInProgressionExpressionLoopGenerator extends AbstractForInProgressionOrRangeLoopGenerator { - private int incrementVar; - private Type incrementType; - - private ForInProgressionExpressionLoopGenerator(@NotNull KtForExpression forExpression) { - super(forExpression); - } - - @Override - public void beforeLoop() { - super.beforeLoop(); - - incrementVar = createLoopTempVariable(asmElementType); - - KotlinType loopRangeType = bindingContext.getType(forExpression.getLoopRange()); - assert loopRangeType != null; - Type asmLoopRangeType = asmType(loopRangeType); - - Collection incrementProp = - loopRangeType.getMemberScope().getContributedVariables(Name.identifier("step"), NoLookupLocation.FROM_BACKEND); - assert incrementProp.size() == 1 : loopRangeType + " " + incrementProp.size(); - incrementType = asmType(incrementProp.iterator().next().getType()); - - gen(forExpression.getLoopRange(), asmLoopRangeType); - v.dup(); - v.dup(); - - generateRangeOrProgressionProperty(asmLoopRangeType, "getFirst", asmElementType, loopParameterType, loopParameterVar); - generateRangeOrProgressionProperty(asmLoopRangeType, "getLast", asmElementType, asmElementType, endVar); - generateRangeOrProgressionProperty(asmLoopRangeType, "getStep", incrementType, incrementType, incrementVar); - } - - @Override - public void checkEmptyLoop(@NotNull Label loopExit) { - loopParameter().put(asmElementType, v); - v.load(endVar, asmElementType); - v.load(incrementVar, incrementType); - - Label negativeIncrement = new Label(); - Label afterIf = new Label(); - - if (asmElementType.getSort() == Type.LONG) { - v.lconst(0L); - v.lcmp(); - v.ifle(negativeIncrement); // if increment < 0, jump - - // increment > 0 - v.lcmp(); - v.ifgt(loopExit); - v.goTo(afterIf); - - // increment < 0 - v.mark(negativeIncrement); - v.lcmp(); - v.iflt(loopExit); - v.mark(afterIf); - } - else { - v.ifle(negativeIncrement); // if increment < 0, jump - - // increment > 0 - v.ificmpgt(loopExit); - v.goTo(afterIf); - - // increment < 0 - v.mark(negativeIncrement); - v.ificmplt(loopExit); - v.mark(afterIf); - } - } - - @Override - protected void assignToLoopParameter() { - } - - @Override - protected void increment(@NotNull Label loopExit) { - checkPostCondition(loopExit); - - StackValue loopParameter = loopParameter(); - loopParameter.put(asmElementType, v); - v.load(incrementVar, asmElementType); - v.add(asmElementType); - - if (asmElementType == Type.BYTE_TYPE || asmElementType == Type.SHORT_TYPE || asmElementType == Type.CHAR_TYPE) { - StackValue.coerce(Type.INT_TYPE, asmElementType, v); - } - - loopParameter.store(StackValue.onStack(asmElementType), v); - } - } - @Override public StackValue visitBreakExpression(@NotNull KtBreakExpression expression, StackValue receiver) { @@ -4187,7 +3532,7 @@ public class ExpressionCodegen extends KtVisitor impleme } @NotNull - private VariableDescriptor getVariableDescriptorNotNull(@NotNull KtElement declaration) { + public VariableDescriptor getVariableDescriptorNotNull(@NotNull KtElement declaration) { VariableDescriptor descriptor = bindingContext.get(VARIABLE, declaration); assert descriptor != null : "Couldn't find variable declaration in binding context " + declaration.getText(); return descriptor; @@ -4774,7 +4119,7 @@ The "returned" value of try expression with no finally is either the last expres } } - private Call makeFakeCall(ReceiverValue initializerAsReceiver) { + public Call makeFakeCall(ReceiverValue initializerAsReceiver) { KtSimpleNameExpression fake = KtPsiFactoryKt.KtPsiFactory(state.getProject(), false).createSimpleName("fake"); return CallMaker.makeCall(fake, initializerAsReceiver); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/RangeCodegenUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/RangeCodegenUtil.java index 1e2171db8a2..3135131c14a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/RangeCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/RangeCodegenUtil.java @@ -74,27 +74,6 @@ public class RangeCodegenUtil { return !rangeType.isMarkedNullable() && getPrimitiveProgressionElementType(rangeType) != null; } - @Nullable - public static ResolvedCall getLoopRangeResolvedCall(@NotNull KtForExpression forExpression, @NotNull BindingContext bindingContext) { - KtExpression loopRange = KtPsiUtil.deparenthesize(forExpression.getLoopRange()); - - if (loopRange instanceof KtQualifiedExpression) { - KtQualifiedExpression qualifiedExpression = (KtQualifiedExpression) loopRange; - KtExpression selector = qualifiedExpression.getSelectorExpression(); - if (selector instanceof KtCallExpression || selector instanceof KtSimpleNameExpression) { - return CallUtilKt.getResolvedCall(selector, bindingContext); - } - } - else if (loopRange instanceof KtSimpleNameExpression || loopRange instanceof KtCallExpression) { - return CallUtilKt.getResolvedCall(loopRange, bindingContext); - } - else if (loopRange instanceof KtBinaryExpression) { - return CallUtilKt.getResolvedCall(((KtBinaryExpression) loopRange).getOperationReference(), bindingContext); - } - - return null; - } - @Nullable private static PrimitiveType getPrimitiveRangeElementType(KotlinType rangeType) { return getPrimitiveRangeOrProgressionElementType(rangeType, RANGE_TO_ELEMENT_TYPE); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/AbstractForInProgressionOrRangeLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/AbstractForInProgressionOrRangeLoopGenerator.kt new file mode 100644 index 00000000000..12396a8a334 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/AbstractForInProgressionOrRangeLoopGenerator.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.forLoop + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.Type + +abstract class AbstractForInProgressionOrRangeLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) + : AbstractForLoopGenerator(codegen, forExpression) +{ + protected var endVar: Int = 0 + + private var loopParameter: StackValue? = null + + init { + assert( + asmElementType.sort == Type.INT || + asmElementType.sort == Type.BYTE || + asmElementType.sort == Type.SHORT || + asmElementType.sort == Type.CHAR || + asmElementType.sort == Type.LONG + ) { + "Unexpected range element type: " + asmElementType + } + } + + override fun beforeLoop() { + super.beforeLoop() + + endVar = createLoopTempVariable(asmElementType) + } + + protected fun checkPostCondition(loopExit: Label) { + assert(endVar != -1) { + "endVar must be allocated, endVar = " + endVar + } + loopParameter().put(asmElementType, v) + v.load(endVar, asmElementType) + if (asmElementType.sort == Type.LONG) { + v.lcmp() + v.ifeq(loopExit) + } + else { + v.ificmpeq(loopExit) + } + } + + override fun checkPreCondition(loopExit: Label) {} + + protected fun loopParameter(): StackValue = + loopParameter ?: StackValue.local(loopParameterVar, loopParameterType).also { loopParameter = it } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/AbstractForInRangeLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/AbstractForInRangeLoopGenerator.kt new file mode 100644 index 00000000000..ff142441bb8 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/AbstractForInRangeLoopGenerator.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.forLoop + +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.Type + +import org.jetbrains.kotlin.codegen.AsmUtil.genIncrement +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.StackValue + +abstract class AbstractForInRangeLoopGenerator : AbstractForInProgressionOrRangeLoopGenerator { + private val step: Int + + constructor(codegen: ExpressionCodegen, forExpression: KtForExpression, step: Int) : super(codegen, forExpression) { + assert(step == 1 || step == -1) { "'step' should be either 1 or -1: " + step } + this.step = step + } + + constructor(codegen: ExpressionCodegen, forExpression: KtForExpression) : this(codegen, forExpression, 1) + + override fun beforeLoop() { + super.beforeLoop() + + storeRangeStartAndEnd() + } + + protected abstract fun storeRangeStartAndEnd() + + override fun checkEmptyLoop(loopExit: Label) { + loopParameter().put(asmElementType, v) + v.load(endVar, asmElementType) + if (asmElementType.sort == Type.LONG) { + v.lcmp() + if (step > 0) { + v.ifgt(loopExit) + } + else { + v.iflt(loopExit) + } + } + else { + if (step > 0) { + v.ificmpgt(loopExit) + } + else { + v.ificmplt(loopExit) + } + } + } + + override fun assignToLoopParameter() {} + + override fun increment(loopExit: Label) { + checkPostCondition(loopExit) + + if (loopParameterType === Type.INT_TYPE) { + v.iinc(loopParameterVar, step) + } + else { + val loopParameter = loopParameter() + loopParameter.put(asmElementType, v) + genIncrement(asmElementType, step, v) + loopParameter.store(StackValue.onStack(asmElementType), v) + } + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/AbstractForLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/AbstractForLoopGenerator.kt new file mode 100644 index 00000000000..6e917186e5a --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/AbstractForLoopGenerator.kt @@ -0,0 +1,161 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.forLoop + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.codegen.filterOutDescriptorsWithSpecialNames +import org.jetbrains.kotlin.diagnostics.DiagnosticUtils +import org.jetbrains.kotlin.psi.KtDestructuringDeclaration +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.Type + +abstract class AbstractForLoopGenerator( + protected val codegen: ExpressionCodegen, + val forExpression: KtForExpression +) { + protected val bindingContext = codegen.bindingContext + protected val v = codegen.v!! + + private val loopParameterStartLabel = Label() + private val bodyEnd = Label() + private val leaveVariableTasks = arrayListOf() + + protected val elementType: KotlinType = bindingContext.getElementType(forExpression) + protected val asmElementType: Type = codegen.asmType(elementType) + + protected var loopParameterVar: Int = 0 + protected lateinit var loopParameterType: Type + + private fun BindingContext.getElementType(forExpression: KtForExpression): KotlinType { + val loopRange = forExpression.loopRange!! + val nextCall = get(BindingContext.LOOP_RANGE_NEXT_RESOLVED_CALL, loopRange) ?: + throw AssertionError("No next() function " + DiagnosticUtils.atLocation(loopRange)) + return nextCall.resultingDescriptor.returnType!! + } + + open fun beforeLoop() { + val loopParameter = forExpression.loopParameter ?: return + val multiParameter = loopParameter.destructuringDeclaration + if (multiParameter != null) { + // E tmp = tmp.next() + loopParameterType = asmElementType + loopParameterVar = createLoopTempVariable(asmElementType) + } + else { + // E e = tmp.next() + val parameterDescriptor = bindingContext.get(BindingContext.VALUE_PARAMETER, loopParameter) + loopParameterType = codegen.asmType(parameterDescriptor!!.type) + loopParameterVar = codegen.myFrameMap.enter(parameterDescriptor, loopParameterType) + scheduleLeaveVariable(Runnable { + codegen.myFrameMap.leave(parameterDescriptor) + v.visitLocalVariable(parameterDescriptor.name.asString(), + loopParameterType.descriptor, null, + loopParameterStartLabel, bodyEnd, + loopParameterVar) + }) + } + } + + abstract fun checkEmptyLoop(loopExit: Label) + + abstract fun checkPreCondition(loopExit: Label) + + fun beforeBody() { + assignToLoopParameter() + v.mark(loopParameterStartLabel) + + val destructuringDeclaration = forExpression.destructuringDeclaration + if (destructuringDeclaration != null) { + generateDestructuringDeclaration(destructuringDeclaration) + } + } + + private fun generateDestructuringDeclaration(destructuringDeclaration: KtDestructuringDeclaration) { + val destructuringStartLabel = Label() + + val componentDescriptors = destructuringDeclaration.entries.map { declaration -> codegen.getVariableDescriptorNotNull(declaration) } + + for (componentDescriptor in componentDescriptors.filterOutDescriptorsWithSpecialNames()) { + val componentAsmType = codegen.asmType(componentDescriptor.returnType!!) + val componentVarIndex = codegen.myFrameMap.enter(componentDescriptor, componentAsmType) + scheduleLeaveVariable(Runnable { + codegen.myFrameMap.leave(componentDescriptor) + v.visitLocalVariable(componentDescriptor.name.asString(), + componentAsmType.descriptor, null, + destructuringStartLabel, bodyEnd, + componentVarIndex) + }) + } + + v.visitLabel(destructuringStartLabel) + + codegen.initializeDestructuringDeclarationVariables( + destructuringDeclaration, + TransientReceiver(elementType), + StackValue.local(loopParameterVar, asmElementType)) + } + + protected abstract fun assignToLoopParameter() + + protected abstract fun increment(loopExit: Label) + + fun body() { + codegen.generateLoopBody(forExpression.body) + } + + private fun scheduleLeaveVariable(runnable: Runnable) { + leaveVariableTasks.add(runnable) + } + + protected fun createLoopTempVariable(type: Type): Int { + val varIndex = codegen.myFrameMap.enterTemp(type) + scheduleLeaveVariable(Runnable { codegen.myFrameMap.leaveTemp(type) }) + return varIndex + } + + fun afterBody(loopExit: Label) { + codegen.markStartLineNumber(forExpression) + + increment(loopExit) + + v.mark(bodyEnd) + } + + fun afterLoop() { + for (task in leaveVariableTasks.asReversed()) { + task.run() + } + } + + // This method consumes range/progression from stack + // The result is stored to local variable + protected fun generateRangeOrProgressionProperty( + loopRangeType: Type, + getterName: String, + getterReturnType: Type, + varType: Type, + varToStore: Int + ) { + v.invokevirtual(loopRangeType.internalName, getterName, "()" + getterReturnType.descriptor, false) + StackValue.local(varToStore, varType).store(StackValue.onStack(getterReturnType), v) + } +} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInArrayIndicesRangeLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInArrayIndicesRangeLoopGenerator.kt new file mode 100644 index 00000000000..ac392701033 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInArrayIndicesRangeLoopGenerator.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.forLoop + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall + +class ForInArrayIndicesRangeLoopGenerator( + codegen: ExpressionCodegen, + forExpression: KtForExpression, + loopRangeCall: ResolvedCall<*> +) : ForInOptimizedIndicesLoopGenerator(codegen, forExpression, loopRangeCall) { + override fun getReceiverSizeAsInt() { + v.arraylength() + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInArrayLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInArrayLoopGenerator.kt new file mode 100644 index 00000000000..5badbd2fdf0 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInArrayLoopGenerator.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.forLoop + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.Type + +import org.jetbrains.kotlin.codegen.AsmUtil.boxType +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE + +class ForInArrayLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) + : AbstractForLoopGenerator(codegen, forExpression) +{ + private var indexVar: Int = 0 + private var arrayVar: Int = 0 + private val loopRangeType: KotlinType = bindingContext.getType(forExpression.loopRange!!)!! + + override fun beforeLoop() { + super.beforeLoop() + + indexVar = createLoopTempVariable(Type.INT_TYPE) + + val loopRange = forExpression.loopRange + val value = codegen.gen(loopRange) + val asmLoopRangeType = codegen.asmType(loopRangeType) + if (value is StackValue.Local && value.type == asmLoopRangeType) { + arrayVar = value.index // no need to copy local variable into another variable + } + else { + arrayVar = createLoopTempVariable(OBJECT_TYPE) + value.put(asmLoopRangeType, v) + v.store(arrayVar, OBJECT_TYPE) + } + + v.iconst(0) + v.store(indexVar, Type.INT_TYPE) + } + + override fun checkEmptyLoop(loopExit: Label) {} + + override fun checkPreCondition(loopExit: Label) { + v.load(indexVar, Type.INT_TYPE) + v.load(arrayVar, OBJECT_TYPE) + v.arraylength() + v.ificmpge(loopExit) + } + + override fun assignToLoopParameter() { + val arrayElParamType = if (KotlinBuiltIns.isArray(loopRangeType)) boxType(asmElementType) else asmElementType + + v.load(arrayVar, OBJECT_TYPE) + v.load(indexVar, Type.INT_TYPE) + v.aload(arrayElParamType) + StackValue.onStack(arrayElParamType).put(asmElementType, codegen.v) + v.store(loopParameterVar, asmElementType) + } + + override fun increment(loopExit: Label) { + v.iinc(indexVar, 1) + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInCharSequenceIndicesRangeLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInCharSequenceIndicesRangeLoopGenerator.kt new file mode 100644 index 00000000000..b1a872098aa --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInCharSequenceIndicesRangeLoopGenerator.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.forLoop + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall + +class ForInCharSequenceIndicesRangeLoopGenerator( + codegen: ExpressionCodegen, + forExpression: KtForExpression, + loopRangeCall: ResolvedCall<*> +) : ForInOptimizedIndicesLoopGenerator(codegen, forExpression, loopRangeCall) { + + override fun getReceiverSizeAsInt() { + v.invokeinterface("java/lang/CharSequence", "length", "()I") + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInCollectionIndicesRangeLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInCollectionIndicesRangeLoopGenerator.kt new file mode 100644 index 00000000000..18cf117b8b8 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInCollectionIndicesRangeLoopGenerator.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.forLoop + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall + +class ForInCollectionIndicesRangeLoopGenerator( + codegen: ExpressionCodegen, + forExpression: KtForExpression, + loopRangeCall: ResolvedCall<*> +) : ForInOptimizedIndicesLoopGenerator(codegen, forExpression, loopRangeCall) { + override fun getReceiverSizeAsInt() { + v.invokeinterface("java/util/Collection", "size", "()I") + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInDownToProgressionLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInDownToProgressionLoopGenerator.kt new file mode 100644 index 00000000000..1912ee9f92f --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInDownToProgressionLoopGenerator.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.forLoop + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue + +class ForInDownToProgressionLoopGenerator( + codegen: ExpressionCodegen, + forExpression: KtForExpression, + loopRangeCall: ResolvedCall<*> +) : AbstractForInRangeLoopGenerator(codegen, forExpression, -1) { + private val from: ReceiverValue = loopRangeCall.extensionReceiver!! + private val to: KtExpression = ExpressionCodegen.getSingleArgumentExpression(loopRangeCall)!! + + override fun storeRangeStartAndEnd() { + loopParameter().store(codegen.generateReceiverValue(from, false), v) + StackValue.local(endVar, asmElementType).store(codegen.gen(to), v) + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInOptimizedIndicesLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInOptimizedIndicesLoopGenerator.kt new file mode 100644 index 00000000000..df7ecd44be7 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInOptimizedIndicesLoopGenerator.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.forLoop + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.org.objectweb.asm.Type + +abstract class ForInOptimizedIndicesLoopGenerator( + codegen: ExpressionCodegen, + forExpression: KtForExpression, + loopRangeCall: ResolvedCall<*> +) : AbstractForInRangeLoopGenerator(codegen, forExpression) { + protected val receiverValue: ReceiverValue = loopRangeCall.extensionReceiver!! + protected val expectedReceiverType: KotlinType = ExpressionCodegen.getExpectedReceiverType(loopRangeCall) + + override fun storeRangeStartAndEnd() { + loopParameter().store(StackValue.constant(0, asmElementType), codegen.v) + + val receiver = codegen.generateReceiverValue(receiverValue, false) + val receiverType = codegen.asmType(expectedReceiverType) + receiver.put(receiverType, codegen.v) + getReceiverSizeAsInt() + codegen.v.iconst(1) + codegen.v.sub(Type.INT_TYPE) + StackValue.local(endVar, asmElementType).store(StackValue.onStack(Type.INT_TYPE), codegen.v) + } + + /** + * `(receiver -> size:I)` + */ + protected abstract fun getReceiverSizeAsInt() +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInProgressionExpressionLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInProgressionExpressionLoopGenerator.kt new file mode 100644 index 00000000000..066de50ed96 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInProgressionExpressionLoopGenerator.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.forLoop + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.Type + +class ForInProgressionExpressionLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) + : AbstractForInProgressionOrRangeLoopGenerator(codegen, forExpression) +{ + private var incrementVar: Int = 0 + private var incrementType: Type? = null + + override fun beforeLoop() { + super.beforeLoop() + + incrementVar = createLoopTempVariable(asmElementType) + + val loopRangeType = bindingContext.getType(forExpression.loopRange!!)!! + val asmLoopRangeType = codegen.asmType(loopRangeType) + + val incrementProp = loopRangeType.memberScope.getContributedVariables(Name.identifier("step"), NoLookupLocation.FROM_BACKEND) + assert(incrementProp.size == 1) { loopRangeType.toString() + " " + incrementProp.size } + incrementType = codegen.asmType(incrementProp.iterator().next().type) + + codegen.gen(forExpression.loopRange, asmLoopRangeType) + v.dup() + v.dup() + + generateRangeOrProgressionProperty(asmLoopRangeType, "getFirst", asmElementType, loopParameterType, + loopParameterVar) + generateRangeOrProgressionProperty(asmLoopRangeType, "getLast", asmElementType, asmElementType, endVar) + generateRangeOrProgressionProperty(asmLoopRangeType, "getStep", incrementType!!, incrementType!!, incrementVar) + } + + override fun checkEmptyLoop(loopExit: Label) { + loopParameter().put(asmElementType, v) + v.load(endVar, asmElementType) + v.load(incrementVar, incrementType!!) + + val negativeIncrement = Label() + val afterIf = Label() + + if (asmElementType.sort == Type.LONG) { + v.lconst(0L) + v.lcmp() + v.ifle(negativeIncrement) // if increment < 0, jump + + // increment > 0 + v.lcmp() + v.ifgt(loopExit) + v.goTo(afterIf) + + // increment < 0 + v.mark(negativeIncrement) + v.lcmp() + v.iflt(loopExit) + v.mark(afterIf) + } + else { + v.ifle(negativeIncrement) // if increment < 0, jump + + // increment > 0 + v.ificmpgt(loopExit) + v.goTo(afterIf) + + // increment < 0 + v.mark(negativeIncrement) + v.ificmplt(loopExit) + v.mark(afterIf) + } + } + + override fun assignToLoopParameter() {} + + override fun increment(loopExit: Label) { + checkPostCondition(loopExit) + + val loopParameter = loopParameter() + loopParameter.put(asmElementType, v) + v.load(incrementVar, asmElementType) + v.add(asmElementType) + + if (asmElementType === Type.BYTE_TYPE || asmElementType === Type.SHORT_TYPE || asmElementType === Type.CHAR_TYPE) { + StackValue.coerce(Type.INT_TYPE, asmElementType, v) + } + + loopParameter.store(StackValue.onStack(asmElementType), v) + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInRangeInstanceLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInRangeInstanceLoopGenerator.kt new file mode 100644 index 00000000000..a16afc9a450 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInRangeInstanceLoopGenerator.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.forLoop + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.psi.KtForExpression + +class ForInRangeInstanceLoopGenerator( + codegen: ExpressionCodegen, + forExpression: KtForExpression +) : AbstractForInRangeLoopGenerator(codegen, forExpression) { + + override fun storeRangeStartAndEnd() { + val loopRangeType = codegen.bindingContext.getType(forExpression.loopRange!!)!! + val asmLoopRangeType = codegen.asmType(loopRangeType) + codegen.gen(forExpression.loopRange, asmLoopRangeType) + v.dup() + + // ranges inherit first and last from corresponding progressions + generateRangeOrProgressionProperty(asmLoopRangeType, "getFirst", asmElementType, loopParameterType, + loopParameterVar) + generateRangeOrProgressionProperty(asmLoopRangeType, "getLast", asmElementType, asmElementType, endVar) + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInRangeLiteralLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInRangeLiteralLoopGenerator.kt new file mode 100644 index 00000000000..9ac93a2e78e --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForInRangeLiteralLoopGenerator.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.forLoop + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue + +class ForInRangeLiteralLoopGenerator( + codegen: ExpressionCodegen, + forExpression: KtForExpression, + loopRangeCall: ResolvedCall<*> +) : AbstractForInRangeLoopGenerator(codegen, forExpression) { + private val from: ReceiverValue = loopRangeCall.dispatchReceiver!! + private val to: KtExpression = ExpressionCodegen.getSingleArgumentExpression(loopRangeCall)!! + + override fun storeRangeStartAndEnd() { + loopParameter().store(codegen.generateReceiverValue(from, false), codegen.v) + StackValue.local(endVar, asmElementType).store(codegen.gen(to), codegen.v) + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForLoopGenerators.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForLoopGenerators.kt new file mode 100644 index 00000000000..f5a856e1310 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForLoopGenerators.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.forLoop + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.RangeCodegenUtil +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.org.objectweb.asm.Type + +fun ExpressionCodegen.getForLoopGenerator(forExpression: KtForExpression) : AbstractForLoopGenerator { + getLoopRangeResolvedCall(forExpression, bindingContext)?.let { loopRangeCall -> + createOptimizedForLoopGeneratorOrNull(forExpression, loopRangeCall)?.let { + return it + } + } + + val loopRange = forExpression.loopRange!! + val loopRangeType = bindingContext.getType(loopRange)!! + val asmLoopRangeType = asmType(loopRangeType) + + return when { + asmLoopRangeType.sort == Type.ARRAY -> + ForInArrayLoopGenerator(this, forExpression) + RangeCodegenUtil.isRange(loopRangeType) -> + ForInRangeInstanceLoopGenerator(this, forExpression) + RangeCodegenUtil.isProgression(loopRangeType) -> + ForInProgressionExpressionLoopGenerator(this, forExpression) + else -> + IteratorForLoopGenerator(this, forExpression) + } +} + +private fun ExpressionCodegen.createOptimizedForLoopGeneratorOrNull( + forExpression: KtForExpression, + loopRangeCall: ResolvedCall +): AbstractForLoopGenerator? { + val loopRangeCallee = loopRangeCall.resultingDescriptor + + return when { + RangeCodegenUtil.isPrimitiveNumberRangeTo(loopRangeCallee) -> + ForInRangeLiteralLoopGenerator(this, forExpression, loopRangeCall) + RangeCodegenUtil.isPrimitiveNumberDownTo(loopRangeCallee) -> + ForInDownToProgressionLoopGenerator(this, forExpression, loopRangeCall) + RangeCodegenUtil.isArrayOrPrimitiveArrayIndices(loopRangeCallee) -> + ForInArrayIndicesRangeLoopGenerator(this, forExpression, loopRangeCall) + RangeCodegenUtil.isCollectionIndices(loopRangeCallee) -> + ForInCollectionIndicesRangeLoopGenerator(this, forExpression, loopRangeCall) + RangeCodegenUtil.isCharSequenceIndices(loopRangeCallee) -> + ForInCharSequenceIndicesRangeLoopGenerator(this, forExpression, loopRangeCall) + else -> null + } +} + +private fun getLoopRangeResolvedCall(forExpression: KtForExpression, bindingContext: BindingContext): ResolvedCall? { + val loopRange = KtPsiUtil.deparenthesize(forExpression.loopRange) + + if (loopRange is KtQualifiedExpression) { + val qualifiedExpression = loopRange as KtQualifiedExpression? + val selector = qualifiedExpression!!.selectorExpression + if (selector is KtCallExpression || selector is KtSimpleNameExpression) { + return selector.getResolvedCall(bindingContext) + } + } + else if (loopRange is KtSimpleNameExpression || loopRange is KtCallExpression) { + return loopRange.getResolvedCall(bindingContext) + } + else if (loopRange is KtBinaryExpression) { + return loopRange.operationReference.getResolvedCall(bindingContext) + } + + return null +} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/IteratorForLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/IteratorForLoopGenerator.kt new file mode 100644 index 00000000000..e7bb69b7679 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/IteratorForLoopGenerator.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.forLoop + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.diagnostics.DiagnosticUtils +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.kotlin.resolve.BindingContext.* +import org.jetbrains.kotlin.resolve.BindingContextUtils.getNotNull +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver +import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.Type + +class IteratorForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) + : AbstractForLoopGenerator(codegen, forExpression) +{ + private var iteratorVarIndex: Int = 0 + private val iteratorCall: ResolvedCall + private val nextCall: ResolvedCall + private val asmTypeForIterator: Type + + init { + val loopRange = forExpression.loopRange!! + this.iteratorCall = getNotNull(bindingContext, + LOOP_RANGE_ITERATOR_RESOLVED_CALL, loopRange, + "No .iterator() function " + DiagnosticUtils.atLocation(loopRange)) + + val iteratorType = iteratorCall.resultingDescriptor.returnType!! + this.asmTypeForIterator = codegen.asmType(iteratorType) + + this.nextCall = getNotNull(bindingContext, + LOOP_RANGE_NEXT_RESOLVED_CALL, loopRange, + "No next() function " + DiagnosticUtils.atLocation(loopRange)) + } + + override fun beforeLoop() { + super.beforeLoop() + + // Iterator tmp = c.iterator() + iteratorVarIndex = createLoopTempVariable(asmTypeForIterator) + StackValue.local(iteratorVarIndex, asmTypeForIterator).store(codegen.invokeFunction(iteratorCall, StackValue.none()), v) + } + + override fun checkEmptyLoop(loopExit: Label) {} + + override fun checkPreCondition(loopExit: Label) { + // tmp.hasNext() + + val loopRange = forExpression.loopRange + val hasNextCall = getNotNull( + codegen.bindingContext, LOOP_RANGE_HAS_NEXT_RESOLVED_CALL, + loopRange!!, + "No hasNext() function " + DiagnosticUtils.atLocation(loopRange) + ) + val fakeCall = codegen.makeFakeCall(TransientReceiver(iteratorCall.resultingDescriptor.returnType!!)) + val result = codegen.invokeFunction(fakeCall, hasNextCall, StackValue.local(iteratorVarIndex, asmTypeForIterator)) + result.put(Type.BOOLEAN_TYPE, v) + + v.ifeq(loopExit) + } + + override fun assignToLoopParameter() { + val fakeCall = codegen.makeFakeCall(TransientReceiver(iteratorCall.resultingDescriptor.returnType!!)) + val value = codegen.invokeFunction(fakeCall, nextCall, StackValue.local(iteratorVarIndex, asmTypeForIterator)) + + StackValue.local(loopParameterVar, loopParameterType).store(value, v) + } + + override fun increment(loopExit: Label) {} +}