Compare commits

...

81 Commits

Author SHA1 Message Date
Anton Bannykh
3789cf33b2 JS: return exit code from the NodeJs kotlinc-js wrapper 2017-07-12 16:32:01 +03:00
Igor Chevdar
890c6a9983 Regenerated box tests 2017-07-12 15:12:34 +03:00
Igor Chevdar
fd380f7545 Split up test on map access onto two tests: stdlib & compiler box test 2017-07-12 15:12:33 +03:00
Mikhail Zarechenskiy
14890890b1 Minor, get rid of unchecked cast 2017-07-12 14:37:27 +03:00
Mikhail Zarechenskiy
ee173077c0 Simplify diagnostic when there are wrong number of type arguments
#KT-12767 Fixed
2017-07-12 14:37:27 +03:00
Mikhail Zarechenskiy
e82c909f75 Use more specific status to report diagnostics
#KT-12737 Fixed
2017-07-12 14:37:26 +03:00
Mikhail Zarechenskiy
178bb900b4 Introduce inapplicable wrong receiver status to improve diagnostics
#KT-10754 Fixed
2017-07-12 14:37:26 +03:00
Mikhail Zarechenskiy
79ee8f452c Add test for obsolete issue
#KT-12688 Obsolete
2017-07-12 14:37:26 +03:00
Toshiaki Kameyama
848f7423ee Do not report "can be private" on properties of private class
So #KT-18822 Fixed
2017-07-12 10:35:10 +03:00
Dmitry Petrov
f4d63158cc Take into account JvmName annotation when generating accessors
#KT-17444 Fixed Target versions 1.1.5
2017-07-12 09:37:16 +03:00
Dmitry Petrov
efb6756cbc Initialize property metadata array before class body generation
Initialization of companion object members (e.g., delegate properties
using provideDelegate convention) can depend on property metadata array,
which in turn can be initialized before other class members.

 #KT-18902 Fixed Target versions 1.1.5
2017-07-12 09:37:16 +03:00
Dmitry Petrov
8a9707c140 Generate proper initialization for possibly nullable SAM wrapper
#KT-18916 Fixed Target versions 1.1.5
2017-07-12 09:37:16 +03:00
Yan Zhulanow
b99007961f NoArg: Do not invoke initializers by default. Require "invokeInitializers" option to be set explicitly (KT-18667, KT-18668) 2017-07-12 00:17:24 +03:00
Yan Zhulanow
a983137978 Kapt: Handle parameter with Java names clashing with Java keywords properly (KT-18377) 2017-07-12 00:17:23 +03:00
Alexey Tsvetkov
e56e3d78c8 Use ANSI escape codes only with CLI tools
Before this change jansi was used by the compiler,
unless "kotlin.colors.enabled" is not set to false.
This caused multiple issues in different build systems,
where newer or older version of jansi could crash the JVM
since it uses native code.

The following short term solutions were discussed:
* Set "kotlin.colors.enabled" to false where jansi is not needed
(basically in any build system).
* Set "kotlin.colors.enabled" to true where jansi is needed,
and use it only when the system property is set to true.

Escaped codes are only needed in CLI tools (kotlinc, REPL),
so the second solution is preferred (less places to set the property).

     #KT-17031 fixed
     #KT-18874 fixed
     #KT-18927 fixed
2017-07-11 21:03:11 +03:00
Kirill Rakhman
cb19a86c44 Add "simplify if with constant" inspection #KT-17919 Fixed 2017-07-11 20:00:33 +03:00
Alexey Andreev
7634673213 Fix module re-importing when inlining functions in JS BE
When inliner reads function's body from other module, it performs
substitution _ -> moduleAlias. However, local alias can't be used
for this purpose, since call site can be in public inline function
itself, so the correct substitution would be -> _.$$imports$$.alias
2017-07-11 19:27:08 +03:00
Mikhail Glukhikh
cc4e55b829 Add groupPath/Name, change displayName for null checks -> safe call
Related to KT-14799
2017-07-11 19:25:38 +03:00
Dimach
7407083624 Add inspection to simplify successive null checks #KT-14799 Fixed 2017-07-11 18:47:26 +03:00
Ilya Chernikov
fc12f37105 Rolling back commits related to Unit -> Void? confersion in RMI interface
since it breaks the compatibility and doesn't actually help - the problem
with NoClassDef Unit is apparently elsewhere.
2017-07-11 15:06:38 +02:00
Ilya Chernikov
3da5ba4efe Switch to the newer gradle plugin, fix jansi problem, in-process compiling in gradle parts
Switching to in-process to avoid compilation warnings caused by introduced
daemon interface changes.
Switching to the gradle plugin 1.1.3 causes jansi incompatibility in
in-process compilation mode, so disabling jansi usage to avoid it.
2017-07-11 15:06:37 +02:00
Mikhail Glukhikh
0ca5c0fcc6 Refactoring (clone remove) of LiftAssignmentOutOfTryFix
Related to KT-18830
2017-07-11 15:28:45 +03:00
Toshiaki Kameyama
8cc9330e63 Introduce "Lift return out of try" intention #KT-18830 Fixed 2017-07-11 15:14:30 +03:00
Toshiaki Kameyama
8f9b680fc6 Support generic type parameters in 'Specify return type explicitly'
So #KT-18074 Fixed
2017-07-11 14:39:22 +03:00
Toshiaki Kameyama
0af3c6542d Correct "after" sample for intention "Convert to apply" #KT-18723 Fixed 2017-07-11 14:06:01 +03:00
Alexey Andreev
5a9adcca2d Improve performance of JS tests
* Do not read protos for descriptors of stdlib and kotlin-tests
  repeatedly
* Parse libraries lazily in inline, so that when no inline function
  exist in a test, we won't parse huge kotlin.js file
* Speed-up source map parser
2017-07-11 11:06:52 +03:00
Dmitry Petrov
83ec8aa918 typealias expansion fixes
- Exception on dynamic type in typealias argument expansion
 #KT-18858 Fixed Target versions 1.1.5
- Wrong report location for repeated annotations in typealias arguments
 #KT-18940 Fixed Target versions 1.1.5
- Don't drop type annotations for dynamic type
 #KT-18944 Fixed Target versions 1.1.5
2017-07-11 10:33:39 +03:00
Nikolay Krasko
a0f11f773b Better diagnostic for error configuration in debugger tests 2017-07-10 15:32:42 +03:00
Nikolay Krasko
68e0727894 Force space between { and first enum entry (KT-18863)
#KT-18863 Fixed
2017-07-10 15:32:42 +03:00
Nikolay Krasko
7f8d6b3008 Don't move line and /**/ comments during reformat (KT-18805)
#KT-18805 Fixed
2017-07-10 15:32:42 +03:00
Mikhail Glukhikh
ebdadf30d8 Get rid of redundant toMap() calls in DelegatingDataFlowInfo 2017-07-10 14:51:52 +03:00
Mikhail Glukhikh
8c9ace7d40 Get rid of Guava in DelegatingDataFlowInfo (except Multimap things) 2017-07-10 14:35:01 +03:00
Mikhail Glukhikh
564cc27a1d Get rid of Guava in CFA (except Multimap things) 2017-07-10 14:34:48 +03:00
Mikhail Glukhikh
dfe2c16bc7 More cleanup: lift return / assignment out 2017-07-10 12:59:58 +03:00
Alexander Udalov
9269de721e JPS: use module path instead of classpath for modular projects
No test added because it would involve running javac 9 and because tests
run JavaBuilder in the same process, this would require either a new
module in our project with dependency on JDK 9 (which would require
everyone to install JDK 9), or complex code that runs javac in another
process
2017-07-10 12:48:21 +03:00
Alexander Udalov
03a6488464 Use module path instead of class path for stdlib on Java 9
Also report the "named does not read unnamed" error, which was not
possible previously because we wouldn't be able to read anything from
kotlin-stdlib (because it was added to the unnamed module by default)
2017-07-10 12:48:21 +03:00
Alexander Udalov
c94b21edd5 Remove obsolete code about runtime versions conflict 2017-07-10 12:48:21 +03:00
Alexander Udalov
4128064f87 Move JavaModuleGraph from 'frontend.java' to 'cli' 2017-07-10 12:48:21 +03:00
Alexander Udalov
5b8e58fe36 Implement module accessibility checks differently in compiler and IDE
Essentially, the logic that was previously in
JvmModuleAccessibilityChecker.diagnosticFor, is moved into a new
abstract method JavaModuleResolver.checkAccessibility, which is
implemented differently in the compiler and in the IDE. In the compiler,
we use our JavaModuleInfo and JavaModuleGraph, as previously. In the
IDE, we use intellij's PsiJavaModule and JavaModuleGraphUtil.

This fixes strange behavior in IDE where some modules could be observed
in an invalid state. The cause of that was the JavaModuleGraph instance
caching modules in IdeJavaModuleResolver, which is a project component.

Moreover, this will allow to report an error "named module does not read
unnamed module" in the compiler, and avoid reporting it in the IDE (see
the comment in IdeJavaModuleResolver about that)
2017-07-10 12:48:21 +03:00
Alexey Andreev
7edb352aeb Copy stdlib-js source map during JPS build 2017-07-10 12:42:33 +03:00
Dmitry Petrov
0ce6bac7eb Generate proper 'compareTo' calls for non-primitive 'a in x .. y'
When we have some custom implementation of Comparable, it's important
that we compare values exactly as 'lowBound <= a && a <= highBound'.

Make sure that evaluation order and compareTo calls match for
optimized and non-optimized case.
2017-07-10 10:51:26 +03:00
Dmitry Petrov
dd5bb78178 Minor: don't use StringBuilder#setLength(0) - absent in JS 2017-07-10 10:51:26 +03:00
Dmitry Petrov
0962aec456 Minor: don't use java.lang.Integer in common test 2017-07-10 10:51:26 +03:00
Dmitry Petrov
aa7db727ba Fold I2L with ICONST_n when beneficial
ICONST_0; I2L -> LCONST_0
ICONST_1; I2L -> LCONST_1
2017-07-10 10:51:26 +03:00
Dmitry Petrov
ad80c3cd7f If at least one of the range bounds is "pure", order doesn't matter 2017-07-10 10:51:26 +03:00
Dmitry Petrov
22e12dc139 Minor: use method names in bytecode text tests 2017-07-10 10:51:26 +03:00
Dmitry Petrov
905a16e1df Maintain proper evaluation order for 'a in x .. y'
As of Kotlin 1.0 and 1.1, expression 'a in x .. y' is considered
equivalent to 'x.rangeTo(y).a', and should be evaluated in the following
order:
1. x
2. y
3. a
4. compare x with a
5. compare y with a (if needed)
2017-07-10 10:51:26 +03:00
Dmitry Petrov
fc3e9318d9 Handle DUPnXm instructions in PopBackwardPropagationTransformer 2017-07-10 10:51:26 +03:00
Dmitry Petrov
6a3ff5ca46 Add tests for intrinsified in/!in and mismatching range types 2017-07-10 10:51:26 +03:00
Dmitry Petrov
850e1b11fc Minor: test modification after review 2017-07-10 10:51:26 +03:00
Dmitry Petrov
64f880ba71 Add test for in/!in in a custom object range 2017-07-10 10:51:26 +03:00
Dmitry Petrov
9d1901fc7c Intrinsify some mismatching range/element combinations for in/in!
It's safe to upcast integer types to Long,
floating-point types to Double.
So we don't have to create a range instance for cases such as

fun testLongInInt(x: Long, a: Int, b: Int) =
    x in a .. b

which is equivalent to

fun testLongInInt(x: Long, a: Int, b: Int) =
    x in a.toLong() .. b.toLong()
2017-07-10 10:51:26 +03:00
Dmitry Petrov
d137b04b0a Pull up logic for ComparisonGenerator selection 2017-07-10 10:51:26 +03:00
Dmitry Petrov
ea95f31f99 Simplify some basic instructions using peephole optimization
DUP_X1; POP = SWAP

p1_a; p1_b; SWAP = p1_b; p1_a
where p1_a, p1_b are instructions without side effects pushing value of
size 1 on stack.
E.g.: ACONST_NULL; ALOAD 0; SWAP = ALOAD 0; ACONST_NULL

NOP; NOP = NOP
2017-07-10 10:51:26 +03:00
Dmitry Petrov
224848163d Use InContinuousRangeExpressionGenerator for primitive range intrinsics 2017-07-10 10:51:26 +03:00
Dmitry Petrov
c855b5c889 Extract SimpleBoundedValue class 2017-07-10 10:51:26 +03:00
Dmitry Petrov
a3409b46b0 Primitive number range literal as BoundedValue 2017-07-10 10:51:26 +03:00
Dmitry Petrov
b693b54a2c Fix DUPn_Xm handling in PopBackwardPropagationTransformer
DUPn_Xm instructions implicitly depend on elements directly under stack
top (which are not "copied", but still required). We can't analyze them
precisely now, and should skip methods containing these instructions.
Fortunately, we didn't generate these instructions under POP before,
but with new range check code generation and constant conditions
elimination such combination of instructions becomes possible.
2017-07-10 10:51:26 +03:00
Dmitry Petrov
4480a9bdfb Introduce BoundedValue as a generalization of (low..high) range
Provide BoundedValue-based implementation of InExpressionGenerator,
test it on range of comparable values.
Drop unneeded test (range of comparables is already tested by
ranges/contains/inComparableRanges.kt).
2017-07-10 10:51:26 +03:00
Dmitry Petrov
f4ea1a2f41 Minor: move RangeValue-related classes to proper packages 2017-07-10 10:51:26 +03:00
Dmitry Petrov
d19eb05382 Introduce AbstractForInProgressionLoopGenerator
AbstractForInProgressionLoopGenerator is a base class for
progression-based 'for' loop generators with progression step unknown
at compile time.
2017-07-10 10:51:26 +03:00
Dmitry Petrov
83ff1a2ef3 Minor: refactor ForInProgressionExpressionLoopGenerator initialization 2017-07-10 10:51:26 +03:00
Dmitry Petrov
6551fdc695 Add test for nullable primitive in range 2017-07-10 10:51:26 +03:00
Dmitry Petrov
db1dcc68ff Minor: cleanup in ExpressionCodegen 2017-07-10 10:51:26 +03:00
Dmitry Petrov
614d90d6ef Skip NOP instructions in fast pop backward propagation 2017-07-10 10:51:26 +03:00
Dmitry Petrov
36cbc0defd Ensure that {Collection, CharSequence}.indices intrinsic works with 'in' 2017-07-10 10:51:26 +03:00
Dmitry Petrov
ec99fa2142 Ensure that 'in/!in x until y' intrinsic works 2017-07-10 10:51:26 +03:00
Dmitry Petrov
05cad83c79 Fix tests for in/in! with primitive range literals
- ClosedFloatingPointRange<T>
- IntRange, etc
- extensions in kotlin.ranges for ranges of Byte/Int/Short
2017-07-10 10:51:26 +03:00
Dmitry Petrov
5decf65d6a Ensure that no range instance is created for 'x in array.indices' 2017-07-10 10:51:26 +03:00
Dmitry Petrov
4320b63922 Ensure that no boolean negation is generated for !in expression
#KT-18777 Fixed Target versions 1.1.4
2017-07-10 10:51:26 +03:00
Dmitry Petrov
b9c0ecf2b1 Ensure that ranges are not created for in-Comparable-range-literal
#KT-6247 Fixed Target versions 1.1.4
2017-07-10 10:51:26 +03:00
Dmitry Petrov
7175361c97 Ensure that BranchedValue for in/!in work well with boolean operations 2017-07-10 10:51:26 +03:00
Dmitry Petrov
a3cd4f415a Add test for comparison used for primitives and Comparable
There's a subtle difference in behavior between comparing
primitive Float/Double (comparison follows IEEE standard)
and boxed Float/Double (comparison is a total order).
Make sure this corner case is preserved.
2017-07-10 10:51:26 +03:00
Dmitry Petrov
fd402cb76c Provide intrinsic for in/in! expression with range of Comparable 2017-07-10 10:51:26 +03:00
Dmitry Petrov
bd2021411c Don't use intrinsic 'in' generator for non-intrinsified 'in' expressions 2017-07-10 10:51:26 +03:00
Dmitry Petrov
a7071ae7af Provide intrinsic generators for in/!in expression
TODO some tests should fail because range of comparables
(e.g., '"Alpha" .. "Omega"') is currently not implemented
2017-07-10 10:51:26 +03:00
Dmitry Petrov
e3320c53f5 Minor: AbstractForInRangeWithGivenBoundsLoopGenerator 2017-07-10 10:51:26 +03:00
Dmitry Petrov
e261b8edf8 Minor: extract high-level interface for ForLoopGenerator 2017-07-10 10:51:26 +03:00
Dmitry Petrov
c870eeac2e RangeCodegenUtil: convert to Kotlin + cleanup 2017-07-10 10:51:26 +03:00
Dmitry Petrov
304e4e8dce RangeCodegenUtil: .java -> .kt 2017-07-10 10:51:26 +03:00
Dmitry Petrov
6140f29818 Ranges: introduce RangeValue class
The overall idea is to unify intrinsics for 'for-in' loop generation
and for 'in'/'!in' expression generation.
2017-07-10 10:51:26 +03:00
447 changed files with 6477 additions and 1852 deletions

View File

@@ -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;

View File

@@ -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) {

View File

@@ -348,9 +348,12 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
}
@Override
protected void generateSyntheticParts() {
protected void generateSyntheticPartsBeforeBody() {
generatePropertyMetadataArrayFieldIfNeeded(classAsmType);
}
@Override
protected void generateSyntheticPartsAfterBody() {
generateFieldForSingleton();
generateCompanionObjectBackingFieldCopies();

View File

@@ -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

View File

@@ -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();

View File

@@ -186,7 +186,7 @@ class MultifileClassPartCodegen(
}
}
override fun generateSyntheticParts() {
override fun generateSyntheticPartsAfterBody() {
generateSyntheticAccessors()
}

View File

@@ -130,7 +130,7 @@ public class PackagePartCodegen extends MemberCodegen<KtFile> {
}
@Override
protected void generateSyntheticParts() {
protected void generateSyntheticPartsAfterBody() {
generateSyntheticAccessors();
}
}

View File

@@ -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;
}
}

View File

@@ -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")
}

View File

@@ -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);

View File

@@ -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)!!)

View File

@@ -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
}

View File

@@ -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
}
}
}

View File

@@ -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
}
}

View File

@@ -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(),

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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() {

View File

@@ -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);
}

View File

@@ -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
}

View File

@@ -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]
}
}
}

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -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
}
}

View File

@@ -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)
}
}
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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")
}
}

View File

@@ -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) {

View File

@@ -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)
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}
}

View File

@@ -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
}

View File

@@ -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)
}
}
}
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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()

View File

@@ -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;

View File

@@ -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)) }
}

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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>)

View File

@@ -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)

View File

@@ -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 })
}
}
}

View File

@@ -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
}
}

View File

@@ -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) {

View File

@@ -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)
}
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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))

View File

@@ -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 {

View File

@@ -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
}
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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) {

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}
}
}

View File

@@ -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(

View File

@@ -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);
}

View File

@@ -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")

View File

@@ -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()))
}
}
}

View File

@@ -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