mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-04-14 08:31:29 +00:00
Compare commits
81 Commits
v1.1.4
...
abannykh/j
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3789cf33b2 | ||
|
|
890c6a9983 | ||
|
|
fd380f7545 | ||
|
|
14890890b1 | ||
|
|
ee173077c0 | ||
|
|
e82c909f75 | ||
|
|
178bb900b4 | ||
|
|
79ee8f452c | ||
|
|
848f7423ee | ||
|
|
f4d63158cc | ||
|
|
efb6756cbc | ||
|
|
8a9707c140 | ||
|
|
b99007961f | ||
|
|
a983137978 | ||
|
|
e56e3d78c8 | ||
|
|
cb19a86c44 | ||
|
|
7634673213 | ||
|
|
cc4e55b829 | ||
|
|
7407083624 | ||
|
|
fc12f37105 | ||
|
|
3da5ba4efe | ||
|
|
0ca5c0fcc6 | ||
|
|
8cc9330e63 | ||
|
|
8f9b680fc6 | ||
|
|
0af3c6542d | ||
|
|
5a9adcca2d | ||
|
|
83ec8aa918 | ||
|
|
a0f11f773b | ||
|
|
68e0727894 | ||
|
|
7f8d6b3008 | ||
|
|
ebdadf30d8 | ||
|
|
8c9ace7d40 | ||
|
|
564cc27a1d | ||
|
|
dfe2c16bc7 | ||
|
|
9269de721e | ||
|
|
03a6488464 | ||
|
|
c94b21edd5 | ||
|
|
4128064f87 | ||
|
|
5b8e58fe36 | ||
|
|
7edb352aeb | ||
|
|
0ce6bac7eb | ||
|
|
dd5bb78178 | ||
|
|
0962aec456 | ||
|
|
aa7db727ba | ||
|
|
ad80c3cd7f | ||
|
|
22e12dc139 | ||
|
|
905a16e1df | ||
|
|
fc3e9318d9 | ||
|
|
6a3ff5ca46 | ||
|
|
850e1b11fc | ||
|
|
64f880ba71 | ||
|
|
9d1901fc7c | ||
|
|
d137b04b0a | ||
|
|
ea95f31f99 | ||
|
|
224848163d | ||
|
|
c855b5c889 | ||
|
|
a3409b46b0 | ||
|
|
b693b54a2c | ||
|
|
4480a9bdfb | ||
|
|
f4ea1a2f41 | ||
|
|
d19eb05382 | ||
|
|
83ff1a2ef3 | ||
|
|
6551fdc695 | ||
|
|
db1dcc68ff | ||
|
|
614d90d6ef | ||
|
|
36cbc0defd | ||
|
|
ec99fa2142 | ||
|
|
05cad83c79 | ||
|
|
5decf65d6a | ||
|
|
4320b63922 | ||
|
|
b9c0ecf2b1 | ||
|
|
7175361c97 | ||
|
|
a3cd4f415a | ||
|
|
fd402cb76c | ||
|
|
bd2021411c | ||
|
|
a7071ae7af | ||
|
|
e3320c53f5 | ||
|
|
e261b8edf8 | ||
|
|
c870eeac2e | ||
|
|
304e4e8dce | ||
|
|
6140f29818 |
@@ -798,6 +798,16 @@ public class AsmUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static void pop2(@NotNull MethodVisitor v, @NotNull Type type) {
|
||||
if (type.getSize() == 2) {
|
||||
v.visitInsn(Opcodes.POP2);
|
||||
v.visitInsn(Opcodes.POP2);
|
||||
}
|
||||
else {
|
||||
v.visitInsn(Opcodes.POP2);
|
||||
}
|
||||
}
|
||||
|
||||
public static void dup(@NotNull InstructionAdapter v, @NotNull Type type) {
|
||||
dup(v, type.getSize());
|
||||
}
|
||||
@@ -814,6 +824,22 @@ public class AsmUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static void dupx(@NotNull InstructionAdapter v, @NotNull Type type) {
|
||||
dupx(v, type.getSize());
|
||||
}
|
||||
|
||||
private static void dupx(@NotNull InstructionAdapter v, int size) {
|
||||
if (size == 2) {
|
||||
v.dup2X2();
|
||||
}
|
||||
else if (size == 1) {
|
||||
v.dupX1();
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
public static void dup(@NotNull InstructionAdapter v, @NotNull Type topOfStack, @NotNull Type afterTop) {
|
||||
if (topOfStack.getSize() == 0 && afterTop.getSize() == 0) {
|
||||
return;
|
||||
|
||||
@@ -39,11 +39,12 @@ import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenForLambda;
|
||||
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.AbstractForLoopGenerator;
|
||||
import org.jetbrains.kotlin.codegen.forLoop.ForLoopGeneratorsKt;
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.ForLoopGenerator;
|
||||
import org.jetbrains.kotlin.codegen.inline.*;
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.*;
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsKt;
|
||||
import org.jetbrains.kotlin.codegen.range.RangeValue;
|
||||
import org.jetbrains.kotlin.codegen.range.RangeValuesKt;
|
||||
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
|
||||
import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
@@ -599,7 +600,10 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
}
|
||||
|
||||
private void generateFor(@NotNull KtForExpression forExpression) {
|
||||
generateForLoop(ForLoopGeneratorsKt.getForLoopGenerator(this, forExpression));
|
||||
KtExpression range = forExpression.getLoopRange();
|
||||
assert range != null : "No loop range in for expression";
|
||||
RangeValue rangeValue = RangeValuesKt.createRangeValueForExpression(this, range);
|
||||
generateForLoop(rangeValue.createForLoopGenerator(this, forExpression));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -623,7 +627,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
return context.getContextKind();
|
||||
}
|
||||
|
||||
private void generateForLoop(AbstractForLoopGenerator generator) {
|
||||
private void generateForLoop(ForLoopGenerator generator) {
|
||||
Label loopExit = new Label();
|
||||
Label loopEntry = new Label();
|
||||
Label continueLabel = new Label();
|
||||
@@ -2044,32 +2048,27 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
return genClosure((KtNamedFunction) expression, samType);
|
||||
}
|
||||
|
||||
Type asmType =
|
||||
state.getSamWrapperClasses().getSamWrapperClass(samType, expression.getContainingKtFile(), this);
|
||||
Type asmType = state.getSamWrapperClasses().getSamWrapperClass(samType, expression.getContainingKtFile(), this);
|
||||
|
||||
return StackValue.operation(asmType, v -> {
|
||||
v.anew(asmType);
|
||||
v.dup();
|
||||
Label afterAll = new Label();
|
||||
|
||||
Type functionType = typeMapper.mapType(samType.getKotlinFunctionType());
|
||||
expression.accept(visitor, StackValue.none()).put(functionType, v);
|
||||
|
||||
Label ifNonNull = new Label();
|
||||
Label afterAll = new Label();
|
||||
|
||||
v.dup();
|
||||
v.ifnonnull(ifNonNull);
|
||||
v.ifnull(afterAll);
|
||||
|
||||
// if null: pop function value and wrapper objects, put null
|
||||
v.pop();
|
||||
v.pop2();
|
||||
v.aconst(null);
|
||||
v.goTo(afterAll);
|
||||
|
||||
v.mark(ifNonNull);
|
||||
int tmp = myFrameMap.enterTemp(functionType);
|
||||
v.store(tmp, functionType);
|
||||
v.anew(asmType);
|
||||
v.dup();
|
||||
v.load(tmp, functionType);
|
||||
v.invokespecial(asmType.getInternalName(), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, functionType), false);
|
||||
myFrameMap.leaveTemp(functionType);
|
||||
|
||||
v.mark(afterAll);
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
@@ -2866,76 +2865,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
private StackValue generateIn(StackValue leftValue, KtExpression rangeExpression, KtSimpleNameExpression operationReference) {
|
||||
KtExpression deparenthesized = KtPsiUtil.deparenthesize(rangeExpression);
|
||||
|
||||
assert deparenthesized != null : "For with empty range expression";
|
||||
boolean isInverted = operationReference.getReferencedNameElementType() == KtTokens.NOT_IN;
|
||||
return StackValue.operation(Type.BOOLEAN_TYPE, v -> {
|
||||
if (RangeCodegenUtil.isPrimitiveRangeSpecializationOfType(leftValue.type, deparenthesized, bindingContext) ||
|
||||
RangeCodegenUtil.isPrimitiveRangeToExtension(operationReference, bindingContext)) {
|
||||
generateInPrimitiveRange(leftValue, (KtBinaryExpression) deparenthesized, isInverted);
|
||||
}
|
||||
else {
|
||||
ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt
|
||||
.getResolvedCallWithAssert(operationReference, bindingContext);
|
||||
StackValue result = invokeFunction(resolvedCall.getCall(), resolvedCall, StackValue.none());
|
||||
result.put(result.type, v);
|
||||
if (isInverted) {
|
||||
genInvertBoolean(v);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Translates x in a..b to a <= x && x <= b
|
||||
* and x !in a..b to a > x || x > b for any primitive type
|
||||
*/
|
||||
private void generateInPrimitiveRange(StackValue argument, KtBinaryExpression rangeExpression, boolean isInverted) {
|
||||
Type rangeType = argument.type;
|
||||
int localVarIndex = myFrameMap.enterTemp(rangeType);
|
||||
// Load left bound
|
||||
gen(rangeExpression.getLeft(), rangeType);
|
||||
// Load x into local variable to avoid StackValue#put side-effects
|
||||
argument.put(rangeType, v);
|
||||
v.store(localVarIndex, rangeType);
|
||||
v.load(localVarIndex, rangeType);
|
||||
|
||||
// If (x < left) goto L1
|
||||
Label l1 = new Label();
|
||||
emitGreaterThan(rangeType, l1);
|
||||
|
||||
// If (x > right) goto L1
|
||||
v.load(localVarIndex, rangeType);
|
||||
gen(rangeExpression.getRight(), rangeType);
|
||||
emitGreaterThan(rangeType, l1);
|
||||
|
||||
Label l2 = new Label();
|
||||
v.iconst(isInverted ? 0 : 1);
|
||||
v.goTo(l2);
|
||||
|
||||
v.mark(l1);
|
||||
v.iconst(isInverted ? 1 : 0);
|
||||
v.mark(l2);
|
||||
myFrameMap.leaveTemp(rangeType);
|
||||
}
|
||||
|
||||
private void emitGreaterThan(Type type, Label label) {
|
||||
if (AsmUtil.isIntPrimitive(type)) {
|
||||
v.ificmpgt(label);
|
||||
}
|
||||
else if (type == Type.LONG_TYPE) {
|
||||
v.lcmp();
|
||||
v.ifgt(label);
|
||||
}
|
||||
// '>' != 'compareTo' for NaN and +/- 0.0
|
||||
else if (type == Type.FLOAT_TYPE || type == Type.DOUBLE_TYPE) {
|
||||
v.cmpg(type);
|
||||
v.ifgt(label);
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException("Unexpected type: " + type);
|
||||
}
|
||||
RangeValue rangeValue = RangeValuesKt.createRangeValueForExpression(this, deparenthesized);
|
||||
return rangeValue.createInExpressionGenerator(this, operationReference).generate(leftValue);
|
||||
}
|
||||
|
||||
private StackValue generateBooleanAnd(KtBinaryExpression expression) {
|
||||
|
||||
@@ -348,9 +348,12 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generateSyntheticParts() {
|
||||
protected void generateSyntheticPartsBeforeBody() {
|
||||
generatePropertyMetadataArrayFieldIfNeeded(classAsmType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generateSyntheticPartsAfterBody() {
|
||||
generateFieldForSingleton();
|
||||
|
||||
generateCompanionObjectBackingFieldCopies();
|
||||
|
||||
@@ -70,7 +70,7 @@ class InterfaceImplBodyCodegen(
|
||||
return classDescriptorImpl
|
||||
}
|
||||
|
||||
override fun generateSyntheticParts() {
|
||||
override fun generateSyntheticPartsAfterBody() {
|
||||
for (memberDescriptor in descriptor.defaultType.memberScope.getContributedDescriptors()) {
|
||||
if (memberDescriptor !is CallableMemberDescriptor) continue
|
||||
|
||||
|
||||
@@ -131,11 +131,18 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
||||
public void generate() {
|
||||
generateDeclaration();
|
||||
|
||||
boolean shouldGenerateSyntheticParts =
|
||||
!(element instanceof KtClassOrObject) ||
|
||||
state.getGenerateDeclaredClassFilter().shouldGenerateClassMembers((KtClassOrObject) element);
|
||||
|
||||
if (shouldGenerateSyntheticParts) {
|
||||
generateSyntheticPartsBeforeBody();
|
||||
}
|
||||
|
||||
generateBody();
|
||||
|
||||
if (!(element instanceof KtClassOrObject) ||
|
||||
state.getGenerateDeclaredClassFilter().shouldGenerateClassMembers((KtClassOrObject) element)) {
|
||||
generateSyntheticParts();
|
||||
if (shouldGenerateSyntheticParts) {
|
||||
generateSyntheticPartsAfterBody();
|
||||
}
|
||||
|
||||
if (state.getClassBuilderMode().generateMetadata) {
|
||||
@@ -147,9 +154,12 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
||||
|
||||
protected abstract void generateDeclaration();
|
||||
|
||||
protected void generateSyntheticPartsBeforeBody() {
|
||||
}
|
||||
|
||||
protected abstract void generateBody();
|
||||
|
||||
protected void generateSyntheticParts() {
|
||||
protected void generateSyntheticPartsAfterBody() {
|
||||
}
|
||||
|
||||
protected abstract void generateKotlinMetadataAnnotation();
|
||||
|
||||
@@ -186,7 +186,7 @@ class MultifileClassPartCodegen(
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateSyntheticParts() {
|
||||
override fun generateSyntheticPartsAfterBody() {
|
||||
generateSyntheticAccessors()
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ public class PackagePartCodegen extends MemberCodegen<KtFile> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generateSyntheticParts() {
|
||||
protected void generateSyntheticPartsAfterBody() {
|
||||
generateSyntheticAccessors();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.RANGES_PACKAGE_FQ_NAME;
|
||||
import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitiveNumberClassDescriptor;
|
||||
|
||||
public class RangeCodegenUtil {
|
||||
private static final ImmutableMap<FqName, PrimitiveType> RANGE_TO_ELEMENT_TYPE;
|
||||
private static final ImmutableMap<FqName, PrimitiveType> PROGRESSION_TO_ELEMENT_TYPE;
|
||||
|
||||
@NotNull
|
||||
public static List<PrimitiveType> supportedRangeTypes() {
|
||||
return Arrays.asList(PrimitiveType.CHAR, PrimitiveType.INT, PrimitiveType.LONG);
|
||||
}
|
||||
|
||||
static {
|
||||
ImmutableMap.Builder<FqName, PrimitiveType> rangeBuilder = ImmutableMap.builder();
|
||||
ImmutableMap.Builder<FqName, PrimitiveType> progressionBuilder = ImmutableMap.builder();
|
||||
for (PrimitiveType primitiveType : supportedRangeTypes()) {
|
||||
FqName rangeClassFqName = RANGES_PACKAGE_FQ_NAME.child(Name.identifier(primitiveType.getTypeName() + "Range"));
|
||||
FqName progressionClassFqName = RANGES_PACKAGE_FQ_NAME.child(Name.identifier(primitiveType.getTypeName() + "Progression"));
|
||||
rangeBuilder.put(rangeClassFqName, primitiveType);
|
||||
progressionBuilder.put(progressionClassFqName, primitiveType);
|
||||
}
|
||||
RANGE_TO_ELEMENT_TYPE = rangeBuilder.build();
|
||||
PROGRESSION_TO_ELEMENT_TYPE = progressionBuilder.build();
|
||||
}
|
||||
|
||||
private RangeCodegenUtil() {}
|
||||
|
||||
public static boolean isRange(KotlinType rangeType) {
|
||||
return !rangeType.isMarkedNullable() && getPrimitiveRangeElementType(rangeType) != null;
|
||||
}
|
||||
|
||||
public static boolean isProgression(KotlinType rangeType) {
|
||||
return !rangeType.isMarkedNullable() && getPrimitiveProgressionElementType(rangeType) != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PrimitiveType getPrimitiveRangeElementType(KotlinType rangeType) {
|
||||
return getPrimitiveRangeOrProgressionElementType(rangeType, RANGE_TO_ELEMENT_TYPE);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PrimitiveType getPrimitiveProgressionElementType(KotlinType rangeType) {
|
||||
return getPrimitiveRangeOrProgressionElementType(rangeType, PROGRESSION_TO_ELEMENT_TYPE);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PrimitiveType getPrimitiveRangeOrProgressionElementType(
|
||||
@NotNull KotlinType rangeOrProgression,
|
||||
@NotNull ImmutableMap<FqName, PrimitiveType> map
|
||||
) {
|
||||
ClassifierDescriptor declarationDescriptor = rangeOrProgression.getConstructor().getDeclarationDescriptor();
|
||||
if (declarationDescriptor == null) return null;
|
||||
FqNameUnsafe fqName = DescriptorUtils.getFqName(declarationDescriptor);
|
||||
if (!fqName.isSafe()) return null;
|
||||
return map.get(fqName.toSafe());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PrimitiveType getPrimitiveRangeOrProgressionElementType(@NotNull FqName rangeOrProgressionName) {
|
||||
PrimitiveType result = RANGE_TO_ELEMENT_TYPE.get(rangeOrProgressionName);
|
||||
return result != null ? result : PROGRESSION_TO_ELEMENT_TYPE.get(rangeOrProgressionName);
|
||||
}
|
||||
|
||||
public static boolean isRangeOrProgression(@NotNull FqName className) {
|
||||
return getPrimitiveRangeOrProgressionElementType(className) != null;
|
||||
}
|
||||
|
||||
public static boolean isPrimitiveNumberRangeTo(CallableDescriptor rangeTo) {
|
||||
if (!"rangeTo".equals(rangeTo.getName().asString())) return false;
|
||||
|
||||
if (!isPrimitiveNumberClassDescriptor(rangeTo.getContainingDeclaration())) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isPrimitiveRangeToExtension(@NotNull CallableDescriptor descriptor) {
|
||||
if (!isTopLevelInPackage(descriptor, "rangeTo", "kotlin.ranges")) return false;
|
||||
|
||||
ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
|
||||
if (extensionReceiver == null) return false;
|
||||
|
||||
return KotlinBuiltIns.isPrimitiveType(extensionReceiver.getType());
|
||||
}
|
||||
|
||||
public static boolean isPrimitiveNumberDownTo(@NotNull CallableDescriptor descriptor) {
|
||||
if (!isTopLevelInPackage(descriptor, "downTo", "kotlin.ranges")) return false;
|
||||
|
||||
ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
|
||||
if (extensionReceiver == null) return false;
|
||||
ClassifierDescriptor extensionReceiverClassifier = extensionReceiver.getType().getConstructor().getDeclarationDescriptor();
|
||||
if (!isPrimitiveNumberClassDescriptor(extensionReceiverClassifier)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isPrimitiveNumberUntil(@NotNull CallableDescriptor descriptor) {
|
||||
if (!isTopLevelInPackage(descriptor, "until", "kotlin.ranges")) return false;
|
||||
|
||||
ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
|
||||
if (extensionReceiver == null) return false;
|
||||
ClassifierDescriptor extensionReceiverClassifier = extensionReceiver.getType().getConstructor().getDeclarationDescriptor();
|
||||
if (!isPrimitiveNumberClassDescriptor(extensionReceiverClassifier)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isArrayOrPrimitiveArrayIndices(@NotNull CallableDescriptor descriptor) {
|
||||
if (!isTopLevelInPackage(descriptor, "indices", "kotlin.collections")) return false;
|
||||
|
||||
ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
|
||||
if (extensionReceiver == null) return false;
|
||||
KotlinType extensionReceiverType = extensionReceiver.getType();
|
||||
if (!KotlinBuiltIns.isArray(extensionReceiverType) && !KotlinBuiltIns.isPrimitiveArray(extensionReceiverType)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isCollectionIndices(@NotNull CallableDescriptor descriptor) {
|
||||
if (!isTopLevelInPackage(descriptor, "indices", "kotlin.collections")) return false;
|
||||
|
||||
ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
|
||||
if (extensionReceiver == null) return false;
|
||||
KotlinType extensionReceiverType = extensionReceiver.getType();
|
||||
if (!KotlinBuiltIns.isCollectionOrNullableCollection(extensionReceiverType)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isCharSequenceIndices(@NotNull CallableDescriptor descriptor) {
|
||||
if (!isTopLevelInPackage(descriptor, "indices", "kotlin.text")) return false;
|
||||
|
||||
ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
|
||||
if (extensionReceiver == null) return false;
|
||||
KotlinType extensionReceiverType = extensionReceiver.getType();
|
||||
if (!KotlinBuiltIns.isCharSequenceOrNullableCharSequence(extensionReceiverType)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isPrimitiveRangeToExtension(@NotNull KtSimpleNameExpression operationReference, @NotNull BindingContext bindingContext) {
|
||||
ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt
|
||||
.getResolvedCallWithAssert(operationReference, bindingContext);
|
||||
ReceiverValue receiver = resolvedCall.getDispatchReceiver();
|
||||
|
||||
/*
|
||||
* Range is optimizable if
|
||||
* 'in' receiver is expression 'rangeTo' from stdlib package
|
||||
* and its argument has same primitive type as generic range parameter.
|
||||
* For non-matching primitive types (e.g. int in double range)
|
||||
* dispatch receiver will be null, because extension method will be called.
|
||||
*/
|
||||
if (!(receiver instanceof ExpressionReceiver)) return false;
|
||||
ExpressionReceiver e = (ExpressionReceiver) receiver;
|
||||
|
||||
ResolvedCall<? extends CallableDescriptor> resolvedReceiver =
|
||||
CallUtilKt.getResolvedCall(e.getExpression(), bindingContext);
|
||||
if (resolvedReceiver == null) return false;
|
||||
|
||||
return isPrimitiveRangeToExtension(resolvedReceiver.getResultingDescriptor());
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks whether for expression 'x in a..b' a..b is primitive integral range
|
||||
* with same type as x.
|
||||
*/
|
||||
public static boolean isPrimitiveRangeSpecializationOfType(
|
||||
@NotNull Type argumentType,
|
||||
@NotNull KtExpression rangeExpression,
|
||||
@NotNull BindingContext bindingContext
|
||||
) {
|
||||
if (rangeExpression instanceof KtBinaryExpression &&
|
||||
((KtBinaryExpression) rangeExpression).getOperationReference().getReferencedNameElementType() == KtTokens.RANGE) {
|
||||
KotlinType kotlinType = bindingContext.getType(rangeExpression);
|
||||
assert kotlinType != null;
|
||||
DeclarationDescriptor descriptor = kotlinType.getConstructor().getDeclarationDescriptor();
|
||||
if (descriptor != null) {
|
||||
FqNameUnsafe fqName = DescriptorUtils.getFqName(descriptor);
|
||||
if (fqName.equals(KotlinBuiltIns.FQ_NAMES.longRange)) {
|
||||
return argumentType == Type.LONG_TYPE;
|
||||
}
|
||||
if (fqName.equals(KotlinBuiltIns.FQ_NAMES.charRange) || fqName.equals(KotlinBuiltIns.FQ_NAMES.intRange)) {
|
||||
return AsmUtil.isIntPrimitive(argumentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isTopLevelInPackage(@NotNull CallableDescriptor descriptor, @NotNull String name, @NotNull String packageName) {
|
||||
if (!name.equals(descriptor.getName().asString())) return false;
|
||||
|
||||
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
||||
if (!(containingDeclaration instanceof PackageFragmentDescriptor)) return false;
|
||||
String packageFqName = ((PackageFragmentDescriptor) containingDeclaration).getFqName().asString();
|
||||
if (!packageName.equals(packageFqName)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.RANGES_PACKAGE_FQ_NAME
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitiveNumberClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
val supportedRangeTypes = listOf(PrimitiveType.CHAR, PrimitiveType.INT, PrimitiveType.LONG)
|
||||
|
||||
private val RANGE_TO_ELEMENT_TYPE: Map<FqName, PrimitiveType> =
|
||||
supportedRangeTypes.associateBy {
|
||||
RANGES_PACKAGE_FQ_NAME.child(Name.identifier(it.typeName.toString() + "Range"))
|
||||
}
|
||||
|
||||
private val PROGRESSION_TO_ELEMENT_TYPE: Map<FqName, PrimitiveType> =
|
||||
supportedRangeTypes.associateBy {
|
||||
RANGES_PACKAGE_FQ_NAME.child(Name.identifier(it.typeName.toString() + "Progression"))
|
||||
}
|
||||
|
||||
fun isPrimitiveRange(rangeType: KotlinType) =
|
||||
!rangeType.isMarkedNullable && getPrimitiveRangeElementType(rangeType) != null
|
||||
|
||||
fun isPrimitiveProgression(rangeType: KotlinType) =
|
||||
!rangeType.isMarkedNullable && getPrimitiveProgressionElementType(rangeType) != null
|
||||
|
||||
fun getPrimitiveRangeElementType(rangeType: KotlinType): PrimitiveType? =
|
||||
getPrimitiveRangeOrProgressionElementType(rangeType, RANGE_TO_ELEMENT_TYPE)
|
||||
|
||||
private fun getPrimitiveProgressionElementType(rangeType: KotlinType) =
|
||||
getPrimitiveRangeOrProgressionElementType(rangeType, PROGRESSION_TO_ELEMENT_TYPE)
|
||||
|
||||
private fun getPrimitiveRangeOrProgressionElementType(
|
||||
rangeOrProgression: KotlinType,
|
||||
map: Map<FqName, PrimitiveType>
|
||||
): PrimitiveType? {
|
||||
val declarationDescriptor = rangeOrProgression.constructor.declarationDescriptor ?: return null
|
||||
val fqName = DescriptorUtils.getFqName(declarationDescriptor).takeIf { it.isSafe } ?: return null
|
||||
return map[fqName.toSafe()]
|
||||
}
|
||||
|
||||
fun getRangeOrProgressionElementType(rangeType: KotlinType): KotlinType? {
|
||||
val rangeTypeDescriptor = rangeType.constructor.declarationDescriptor ?: return null
|
||||
val builtIns = rangeTypeDescriptor.builtIns
|
||||
|
||||
return when {
|
||||
isTopLevelInPackage(rangeTypeDescriptor, "CharRange", "kotlin.ranges") -> builtIns.charType
|
||||
isTopLevelInPackage(rangeTypeDescriptor, "IntRange", "kotlin.ranges") -> builtIns.intType
|
||||
isTopLevelInPackage(rangeTypeDescriptor, "LongRange", "kotlin.ranges") -> builtIns.longType
|
||||
|
||||
isTopLevelInPackage(rangeTypeDescriptor, "CharProgression", "kotlin.ranges") -> builtIns.charType
|
||||
isTopLevelInPackage(rangeTypeDescriptor, "IntProgression", "kotlin.ranges") -> builtIns.intType
|
||||
isTopLevelInPackage(rangeTypeDescriptor, "LongProgression", "kotlin.ranges") -> builtIns.longType
|
||||
|
||||
isTopLevelInPackage(rangeTypeDescriptor, "ClosedFloatRange", "kotlin.ranges") -> builtIns.floatType
|
||||
isTopLevelInPackage(rangeTypeDescriptor, "ClosedDoubleRange", "kotlin.ranges") -> builtIns.doubleType
|
||||
|
||||
isTopLevelInPackage(rangeTypeDescriptor, "ClosedRange", "kotlin.ranges") -> rangeType.arguments.singleOrNull()?.type
|
||||
|
||||
isTopLevelInPackage(rangeTypeDescriptor, "ClosedFloatingPointRange", "kotlin.ranges") -> rangeType.arguments.singleOrNull()?.type
|
||||
|
||||
isTopLevelInPackage(rangeTypeDescriptor, "ComparableRange", "kotlin.ranges") -> rangeType.arguments.singleOrNull()?.type
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun getPrimitiveRangeOrProgressionElementType(rangeOrProgressionName: FqName): PrimitiveType? =
|
||||
RANGE_TO_ELEMENT_TYPE[rangeOrProgressionName] ?:
|
||||
PROGRESSION_TO_ELEMENT_TYPE[rangeOrProgressionName]
|
||||
|
||||
fun isRangeOrProgression(className: FqName) =
|
||||
getPrimitiveRangeOrProgressionElementType(className) != null
|
||||
|
||||
fun isPrimitiveNumberRangeTo(rangeTo: CallableDescriptor) =
|
||||
"rangeTo" == rangeTo.name.asString() && isPrimitiveNumberClassDescriptor(rangeTo.containingDeclaration) ||
|
||||
isPrimitiveRangeToExtension(rangeTo)
|
||||
|
||||
private fun isPrimitiveRangeToExtension(descriptor: CallableDescriptor): Boolean {
|
||||
if (!isTopLevelInPackage(descriptor, "rangeTo", "kotlin.ranges")) return false
|
||||
|
||||
val extensionReceiver = descriptor.extensionReceiverParameter ?: return false
|
||||
return KotlinBuiltIns.isPrimitiveType(extensionReceiver.type)
|
||||
}
|
||||
|
||||
fun isPrimitiveNumberDownTo(descriptor: CallableDescriptor): Boolean {
|
||||
if (!isTopLevelInPackage(descriptor, "downTo", "kotlin.ranges")) return false
|
||||
|
||||
val extensionReceiver = descriptor.extensionReceiverParameter ?: return false
|
||||
val extensionReceiverClassifier = extensionReceiver.type.constructor.declarationDescriptor
|
||||
return isPrimitiveNumberClassDescriptor(extensionReceiverClassifier)
|
||||
}
|
||||
|
||||
fun isPrimitiveNumberUntil(descriptor: CallableDescriptor): Boolean {
|
||||
if (!isTopLevelInPackage(descriptor, "until", "kotlin.ranges")) return false
|
||||
|
||||
val extensionReceiver = descriptor.extensionReceiverParameter ?: return false
|
||||
val extensionReceiverClassifier = extensionReceiver.type.constructor.declarationDescriptor
|
||||
return isPrimitiveNumberClassDescriptor(extensionReceiverClassifier)
|
||||
}
|
||||
|
||||
fun isArrayOrPrimitiveArrayIndices(descriptor: CallableDescriptor): Boolean {
|
||||
if (!isTopLevelInPackage(descriptor, "indices", "kotlin.collections")) return false
|
||||
|
||||
val extensionReceiver = descriptor.extensionReceiverParameter ?: return false
|
||||
val extensionReceiverType = extensionReceiver.type
|
||||
return KotlinBuiltIns.isArray(extensionReceiverType) || KotlinBuiltIns.isPrimitiveArray(extensionReceiverType)
|
||||
}
|
||||
|
||||
fun isCollectionIndices(descriptor: CallableDescriptor): Boolean {
|
||||
if (!isTopLevelInPackage(descriptor, "indices", "kotlin.collections")) return false
|
||||
|
||||
val extensionReceiver = descriptor.extensionReceiverParameter ?: return false
|
||||
val extensionReceiverType = extensionReceiver.type
|
||||
return KotlinBuiltIns.isCollectionOrNullableCollection(extensionReceiverType)
|
||||
}
|
||||
|
||||
fun isCharSequenceIndices(descriptor: CallableDescriptor): Boolean {
|
||||
if (!isTopLevelInPackage(descriptor, "indices", "kotlin.text")) return false
|
||||
|
||||
val extensionReceiver = descriptor.extensionReceiverParameter ?: return false
|
||||
val extensionReceiverType = extensionReceiver.type
|
||||
return KotlinBuiltIns.isCharSequenceOrNullableCharSequence(extensionReceiverType)
|
||||
}
|
||||
|
||||
fun isComparableRangeTo(descriptor: CallableDescriptor): Boolean {
|
||||
if (!isTopLevelInPackage(descriptor, "rangeTo", "kotlin.ranges")) return false
|
||||
|
||||
val extensionReceiver = descriptor.original.extensionReceiverParameter ?: return false
|
||||
val extensionReceiverTypeDescriptor = extensionReceiver.type.constructor.declarationDescriptor as? TypeParameterDescriptor ?: return false
|
||||
val upperBoundType = extensionReceiverTypeDescriptor.upperBounds.singleOrNull() ?: return false
|
||||
val upperBoundClassDescriptor = upperBoundType.constructor.declarationDescriptor as? ClassDescriptor ?: return false
|
||||
if (!isTopLevelInPackage(upperBoundClassDescriptor, "Comparable", "kotlin")) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun isClosedRangeContains(descriptor: CallableDescriptor): Boolean {
|
||||
if (descriptor.name.asString() != "contains") return false
|
||||
val containingClassDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return false
|
||||
if (!isTopLevelInPackage(containingClassDescriptor, "ClosedRange", "kotlin.ranges")) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun isPrimitiveRangeContains(descriptor: CallableDescriptor): Boolean {
|
||||
if (descriptor.name.asString() != "contains") return false
|
||||
val dispatchReceiverType = descriptor.dispatchReceiverParameter?.type ?: return false
|
||||
if (!isPrimitiveRange(dispatchReceiverType)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun isPrimitiveNumberRangeExtensionContainsPrimitiveNumber(descriptor: CallableDescriptor): Boolean {
|
||||
if (descriptor.name.asString() != "contains") return false
|
||||
|
||||
val extensionReceiverType = descriptor.extensionReceiverParameter?.type ?: return false
|
||||
|
||||
val rangeElementType = getRangeOrProgressionElementType(extensionReceiverType) ?: return false
|
||||
if (!isPrimitiveNumberType(rangeElementType)) return false
|
||||
|
||||
val argumentType = descriptor.valueParameters.singleOrNull()?.type ?: return false
|
||||
if (!isPrimitiveNumberType(argumentType)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun isPrimitiveNumberType(type: KotlinType) =
|
||||
KotlinBuiltIns.isByte(type) ||
|
||||
KotlinBuiltIns.isShort(type) ||
|
||||
KotlinBuiltIns.isInt(type) ||
|
||||
KotlinBuiltIns.isChar(type) ||
|
||||
KotlinBuiltIns.isLong(type) ||
|
||||
KotlinBuiltIns.isFloat(type) ||
|
||||
KotlinBuiltIns.isDouble(type)
|
||||
|
||||
fun isClosedFloatingPointRangeContains(descriptor: CallableDescriptor): Boolean {
|
||||
if (descriptor.name.asString() != "contains") return false
|
||||
val containingClassDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return false
|
||||
if (!isTopLevelInPackage(containingClassDescriptor, "ClosedFloatingPointRange", "kotlin.ranges")) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun getClosedFloatingPointRangeElementType(rangeType: KotlinType): KotlinType? {
|
||||
val classDescriptor = rangeType.constructor.declarationDescriptor as? ClassDescriptor ?: return null
|
||||
if (!isTopLevelInPackage(classDescriptor, "ClosedFloatingPointRange", "kotlin.ranges")) return null
|
||||
return rangeType.arguments.singleOrNull()?.type
|
||||
}
|
||||
|
||||
private fun isTopLevelInPackage(descriptor: DeclarationDescriptor, name: String, packageName: String): Boolean {
|
||||
if (name != descriptor.name.asString()) return false
|
||||
|
||||
val containingDeclaration = descriptor.containingDeclaration as? PackageFragmentDescriptor ?: return false
|
||||
val packageFqName = containingDeclaration.fqName.asString()
|
||||
return packageName == packageFqName
|
||||
}
|
||||
|
||||
fun getAsmRangeElementTypeForPrimitiveRangeOrProgression(rangeCallee: CallableDescriptor): Type {
|
||||
val rangeType = rangeCallee.returnType!!
|
||||
|
||||
getPrimitiveRangeElementType(rangeType)?.let {
|
||||
return AsmTypes.valueTypeForPrimitive(it)
|
||||
}
|
||||
|
||||
getPrimitiveProgressionElementType(rangeType)?.let {
|
||||
return AsmTypes.valueTypeForPrimitive(it)
|
||||
}
|
||||
|
||||
getClosedFloatingPointRangeElementType(rangeType)?.let {
|
||||
when {
|
||||
KotlinBuiltIns.isDouble(it) -> return Type.DOUBLE_TYPE
|
||||
KotlinBuiltIns.isFloat(it) -> return Type.FLOAT_TYPE
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
throw AssertionError("Unexpected range type: $rangeType")
|
||||
}
|
||||
@@ -105,10 +105,14 @@ public class ScriptCodegen extends MemberCodegen<KtScript> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generateSyntheticParts() {
|
||||
protected void generateSyntheticPartsBeforeBody() {
|
||||
generatePropertyMetadataArrayFieldIfNeeded(classAsmType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generateSyntheticPartsAfterBody() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generateKotlinMetadataAnnotation() {
|
||||
generateKotlinClassMetadataAnnotation(scriptDescriptor, true);
|
||||
|
||||
@@ -413,4 +413,10 @@ fun extractReificationArgument(type: KotlinType): Pair<TypeParameterDescriptor,
|
||||
}
|
||||
|
||||
fun unwrapInitialSignatureDescriptor(function: FunctionDescriptor): FunctionDescriptor =
|
||||
function.initialSignatureDescriptor ?: function
|
||||
function.initialSignatureDescriptor ?: function
|
||||
|
||||
fun ExpressionCodegen.generateCallReceiver(rangeCall: ResolvedCall<out CallableDescriptor>): StackValue =
|
||||
generateReceiverValue(rangeCall.extensionReceiver ?: rangeCall.dispatchReceiver!!, false)
|
||||
|
||||
fun ExpressionCodegen.generateCallSingleArgument(rangeCall: ResolvedCall<out CallableDescriptor>): StackValue =
|
||||
gen(ExpressionCodegen.getSingleArgumentExpression(rangeCall)!!)
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* 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.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.RangeCodegenUtil
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
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.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
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)
|
||||
isSubtypeOfCharSequence(loopRangeType, state.module.builtIns) ->
|
||||
ForInCharSequenceLoopGenerator(this, forExpression)
|
||||
else ->
|
||||
IteratorForLoopGenerator(this, forExpression)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSubtypeOfCharSequence(type: KotlinType, builtIns: KotlinBuiltIns) =
|
||||
KotlinTypeChecker.DEFAULT.isSubtypeOf(type, builtIns.getBuiltInClassByName(Name.identifier("CharSequence")).defaultType)
|
||||
|
||||
private fun ExpressionCodegen.createOptimizedForLoopGeneratorOrNull(
|
||||
forExpression: KtForExpression,
|
||||
loopRangeCall: ResolvedCall<out CallableDescriptor>
|
||||
): AbstractForLoopGenerator? {
|
||||
val loopRangeCallee = loopRangeCall.resultingDescriptor
|
||||
|
||||
return when {
|
||||
RangeCodegenUtil.isPrimitiveNumberRangeTo(loopRangeCallee) ->
|
||||
ForInRangeLiteralLoopGenerator(this, forExpression, loopRangeCall)
|
||||
RangeCodegenUtil.isPrimitiveNumberDownTo(loopRangeCallee) ->
|
||||
ForInDownToProgressionLoopGenerator(this, forExpression, loopRangeCall)
|
||||
RangeCodegenUtil.isPrimitiveNumberUntil(loopRangeCallee) ->
|
||||
ForInUntilRangeLoopGenerator(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<out CallableDescriptor>? {
|
||||
val loopRange = KtPsiUtil.deparenthesize(forExpression.loopRange)
|
||||
|
||||
when (loopRange) {
|
||||
is KtQualifiedExpression -> {
|
||||
val qualifiedExpression = loopRange as KtQualifiedExpression?
|
||||
val selector = qualifiedExpression!!.selectorExpression
|
||||
if (selector is KtCallExpression || selector is KtSimpleNameExpression) {
|
||||
return selector.getResolvedCall(bindingContext)
|
||||
}
|
||||
}
|
||||
is KtSimpleNameExpression, is KtCallExpression -> return loopRange.getResolvedCall(bindingContext)
|
||||
is KtBinaryExpression -> return loopRange.operationReference.getResolvedCall(bindingContext)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -619,11 +619,11 @@ class MethodInliner(
|
||||
}
|
||||
|
||||
private fun wrapException(originalException: Throwable, node: MethodNode, errorSuffix: String): RuntimeException {
|
||||
if (originalException is InlineException) {
|
||||
return InlineException("$errorPrefix: $errorSuffix", originalException)
|
||||
return if (originalException is InlineException) {
|
||||
InlineException("$errorPrefix: $errorSuffix", originalException)
|
||||
}
|
||||
else {
|
||||
return InlineException("$errorPrefix: $errorSuffix\nCause: ${node.nodeText}", originalException)
|
||||
InlineException("$errorPrefix: $errorSuffix\nCause: ${node.nodeText}", originalException)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -679,11 +679,11 @@ class MethodInliner(
|
||||
localReturns.add(LocalReturn(returnInsn, insertBeforeInsn, sourceValueFrame))
|
||||
|
||||
if (returnInsn.opcode != Opcodes.RETURN) {
|
||||
if (returnInsn.opcode == Opcodes.LRETURN || returnInsn.opcode == Opcodes.DRETURN) {
|
||||
returnVariableSize = 2
|
||||
returnVariableSize = if (returnInsn.opcode == Opcodes.LRETURN || returnInsn.opcode == Opcodes.DRETURN) {
|
||||
2
|
||||
}
|
||||
else {
|
||||
returnVariableSize = 1
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,8 +98,8 @@ open class NestedSourceMapper(
|
||||
override fun mapLineNumber(lineNumber: Int): Int {
|
||||
val mappedLineNumber = visitedLines.get(lineNumber)
|
||||
|
||||
if (mappedLineNumber > 0) {
|
||||
return mappedLineNumber
|
||||
return if (mappedLineNumber > 0) {
|
||||
mappedLineNumber
|
||||
}
|
||||
else {
|
||||
val rangeForMapping =
|
||||
@@ -111,7 +111,7 @@ open class NestedSourceMapper(
|
||||
visitedLines.put(lineNumber, newLineNumber)
|
||||
}
|
||||
lastVisitedRange = rangeForMapping
|
||||
return newLineNumber
|
||||
newLineNumber
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization
|
||||
|
||||
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.FastPopBackwardPropagationTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.StackPeepholeOptimizationsTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.PopBackwardPropagationTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.prepareForEmitting
|
||||
@@ -55,7 +55,7 @@ class OptimizationMethodVisitor(
|
||||
RedundantCheckCastEliminationMethodTransformer(),
|
||||
ConstantConditionEliminationMethodTransformer(),
|
||||
RedundantBoxingMethodTransformer(),
|
||||
FastPopBackwardPropagationTransformer(),
|
||||
StackPeepholeOptimizationsTransformer(),
|
||||
PopBackwardPropagationTransformer(),
|
||||
DeadCodeEliminationMethodTransformer(),
|
||||
RedundantGotoMethodTransformer(),
|
||||
|
||||
@@ -18,8 +18,8 @@ package org.jetbrains.kotlin.codegen.optimization.boxing
|
||||
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.RangeCodegenUtil
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.codegen.isRangeOrProgression
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -213,7 +213,7 @@ fun AbstractInsnNode.isIteratorMethodCallOfProgression(values: List<BasicValue>)
|
||||
}
|
||||
|
||||
fun isProgressionClass(type: Type) =
|
||||
RangeCodegenUtil.isRangeOrProgression(buildFqNameByInternal(type.internalName))
|
||||
isRangeOrProgression(buildFqNameByInternal(type.internalName))
|
||||
|
||||
fun AbstractInsnNode.isAreEqualIntrinsicForSameTypedBoxedValues(values: List<BasicValue>) =
|
||||
isAreEqualIntrinsic() && areSameTypedBoxedValues(values)
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* 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.optimization.boxing
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.InsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
class FastPopBackwardPropagationTransformer : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
while (transformOnce(methodNode)) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformOnce(methodNode: MethodNode): Boolean {
|
||||
val toRemove = ArrayList<AbstractInsnNode>()
|
||||
val toReplaceWithNop = ArrayList<AbstractInsnNode>()
|
||||
|
||||
val insns = methodNode.instructions.toArray()
|
||||
|
||||
for (i in 1 until insns.size) {
|
||||
val insn = insns[i]
|
||||
val prev = insns[i - 1]
|
||||
|
||||
when (insn.opcode) {
|
||||
Opcodes.POP -> {
|
||||
if (prev.isEliminatedByPop()) {
|
||||
toReplaceWithNop.add(insn)
|
||||
toRemove.add(prev)
|
||||
}
|
||||
}
|
||||
|
||||
Opcodes.POP2 -> {
|
||||
if (prev.isEliminatedByPop2()) {
|
||||
toReplaceWithNop.add(insn)
|
||||
toRemove.add(prev)
|
||||
}
|
||||
else if (i > 1) {
|
||||
val prev2 = insns[i - 2]
|
||||
if (prev.isEliminatedByPop() && prev2.isEliminatedByPop()) {
|
||||
toReplaceWithNop.add(insn)
|
||||
toRemove.add(prev)
|
||||
toRemove.add(prev2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toReplaceWithNop.forEach { methodNode.instructions.set(it, InsnNode(Opcodes.NOP)) }
|
||||
toRemove.forEach { methodNode.instructions.remove(it) }
|
||||
|
||||
return toReplaceWithNop.isNotEmpty() &&
|
||||
toRemove.isNotEmpty()
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.isEliminatedByPop() =
|
||||
opcode in Opcodes.ACONST_NULL..Opcodes.FCONST_2 ||
|
||||
opcode in Opcodes.BIPUSH..Opcodes.ILOAD ||
|
||||
opcode == Opcodes.FLOAD ||
|
||||
opcode == Opcodes.ALOAD ||
|
||||
isUnitInstance() ||
|
||||
opcode == Opcodes.DUP
|
||||
|
||||
private fun AbstractInsnNode.isEliminatedByPop2() =
|
||||
opcode == Opcodes.LCONST_0 || opcode == Opcodes.LCONST_1 ||
|
||||
opcode == Opcodes.DCONST_0 || opcode == Opcodes.DCONST_1 ||
|
||||
opcode == Opcodes.LLOAD ||
|
||||
opcode == Opcodes.DLOAD ||
|
||||
opcode == Opcodes.DUP2
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization.boxing
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.OptimizationMethodVisitor
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.debugText
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isLoadOperation
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.peekWords
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.removeNodeGetNext
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
@@ -74,46 +76,84 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
|
||||
postprocessNops()
|
||||
}
|
||||
|
||||
private fun analyzeMethodBody(): Array<out Frame<SourceValue>?> =
|
||||
Analyzer<SourceValue>(object : SourceInterpreter() {
|
||||
override fun naryOperation(insn: AbstractInsnNode, values: MutableList<out SourceValue>): SourceValue {
|
||||
for (value in values) {
|
||||
value.insns.markAsDontTouch()
|
||||
}
|
||||
return super.naryOperation(insn, values)
|
||||
}
|
||||
private fun analyzeMethodBody(): Array<out Frame<SourceValue>?> {
|
||||
val frames = Analyzer<SourceValue>(HazardsTrackingInterpreter()).analyze("fake", methodNode)
|
||||
|
||||
override fun copyOperation(insn: AbstractInsnNode, value: SourceValue): SourceValue {
|
||||
value.insns.markAsDontTouch()
|
||||
return super.copyOperation(insn, value)
|
||||
}
|
||||
postprocessDupNxM(frames)
|
||||
|
||||
override fun unaryOperation(insn: AbstractInsnNode, value: SourceValue): SourceValue {
|
||||
if (insn.opcode != Opcodes.CHECKCAST && !insn.isPrimitiveTypeConversion()) {
|
||||
value.insns.markAsDontTouch()
|
||||
}
|
||||
return super.unaryOperation(insn, value)
|
||||
}
|
||||
return frames
|
||||
}
|
||||
|
||||
override fun binaryOperation(insn: AbstractInsnNode, value1: SourceValue, value2: SourceValue): SourceValue {
|
||||
value1.insns.markAsDontTouch()
|
||||
value2.insns.markAsDontTouch()
|
||||
return super.binaryOperation(insn, value1, value2)
|
||||
}
|
||||
private fun postprocessDupNxM(frames: Array<out Frame<SourceValue>?>) {
|
||||
val insns = methodNode.instructions.toArray()
|
||||
for (i in frames.indices) {
|
||||
val frame = frames[i] ?: continue
|
||||
val insn = insns[i]
|
||||
|
||||
override fun ternaryOperation(insn: AbstractInsnNode, value1: SourceValue, value2: SourceValue, value3: SourceValue): SourceValue {
|
||||
value1.insns.markAsDontTouch()
|
||||
value2.insns.markAsDontTouch()
|
||||
value3.insns.markAsDontTouch()
|
||||
return super.ternaryOperation(insn, value1, value2, value3)
|
||||
when (insn.opcode) {
|
||||
Opcodes.DUP_X1 -> {
|
||||
val top2 = frame.peekWords(1, 1) ?: throwIncorrectBytecode(insn, frame)
|
||||
top2.forEach { it.insns.markAsDontTouch() }
|
||||
}
|
||||
Opcodes.DUP2_X1 -> {
|
||||
val top3 = frame.peekWords(2, 1) ?: throwIncorrectBytecode(insn, frame)
|
||||
top3.forEach { it.insns.markAsDontTouch() }
|
||||
}
|
||||
Opcodes.DUP_X2 -> {
|
||||
val top3 = frame.peekWords(1, 2) ?: throwIncorrectBytecode(insn, frame)
|
||||
top3.forEach { it.insns.markAsDontTouch() }
|
||||
}
|
||||
Opcodes.DUP2_X2 -> {
|
||||
val top4 = frame.peekWords(2, 2) ?: throwIncorrectBytecode(insn, frame)
|
||||
top4.forEach { it.insns.markAsDontTouch() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Collection<AbstractInsnNode>.markAsDontTouch() {
|
||||
forEach {
|
||||
dontTouchInsnIndices[insnList.indexOf(it)] = true
|
||||
}
|
||||
}
|
||||
}).analyze("fake", methodNode)
|
||||
private fun throwIncorrectBytecode(insn: AbstractInsnNode?, frame: Frame<SourceValue>): Nothing {
|
||||
throw AssertionError("Incorrect bytecode at ${methodNode.instructions.indexOf(insn)}: ${insn.debugText} $frame")
|
||||
}
|
||||
|
||||
private inner class HazardsTrackingInterpreter : SourceInterpreter() {
|
||||
override fun naryOperation(insn: AbstractInsnNode, values: MutableList<out SourceValue>): SourceValue {
|
||||
for (value in values) {
|
||||
value.insns.markAsDontTouch()
|
||||
}
|
||||
return super.naryOperation(insn, values)
|
||||
}
|
||||
|
||||
override fun copyOperation(insn: AbstractInsnNode, value: SourceValue): SourceValue {
|
||||
value.insns.markAsDontTouch()
|
||||
return super.copyOperation(insn, value)
|
||||
}
|
||||
|
||||
override fun unaryOperation(insn: AbstractInsnNode, value: SourceValue): SourceValue {
|
||||
if (insn.opcode != Opcodes.CHECKCAST && !insn.isPrimitiveTypeConversion()) {
|
||||
value.insns.markAsDontTouch()
|
||||
}
|
||||
return super.unaryOperation(insn, value)
|
||||
}
|
||||
|
||||
override fun binaryOperation(insn: AbstractInsnNode, value1: SourceValue, value2: SourceValue): SourceValue {
|
||||
value1.insns.markAsDontTouch()
|
||||
value2.insns.markAsDontTouch()
|
||||
return super.binaryOperation(insn, value1, value2)
|
||||
}
|
||||
|
||||
override fun ternaryOperation(insn: AbstractInsnNode, value1: SourceValue, value2: SourceValue, value3: SourceValue): SourceValue {
|
||||
value1.insns.markAsDontTouch()
|
||||
value2.insns.markAsDontTouch()
|
||||
value3.insns.markAsDontTouch()
|
||||
return super.ternaryOperation(insn, value1, value2, value3)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Collection<AbstractInsnNode>.markAsDontTouch() {
|
||||
forEach {
|
||||
dontTouchInsnIndices[insnList.indexOf(it)] = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun computeTransformations() {
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType;
|
||||
import org.jetbrains.kotlin.codegen.RangeCodegenUtil;
|
||||
import org.jetbrains.kotlin.codegen.RangeCodegenUtilKt;
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IteratorNext;
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
@@ -32,7 +32,7 @@ public class ProgressionIteratorBasicValue extends StrictBasicValue {
|
||||
private final static ImmutableMap<String, Type> VALUES_TYPENAME_TO_TYPE;
|
||||
static {
|
||||
ImmutableMap.Builder<String, Type> builder = ImmutableMap.builder();
|
||||
for (PrimitiveType primitiveType : RangeCodegenUtil.supportedRangeTypes()) {
|
||||
for (PrimitiveType primitiveType : RangeCodegenUtilKt.getSupportedRangeTypes()) {
|
||||
builder.put(primitiveType.getTypeName().asString(), Type.getType(JvmPrimitiveType.get(primitiveType).getDesc()));
|
||||
}
|
||||
VALUES_TYPENAME_TO_TYPE = builder.build();
|
||||
@@ -41,7 +41,7 @@ public class ProgressionIteratorBasicValue extends StrictBasicValue {
|
||||
private static final ImmutableMap<PrimitiveType, ProgressionIteratorBasicValue> ITERATOR_VALUE_BY_ELEMENT_PRIMITIVE_TYPE;
|
||||
static {
|
||||
ImmutableMap.Builder<PrimitiveType, ProgressionIteratorBasicValue> builder = ImmutableMap.builder();
|
||||
for (PrimitiveType elementType : RangeCodegenUtil.supportedRangeTypes()) {
|
||||
for (PrimitiveType elementType : RangeCodegenUtilKt.getSupportedRangeTypes()) {
|
||||
builder.put(elementType, new ProgressionIteratorBasicValue(elementType.getTypeName().asString()));
|
||||
}
|
||||
ITERATOR_VALUE_BY_ELEMENT_PRIMITIVE_TYPE = builder.build();
|
||||
@@ -67,7 +67,7 @@ public class ProgressionIteratorBasicValue extends StrictBasicValue {
|
||||
@Nullable
|
||||
public static ProgressionIteratorBasicValue byProgressionClassType(@NotNull Type progressionClassType) {
|
||||
FqName classFqName = new FqName(progressionClassType.getClassName());
|
||||
PrimitiveType elementType = RangeCodegenUtil.getPrimitiveRangeOrProgressionElementType(classFqName);
|
||||
PrimitiveType elementType = RangeCodegenUtilKt.getPrimitiveRangeOrProgressionElementType(classFqName);
|
||||
return ITERATOR_VALUE_BY_ELEMENT_PRIMITIVE_TYPE.get(elementType);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.optimization.boxing
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.findPreviousOrNull
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.InsnList
|
||||
import org.jetbrains.org.objectweb.asm.tree.InsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
class StackPeepholeOptimizationsTransformer : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
while (transformOnce(methodNode)) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformOnce(methodNode: MethodNode): Boolean {
|
||||
val actions = ArrayList<(InsnList) -> Unit>()
|
||||
|
||||
val insns = methodNode.instructions.toArray()
|
||||
|
||||
forInsn@ for (i in 1 until insns.size) {
|
||||
val insn = insns[i]
|
||||
val prev = insn.previous
|
||||
val prevNonNop = insn.findPreviousOrNull { it.opcode != Opcodes.NOP } ?: continue@forInsn
|
||||
|
||||
when (insn.opcode) {
|
||||
Opcodes.POP -> {
|
||||
when {
|
||||
prevNonNop.isEliminatedByPop() -> actions.add {
|
||||
it.set(insn, InsnNode(Opcodes.NOP))
|
||||
it.remove(prevNonNop)
|
||||
}
|
||||
prevNonNop.opcode == Opcodes.DUP_X1 -> actions.add {
|
||||
it.remove(insn)
|
||||
it.set(prevNonNop, InsnNode(Opcodes.SWAP))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Opcodes.SWAP -> {
|
||||
val prevNonNop2 = prevNonNop.findPreviousOrNull { it.opcode != Opcodes.NOP } ?: continue@forInsn
|
||||
if (prevNonNop.isPurePushOfSize1() && prevNonNop2.isPurePushOfSize1()) {
|
||||
actions.add {
|
||||
it.remove(insn)
|
||||
it.set(prevNonNop, prevNonNop2.clone(emptyMap()))
|
||||
it.set(prevNonNop2, prevNonNop.clone(emptyMap()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Opcodes.I2L -> {
|
||||
when (prevNonNop.opcode) {
|
||||
Opcodes.ICONST_0 -> actions.add {
|
||||
it.remove(insn)
|
||||
it.set(prevNonNop, InsnNode(Opcodes.LCONST_0))
|
||||
}
|
||||
Opcodes.ICONST_1 -> actions.add {
|
||||
it.remove(insn)
|
||||
it.set(prevNonNop, InsnNode(Opcodes.LCONST_1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Opcodes.POP2 -> {
|
||||
if (prevNonNop.isEliminatedByPop2()) {
|
||||
actions.add {
|
||||
it.set(insn, InsnNode(Opcodes.NOP))
|
||||
it.remove(prevNonNop)
|
||||
}
|
||||
}
|
||||
else if (i > 1) {
|
||||
val prevNonNop2 = prevNonNop.findPreviousOrNull { it.opcode != Opcodes.NOP } ?: continue@forInsn
|
||||
if (prevNonNop.isEliminatedByPop() && prevNonNop2.isEliminatedByPop()) {
|
||||
actions.add {
|
||||
it.set(insn, InsnNode(Opcodes.NOP))
|
||||
it.remove(prevNonNop)
|
||||
it.remove(prevNonNop2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Opcodes.NOP ->
|
||||
if (prev.opcode == Opcodes.NOP) {
|
||||
actions.add {
|
||||
it.remove(prev)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actions.forEach { it(methodNode.instructions) }
|
||||
|
||||
return actions.isNotEmpty()
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.isEliminatedByPop() =
|
||||
isPurePushOfSize1() ||
|
||||
opcode == Opcodes.DUP
|
||||
|
||||
private fun AbstractInsnNode.isPurePushOfSize1(): Boolean =
|
||||
opcode in Opcodes.ACONST_NULL..Opcodes.FCONST_2 ||
|
||||
opcode in Opcodes.BIPUSH..Opcodes.ILOAD ||
|
||||
opcode == Opcodes.FLOAD ||
|
||||
opcode == Opcodes.ALOAD ||
|
||||
isUnitInstance()
|
||||
|
||||
private fun AbstractInsnNode.isEliminatedByPop2() =
|
||||
isPurePushOfSize2() ||
|
||||
opcode == Opcodes.DUP2
|
||||
|
||||
private fun AbstractInsnNode.isPurePushOfSize2(): Boolean =
|
||||
opcode == Opcodes.LCONST_0 || opcode == Opcodes.LCONST_1 ||
|
||||
opcode == Opcodes.DCONST_0 || opcode == Opcodes.DCONST_1 ||
|
||||
opcode == Opcodes.LLOAD ||
|
||||
opcode == Opcodes.DLOAD
|
||||
}
|
||||
|
||||
@@ -142,20 +142,20 @@ internal class FixStackAnalyzer(
|
||||
}
|
||||
|
||||
override fun pop(): BasicValue {
|
||||
if (extraStack.isNotEmpty()) {
|
||||
return extraStack.pop()
|
||||
return if (extraStack.isNotEmpty()) {
|
||||
extraStack.pop()
|
||||
}
|
||||
else {
|
||||
return super.pop()
|
||||
super.pop()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getStack(i: Int): BasicValue {
|
||||
if (i < super.getMaxStackSize()) {
|
||||
return super.getStack(i)
|
||||
return if (i < super.getMaxStackSize()) {
|
||||
super.getStack(i)
|
||||
}
|
||||
else {
|
||||
return extraStack[i - maxStackSize]
|
||||
extraStack[i - maxStackSize]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,31 @@ fun <V : Value> Frame<V>.top(): V? =
|
||||
fun <V : Value> Frame<V>.peek(offset: Int): V? =
|
||||
if (stackSize > offset) getStack(stackSize - offset - 1) else null
|
||||
|
||||
private fun <V : Value> Frame<V>.peekWordsTo(dest: MutableList<V>, size: Int, offset0: Int = 0): Int {
|
||||
var offset = offset0
|
||||
var totalSize = 0
|
||||
while (totalSize < size) {
|
||||
val value = peek(offset++) ?: return -1
|
||||
dest.add(value)
|
||||
totalSize += value.size
|
||||
}
|
||||
if (totalSize > size) return -1
|
||||
return offset
|
||||
}
|
||||
|
||||
fun <V : Value> Frame<V>.peekWords(size: Int): List<V>? {
|
||||
val result = ArrayList<V>(size)
|
||||
return if (peekWordsTo(result, size) < 0) null else result
|
||||
}
|
||||
|
||||
fun <V : Value> Frame<V>.peekWords(size1: Int, size2: Int): List<V>? {
|
||||
val result = ArrayList<V>(size1 + size2)
|
||||
val offset = peekWordsTo(result, size1)
|
||||
if (offset < 0) return null
|
||||
if (peekWordsTo(result, size2, offset) < 0) return null
|
||||
return result
|
||||
}
|
||||
|
||||
class SavedStackDescriptor(
|
||||
val savedValues: List<BasicValue>,
|
||||
val firstLocalVarIndex: Int
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
abstract class AbstractBoundedValue(
|
||||
protected val codegen: ExpressionCodegen,
|
||||
protected val rangeCall: ResolvedCall<out CallableDescriptor>,
|
||||
override val isLowInclusive: Boolean = true,
|
||||
override val isHighInclusive: Boolean = true
|
||||
) : BoundedValue {
|
||||
override val instanceType: Type = codegen.asmType(rangeCall.resultingDescriptor.returnType!!)
|
||||
|
||||
override fun putInstance(v: InstructionAdapter, type: Type) {
|
||||
codegen.invokeFunction(rangeCall.call, rangeCall, StackValue.none()).put(type, v)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.generateCallReceiver
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.ForInArrayIndicesRangeLoopGenerator
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class ArrayIndicesRangeValue(rangeCall: ResolvedCall<out CallableDescriptor>): PrimitiveNumberRangeIntrinsicRangeValue(rangeCall) {
|
||||
private val expectedReceiverType: KotlinType = ExpressionCodegen.getExpectedReceiverType(rangeCall)
|
||||
|
||||
override fun getBoundedValue(codegen: ExpressionCodegen) =
|
||||
object : AbstractBoundedValue(codegen, rangeCall) {
|
||||
override fun putHighLow(v: InstructionAdapter, type: Type) {
|
||||
codegen.generateCallReceiver(rangeCall).put(codegen.asmType(expectedReceiverType), v)
|
||||
v.arraylength()
|
||||
StackValue.coerce(Type.INT_TYPE, type, v)
|
||||
|
||||
StackValue.constant(0, asmElementType).put(type, v)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
|
||||
ForInArrayIndicesRangeLoopGenerator(codegen, forExpression, rangeCall)
|
||||
|
||||
}
|
||||
@@ -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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.ForInArrayLoopGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
|
||||
class ArrayRangeValue : RangeValue {
|
||||
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
|
||||
ForInArrayLoopGenerator(codegen, forExpression)
|
||||
|
||||
override fun createInExpressionGenerator(codegen: ExpressionCodegen, operatorReference: KtSimpleNameExpression): InExpressionGenerator =
|
||||
CallBasedInExpressionGenerator(codegen, operatorReference)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.range.comparison.getComparisonGeneratorForRangeContainsCall
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
|
||||
abstract class CallIntrinsicRangeValue(protected val rangeCall: ResolvedCall<out CallableDescriptor>): RangeValue {
|
||||
protected abstract fun isIntrinsicInCall(resolvedCallForIn: ResolvedCall<out CallableDescriptor>): Boolean
|
||||
|
||||
protected abstract fun createIntrinsicInExpressionGenerator(codegen: ExpressionCodegen, operatorReference: KtSimpleNameExpression, resolvedCall: ResolvedCall<out CallableDescriptor>): InExpressionGenerator
|
||||
|
||||
override fun createInExpressionGenerator(codegen: ExpressionCodegen, operatorReference: KtSimpleNameExpression): InExpressionGenerator {
|
||||
val resolvedCall = operatorReference.getResolvedCallWithAssert(codegen.bindingContext)
|
||||
return if (isIntrinsicInCall(resolvedCall))
|
||||
createIntrinsicInExpressionGenerator(codegen, operatorReference, resolvedCall)
|
||||
else
|
||||
CallBasedInExpressionGenerator(codegen, operatorReference)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.generateCallReceiver
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.ForInCharSequenceIndicesRangeLoopGenerator
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class CharSequenceIndicesRangeValue(rangeCall: ResolvedCall<out CallableDescriptor>): PrimitiveNumberRangeIntrinsicRangeValue(rangeCall) {
|
||||
private val expectedReceiverType: KotlinType = ExpressionCodegen.getExpectedReceiverType(rangeCall)
|
||||
|
||||
override fun getBoundedValue(codegen: ExpressionCodegen) =
|
||||
object : AbstractBoundedValue(codegen, rangeCall) {
|
||||
override fun putHighLow(v: InstructionAdapter, type: Type) {
|
||||
codegen.generateCallReceiver(rangeCall).put(codegen.asmType(expectedReceiverType), v)
|
||||
v.invokeinterface("java/lang/CharSequence", "length", "()I")
|
||||
StackValue.coerce(Type.INT_TYPE, type, v)
|
||||
|
||||
StackValue.constant(0, asmElementType).put(type, v)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
|
||||
ForInCharSequenceIndicesRangeLoopGenerator(codegen, forExpression, rangeCall)
|
||||
}
|
||||
@@ -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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.ForInCharSequenceLoopGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
|
||||
class CharSequenceRangeValue : RangeValue {
|
||||
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
|
||||
ForInCharSequenceLoopGenerator(codegen, forExpression)
|
||||
|
||||
override fun createInExpressionGenerator(codegen: ExpressionCodegen, operatorReference: KtSimpleNameExpression): InExpressionGenerator =
|
||||
CallBasedInExpressionGenerator(codegen, operatorReference)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.generateCallReceiver
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.ForInCollectionIndicesRangeLoopGenerator
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class CollectionIndicesRangeValue(rangeCall: ResolvedCall<out CallableDescriptor>): PrimitiveNumberRangeIntrinsicRangeValue(rangeCall) {
|
||||
private val expectedReceiverType: KotlinType = ExpressionCodegen.getExpectedReceiverType(rangeCall)
|
||||
|
||||
override fun getBoundedValue(codegen: ExpressionCodegen) =
|
||||
object : AbstractBoundedValue(codegen, rangeCall) {
|
||||
override fun putHighLow(v: InstructionAdapter, type: Type) {
|
||||
codegen.generateCallReceiver(rangeCall).put(codegen.asmType(expectedReceiverType), v)
|
||||
v.invokeinterface("java/util/Collection", "size", "()I")
|
||||
StackValue.coerce(Type.INT_TYPE, type, v)
|
||||
|
||||
StackValue.constant(0, asmElementType).put(type, v)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
|
||||
ForInCollectionIndicesRangeLoopGenerator(codegen, forExpression, rangeCall)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.isClosedRangeContains
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.IteratorForLoopGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.InContinuousRangeOfComparableExpressionGenerator
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
|
||||
class ComparableRangeLiteralRangeValue(
|
||||
codegen: ExpressionCodegen,
|
||||
rangeCall: ResolvedCall<out CallableDescriptor>
|
||||
) : CallIntrinsicRangeValue(rangeCall) {
|
||||
private val boundedValue = SimpleBoundedValue(codegen, rangeCall)
|
||||
|
||||
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
|
||||
IteratorForLoopGenerator(codegen, forExpression)
|
||||
|
||||
override fun isIntrinsicInCall(resolvedCallForIn: ResolvedCall<out CallableDescriptor>) =
|
||||
isClosedRangeContains(resolvedCallForIn.resultingDescriptor)
|
||||
|
||||
override fun createIntrinsicInExpressionGenerator(
|
||||
codegen: ExpressionCodegen,
|
||||
operatorReference: KtSimpleNameExpression,
|
||||
resolvedCall: ResolvedCall<out CallableDescriptor>
|
||||
) = InContinuousRangeOfComparableExpressionGenerator(operatorReference, boundedValue)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.generateCallReceiver
|
||||
import org.jetbrains.kotlin.codegen.generateCallSingleArgument
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.ForInDownToProgressionLoopGenerator
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
|
||||
class DownToProgressionRangeValue(rangeCall: ResolvedCall<out CallableDescriptor>): PrimitiveNumberRangeIntrinsicRangeValue(rangeCall) {
|
||||
override fun getBoundedValue(codegen: ExpressionCodegen) =
|
||||
SimpleBoundedValue(
|
||||
codegen, rangeCall,
|
||||
lowBound = codegen.generateCallSingleArgument(rangeCall),
|
||||
highBound = codegen.generateCallReceiver(rangeCall)
|
||||
)
|
||||
|
||||
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
|
||||
ForInDownToProgressionLoopGenerator(codegen, forExpression, rangeCall)
|
||||
}
|
||||
@@ -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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.IteratorForLoopGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
|
||||
class IterableRangeValue : RangeValue {
|
||||
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
|
||||
IteratorForLoopGenerator(codegen, forExpression)
|
||||
|
||||
override fun createInExpressionGenerator(codegen: ExpressionCodegen, operatorReference: KtSimpleNameExpression): InExpressionGenerator =
|
||||
CallBasedInExpressionGenerator(codegen, operatorReference)
|
||||
}
|
||||
@@ -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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.range.comparison.getComparisonGeneratorForRangeContainsCall
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.InPrimitiveContinuousRangeExpressionGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
|
||||
abstract class PrimitiveNumberRangeIntrinsicRangeValue(rangeCall: ResolvedCall<out CallableDescriptor>): CallIntrinsicRangeValue(rangeCall) {
|
||||
protected val asmElementType = getAsmRangeElementTypeForPrimitiveRangeOrProgression(rangeCall.resultingDescriptor)
|
||||
|
||||
override fun isIntrinsicInCall(resolvedCallForIn: ResolvedCall<out CallableDescriptor>) =
|
||||
resolvedCallForIn.resultingDescriptor.let {
|
||||
isPrimitiveRangeContains(it) ||
|
||||
isClosedFloatingPointRangeContains(it) ||
|
||||
isPrimitiveNumberRangeExtensionContainsPrimitiveNumber(it)
|
||||
}
|
||||
|
||||
override fun createIntrinsicInExpressionGenerator(
|
||||
codegen: ExpressionCodegen,
|
||||
operatorReference: KtSimpleNameExpression,
|
||||
resolvedCall: ResolvedCall<out CallableDescriptor>
|
||||
): InExpressionGenerator {
|
||||
val comparisonGenerator = getComparisonGeneratorForRangeContainsCall(codegen, resolvedCall)
|
||||
return if (comparisonGenerator != null)
|
||||
InPrimitiveContinuousRangeExpressionGenerator(operatorReference, getBoundedValue(codegen), comparisonGenerator)
|
||||
else
|
||||
CallBasedInExpressionGenerator(codegen, operatorReference)
|
||||
}
|
||||
|
||||
protected abstract fun getBoundedValue(codegen: ExpressionCodegen): BoundedValue
|
||||
}
|
||||
@@ -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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.ForInRangeLiteralLoopGenerator
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
|
||||
class PrimitiveNumberRangeLiteralRangeValue(rangeCall: ResolvedCall<out CallableDescriptor>): PrimitiveNumberRangeIntrinsicRangeValue(rangeCall) {
|
||||
override fun getBoundedValue(codegen: ExpressionCodegen) =
|
||||
SimpleBoundedValue(codegen, rangeCall)
|
||||
|
||||
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
|
||||
ForInRangeLiteralLoopGenerator(codegen, forExpression, rangeCall)
|
||||
}
|
||||
@@ -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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.ForInUntilRangeLoopGenerator
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
|
||||
class PrimitiveNumberUntilRangeValue(rangeCall: ResolvedCall<out CallableDescriptor>): PrimitiveNumberRangeIntrinsicRangeValue(rangeCall) {
|
||||
override fun getBoundedValue(codegen: ExpressionCodegen) =
|
||||
SimpleBoundedValue(codegen, rangeCall, isLowInclusive = true, isHighInclusive = false)
|
||||
|
||||
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
|
||||
ForInUntilRangeLoopGenerator(codegen, forExpression, rangeCall)
|
||||
}
|
||||
@@ -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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.ForInProgressionExpressionLoopGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
|
||||
class PrimitiveProgressionRangeValue : RangeValue {
|
||||
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
|
||||
ForInProgressionExpressionLoopGenerator(codegen, forExpression)
|
||||
|
||||
override fun createInExpressionGenerator(codegen: ExpressionCodegen, operatorReference: KtSimpleNameExpression): InExpressionGenerator =
|
||||
CallBasedInExpressionGenerator(codegen, operatorReference)
|
||||
}
|
||||
@@ -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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.ForInRangeInstanceLoopGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
|
||||
class PrimitiveRangeRangeValue : RangeValue {
|
||||
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
|
||||
ForInRangeInstanceLoopGenerator(codegen, forExpression)
|
||||
|
||||
override fun createInExpressionGenerator(codegen: ExpressionCodegen, operatorReference: KtSimpleNameExpression): InExpressionGenerator =
|
||||
CallBasedInExpressionGenerator(codegen, operatorReference)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.ForLoopGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
interface RangeValue {
|
||||
fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator
|
||||
|
||||
fun createInExpressionGenerator(codegen: ExpressionCodegen, operatorReference: KtSimpleNameExpression): InExpressionGenerator
|
||||
}
|
||||
|
||||
|
||||
interface BoundedValue {
|
||||
val instanceType: Type
|
||||
|
||||
fun putInstance(v: InstructionAdapter, type: Type)
|
||||
|
||||
// It is necessary to maintain the proper evaluation order as of Kotlin 1.0 and 1.1
|
||||
// to evaluate range bounds left to right and put them on stack as 'high; low'.
|
||||
fun putHighLow(v: InstructionAdapter, type: Type)
|
||||
|
||||
val isLowInclusive: Boolean
|
||||
val isHighInclusive: Boolean
|
||||
}
|
||||
|
||||
fun BoundedValue.asStackValue(): StackValue =
|
||||
object : StackValue(instanceType) {
|
||||
override fun putSelector(type: Type, v: InstructionAdapter) {
|
||||
putInstance(v, type)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.range
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
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.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
fun ExpressionCodegen.createRangeValueForExpression(rangeExpression: KtExpression): RangeValue {
|
||||
getResolvedCallForRangeExpression(bindingContext, rangeExpression)?.let {
|
||||
createIntrinsifiedRangeValueOrNull(it)?.let {
|
||||
return it
|
||||
}
|
||||
}
|
||||
|
||||
val rangeType = bindingContext.getType(rangeExpression)!!
|
||||
val asmRangeType = asmType(rangeType)
|
||||
|
||||
return when {
|
||||
asmRangeType.sort == Type.ARRAY ->
|
||||
ArrayRangeValue()
|
||||
isPrimitiveRange(rangeType) ->
|
||||
PrimitiveRangeRangeValue()
|
||||
isPrimitiveProgression(rangeType) ->
|
||||
PrimitiveProgressionRangeValue()
|
||||
isSubtypeOfCharSequence(rangeType, state.module.builtIns) ->
|
||||
CharSequenceRangeValue()
|
||||
else ->
|
||||
IterableRangeValue()
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSubtypeOfCharSequence(type: KotlinType, builtIns: KotlinBuiltIns) =
|
||||
KotlinTypeChecker.DEFAULT.isSubtypeOf(type, builtIns.getBuiltInClassByName(Name.identifier("CharSequence")).defaultType)
|
||||
|
||||
private fun getResolvedCallForRangeExpression(
|
||||
bindingContext: BindingContext,
|
||||
rangeExpression: KtExpression
|
||||
): ResolvedCall<out CallableDescriptor>? {
|
||||
val expression = KtPsiUtil.deparenthesize(rangeExpression) ?: return null
|
||||
|
||||
return when (expression) {
|
||||
is KtQualifiedExpression ->
|
||||
expression.selectorExpression.let { selector ->
|
||||
if (selector is KtCallExpression || selector is KtSimpleNameExpression)
|
||||
selector.getResolvedCall(bindingContext)
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
is KtSimpleNameExpression, is KtCallExpression ->
|
||||
expression.getResolvedCall(bindingContext)
|
||||
is KtBinaryExpression ->
|
||||
expression.operationReference.getResolvedCall(bindingContext)
|
||||
else ->
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun ExpressionCodegen.createIntrinsifiedRangeValueOrNull(rangeCall: ResolvedCall<out CallableDescriptor>): RangeValue? {
|
||||
val rangeCallee = rangeCall.resultingDescriptor
|
||||
|
||||
return when {
|
||||
isPrimitiveNumberRangeTo(rangeCallee) ->
|
||||
PrimitiveNumberRangeLiteralRangeValue(rangeCall)
|
||||
isPrimitiveNumberDownTo(rangeCallee) ->
|
||||
DownToProgressionRangeValue(rangeCall)
|
||||
isPrimitiveNumberUntil(rangeCallee) ->
|
||||
PrimitiveNumberUntilRangeValue(rangeCall)
|
||||
isArrayOrPrimitiveArrayIndices(rangeCallee) ->
|
||||
ArrayIndicesRangeValue(rangeCall)
|
||||
isCollectionIndices(rangeCallee) ->
|
||||
CollectionIndicesRangeValue(rangeCall)
|
||||
isCharSequenceIndices(rangeCallee) ->
|
||||
CharSequenceIndicesRangeValue(rangeCall)
|
||||
isComparableRangeTo(rangeCallee) ->
|
||||
ComparableRangeLiteralRangeValue(this, rangeCall)
|
||||
else ->
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class SimpleBoundedValue(
|
||||
codegen: ExpressionCodegen,
|
||||
rangeCall: ResolvedCall<out CallableDescriptor>,
|
||||
private val lowBound: StackValue,
|
||||
isLowInclusive: Boolean,
|
||||
private val highBound: StackValue,
|
||||
isHighInclusive: Boolean
|
||||
): AbstractBoundedValue(codegen, rangeCall, isLowInclusive, isHighInclusive) {
|
||||
constructor(
|
||||
codegen: ExpressionCodegen,
|
||||
rangeCall: ResolvedCall<out CallableDescriptor>,
|
||||
isLowInclusive: Boolean = true,
|
||||
isHighInclusive: Boolean = true
|
||||
) : this(
|
||||
codegen,
|
||||
rangeCall,
|
||||
codegen.generateCallReceiver(rangeCall),
|
||||
isLowInclusive,
|
||||
codegen.generateCallSingleArgument(rangeCall),
|
||||
isHighInclusive
|
||||
)
|
||||
|
||||
constructor(
|
||||
codegen: ExpressionCodegen,
|
||||
rangeCall: ResolvedCall<out CallableDescriptor>,
|
||||
lowBound: StackValue,
|
||||
highBound: StackValue
|
||||
) : this(codegen, rangeCall, lowBound, true, highBound, true)
|
||||
|
||||
override fun putHighLow(v: InstructionAdapter, type: Type) {
|
||||
if (!lowBound.canHaveSideEffects() || !highBound.canHaveSideEffects()) {
|
||||
highBound.put(type, v)
|
||||
lowBound.put(type, v)
|
||||
}
|
||||
else {
|
||||
lowBound.put(type, v)
|
||||
highBound.put(type, v)
|
||||
AsmUtil.swap(v, type, type)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.range.comparison
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.getRangeOrProgressionElementType
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
interface ComparisonGenerator {
|
||||
val comparedType: Type
|
||||
|
||||
fun jumpIfGreaterOrEqual(v: InstructionAdapter, label: Label)
|
||||
fun jumpIfLessOrEqual(v: InstructionAdapter, label: Label)
|
||||
fun jumpIfGreater(v: InstructionAdapter, label: Label)
|
||||
fun jumpIfLess(v: InstructionAdapter, label: Label)
|
||||
}
|
||||
|
||||
fun getComparisonGeneratorForPrimitiveType(type: Type): ComparisonGenerator =
|
||||
when {
|
||||
type.isRepresentedAsPrimitiveInt() -> IntComparisonGenerator
|
||||
type == Type.LONG_TYPE -> LongComparisonGenerator
|
||||
type == Type.FLOAT_TYPE -> FloatComparisonGenerator
|
||||
type == Type.DOUBLE_TYPE -> DoubleComparisonGenerator
|
||||
else -> throw UnsupportedOperationException("Unexpected primitive type: " + type)
|
||||
}
|
||||
|
||||
fun getComparisonGeneratorForRangeContainsCall(codegen: ExpressionCodegen, call: ResolvedCall<out CallableDescriptor>): ComparisonGenerator? {
|
||||
val descriptor = call.resultingDescriptor
|
||||
|
||||
val receiverType = descriptor.extensionReceiverParameter?.type ?: descriptor.dispatchReceiverParameter?.type ?: return null
|
||||
|
||||
val elementType = getRangeOrProgressionElementType(receiverType) ?: return null
|
||||
|
||||
val valueParameterType = descriptor.valueParameters.singleOrNull()?.type ?: return null
|
||||
|
||||
val asmElementType = codegen.asmType(elementType)
|
||||
val asmValueParameterType = codegen.asmType(valueParameterType)
|
||||
|
||||
return when {
|
||||
asmElementType == asmValueParameterType ->
|
||||
getComparisonGeneratorForPrimitiveType(asmElementType)
|
||||
|
||||
asmElementType.isRepresentedAsPrimitiveInt() && asmValueParameterType.isRepresentedAsPrimitiveInt() ->
|
||||
IntComparisonGenerator
|
||||
|
||||
asmElementType.isRepresentedAsPrimitiveInt() && asmValueParameterType == Type.LONG_TYPE ||
|
||||
asmValueParameterType.isRepresentedAsPrimitiveInt() && asmElementType == Type.LONG_TYPE ->
|
||||
LongComparisonGenerator
|
||||
|
||||
asmElementType == Type.FLOAT_TYPE && asmValueParameterType == Type.DOUBLE_TYPE ||
|
||||
asmElementType == Type.DOUBLE_TYPE && asmValueParameterType == Type.FLOAT_TYPE ->
|
||||
DoubleComparisonGenerator
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun Type.isRepresentedAsPrimitiveInt() =
|
||||
this == Type.INT_TYPE || this == Type.SHORT_TYPE || this == Type.BYTE_TYPE || this == Type.CHAR_TYPE
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.range.comparison
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
sealed class FloatingPointComparisonGenerator(override val comparedType: Type): ComparisonGenerator {
|
||||
override fun jumpIfGreaterOrEqual(v: InstructionAdapter, label: Label) {
|
||||
v.cmpl(comparedType)
|
||||
v.ifge(label)
|
||||
}
|
||||
|
||||
override fun jumpIfLessOrEqual(v: InstructionAdapter, label: Label) {
|
||||
v.cmpg(comparedType)
|
||||
v.ifle(label)
|
||||
}
|
||||
|
||||
override fun jumpIfGreater(v: InstructionAdapter, label: Label) {
|
||||
v.cmpl(comparedType)
|
||||
v.ifgt(label)
|
||||
}
|
||||
|
||||
override fun jumpIfLess(v: InstructionAdapter, label: Label) {
|
||||
v.cmpg(comparedType)
|
||||
v.iflt(label)
|
||||
}
|
||||
}
|
||||
|
||||
object FloatComparisonGenerator : FloatingPointComparisonGenerator(Type.FLOAT_TYPE)
|
||||
|
||||
object DoubleComparisonGenerator : FloatingPointComparisonGenerator(Type.DOUBLE_TYPE)
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.range.comparison
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
object IntComparisonGenerator : ComparisonGenerator {
|
||||
override val comparedType: Type = Type.INT_TYPE
|
||||
|
||||
override fun jumpIfGreaterOrEqual(v: InstructionAdapter, label: Label) {
|
||||
v.ificmpge(label)
|
||||
}
|
||||
|
||||
override fun jumpIfLessOrEqual(v: InstructionAdapter, label: Label) {
|
||||
v.ificmple(label)
|
||||
}
|
||||
|
||||
override fun jumpIfGreater(v: InstructionAdapter, label: Label) {
|
||||
v.ificmpgt(label)
|
||||
}
|
||||
|
||||
override fun jumpIfLess(v: InstructionAdapter, label: Label) {
|
||||
v.ificmplt(label)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.range.comparison
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
object LongComparisonGenerator : ComparisonGenerator {
|
||||
override val comparedType: Type = Type.LONG_TYPE
|
||||
|
||||
override fun jumpIfGreaterOrEqual(v: InstructionAdapter, label: Label) {
|
||||
v.lcmp()
|
||||
v.ifge(label)
|
||||
}
|
||||
|
||||
override fun jumpIfLessOrEqual(v: InstructionAdapter, label: Label) {
|
||||
v.lcmp()
|
||||
v.ifle(label)
|
||||
}
|
||||
|
||||
override fun jumpIfGreater(v: InstructionAdapter, label: Label) {
|
||||
v.lcmp()
|
||||
v.ifgt(label)
|
||||
}
|
||||
|
||||
override fun jumpIfLess(v: InstructionAdapter, label: Label) {
|
||||
v.lcmp()
|
||||
v.iflt(label)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.range.comparison
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
object ObjectComparisonGenerator : ComparisonGenerator {
|
||||
override val comparedType: Type = Type.getObjectType("java/lang/Comparable")
|
||||
|
||||
override fun jumpIfGreaterOrEqual(v: InstructionAdapter, label: Label) {
|
||||
invokeCompare(v)
|
||||
v.ifge(label)
|
||||
}
|
||||
|
||||
override fun jumpIfLessOrEqual(v: InstructionAdapter, label: Label) {
|
||||
invokeCompare(v)
|
||||
v.ifle(label)
|
||||
}
|
||||
|
||||
override fun jumpIfGreater(v: InstructionAdapter, label: Label) {
|
||||
invokeCompare(v)
|
||||
v.ifgt(label)
|
||||
}
|
||||
|
||||
override fun jumpIfLess(v: InstructionAdapter, label: Label) {
|
||||
invokeCompare(v)
|
||||
v.iflt(label)
|
||||
}
|
||||
|
||||
private fun invokeCompare(v: InstructionAdapter) {
|
||||
v.invokeinterface("java/lang/Comparable", "compareTo", "(Ljava/lang/Object;)I")
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.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
|
||||
@@ -25,15 +24,7 @@ import org.jetbrains.org.objectweb.asm.Type
|
||||
abstract class AbstractForInExclusiveRangeLoopGenerator(
|
||||
codegen: ExpressionCodegen,
|
||||
forExpression: KtForExpression
|
||||
) : AbstractForInRangeLoopGenerator(codegen, forExpression) {
|
||||
protected abstract fun generateFrom(): StackValue
|
||||
protected abstract fun generateTo(): StackValue
|
||||
|
||||
override fun storeRangeStartAndEnd() {
|
||||
loopParameter().store(generateFrom(), v)
|
||||
StackValue.local(endVar, asmElementType).store(generateTo(), v)
|
||||
}
|
||||
|
||||
) : AbstractForInRangeWithGivenBoundsLoopGenerator(codegen, forExpression) {
|
||||
override fun checkEmptyLoop(loopExit: Label) {}
|
||||
|
||||
override fun checkPreCondition(loopExit: Label) {
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
@@ -24,38 +24,36 @@ 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)
|
||||
abstract class AbstractForInProgressionLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression)
|
||||
: AbstractForInProgressionOrRangeLoopGenerator(codegen, forExpression)
|
||||
{
|
||||
private var incrementVar: Int = 0
|
||||
private var incrementType: Type? = null
|
||||
protected var incrementVar: Int = -1
|
||||
protected val asmLoopRangeType: Type
|
||||
protected val incrementType: Type
|
||||
|
||||
init {
|
||||
val loopRangeType = bindingContext.getType(forExpression.loopRange!!)!!
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
storeProgressionParametersToLocalVars()
|
||||
}
|
||||
|
||||
protected abstract fun storeProgressionParametersToLocalVars()
|
||||
|
||||
override fun checkEmptyLoop(loopExit: Label) {
|
||||
loopParameter().put(asmElementType, v)
|
||||
v.load(endVar, asmElementType)
|
||||
v.load(incrementVar, incrementType!!)
|
||||
v.load(incrementVar, incrementType)
|
||||
|
||||
val negativeIncrement = Label()
|
||||
val afterIf = Label()
|
||||
@@ -106,4 +104,4 @@ class ForInProgressionExpressionLoopGenerator(codegen: ExpressionCodegen, forExp
|
||||
|
||||
loopParameter.store(StackValue.onStack(asmElementType), v)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
@@ -25,7 +25,7 @@ import org.jetbrains.org.objectweb.asm.Type
|
||||
abstract class AbstractForInProgressionOrRangeLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression)
|
||||
: AbstractForLoopGenerator(codegen, forExpression)
|
||||
{
|
||||
protected var endVar: Int = 0
|
||||
protected var endVar: Int = -1
|
||||
|
||||
private var loopParameter: StackValue? = null
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
|
||||
abstract class AbstractForInRangeWithGivenBoundsLoopGenerator(
|
||||
codegen: ExpressionCodegen,
|
||||
forExpression: KtForExpression,
|
||||
step: Int = 1
|
||||
) : AbstractForInRangeLoopGenerator(codegen, forExpression, step) {
|
||||
protected abstract fun generateFrom(): StackValue
|
||||
protected abstract fun generateTo(): StackValue
|
||||
|
||||
override fun storeRangeStartAndEnd() {
|
||||
loopParameter().store(generateFrom(), v)
|
||||
StackValue.local(endVar, asmElementType).store(generateTo(), v)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
@@ -30,8 +30,8 @@ import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
abstract class AbstractForLoopGenerator(
|
||||
protected val codegen: ExpressionCodegen,
|
||||
val forExpression: KtForExpression
|
||||
) {
|
||||
override val forExpression: KtForExpression
|
||||
) : ForLoopGenerator {
|
||||
protected val bindingContext = codegen.bindingContext
|
||||
protected val v = codegen.v!!
|
||||
|
||||
@@ -42,7 +42,7 @@ abstract class AbstractForLoopGenerator(
|
||||
protected val elementType: KotlinType = bindingContext.getElementType(forExpression)
|
||||
protected val asmElementType: Type = codegen.asmType(elementType)
|
||||
|
||||
protected var loopParameterVar: Int = 0
|
||||
protected var loopParameterVar: Int = -1
|
||||
protected lateinit var loopParameterType: Type
|
||||
|
||||
private fun BindingContext.getElementType(forExpression: KtForExpression): KotlinType {
|
||||
@@ -52,7 +52,7 @@ abstract class AbstractForLoopGenerator(
|
||||
return nextCall.resultingDescriptor.returnType!!
|
||||
}
|
||||
|
||||
open fun beforeLoop() {
|
||||
override fun beforeLoop() {
|
||||
val loopParameter = forExpression.loopParameter ?: return
|
||||
val multiParameter = loopParameter.destructuringDeclaration
|
||||
if (multiParameter != null) {
|
||||
@@ -75,11 +75,7 @@ abstract class AbstractForLoopGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun checkEmptyLoop(loopExit: Label)
|
||||
|
||||
abstract fun checkPreCondition(loopExit: Label)
|
||||
|
||||
fun beforeBody() {
|
||||
override fun beforeBody() {
|
||||
assignToLoopParameter()
|
||||
v.mark(loopParameterStartLabel)
|
||||
|
||||
@@ -118,7 +114,7 @@ abstract class AbstractForLoopGenerator(
|
||||
|
||||
protected abstract fun checkPostConditionAndIncrement(loopExit: Label)
|
||||
|
||||
fun body() {
|
||||
override fun body() {
|
||||
codegen.generateLoopBody(forExpression.body)
|
||||
}
|
||||
|
||||
@@ -132,7 +128,7 @@ abstract class AbstractForLoopGenerator(
|
||||
return varIndex
|
||||
}
|
||||
|
||||
fun afterBody(loopExit: Label) {
|
||||
override fun afterBody(loopExit: Label) {
|
||||
codegen.markStartLineNumber(forExpression)
|
||||
|
||||
checkPostConditionAndIncrement(loopExit)
|
||||
@@ -140,7 +136,7 @@ abstract class AbstractForLoopGenerator(
|
||||
v.mark(bodyEnd)
|
||||
}
|
||||
|
||||
fun afterLoop() {
|
||||
override fun afterLoop() {
|
||||
for (task in leaveVariableTasks.asReversed()) {
|
||||
task.run()
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
@@ -27,12 +27,11 @@ class ForInDownToProgressionLoopGenerator(
|
||||
codegen: ExpressionCodegen,
|
||||
forExpression: KtForExpression,
|
||||
loopRangeCall: ResolvedCall<*>
|
||||
) : AbstractForInRangeLoopGenerator(codegen, forExpression, -1) {
|
||||
) : AbstractForInRangeWithGivenBoundsLoopGenerator(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)
|
||||
}
|
||||
override fun generateFrom(): StackValue = codegen.generateReceiverValue(from, false)
|
||||
|
||||
override fun generateTo(): StackValue = codegen.gen(to)
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
|
||||
class ForInProgressionExpressionLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression)
|
||||
: AbstractForInProgressionLoopGenerator(codegen, forExpression)
|
||||
{
|
||||
override fun storeProgressionParametersToLocalVars() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
@@ -31,8 +31,7 @@ class ForInRangeInstanceLoopGenerator(
|
||||
v.dup()
|
||||
|
||||
// ranges inherit first and last from corresponding progressions
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, "getFirst", asmElementType, loopParameterType,
|
||||
loopParameterVar)
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, "getFirst", asmElementType, loopParameterType, loopParameterVar)
|
||||
generateRangeOrProgressionProperty(asmLoopRangeType, "getLast", asmElementType, asmElementType, endVar)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
@@ -27,12 +27,16 @@ class ForInRangeLiteralLoopGenerator(
|
||||
codegen: ExpressionCodegen,
|
||||
forExpression: KtForExpression,
|
||||
loopRangeCall: ResolvedCall<*>
|
||||
) : AbstractForInRangeLoopGenerator(codegen, forExpression) {
|
||||
) : AbstractForInRangeWithGivenBoundsLoopGenerator(codegen, forExpression) {
|
||||
private val from: ReceiverValue = loopRangeCall.dispatchReceiver!!
|
||||
private val to: KtExpression = ExpressionCodegen.getSingleArgumentExpression(loopRangeCall)!!
|
||||
|
||||
override fun generateFrom(): StackValue = codegen.generateReceiverValue(from, false)
|
||||
|
||||
override fun generateTo(): StackValue = codegen.gen(to)
|
||||
|
||||
override fun storeRangeStartAndEnd() {
|
||||
loopParameter().store(codegen.generateReceiverValue(from, false), codegen.v)
|
||||
StackValue.local(endVar, asmElementType).store(codegen.gen(to), codegen.v)
|
||||
loopParameter().store(generateFrom(), v)
|
||||
StackValue.local(endVar, asmElementType).store(generateTo(), v)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
@@ -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.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
|
||||
interface ForLoopGenerator {
|
||||
val forExpression: KtForExpression
|
||||
fun beforeLoop()
|
||||
fun checkEmptyLoop(loopExit: Label)
|
||||
fun checkPreCondition(loopExit: Label)
|
||||
fun beforeBody()
|
||||
fun body()
|
||||
fun afterBody(loopExit: Label)
|
||||
fun afterLoop()
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forLoop
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.range.inExpression
|
||||
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class CallBasedInExpressionGenerator(
|
||||
val codegen: ExpressionCodegen,
|
||||
operatorReference: KtSimpleNameExpression
|
||||
) : InExpressionGenerator {
|
||||
private val resolvedCall = operatorReference.getResolvedCallWithAssert(codegen.bindingContext)
|
||||
private val isInverted = operatorReference.getReferencedNameElementType() == KtTokens.NOT_IN
|
||||
|
||||
override fun generate(argument: StackValue): BranchedValue =
|
||||
gen(argument).let { if (isInverted) Invert(it) else it }
|
||||
|
||||
private fun gen(argument: StackValue): BranchedValue =
|
||||
object : BranchedValue(argument, null, argument.type, Opcodes.IFEQ) {
|
||||
override fun putSelector(type: Type, v: InstructionAdapter) {
|
||||
invokeFunction(v)
|
||||
}
|
||||
|
||||
override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) {
|
||||
invokeFunction(v)
|
||||
v.visitJumpInsn(if (jumpIfFalse) Opcodes.IFEQ else Opcodes.IFNE, jumpLabel)
|
||||
}
|
||||
|
||||
private fun invokeFunction(v: InstructionAdapter) {
|
||||
val result = codegen.invokeFunction(resolvedCall.call, resolvedCall, none())
|
||||
result.put(result.type, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.range.inExpression
|
||||
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.BranchedValue
|
||||
import org.jetbrains.kotlin.codegen.Invert
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.range.BoundedValue
|
||||
import org.jetbrains.kotlin.codegen.range.comparison.ObjectComparisonGenerator
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class InContinuousRangeOfComparableExpressionGenerator(
|
||||
operatorReference: KtSimpleNameExpression,
|
||||
private val boundedValue: BoundedValue
|
||||
) : InExpressionGenerator {
|
||||
private val isNotIn = operatorReference.getReferencedNameElementType() == KtTokens.NOT_IN
|
||||
private val comparisonGenerator = ObjectComparisonGenerator
|
||||
|
||||
override fun generate(argument: StackValue): BranchedValue =
|
||||
gen(argument).let { if (isNotIn) Invert(it) else it }
|
||||
|
||||
private fun gen(argument: StackValue): BranchedValue =
|
||||
object : BranchedValue(argument, null, comparisonGenerator.comparedType, Opcodes.IFEQ) {
|
||||
override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) {
|
||||
if (jumpIfFalse) {
|
||||
genJumpIfFalse(v, jumpLabel)
|
||||
}
|
||||
else {
|
||||
genJumpIfTrue(v, jumpLabel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun genJumpIfTrue(v: InstructionAdapter, jumpLabel: Label) {
|
||||
// if (arg is in range) goto jumpLabel
|
||||
|
||||
val exitLabel1 = Label()
|
||||
val exitLabel2 = Label()
|
||||
|
||||
boundedValue.putHighLow(v, operandType)
|
||||
arg1.put(operandType, v)
|
||||
AsmUtil.dupx(v, operandType)
|
||||
|
||||
// On stack: high arg low arg
|
||||
// if (low bound is NOT satisfied) goto exitLabel1
|
||||
if (boundedValue.isLowInclusive) {
|
||||
// arg < low
|
||||
v.swap()
|
||||
comparisonGenerator.jumpIfLess(v, exitLabel1)
|
||||
}
|
||||
else {
|
||||
// arg <= low
|
||||
v.swap()
|
||||
comparisonGenerator.jumpIfLessOrEqual(v, exitLabel1)
|
||||
}
|
||||
|
||||
// On stack: high arg
|
||||
// if (high bound is satisfied) goto jumpLabel
|
||||
if (boundedValue.isHighInclusive) {
|
||||
// arg <= high
|
||||
v.swap()
|
||||
comparisonGenerator.jumpIfLessOrEqual(v, jumpLabel)
|
||||
}
|
||||
else {
|
||||
// arg < high
|
||||
v.swap()
|
||||
comparisonGenerator.jumpIfLess(v, jumpLabel)
|
||||
}
|
||||
v.goTo(exitLabel2)
|
||||
|
||||
v.mark(exitLabel1)
|
||||
AsmUtil.pop2(v, operandType)
|
||||
|
||||
v.mark(exitLabel2)
|
||||
}
|
||||
|
||||
private fun genJumpIfFalse(v: InstructionAdapter, jumpLabel: Label) {
|
||||
// if (arg is NOT in range) goto jumpLabel
|
||||
|
||||
val cmpHighLabel = Label()
|
||||
|
||||
boundedValue.putHighLow(v, operandType)
|
||||
arg1.put(operandType, v)
|
||||
AsmUtil.dupx(v, operandType)
|
||||
|
||||
// On stack: high arg low arg
|
||||
// if ([low bound is satisfied]) goto cmpHighLabel
|
||||
if (boundedValue.isLowInclusive) {
|
||||
// arg >= low
|
||||
v.swap()
|
||||
comparisonGenerator.jumpIfGreaterOrEqual(v, cmpHighLabel)
|
||||
}
|
||||
else {
|
||||
// arg > low
|
||||
v.swap()
|
||||
comparisonGenerator.jumpIfGreater(v, cmpHighLabel)
|
||||
}
|
||||
|
||||
// Low bound is NOT satisfied, clear stack and goto jumpLabel
|
||||
AsmUtil.pop2(v, operandType)
|
||||
v.goTo(jumpLabel)
|
||||
|
||||
v.mark(cmpHighLabel)
|
||||
// On stack: high arg
|
||||
// if ([high bound is NOT satisfied]) goto jumpLabel
|
||||
if (boundedValue.isHighInclusive) {
|
||||
// arg > high
|
||||
v.swap()
|
||||
comparisonGenerator.jumpIfGreater(v, jumpLabel)
|
||||
}
|
||||
else {
|
||||
// arg >= high
|
||||
v.swap()
|
||||
comparisonGenerator.jumpIfGreaterOrEqual(v, jumpLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.range.inExpression
|
||||
|
||||
import org.jetbrains.kotlin.codegen.BranchedValue
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
|
||||
interface InExpressionGenerator {
|
||||
fun generate(argument: StackValue): BranchedValue
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.range.inExpression
|
||||
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.BranchedValue
|
||||
import org.jetbrains.kotlin.codegen.Invert
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.range.BoundedValue
|
||||
import org.jetbrains.kotlin.codegen.range.comparison.ComparisonGenerator
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class InPrimitiveContinuousRangeExpressionGenerator(
|
||||
operatorReference: KtSimpleNameExpression,
|
||||
private val boundedValue: BoundedValue,
|
||||
private val comparisonGenerator: ComparisonGenerator
|
||||
) : InExpressionGenerator {
|
||||
private val isNotIn = operatorReference.getReferencedNameElementType() == KtTokens.NOT_IN
|
||||
|
||||
override fun generate(argument: StackValue): BranchedValue =
|
||||
gen(argument).let { if (isNotIn) Invert(it) else it }
|
||||
|
||||
private fun gen(argument: StackValue): BranchedValue =
|
||||
object : BranchedValue(argument, null, comparisonGenerator.comparedType, Opcodes.IFEQ) {
|
||||
override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) {
|
||||
if (jumpIfFalse) {
|
||||
genJumpIfFalse(v, jumpLabel)
|
||||
}
|
||||
else {
|
||||
genJumpIfTrue(v, jumpLabel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun genJumpIfTrue(v: InstructionAdapter, jumpLabel: Label) {
|
||||
// if (arg is in range) goto jumpLabel
|
||||
|
||||
val exitLabel1 = Label()
|
||||
val exitLabel2 = Label()
|
||||
|
||||
boundedValue.putHighLow(v, operandType)
|
||||
arg1.put(operandType, v)
|
||||
AsmUtil.dupx(v, operandType)
|
||||
|
||||
// On stack: high arg low arg
|
||||
// if (low bound is NOT satisfied) goto exitLabel1
|
||||
if (boundedValue.isLowInclusive) {
|
||||
// low > arg
|
||||
comparisonGenerator.jumpIfGreater(v, exitLabel1)
|
||||
}
|
||||
else {
|
||||
// low >= arg
|
||||
comparisonGenerator.jumpIfGreaterOrEqual(v, exitLabel1)
|
||||
}
|
||||
|
||||
// On stack: high arg
|
||||
// if (high bound is satisfied) goto jumpLabel
|
||||
if (boundedValue.isHighInclusive) {
|
||||
// high >= arg
|
||||
comparisonGenerator.jumpIfGreaterOrEqual(v, jumpLabel)
|
||||
}
|
||||
else {
|
||||
// high > arg
|
||||
comparisonGenerator.jumpIfGreater(v, jumpLabel)
|
||||
}
|
||||
v.goTo(exitLabel2)
|
||||
|
||||
v.mark(exitLabel1)
|
||||
AsmUtil.pop2(v, operandType)
|
||||
|
||||
v.mark(exitLabel2)
|
||||
}
|
||||
|
||||
private fun genJumpIfFalse(v: InstructionAdapter, jumpLabel: Label) {
|
||||
// if (arg is NOT in range) goto jumpLabel
|
||||
|
||||
val cmpHighLabel = Label()
|
||||
|
||||
boundedValue.putHighLow(v, operandType)
|
||||
arg1.put(operandType, v)
|
||||
AsmUtil.dupx(v, operandType)
|
||||
|
||||
// On stack: high arg low arg
|
||||
// if ([low bound is satisfied]) goto cmpHighLabel
|
||||
if (boundedValue.isLowInclusive) {
|
||||
// low <= arg
|
||||
comparisonGenerator.jumpIfLessOrEqual(v, cmpHighLabel)
|
||||
}
|
||||
else {
|
||||
// low < arg
|
||||
comparisonGenerator.jumpIfLess(v, cmpHighLabel)
|
||||
}
|
||||
|
||||
// Low bound is NOT satisfied, clear stack and goto jumpLabel
|
||||
AsmUtil.pop2(v, operandType)
|
||||
v.goTo(jumpLabel)
|
||||
|
||||
v.mark(cmpHighLabel)
|
||||
// On stack: high arg
|
||||
// if ([high bound is NOT satisfied]) goto jumpLabel
|
||||
if (boundedValue.isHighInclusive) {
|
||||
// high < arg
|
||||
comparisonGenerator.jumpIfLess(v, jumpLabel)
|
||||
}
|
||||
else {
|
||||
// high <= arg
|
||||
comparisonGenerator.jumpIfLessOrEqual(v, jumpLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
|
||||
enum class FieldAccessorKind(val suffix: String) {
|
||||
NORMAL("p"),
|
||||
@@ -26,15 +27,18 @@ enum class FieldAccessorKind(val suffix: String) {
|
||||
override fun toString() = suffix
|
||||
}
|
||||
|
||||
private fun CallableMemberDescriptor.getJvmName() =
|
||||
DescriptorUtils.getJvmName(this) ?: name.asString()
|
||||
|
||||
fun getAccessorNameSuffix(descriptor: CallableMemberDescriptor, superCallDescriptor: ClassDescriptor?,
|
||||
accessorKind: FieldAccessorKind): String {
|
||||
val suffix = when (descriptor) {
|
||||
is ConstructorDescriptor ->
|
||||
return "will be ignored"
|
||||
is SimpleFunctionDescriptor ->
|
||||
descriptor.name.asString()
|
||||
descriptor.getJvmName()
|
||||
is PropertyDescriptor ->
|
||||
descriptor.name.asString() + "$" + accessorKind
|
||||
descriptor.getJvmName() + "$" + accessorKind
|
||||
else ->
|
||||
throw UnsupportedOperationException("Do not know how to create accessor for descriptor " + descriptor)
|
||||
}
|
||||
|
||||
@@ -142,6 +142,9 @@ abstract class CLITool<A : CommonToolArguments> {
|
||||
if (System.getProperty("java.awt.headless") == null) {
|
||||
System.setProperty("java.awt.headless", "true")
|
||||
}
|
||||
if (System.getProperty(PlainTextMessageRenderer.KOTLIN_COLORS_ENABLED_PROPERTY) == null) {
|
||||
System.setProperty(PlainTextMessageRenderer.KOTLIN_COLORS_ENABLED_PROPERTY, "true")
|
||||
}
|
||||
val exitCode = doMainNoExit(compiler, args)
|
||||
if (exitCode != ExitCode.OK) {
|
||||
System.exit(exitCode.code)
|
||||
|
||||
@@ -92,19 +92,9 @@ class AnalyzerWithCompilerReport(private val messageCollector: MessageCollector)
|
||||
return messageCollector.hasErrors()
|
||||
}
|
||||
|
||||
interface Analyzer {
|
||||
fun analyze(): AnalysisResult
|
||||
|
||||
fun reportEnvironmentErrors() {
|
||||
}
|
||||
}
|
||||
|
||||
fun analyzeAndReport(files: Collection<KtFile>, analyzer: Analyzer) {
|
||||
analysisResult = analyzer.analyze()
|
||||
fun analyzeAndReport(files: Collection<KtFile>, analyze: () -> AnalysisResult) {
|
||||
analysisResult = analyze()
|
||||
reportSyntaxErrors(files)
|
||||
if (analysisResult.bindingContext.diagnostics.any { it.isValid && it.severity == Severity.ERROR }) {
|
||||
analyzer.reportEnvironmentErrors()
|
||||
}
|
||||
reportDiagnostics(analysisResult.bindingContext.diagnostics, messageCollector)
|
||||
reportIncompleteHierarchies()
|
||||
reportAlternativeSignatureErrors()
|
||||
|
||||
@@ -30,12 +30,13 @@ import java.util.Set;
|
||||
import static org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*;
|
||||
|
||||
public abstract class PlainTextMessageRenderer implements MessageRenderer {
|
||||
public static final String KOTLIN_COLORS_ENABLED_PROPERTY = "kotlin.colors.enabled";
|
||||
public static final boolean COLOR_ENABLED;
|
||||
|
||||
static {
|
||||
boolean colorEnabled = false;
|
||||
// TODO: investigate why ANSI escape codes on Windows only work in REPL for some reason
|
||||
if (!SystemInfo.isWindows && !"false".equals(System.getProperty("kotlin.colors.enabled"))) {
|
||||
if (!SystemInfo.isWindows && "true".equals(System.getProperty(KOTLIN_COLORS_ENABLED_PROPERTY))) {
|
||||
try {
|
||||
// AnsiConsole doesn't check isatty() for stderr (see https://github.com/fusesource/jansi/pull/35).
|
||||
colorEnabled = CLibrary.isatty(CLibrary.STDERR_FILENO) != 0;
|
||||
|
||||
@@ -68,11 +68,11 @@ class ModuleVisibilityHelperImpl : ModuleVisibilityHelper {
|
||||
|
||||
private fun findModule(descriptor: DeclarationDescriptor, modules: Collection<Module>): Module? {
|
||||
val sourceElement = getSourceElement(descriptor)
|
||||
if (sourceElement is KotlinSourceElement) {
|
||||
return modules.singleOrNull() ?: modules.firstOrNull { sourceElement.psi.containingKtFile.virtualFile.path in it.getSourceFiles() }
|
||||
return if (sourceElement is KotlinSourceElement) {
|
||||
modules.singleOrNull() ?: modules.firstOrNull { sourceElement.psi.containingKtFile.virtualFile.path in it.getSourceFiles() }
|
||||
}
|
||||
else {
|
||||
return modules.firstOrNull { module ->
|
||||
modules.firstOrNull { module ->
|
||||
isContainedByCompiledPartOfOurModule(descriptor, File(module.getOutputDirectory())) ||
|
||||
module.getFriendPaths().any { isContainedByCompiledPartOfOurModule(descriptor, File(it)) }
|
||||
}
|
||||
|
||||
@@ -162,7 +162,8 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
|
||||
return COMPILATION_ERROR;
|
||||
}
|
||||
|
||||
AnalyzerWithCompilerReport analyzerWithCompilerReport = analyzeAndReportErrors(messageCollector, sourcesFiles, config);
|
||||
AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(messageCollector);
|
||||
analyzerWithCompilerReport.analyzeAndReport(sourcesFiles, () -> TopDownAnalyzerFacadeForJS.analyzeFiles(sourcesFiles, config));
|
||||
if (analyzerWithCompilerReport.hasErrors()) {
|
||||
return COMPILATION_ERROR;
|
||||
}
|
||||
@@ -280,24 +281,6 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
|
||||
messageCollector.report(LOGGING, "Compiling source files: " + StringsKt.join(fileNames, ", "), null);
|
||||
}
|
||||
|
||||
private static AnalyzerWithCompilerReport analyzeAndReportErrors(
|
||||
@NotNull MessageCollector messageCollector, @NotNull List<KtFile> sources, @NotNull JsConfig config
|
||||
) {
|
||||
AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(messageCollector);
|
||||
analyzerWithCompilerReport.analyzeAndReport(sources, new AnalyzerWithCompilerReport.Analyzer() {
|
||||
@NotNull
|
||||
@Override
|
||||
public AnalysisResult analyze() {
|
||||
return TopDownAnalyzerFacadeForJS.analyzeFiles(sources, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportEnvironmentErrors() {
|
||||
}
|
||||
});
|
||||
return analyzerWithCompilerReport;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupPlatformSpecificArgumentsAndServices(
|
||||
@NotNull CompilerConfiguration configuration, @NotNull K2JSCompilerArguments arguments,
|
||||
|
||||
@@ -30,10 +30,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.CompileEnvironmentUtil
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.cli.jvm.config.*
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
|
||||
import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.ReplFromTerminal
|
||||
@@ -106,11 +103,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
||||
}
|
||||
}
|
||||
|
||||
val classpath = getClasspath(paths, arguments)
|
||||
configuration.addJvmClasspathRoots(classpath)
|
||||
for (modularRoot in arguments.javaModulePath?.split(File.pathSeparatorChar).orEmpty()) {
|
||||
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(File(modularRoot)))
|
||||
}
|
||||
configureContentRoots(paths, arguments, configuration)
|
||||
|
||||
configuration.put(CommonConfigurationKeys.MODULE_NAME, arguments.moduleName ?: JvmAbi.DEFAULT_MODULE_NAME)
|
||||
|
||||
@@ -383,21 +376,35 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
||||
arguments.declarationsOutputPath?.let { configuration.put(JVMConfigurationKeys.DECLARATIONS_JSON_PATH, it) }
|
||||
}
|
||||
|
||||
private fun getClasspath(paths: KotlinPaths, arguments: K2JVMCompilerArguments): List<File> {
|
||||
val classpath = arrayListOf<File>()
|
||||
if (arguments.classpath != null) {
|
||||
classpath.addAll(arguments.classpath.split(File.pathSeparatorChar).map(::File))
|
||||
private fun configureContentRoots(paths: KotlinPaths, arguments: K2JVMCompilerArguments, configuration: CompilerConfiguration) {
|
||||
for (path in arguments.classpath?.split(File.pathSeparatorChar).orEmpty()) {
|
||||
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(File(path)))
|
||||
}
|
||||
|
||||
for (modularRoot in arguments.javaModulePath?.split(File.pathSeparatorChar).orEmpty()) {
|
||||
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(File(modularRoot)))
|
||||
}
|
||||
|
||||
val isModularJava = configuration.get(JVMConfigurationKeys.JDK_HOME).let { it != null && CoreJrtFileSystem.isModularJdk(it) }
|
||||
fun addRoot(moduleName: String, file: File) {
|
||||
if (isModularJava) {
|
||||
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(file))
|
||||
configuration.add(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, moduleName)
|
||||
}
|
||||
else {
|
||||
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(file))
|
||||
}
|
||||
}
|
||||
|
||||
if (!arguments.noStdlib) {
|
||||
classpath.add(paths.runtimePath)
|
||||
classpath.add(paths.scriptRuntimePath)
|
||||
addRoot("kotlin.stdlib", paths.stdlibPath)
|
||||
addRoot("kotlin.script.runtime", paths.scriptRuntimePath)
|
||||
}
|
||||
// "-no-stdlib" implies "-no-reflect": otherwise we would be able to transitively read stdlib classes through kotlin-reflect,
|
||||
// which is likely not what user wants since s/he manually provided "-no-stdlib"
|
||||
if (!arguments.noReflect && !arguments.noStdlib) {
|
||||
classpath.add(paths.reflectPath)
|
||||
addRoot("kotlin.reflect", paths.reflectPath)
|
||||
}
|
||||
return classpath
|
||||
}
|
||||
|
||||
private fun setupJdkClasspathRoots(arguments: K2JVMCompilerArguments, configuration: CompilerConfiguration, messageCollector: MessageCollector): ExitCode {
|
||||
|
||||
@@ -34,11 +34,11 @@ import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.index.JavaRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.JavaModuleGraph
|
||||
import org.jetbrains.kotlin.config.ContentRoot
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.isValidJavaFqName
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleGraph
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
@@ -52,7 +52,7 @@ internal class ClasspathRootsResolver(
|
||||
private val additionalModules: List<String>,
|
||||
private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile?
|
||||
) {
|
||||
private val javaModuleFinder = CliJavaModuleFinder(VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL))
|
||||
val javaModuleFinder = CliJavaModuleFinder(VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL))
|
||||
val javaModuleGraph = JavaModuleGraph(javaModuleFinder)
|
||||
|
||||
data class RootsAndModules(val roots: List<JavaRoot>, val modules: List<JavaModule>)
|
||||
|
||||
@@ -229,7 +229,8 @@ class KotlinCoreEnvironment private constructor(
|
||||
|
||||
project.registerService(
|
||||
JavaModuleResolver::class.java,
|
||||
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules)
|
||||
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules,
|
||||
classpathRootsResolver.javaModuleFinder.systemModules.toList())
|
||||
)
|
||||
|
||||
val finderFactory = CliVirtualFileFinderFactory(rootsIndex)
|
||||
|
||||
@@ -18,9 +18,9 @@ package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.io.JarUtil
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.impl.PsiModificationTrackerImpl
|
||||
import com.intellij.psi.search.DelegatingGlobalSearchScope
|
||||
@@ -34,7 +34,8 @@ import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||
import org.jetbrains.kotlin.cli.common.checkKotlinPackageUsage
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.OUTPUT
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.WARNING
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
|
||||
import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAll
|
||||
@@ -59,14 +60,11 @@ import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.script.tryConstructClassFromStringArgs
|
||||
import org.jetbrains.kotlin.util.PerformanceCounter
|
||||
import org.jetbrains.kotlin.utils.KotlinPaths
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.net.URLClassLoader
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.jar.Attributes
|
||||
|
||||
object KotlinToJVMBytecodeCompiler {
|
||||
|
||||
@@ -190,14 +188,26 @@ object KotlinToJVMBytecodeCompiler {
|
||||
}
|
||||
|
||||
for (module in chunk) {
|
||||
for (javaRootPath in module.getJavaSourceRoots()) {
|
||||
configuration.addJavaSourceRoot(File(javaRootPath.path), javaRootPath.packagePrefix)
|
||||
for ((path, packagePrefix) in module.getJavaSourceRoots()) {
|
||||
configuration.addJavaSourceRoot(File(path), packagePrefix)
|
||||
}
|
||||
}
|
||||
|
||||
val isJava9Module = chunk.any { module ->
|
||||
module.getJavaSourceRoots().any { (path, packagePrefix) ->
|
||||
val file = File(path)
|
||||
packagePrefix == null &&
|
||||
(file.name == PsiJavaModule.MODULE_INFO_FILE ||
|
||||
(file.isDirectory && file.listFiles().any { it.name == PsiJavaModule.MODULE_INFO_FILE }))
|
||||
}
|
||||
}
|
||||
|
||||
for (module in chunk) {
|
||||
for (classpathRoot in module.getClasspathRoots()) {
|
||||
configuration.addJvmClasspathRoot(File(classpathRoot))
|
||||
configuration.add(
|
||||
JVMConfigurationKeys.CONTENT_ROOTS,
|
||||
if (isJava9Module) JvmModulePathRoot(File(classpathRoot)) else JvmClasspathRoot(File(classpathRoot))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,30 +375,24 @@ object KotlinToJVMBytecodeCompiler {
|
||||
|
||||
val analysisStart = PerformanceCounter.currentTime()
|
||||
val analyzerWithCompilerReport = AnalyzerWithCompilerReport(collector)
|
||||
analyzerWithCompilerReport.analyzeAndReport(sourceFiles, object : AnalyzerWithCompilerReport.Analyzer {
|
||||
override fun analyze(): AnalysisResult {
|
||||
val project = environment.project
|
||||
val moduleOutputs = environment.configuration.get(JVMConfigurationKeys.MODULES)?.mapNotNull { module ->
|
||||
environment.findLocalFile(module.getOutputDirectory())
|
||||
}.orEmpty()
|
||||
val sourcesOnly = TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, sourceFiles)
|
||||
// To support partial and incremental compilation, we add the scope which contains binaries from output directories
|
||||
// of the compiled modules (.class) to the list of scopes of the source module
|
||||
val scope = if (moduleOutputs.isEmpty()) sourcesOnly else sourcesOnly.uniteWith(DirectoriesScope(project, moduleOutputs))
|
||||
return TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
|
||||
project,
|
||||
sourceFiles,
|
||||
CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace(),
|
||||
environment.configuration,
|
||||
environment::createPackagePartProvider,
|
||||
sourceModuleSearchScope = scope
|
||||
)
|
||||
}
|
||||
|
||||
override fun reportEnvironmentErrors() {
|
||||
reportRuntimeConflicts(collector, environment.configuration.jvmClasspathRoots)
|
||||
}
|
||||
})
|
||||
analyzerWithCompilerReport.analyzeAndReport(sourceFiles) {
|
||||
val project = environment.project
|
||||
val moduleOutputs = environment.configuration.get(JVMConfigurationKeys.MODULES)?.mapNotNull { module ->
|
||||
environment.findLocalFile(module.getOutputDirectory())
|
||||
}.orEmpty()
|
||||
val sourcesOnly = TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, sourceFiles)
|
||||
// To support partial and incremental compilation, we add the scope which contains binaries from output directories
|
||||
// of the compiled modules (.class) to the list of scopes of the source module
|
||||
val scope = if (moduleOutputs.isEmpty()) sourcesOnly else sourcesOnly.uniteWith(DirectoriesScope(project, moduleOutputs))
|
||||
TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
|
||||
project,
|
||||
sourceFiles,
|
||||
CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace(),
|
||||
environment.configuration,
|
||||
environment::createPackagePartProvider,
|
||||
sourceModuleSearchScope = scope
|
||||
)
|
||||
}
|
||||
|
||||
val analysisNanos = PerformanceCounter.currentTime() - analysisStart
|
||||
|
||||
@@ -476,28 +480,4 @@ object KotlinToJVMBytecodeCompiler {
|
||||
|
||||
private val KotlinCoreEnvironment.messageCollector: MessageCollector
|
||||
get() = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
|
||||
private fun reportRuntimeConflicts(messageCollector: MessageCollector, jvmClasspathRoots: List<File>) {
|
||||
fun String.removeIdeaVersionSuffix(): String {
|
||||
val versionIndex = indexOfAny(arrayListOf("-IJ", "-Idea"))
|
||||
return if (versionIndex >= 0) substring(0, versionIndex) else this
|
||||
}
|
||||
|
||||
val runtimes = jvmClasspathRoots.map {
|
||||
try {
|
||||
it.canonicalFile
|
||||
}
|
||||
catch (e: IOException) {
|
||||
it
|
||||
}
|
||||
}.filter { it.name == PathUtil.KOTLIN_JAVA_RUNTIME_JAR && it.exists() }
|
||||
|
||||
val runtimeVersions = runtimes.map {
|
||||
JarUtil.getJarAttribute(it, Attributes.Name.IMPLEMENTATION_VERSION).orEmpty().removeIdeaVersionSuffix()
|
||||
}
|
||||
|
||||
if (runtimeVersions.toSet().size > 1) {
|
||||
messageCollector.report(ERROR, "Conflicting versions of Kotlin runtime on classpath: " + runtimes.joinToString { it.path })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,31 +18,63 @@ package org.jetbrains.kotlin.cli.jvm.modules
|
||||
|
||||
import com.intellij.ide.highlighter.JavaClassFileType
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleGraph
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
|
||||
|
||||
class CliJavaModuleResolver(
|
||||
override val moduleGraph: JavaModuleGraph,
|
||||
private val javaModules: List<JavaModule>
|
||||
private val moduleGraph: JavaModuleGraph,
|
||||
private val userModules: List<JavaModule>,
|
||||
private val systemModules: List<JavaModule.Explicit>
|
||||
) : JavaModuleResolver {
|
||||
init {
|
||||
assert(javaModules.count { !it.isBinary } <= 1) {
|
||||
"Modules computed by ClasspathRootsResolver cannot have more than one source module: $javaModules"
|
||||
assert(userModules.count { !it.isBinary } <= 1) {
|
||||
"Modules computed by ClasspathRootsResolver cannot have more than one source module: $userModules"
|
||||
}
|
||||
}
|
||||
|
||||
private val sourceModule = javaModules.firstOrNull { !it.isBinary }
|
||||
private val sourceModule: JavaModule? = userModules.firstOrNull { !it.isBinary }
|
||||
|
||||
override fun findJavaModule(file: VirtualFile): JavaModule? =
|
||||
when (file.fileType) {
|
||||
KotlinFileType.INSTANCE, JavaFileType.INSTANCE -> sourceModule
|
||||
JavaClassFileType.INSTANCE -> javaModules.firstOrNull { module ->
|
||||
module.isBinary && VfsUtilCore.isAncestor(module.moduleRoot, file, false)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
private fun findJavaModule(file: VirtualFile): JavaModule? {
|
||||
if (file.fileSystem.protocol == StandardFileSystems.JRT_PROTOCOL) {
|
||||
return systemModules.firstOrNull { module -> file in module }
|
||||
}
|
||||
|
||||
return when (file.fileType) {
|
||||
KotlinFileType.INSTANCE, JavaFileType.INSTANCE -> sourceModule
|
||||
JavaClassFileType.INSTANCE -> userModules.firstOrNull { module -> module.isBinary && file in module }
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private operator fun JavaModule.contains(file: VirtualFile): Boolean =
|
||||
VfsUtilCore.isAncestor(moduleRoot, file, false)
|
||||
|
||||
override fun checkAccessibility(
|
||||
fileFromOurModule: VirtualFile?, referencedFile: VirtualFile, referencedPackage: FqName?
|
||||
): JavaModuleResolver.AccessError? {
|
||||
val ourModule = fileFromOurModule?.let(this::findJavaModule)
|
||||
val theirModule = this.findJavaModule(referencedFile)
|
||||
|
||||
if (ourModule?.name == theirModule?.name) return null
|
||||
|
||||
if (theirModule == null) {
|
||||
return JavaModuleResolver.AccessError.ModuleDoesNotReadUnnamedModule
|
||||
}
|
||||
|
||||
if (ourModule != null && !moduleGraph.reads(ourModule.name, theirModule.name)) {
|
||||
return JavaModuleResolver.AccessError.ModuleDoesNotReadModule(theirModule.name)
|
||||
}
|
||||
|
||||
val fqName = referencedPackage ?: return null
|
||||
if (!theirModule.exports(fqName) && (ourModule == null || !theirModule.exportsTo(fqName, ourModule.name))) {
|
||||
return JavaModuleResolver.AccessError.ModuleDoesNotExportPackage(theirModule.name)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.jvm.modules
|
||||
package org.jetbrains.kotlin.cli.jvm.modules
|
||||
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
|
||||
class JavaModuleGraph(finder: JavaModuleFinder) {
|
||||
@@ -113,14 +113,14 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
|
||||
val diagnostics = trace.bindingContext.diagnostics
|
||||
val hasErrors = diagnostics.any { it.severity == Severity.ERROR }
|
||||
if (hasErrors) {
|
||||
return if (hasErrors) {
|
||||
replState.lineFailure(linePsi, codeLine)
|
||||
return ReplLineAnalysisResult.WithErrors(diagnostics)
|
||||
ReplLineAnalysisResult.WithErrors(diagnostics)
|
||||
}
|
||||
else {
|
||||
val scriptDescriptor = context.scripts[linePsi.script]!!
|
||||
replState.lineSuccess(linePsi, codeLine, scriptDescriptor)
|
||||
return ReplLineAnalysisResult.Successful(scriptDescriptor, diagnostics)
|
||||
ReplLineAnalysisResult.Successful(scriptDescriptor, diagnostics)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -96,11 +96,11 @@ class ReplFromTerminal(
|
||||
}
|
||||
|
||||
val lineResult = eval(line)
|
||||
if (lineResult is ReplEvalResult.Incomplete) {
|
||||
return WhatNextAfterOneLine.INCOMPLETE
|
||||
return if (lineResult is ReplEvalResult.Incomplete) {
|
||||
WhatNextAfterOneLine.INCOMPLETE
|
||||
}
|
||||
else {
|
||||
return WhatNextAfterOneLine.READ_LINE
|
||||
WhatNextAfterOneLine.READ_LINE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.metadata
|
||||
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||
import org.jetbrains.kotlin.analyzer.common.DefaultAnalyzerFacade
|
||||
import org.jetbrains.kotlin.builtins.BuiltInsBinaryVersion
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
@@ -63,11 +62,11 @@ open class MetadataSerializer(private val dependOnOldBuiltIns: Boolean) {
|
||||
}
|
||||
|
||||
val analyzer = AnalyzerWithCompilerReport(messageCollector)
|
||||
analyzer.analyzeAndReport(files, object : AnalyzerWithCompilerReport.Analyzer {
|
||||
override fun analyze(): AnalysisResult = DefaultAnalyzerFacade.analyzeFiles(files, moduleName, dependOnOldBuiltIns) {
|
||||
_, content -> environment.createPackagePartProvider(content.moduleContentScope)
|
||||
analyzer.analyzeAndReport(files) {
|
||||
DefaultAnalyzerFacade.analyzeFiles(files, moduleName, dependOnOldBuiltIns) { _, content ->
|
||||
environment.createPackagePartProvider(content.moduleContentScope)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (analyzer.hasErrors()) return
|
||||
|
||||
|
||||
@@ -33,9 +33,8 @@ open class BasicCompilerServicesWithResultsFacadeServer(
|
||||
) : CompilerServicesFacadeBase,
|
||||
UnicastRemoteObject(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory)
|
||||
{
|
||||
override fun report(category: Int, severity: Int, message: String?, attachment: Serializable?): Void? {
|
||||
override fun report(category: Int, severity: Int, message: String?, attachment: Serializable?) {
|
||||
messageCollector.reportFromDaemon(outputsCollector, category, severity, message, attachment)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,27 +56,24 @@ open class CompilerCallbackServicesFacadeServer(
|
||||
|
||||
override fun incrementalCache_getModuleMappingData(target: TargetId): ByteArray? = incrementalCompilationComponents!!.getIncrementalCache(target).getModuleMappingData()
|
||||
|
||||
override fun incrementalCache_registerInline(target: TargetId, fromPath: String, jvmSignature: String, toPath: String): Void? {
|
||||
override fun incrementalCache_registerInline(target: TargetId, fromPath: String, jvmSignature: String, toPath: String) {
|
||||
incrementalCompilationComponents!!.getIncrementalCache(target).registerInline(fromPath, jvmSignature, toPath)
|
||||
return null
|
||||
}
|
||||
|
||||
override fun incrementalCache_getClassFilePath(target: TargetId, internalClassName: String): String = incrementalCompilationComponents!!.getIncrementalCache(target).getClassFilePath(internalClassName)
|
||||
|
||||
override fun incrementalCache_close(target: TargetId): Void? {
|
||||
override fun incrementalCache_close(target: TargetId) {
|
||||
incrementalCompilationComponents!!.getIncrementalCache(target).close()
|
||||
return null
|
||||
}
|
||||
|
||||
override fun lookupTracker_requiresPosition() = incrementalCompilationComponents!!.getLookupTracker().requiresPosition
|
||||
|
||||
override fun lookupTracker_record(lookups: Collection<LookupInfo>): Void? {
|
||||
override fun lookupTracker_record(lookups: Collection<LookupInfo>) {
|
||||
val lookupTracker = incrementalCompilationComponents!!.getLookupTracker()
|
||||
|
||||
for (it in lookups) {
|
||||
lookupTracker.record(it.filePath, it.position, it.scopeFqName, it.scopeKind, it.name)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private val lookupTracker_isDoNothing: Boolean = incrementalCompilationComponents?.getLookupTracker() === LookupTracker.DO_NOTHING
|
||||
|
||||
@@ -444,7 +444,7 @@ class DaemonReportingTargets(val out: PrintStream? = null,
|
||||
val messageCollector: MessageCollector? = null,
|
||||
val compilerServices: CompilerServicesFacadeBase? = null)
|
||||
|
||||
internal fun DaemonReportingTargets.report(category: DaemonReportCategory, message: String, source: String? = null): Unit {
|
||||
internal fun DaemonReportingTargets.report(category: DaemonReportCategory, message: String, source: String? = null) {
|
||||
val sourceMessage: String by lazy { source?.let { "[$it] $message" } ?: message }
|
||||
out?.println("${category.name}: $sourceMessage")
|
||||
messages?.add(DaemonReportMessage(category, sourceMessage))
|
||||
|
||||
@@ -27,9 +27,8 @@ class RemoteInputStreamServer(val `in`: InputStream, port: Int = SOCKET_ANY_FREE
|
||||
: RemoteInputStream,
|
||||
UnicastRemoteObject(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory)
|
||||
{
|
||||
override fun close(): Void? {
|
||||
override fun close() {
|
||||
`in`.close()
|
||||
return null
|
||||
}
|
||||
|
||||
override fun read(length: Int): ByteArray {
|
||||
|
||||
@@ -28,18 +28,15 @@ class RemoteOutputStreamServer(val out: OutputStream, port: Int = SOCKET_ANY_FRE
|
||||
: RemoteOutputStream,
|
||||
UnicastRemoteObject(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory)
|
||||
{
|
||||
override fun close(): Void? {
|
||||
override fun close() {
|
||||
out.close()
|
||||
return null
|
||||
}
|
||||
|
||||
override fun write(data: ByteArray, offset: Int, length: Int): Void? {
|
||||
override fun write(data: ByteArray, offset: Int, length: Int) {
|
||||
out.write(data, offset, length)
|
||||
return null
|
||||
}
|
||||
|
||||
override fun write(dataByte: Int): Void? {
|
||||
override fun write(dataByte: Int) {
|
||||
out.write(dataByte)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import java.rmi.RemoteException
|
||||
|
||||
interface CompilationResults : Remote {
|
||||
@Throws(RemoteException::class)
|
||||
fun add(compilationResultCategory: Int, value: Serializable): Void?
|
||||
fun add(compilationResultCategory: Int, value: Serializable)
|
||||
}
|
||||
|
||||
enum class CompilationResultCategory(val code: Int) {
|
||||
|
||||
@@ -56,13 +56,13 @@ interface CompilerCallbackServicesFacade : Remote {
|
||||
fun incrementalCache_getModuleMappingData(target: TargetId): ByteArray?
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun incrementalCache_registerInline(target: TargetId, fromPath: String, jvmSignature: String, toPath: String): Void?
|
||||
fun incrementalCache_registerInline(target: TargetId, fromPath: String, jvmSignature: String, toPath: String)
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun incrementalCache_getClassFilePath(target: TargetId, internalClassName: String): String
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun incrementalCache_close(target: TargetId): Void?
|
||||
fun incrementalCache_close(target: TargetId)
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun incrementalCache_getMultifileFacadeParts(target: TargetId, internalName: String): Collection<String>?
|
||||
@@ -73,7 +73,7 @@ interface CompilerCallbackServicesFacade : Remote {
|
||||
fun lookupTracker_requiresPosition(): Boolean
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun lookupTracker_record(lookups: Collection<LookupInfo>): Void?
|
||||
fun lookupTracker_record(lookups: Collection<LookupInfo>)
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun lookupTracker_isDoNothing(): Boolean
|
||||
|
||||
@@ -25,7 +25,7 @@ interface CompilerServicesFacadeBase : Remote {
|
||||
* Reports different kind of diagnostic messages from compile daemon to compile daemon clients (jps, gradle, ...)
|
||||
*/
|
||||
@Throws(RemoteException::class)
|
||||
fun report(category: Int, severity: Int, message: String?, attachment: Serializable?): Void?
|
||||
fun report(category: Int, severity: Int, message: String?, attachment: Serializable?)
|
||||
}
|
||||
|
||||
enum class ReportCategory(val code: Int) {
|
||||
|
||||
@@ -22,7 +22,7 @@ import java.rmi.RemoteException
|
||||
interface RemoteInputStream : Remote {
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun close(): Void?
|
||||
fun close()
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun read(length: Int): ByteArray
|
||||
|
||||
@@ -22,8 +22,8 @@ import java.rmi.RemoteException
|
||||
interface RemoteOperationsTracer : Remote {
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun before(id: String): Void?
|
||||
fun before(id: String)
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun after(id: String): Void?
|
||||
fun after(id: String)
|
||||
}
|
||||
|
||||
@@ -22,11 +22,11 @@ import java.rmi.RemoteException
|
||||
interface RemoteOutputStream : Remote {
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun close(): Void?
|
||||
fun close()
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun write(data: ByteArray, offset: Int, length: Int): Void?
|
||||
fun write(data: ByteArray, offset: Int, length: Int)
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun write(dataByte: Int): Void?
|
||||
fun write(dataByte: Int)
|
||||
}
|
||||
|
||||
@@ -27,14 +27,14 @@ class JavaPropertyInitializerEvaluatorImpl : JavaPropertyInitializerEvaluator {
|
||||
val evaluated = field.initializerValue ?: return null
|
||||
|
||||
val factory = ConstantValueFactory(descriptor.builtIns)
|
||||
when (evaluated) {
|
||||
//Note: evaluated expression may be of class that does not match field type in some cases
|
||||
// tested for Int, left other checks just in case
|
||||
return when (evaluated) {
|
||||
//Note: evaluated expression may be of class that does not match field type in some cases
|
||||
// tested for Int, left other checks just in case
|
||||
is Byte, is Short, is Int, is Long -> {
|
||||
return factory.createIntegerConstantValue((evaluated as Number).toLong(), descriptor.type)
|
||||
factory.createIntegerConstantValue((evaluated as Number).toLong(), descriptor.type)
|
||||
}
|
||||
else -> {
|
||||
return factory.createConstantValue(evaluated)
|
||||
factory.createConstantValue(evaluated)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,9 @@ import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.checkers.ClassifierUsageChecker
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver.AccessError.*
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor
|
||||
|
||||
@@ -61,33 +62,19 @@ class JvmModuleAccessibilityChecker(project: Project) : CallChecker {
|
||||
): Diagnostic? {
|
||||
val referencedFile = findVirtualFile(targetClassOrPackage, originalDescriptor) ?: return null
|
||||
|
||||
// TODO: do not resolve our JavaModule every time, invent a way to obtain it from ModuleDescriptor and do it once in constructor
|
||||
val ourModule = fileFromOurModule?.let(moduleResolver::findJavaModule)
|
||||
val theirModule = moduleResolver.findJavaModule(referencedFile)
|
||||
val referencedPackageFqName =
|
||||
DescriptorUtils.getParentOfType(targetClassOrPackage, PackageFragmentDescriptor::class.java, false)?.fqName
|
||||
val diagnostic = moduleResolver.checkAccessibility(fileFromOurModule, referencedFile, referencedPackageFqName)
|
||||
|
||||
// If we're both in the unnamed module, it's OK, no error should be reported
|
||||
if (ourModule == null && theirModule == null) return null
|
||||
|
||||
if (theirModule == null) {
|
||||
// We should probably prohibit this usage according to JPMS (named module cannot use types from unnamed module),
|
||||
// but we cannot be sure that a module without module-info.java is going to be actually used as an unnamed module.
|
||||
// It could also be an automatic module, in which case it would be read by every module.
|
||||
return null
|
||||
return when (diagnostic) {
|
||||
is ModuleDoesNotReadUnnamedModule ->
|
||||
JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE.on(reportOn)
|
||||
is ModuleDoesNotReadModule ->
|
||||
JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE.on(reportOn, diagnostic.dependencyModuleName)
|
||||
is ModuleDoesNotExportPackage ->
|
||||
JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE.on(reportOn, diagnostic.dependencyModuleName, referencedPackageFqName!!.asString())
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (ourModule?.name == theirModule.name) return null
|
||||
|
||||
if (ourModule != null && !moduleResolver.moduleGraph.reads(ourModule.name, theirModule.name)) {
|
||||
return ErrorsJvm.JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE.on(reportOn, theirModule.name)
|
||||
}
|
||||
|
||||
val containingPackage = DescriptorUtils.getParentOfType(targetClassOrPackage, PackageFragmentDescriptor::class.java, false)
|
||||
val fqName = containingPackage?.fqName ?: return null
|
||||
if (!theirModule.exports(fqName) && (ourModule == null || !theirModule.exportsTo(fqName, ourModule.name))) {
|
||||
return ErrorsJvm.JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE.on(reportOn, theirModule.name, fqName.asString())
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun findVirtualFile(
|
||||
|
||||
@@ -115,6 +115,7 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
|
||||
MAP.put(OBSOLETE_SUSPEND_INLINE_FUNCTIONS_ABI, "Cannot inline suspend function built with compiler version less than 1.1.4/1.2-M1");
|
||||
|
||||
MAP.put(JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE, "Symbol is declared in module ''{0}'' which current module does not depend on", STRING);
|
||||
MAP.put(JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE, "Symbol is declared in unnamed module which is not read by current module");
|
||||
MAP.put(JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE, "Symbol is declared in module ''{0}'' which does not export package ''{1}''", STRING, STRING);
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ public interface ErrorsJvm {
|
||||
DiagnosticFactory0<PsiElement> OBSOLETE_SUSPEND_INLINE_FUNCTIONS_ABI = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory1<PsiElement, String> JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory2<PsiElement, String, String> JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
|
||||
@@ -74,7 +74,8 @@ class JavaModuleInfo(
|
||||
}
|
||||
|
||||
override fun visitExport(packageFqName: String, access: Int, modules: Array<String>?) {
|
||||
exports.add(Exports(FqName(packageFqName), modules?.toList().orEmpty()))
|
||||
// For some reason, '/' is the delimiter in packageFqName here
|
||||
exports.add(Exports(FqName(packageFqName.replace('/', '.')), modules?.toList().orEmpty()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,16 @@ package org.jetbrains.kotlin.resolve.jvm.modules
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
interface JavaModuleResolver {
|
||||
val moduleGraph: JavaModuleGraph
|
||||
fun checkAccessibility(fileFromOurModule: VirtualFile?, referencedFile: VirtualFile, referencedPackage: FqName?): AccessError?
|
||||
|
||||
// For any .java, .kt or .class file in the project, returns the corresponding module or null if there's none
|
||||
fun findJavaModule(file: VirtualFile): JavaModule?
|
||||
sealed class AccessError {
|
||||
object ModuleDoesNotReadUnnamedModule : AccessError()
|
||||
data class ModuleDoesNotReadModule(val dependencyModuleName: String) : AccessError()
|
||||
data class ModuleDoesNotExportPackage(val dependencyModuleName: String) : AccessError()
|
||||
}
|
||||
|
||||
companion object SERVICE {
|
||||
fun getInstance(project: Project): JavaModuleResolver =
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user