Compare commits

...

13 Commits

Author SHA1 Message Date
Alexey Tsvetkov
4e10524394 JS: do not generate break for top level return during inline 2015-04-23 20:47:11 +03:00
Alexey Tsvetkov
e5cff6a487 Minor in JS: store js test directives in a list 2015-04-23 20:29:22 +03:00
Alexey Tsvetkov
1ae3ca2734 Minor in JS: simplify count vars directive 2015-04-23 20:25:45 +03:00
Alexey Tsvetkov
1e3ee9ffe2 JS: do not alias 'this' literal in a receiver position 2015-04-23 19:34:08 +03:00
Alexey Tsvetkov
f53c811a38 JS: do not create variable for result, if inline call is only return subexpression 2015-04-23 19:34:08 +03:00
Alexey Tsvetkov
339c28ae9e JS: do not create variable for result, if inline function has one top level return 2015-04-23 19:34:08 +03:00
Alexey Tsvetkov
c86c19efa6 JS: do not create var for result if statement is 'someVar = inlineCall()' 2015-04-22 19:22:20 +03:00
Alexey Tsvetkov
aebaf317d3 Minor in JS: moved test for additional vars during inline to separate directory 2015-04-22 19:17:04 +03:00
Alexey Tsvetkov
3c51a363c4 JS: converted FunctionInlineMutator to Kotlin 2015-04-21 18:34:16 +03:00
Alexey Tsvetkov
ed3852dc7f JS: changed file extension FunctionInlineMutator.kt->.java 2015-04-21 18:34:09 +03:00
Alexey Sedunov
878095da0c Control-Flow: Assign pseudo-values to local functions (except those
declared directly in the block)
2015-04-21 18:01:36 +03:00
Alexey Sedunov
ce4750eda3 Control-Flow: Add tests for pseudo-values of labeled expressions 2015-04-21 18:01:35 +03:00
Dmitry Jemerov
59b5b273c3 escape identifiers in generated super method call in Override/Implement
#KT-6669 Fixed
2015-04-21 18:01:35 +03:00
42 changed files with 1078 additions and 304 deletions

View File

@@ -111,7 +111,7 @@ public interface JetControlFlowBuilder {
@NotNull
InstructionWithValue createAnonymousObject(@NotNull JetObjectLiteralExpression expression);
@NotNull
InstructionWithValue createFunctionLiteral(@NotNull JetFunctionLiteralExpression expression);
InstructionWithValue createLambda(@NotNull JetFunction expression);
@NotNull
InstructionWithValue loadStringTemplate(@NotNull JetStringTemplateExpression expression, @NotNull List<PseudoValue> inputValues);

View File

@@ -55,8 +55,8 @@ public abstract class JetControlFlowBuilderAdapter implements JetControlFlowBuil
@NotNull
@Override
public InstructionWithValue createFunctionLiteral(@NotNull JetFunctionLiteralExpression expression) {
return getDelegateBuilder().createFunctionLiteral(expression);
public InstructionWithValue createLambda(@NotNull JetFunction expression) {
return getDelegateBuilder().createLambda(expression);
}
@NotNull

View File

@@ -1093,17 +1093,25 @@ public class JetControlFlowProcessor {
return parent.getParent() instanceof JetDoWhileExpression;
}
private void visitFunction(@NotNull JetFunction function) {
processLocalDeclaration(function);
boolean isAnonymousFunction = function instanceof JetFunctionLiteral || function.getName() == null;
if (isAnonymousFunction || (function.isLocal() && !(function.getParent() instanceof JetBlockExpression))) {
builder.createLambda(function);
}
}
@Override
public void visitNamedFunction(@NotNull JetNamedFunction function) {
processLocalDeclaration(function);
visitFunction(function);
}
@Override
public void visitFunctionLiteralExpression(@NotNull JetFunctionLiteralExpression expression) {
mark(expression);
JetFunctionLiteral functionLiteral = expression.getFunctionLiteral();
processLocalDeclaration(functionLiteral);
builder.createFunctionLiteral(expression);
visitFunction(functionLiteral);
copyValue(functionLiteral, expression);
}
@Override

View File

@@ -425,8 +425,8 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd
@NotNull
@Override
public InstructionWithValue createFunctionLiteral(@NotNull JetFunctionLiteralExpression expression) {
return read(expression);
public InstructionWithValue createLambda(@NotNull JetFunction expression) {
return read(expression instanceof JetFunctionLiteral ? (JetFunctionLiteralExpression) expression.getParent() : expression);
}
@NotNull

View File

@@ -0,0 +1,80 @@
== test ==
fun test(b: Boolean): (Int) -> Int {
if (b) {
fun(n: Int) = n + 1
}
else {
fun(n: Int) = n - 1
}
}
---------------------
L0:
1 <START>
v(b: Boolean)
magic[FAKE_INITIALIZER](b: Boolean) -> <v0>
w(b|<v0>)
2 mark({ if (b) { fun(n: Int) = n + 1 } else { fun(n: Int) = n - 1 } })
mark(if (b) { fun(n: Int) = n + 1 } else { fun(n: Int) = n - 1 })
r(b) -> <v1>
jf(L2|<v1>) NEXT:[mark({ fun(n: Int) = n - 1 }), mark({ fun(n: Int) = n + 1 })]
3 mark({ fun(n: Int) = n + 1 })
jmp?(L3) NEXT:[r(fun(n: Int) = n + 1) -> <v2>, d(fun(n: Int) = n + 1)]
d(fun(n: Int) = n + 1) NEXT:[<SINK>]
L3 [after local declaration]:
r(fun(n: Int) = n + 1) -> <v2> PREV:[jmp?(L3)]
2 jmp(L6) NEXT:[merge(if (b) { fun(n: Int) = n + 1 } else { fun(n: Int) = n - 1 }|<v2>, <v3>) -> <v4>]
L2 [else branch]:
3 mark({ fun(n: Int) = n - 1 }) PREV:[jf(L2|<v1>)]
jmp?(L7) NEXT:[r(fun(n: Int) = n - 1) -> <v3>, d(fun(n: Int) = n - 1)]
d(fun(n: Int) = n - 1) NEXT:[<SINK>]
L7 [after local declaration]:
r(fun(n: Int) = n - 1) -> <v3> PREV:[jmp?(L7)]
L6 ['if' expression result]:
2 merge(if (b) { fun(n: Int) = n + 1 } else { fun(n: Int) = n - 1 }|<v2>, <v3>) -> <v4> PREV:[jmp(L6), r(fun(n: Int) = n - 1) -> <v3>]
L1:
1 <END> NEXT:[<SINK>]
error:
<ERROR> PREV:[]
sink:
<SINK> PREV:[<ERROR>, <END>, d(fun(n: Int) = n + 1), d(fun(n: Int) = n - 1)]
=====================
== anonymous_0 ==
fun(n: Int) = n + 1
---------------------
L4:
4 <START>
v(n: Int)
magic[FAKE_INITIALIZER](n: Int) -> <v0>
w(n|<v0>)
r(n) -> <v1>
r(1) -> <v2>
mark(n + 1)
call(n + 1, plus|<v1>, <v2>) -> <v3>
ret(*|<v3>) L5
L5:
<END> NEXT:[<SINK>]
error:
<ERROR> PREV:[]
sink:
<SINK> PREV:[<ERROR>, <END>]
=====================
== anonymous_1 ==
fun(n: Int) = n - 1
---------------------
L8:
4 <START>
v(n: Int)
magic[FAKE_INITIALIZER](n: Int) -> <v0>
w(n|<v0>)
r(n) -> <v1>
r(1) -> <v2>
mark(n - 1)
call(n - 1, minus|<v1>, <v2>) -> <v3>
ret(*|<v3>) L9
L9:
<END> NEXT:[<SINK>]
error:
<ERROR> PREV:[]
sink:
<SINK> PREV:[<ERROR>, <END>]
=====================

View File

@@ -0,0 +1,8 @@
fun test(b: Boolean): (Int) -> Int {
if (b) {
fun(n: Int) = n + 1
}
else {
fun(n: Int) = n - 1
}
}

View File

@@ -0,0 +1,35 @@
== test ==
fun test(b: Boolean): (Int) -> Int {
if (b) {
fun(n: Int) = n + 1
}
else {
fun(n: Int) = n - 1
}
}
---------------------
<v0>: Boolean NEW: magic[FAKE_INITIALIZER](b: Boolean) -> <v0>
b <v1>: Boolean NEW: r(b) -> <v1>
fun(n: Int) = n + 1 <v2>: * NEW: r(fun(n: Int) = n + 1) -> <v2>
{ fun(n: Int) = n + 1 } <v2>: * COPY
fun(n: Int) = n - 1 <v3>: * NEW: r(fun(n: Int) = n - 1) -> <v3>
{ fun(n: Int) = n - 1 } <v3>: * COPY
if (b) { fun(n: Int) = n + 1 } else { fun(n: Int) = n - 1 } <v4>: * NEW: merge(if (b) { fun(n: Int) = n + 1 } else { fun(n: Int) = n - 1 }|<v2>, <v3>) -> <v4>
{ if (b) { fun(n: Int) = n + 1 } else { fun(n: Int) = n - 1 } } <v4>: * COPY
=====================
== anonymous_0 ==
fun(n: Int) = n + 1
---------------------
<v0>: Int NEW: magic[FAKE_INITIALIZER](n: Int) -> <v0>
n <v1>: Int NEW: r(n) -> <v1>
1 <v2>: Int NEW: r(1) -> <v2>
n + 1 <v3>: Int NEW: call(n + 1, plus|<v1>, <v2>) -> <v3>
=====================
== anonymous_1 ==
fun(n: Int) = n - 1
---------------------
<v0>: Int NEW: magic[FAKE_INITIALIZER](n: Int) -> <v0>
n <v1>: Int NEW: r(n) -> <v1>
1 <v2>: Int NEW: r(1) -> <v2>
n - 1 <v3>: Int NEW: call(n - 1, minus|<v1>, <v2>) -> <v3>
=====================

View File

@@ -0,0 +1,63 @@
== test ==
fun test() {
val f = fun(n: Int): Int { return 1 }
val g = fun foo(n: Int) = 2
}
---------------------
L0:
1 <START>
2 mark({ val f = fun(n: Int): Int { return 1 } val g = fun foo(n: Int) = 2 })
v(val f = fun(n: Int): Int { return 1 })
jmp?(L2) NEXT:[r(fun(n: Int): Int { return 1 }) -> <v0>, d(fun(n: Int): Int { return 1 })]
d(fun(n: Int): Int { return 1 }) NEXT:[<SINK>]
L2 [after local declaration]:
r(fun(n: Int): Int { return 1 }) -> <v0> PREV:[jmp?(L2)]
w(f|<v0>)
v(val g = fun foo(n: Int) = 2)
jmp?(L5) NEXT:[r(fun foo(n: Int) = 2) -> <v1>, d(fun foo(n: Int) = 2)]
d(fun foo(n: Int) = 2) NEXT:[<SINK>]
L5 [after local declaration]:
r(fun foo(n: Int) = 2) -> <v1> PREV:[jmp?(L5)]
w(g|<v1>)
L1:
1 <END> NEXT:[<SINK>]
error:
<ERROR> PREV:[]
sink:
<SINK> PREV:[<ERROR>, <END>, d(fun(n: Int): Int { return 1 }), d(fun foo(n: Int) = 2)]
=====================
== anonymous_0 ==
fun(n: Int): Int { return 1 }
---------------------
L3:
3 <START>
v(n: Int)
magic[FAKE_INITIALIZER](n: Int) -> <v0>
w(n|<v0>)
4 mark({ return 1 })
r(1) -> <v1>
ret(*|<v1>) L4
L4:
3 <END> NEXT:[<SINK>]
error:
<ERROR> PREV:[]
sink:
<SINK> PREV:[<ERROR>, <END>]
=====================
== foo ==
fun foo(n: Int) = 2
---------------------
L6:
3 <START>
v(n: Int)
magic[FAKE_INITIALIZER](n: Int) -> <v0>
w(n|<v0>)
r(2) -> <v1>
ret(*|<v1>) L7
L7:
<END> NEXT:[<SINK>]
error:
<ERROR> PREV:[]
sink:
<SINK> PREV:[<ERROR>, <END>]
=====================

View File

@@ -0,0 +1,4 @@
fun test() {
val f = fun(n: Int): Int { return 1 }
val g = fun foo(n: Int) = 2
}

View File

@@ -0,0 +1,23 @@
== test ==
fun test() {
val f = fun(n: Int): Int { return 1 }
val g = fun foo(n: Int) = 2
}
---------------------
fun(n: Int): Int { return 1 } <v0>: {<: (Int) -> Int} NEW: r(fun(n: Int): Int { return 1 }) -> <v0>
fun foo(n: Int) = 2 <v1>: {<: (Int) -> Int} NEW: r(fun foo(n: Int) = 2) -> <v1>
=====================
== anonymous_0 ==
fun(n: Int): Int { return 1 }
---------------------
<v0>: Int NEW: magic[FAKE_INITIALIZER](n: Int) -> <v0>
1 <v1>: Int NEW: r(1) -> <v1>
return 1 !<v2>: *
{ return 1 } !<v2>: * COPY
=====================
== foo ==
fun foo(n: Int) = 2
---------------------
<v0>: Int NEW: magic[FAKE_INITIALIZER](n: Int) -> <v0>
2 <v1>: Int NEW: r(2) -> <v1>
=====================

View File

@@ -0,0 +1,78 @@
== test ==
fun test(b: Boolean): (Int) -> Int {
if (b) {
fun foo(n: Int) = n + 1
}
else {
fun bar(n: Int) = n - 1
}
}
---------------------
L0:
1 <START>
v(b: Boolean)
magic[FAKE_INITIALIZER](b: Boolean) -> <v0>
w(b|<v0>)
2 mark({ if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 } })
mark(if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 })
r(b) -> <v1>
jf(L2|<v1>) NEXT:[mark({ fun bar(n: Int) = n - 1 }), mark({ fun foo(n: Int) = n + 1 })]
3 mark({ fun foo(n: Int) = n + 1 })
jmp?(L3) NEXT:[jmp(L6), d(fun foo(n: Int) = n + 1)]
d(fun foo(n: Int) = n + 1) NEXT:[<SINK>]
L3 [after local declaration]:
2 jmp(L6) NEXT:[merge(if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 }|!<v2>, !<v3>) -> <v4>] PREV:[jmp?(L3)]
L2 [else branch]:
3 mark({ fun bar(n: Int) = n - 1 }) PREV:[jf(L2|<v1>)]
jmp?(L7) NEXT:[merge(if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 }|!<v2>, !<v3>) -> <v4>, d(fun bar(n: Int) = n - 1)]
d(fun bar(n: Int) = n - 1) NEXT:[<SINK>]
L6 ['if' expression result]:
L7 [after local declaration]:
2 merge(if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 }|!<v2>, !<v3>) -> <v4> PREV:[jmp(L6), jmp?(L7)]
L1:
1 <END> NEXT:[<SINK>]
error:
<ERROR> PREV:[]
sink:
<SINK> PREV:[<ERROR>, <END>, d(fun foo(n: Int) = n + 1), d(fun bar(n: Int) = n - 1)]
=====================
== foo ==
fun foo(n: Int) = n + 1
---------------------
L4:
4 <START>
v(n: Int)
magic[FAKE_INITIALIZER](n: Int) -> <v0>
w(n|<v0>)
r(n) -> <v1>
r(1) -> <v2>
mark(n + 1)
call(n + 1, plus|<v1>, <v2>) -> <v3>
ret(*|<v3>) L5
L5:
<END> NEXT:[<SINK>]
error:
<ERROR> PREV:[]
sink:
<SINK> PREV:[<ERROR>, <END>]
=====================
== bar ==
fun bar(n: Int) = n - 1
---------------------
L8:
4 <START>
v(n: Int)
magic[FAKE_INITIALIZER](n: Int) -> <v0>
w(n|<v0>)
r(n) -> <v1>
r(1) -> <v2>
mark(n - 1)
call(n - 1, minus|<v1>, <v2>) -> <v3>
ret(*|<v3>) L9
L9:
<END> NEXT:[<SINK>]
error:
<ERROR> PREV:[]
sink:
<SINK> PREV:[<ERROR>, <END>]
=====================

View File

@@ -0,0 +1,8 @@
fun test(b: Boolean): (Int) -> Int {
if (b) {
fun foo(n: Int) = n + 1
}
else {
fun bar(n: Int) = n - 1
}
}

View File

@@ -0,0 +1,33 @@
== test ==
fun test(b: Boolean): (Int) -> Int {
if (b) {
fun foo(n: Int) = n + 1
}
else {
fun bar(n: Int) = n - 1
}
}
---------------------
<v0>: Boolean NEW: magic[FAKE_INITIALIZER](b: Boolean) -> <v0>
b <v1>: Boolean NEW: r(b) -> <v1>
{ fun foo(n: Int) = n + 1 } !<v2>: *
{ fun bar(n: Int) = n - 1 } !<v3>: *
if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 } <v4>: * NEW: merge(if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 }|!<v2>, !<v3>) -> <v4>
{ if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 } } <v4>: * COPY
=====================
== foo ==
fun foo(n: Int) = n + 1
---------------------
<v0>: Int NEW: magic[FAKE_INITIALIZER](n: Int) -> <v0>
n <v1>: Int NEW: r(n) -> <v1>
1 <v2>: Int NEW: r(1) -> <v2>
n + 1 <v3>: Int NEW: call(n + 1, plus|<v1>, <v2>) -> <v3>
=====================
== bar ==
fun bar(n: Int) = n - 1
---------------------
<v0>: Int NEW: magic[FAKE_INITIALIZER](n: Int) -> <v0>
n <v1>: Int NEW: r(n) -> <v1>
1 <v2>: Int NEW: r(1) -> <v2>
n - 1 <v3>: Int NEW: call(n - 1, minus|<v1>, <v2>) -> <v3>
=====================

View File

@@ -0,0 +1,26 @@
== foo ==
fun foo(): Int {
val t = @x (1 + 2)
return t
}
---------------------
L0:
1 <START>
2 mark({ val t = @x (1 + 2) return t })
v(val t = @x (1 + 2))
mark(@x (1 + 2))
mark((1 + 2))
r(1) -> <v0>
r(2) -> <v1>
mark(1 + 2)
call(1 + 2, plus|<v0>, <v1>) -> <v2>
w(t|<v2>)
r(t) -> <v3>
ret(*|<v3>) L1
L1:
1 <END> NEXT:[<SINK>]
error:
<ERROR> PREV:[]
sink:
<SINK> PREV:[<ERROR>, <END>]
=====================

View File

@@ -0,0 +1,4 @@
fun foo(): Int {
val t = @x (1 + 2)
return t
}

View File

@@ -0,0 +1,15 @@
== foo ==
fun foo(): Int {
val t = @x (1 + 2)
return t
}
---------------------
1 <v0>: Int NEW: r(1) -> <v0>
2 <v1>: Int NEW: r(2) -> <v1>
1 + 2 <v2>: Int NEW: call(1 + 2, plus|<v0>, <v1>) -> <v2>
(1 + 2) <v2>: Int COPY
@x (1 + 2) <v2>: Int COPY
t <v3>: Int NEW: r(t) -> <v3>
return t !<v4>: *
{ val t = @x (1 + 2) return t } !<v4>: * COPY
=====================

View File

@@ -102,7 +102,10 @@ public abstract class AbstractPseudocodeTest extends KotlinTestWithEnvironment {
String label;
assert (correspondingElement instanceof JetNamedDeclaration || correspondingElement instanceof JetPropertyAccessor) :
"Unexpected element class is pseudocode: " + correspondingElement.getClass();
if (correspondingElement instanceof JetFunctionLiteral) {
boolean isAnonymousFunction =
correspondingElement instanceof JetFunctionLiteral
|| (correspondingElement instanceof JetNamedFunction && correspondingElement.getName() == null);
if (isAnonymousFunction) {
label = "anonymous_" + i++;
}
else if (correspondingElement instanceof JetNamedDeclaration) {

View File

@@ -345,12 +345,30 @@ public class ControlFlowTestGenerated extends AbstractControlFlowTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/cfg/declarations/functions"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("anonymousFunctionInBlock.kt")
public void testAnonymousFunctionInBlock() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/anonymousFunctionInBlock.kt");
doTest(fileName);
}
@TestMetadata("FailFunction.kt")
public void testFailFunction() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/FailFunction.kt");
doTest(fileName);
}
@TestMetadata("functionAsExpression.kt")
public void testFunctionAsExpression() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/functionAsExpression.kt");
doTest(fileName);
}
@TestMetadata("namedFunctionInBlock.kt")
public void testNamedFunctionInBlock() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/namedFunctionInBlock.kt");
doTest(fileName);
}
@TestMetadata("typeParameter.kt")
public void testTypeParameter() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/typeParameter.kt");
@@ -508,6 +526,12 @@ public class ControlFlowTestGenerated extends AbstractControlFlowTest {
doTest(fileName);
}
@TestMetadata("labeledExpression.kt")
public void testLabeledExpression() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/expressions/labeledExpression.kt");
doTest(fileName);
}
@TestMetadata("LazyBooleans.kt")
public void testLazyBooleans() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/expressions/LazyBooleans.kt");

View File

@@ -347,12 +347,30 @@ public class PseudoValueTestGenerated extends AbstractPseudoValueTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/cfg/declarations/functions"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("anonymousFunctionInBlock.kt")
public void testAnonymousFunctionInBlock() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/anonymousFunctionInBlock.kt");
doTest(fileName);
}
@TestMetadata("FailFunction.kt")
public void testFailFunction() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/FailFunction.kt");
doTest(fileName);
}
@TestMetadata("functionAsExpression.kt")
public void testFunctionAsExpression() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/functionAsExpression.kt");
doTest(fileName);
}
@TestMetadata("namedFunctionInBlock.kt")
public void testNamedFunctionInBlock() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/namedFunctionInBlock.kt");
doTest(fileName);
}
@TestMetadata("typeParameter.kt")
public void testTypeParameter() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/typeParameter.kt");
@@ -510,6 +528,12 @@ public class PseudoValueTestGenerated extends AbstractPseudoValueTest {
doTest(fileName);
}
@TestMetadata("labeledExpression.kt")
public void testLabeledExpression() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/expressions/labeledExpression.kt");
doTest(fileName);
}
@TestMetadata("LazyBooleans.kt")
public void testLazyBooleans() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/expressions/LazyBooleans.kt");

View File

@@ -828,6 +828,10 @@ fun main(args: Array<String>) {
testClass(javaClass<AbstractJsCodeTest>()) {
model("jsCode/cases")
}
testClass(javaClass<AbstractInlineSizeReductionTest>()) {
model("inlineSizeReduction/cases")
}
}
testGroup("js/js.tests/test", "compiler/testData") {

View File

@@ -248,9 +248,9 @@ public abstract class OverrideImplementMethodsHandler implements LanguageCodeIns
StringBuilder builder = new StringBuilder();
builder.append("super");
if (classOrObject.getDelegationSpecifiers().size() > 1) {
builder.append("<").append(descriptor.getContainingDeclaration().getName()).append(">");
builder.append("<").append(DescriptorRenderer.COMPACT.renderName(descriptor.getContainingDeclaration().getName())).append(">");
}
builder.append(".").append(descriptor.getName());
builder.append(".").append(DescriptorRenderer.COMPACT.renderName(descriptor.getName()));
if (descriptor instanceof FunctionDescriptor) {
builder.append("(");
@@ -260,7 +260,7 @@ public abstract class OverrideImplementMethodsHandler implements LanguageCodeIns
builder.append(", ");
}
first = false;
builder.append(parameterDescriptor.getName());
builder.append(DescriptorRenderer.COMPACT.renderName(parameterDescriptor.getName()));
}
builder.append(")");
}

View File

@@ -0,0 +1,7 @@
open class A {
open fun foo(`object` : Any): Int = 0
}
class C : A {
<caret>
}

View File

@@ -0,0 +1,9 @@
open class A {
open fun foo(`object` : Any): Int = 0
}
class C : A {
override fun foo(`object`: Any): Int {
<selection><caret>return super.foo(`object`)</selection>
}
}

View File

@@ -201,4 +201,8 @@ public final class OverrideImplementTest extends AbstractOverrideImplementTest {
public void testStarProjections() {
doImplementFileTest();
}
public void testEscapeIdentifiers() {
doOverrideFileTest();
}
}

View File

@@ -1,243 +0,0 @@
/*
* Copyright 2008 Google Inc.
*
* 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.js.inline;
import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.js.inline.context.FunctionContext;
import org.jetbrains.kotlin.js.inline.context.InliningContext;
import org.jetbrains.kotlin.js.inline.context.NamingContext;
import java.util.List;
import static org.jetbrains.kotlin.js.inline.clean.CleanPackage.removeDefaultInitializers;
import static org.jetbrains.kotlin.js.inline.util.UtilPackage.*;
class FunctionInlineMutator {
private final JsInvocation call;
private final InliningContext inliningContext;
private final JsFunction invokedFunction;
private final boolean isResultNeeded;
private final NamingContext namingContext;
private final JsBlock body;
private JsExpression resultExpr = null;
private JsLabel breakLabel = null;
public static InlineableResult getInlineableCallReplacement(
@NotNull JsInvocation call,
@NotNull InliningContext inliningContext
) {
FunctionInlineMutator mutator = new FunctionInlineMutator(call, inliningContext);
mutator.process();
JsStatement inlineableBody = mutator.body;
if (mutator.breakLabel != null) {
mutator.breakLabel.setStatement(inlineableBody);
inlineableBody = mutator.breakLabel;
}
JsExpression resultExpression = null;
if (mutator.isResultNeeded) {
resultExpression = mutator.resultExpr;
}
return new InlineableResult(inlineableBody, resultExpression);
}
private FunctionInlineMutator(@NotNull JsInvocation call, @NotNull InliningContext inliningContext) {
this.inliningContext = inliningContext;
this.call = call;
FunctionContext functionContext = inliningContext.getFunctionContext();
invokedFunction = functionContext.getFunctionDefinition(call);
body = invokedFunction.getBody().deepCopy();
isResultNeeded = isResultNeeded(call);
namingContext = inliningContext.newNamingContext();
}
private void process() {
List<JsExpression> arguments = getArguments();
List<JsParameter> parameters = getParameters();
replaceThis();
removeDefaultInitializers(arguments, parameters, body);
aliasArgumentsIfNeeded(namingContext, arguments, parameters);
renameLocalNames(namingContext, invokedFunction);
removeStatementsAfterTopReturn();
if (isResultNeeded && canBeExpression(body)) {
resultExpr = asExpression(body);
body.getStatements().clear();
/** JsExpression can be immutable, so need to reassign */
resultExpr = (JsExpression) namingContext.applyRenameTo(resultExpr);
} else {
processReturns();
namingContext.applyRenameTo(body);
}
}
private void replaceThis() {
if (!hasThisReference(body)) return;
JsExpression thisReplacement = getThisReplacement(call);
if (thisReplacement == null) return;
if (needToAlias(thisReplacement)) {
JsName thisName = namingContext.getFreshName(getThisAlias());
namingContext.newVar(thisName, thisReplacement);
thisReplacement = thisName.makeRef();
}
replaceThisReference(body, thisReplacement);
}
private void removeStatementsAfterTopReturn() {
List<JsStatement> statements = body.getStatements();
int statementsSize = statements.size();
for (int i = 0; i < statementsSize; i++) {
JsStatement statement = statements.get(i);
if (statement instanceof JsReturn) {
statements.subList(i + 1, statementsSize).clear();
break;
}
}
}
private void processReturns() {
int returnCount = collectInstances(JsReturn.class, body).size();
if (returnCount == 0) {
// TODO return Unit (KT-5647)
resultExpr = JsLiteral.UNDEFINED;
} else {
doReplaceReturns(returnCount);
}
}
private void doReplaceReturns(int returnCount) {
JsReturn returnOnTop = ContainerUtil.findInstance(body.getStatements(), JsReturn.class);
boolean hasReturnOnTopLevel = returnOnTop != null;
if (isResultNeeded) {
JsName resultName = namingContext.getFreshName(getResultLabel());
namingContext.newVar(resultName, null);
resultExpr = resultName.makeRef();
}
boolean needBreakLabel = !(returnCount == 1 && hasReturnOnTopLevel);
JsNameRef breakLabelRef = null;
if (needBreakLabel) {
JsName breakName = namingContext.getFreshName(getBreakLabel());
breakLabelRef = breakName.makeRef();
breakLabel = new JsLabel(breakName);
}
assert resultExpr == null || resultExpr instanceof JsNameRef;
replaceReturns(body, (JsNameRef) resultExpr, breakLabelRef);
}
@NotNull
private List<JsExpression> getArguments() {
List<JsExpression> arguments = call.getArguments();
if (isCallInvocation(call)) {
return arguments.subList(1, arguments.size());
}
return arguments;
}
private boolean isResultNeeded(JsInvocation call) {
JsContext<JsStatement> statementContext = inliningContext.getStatementContext();
JsStatement currentStatement = statementContext.getCurrentNode();
return !(currentStatement instanceof JsExpressionStatement)
|| call != ((JsExpressionStatement) currentStatement).getExpression();
}
@NotNull
private List<JsParameter> getParameters() {
return invokedFunction.getParameters();
}
@NotNull
private String getResultLabel() {
return getLabelPrefix() + "result";
}
@NotNull
private String getBreakLabel() {
return getLabelPrefix() + "break";
}
@SuppressWarnings("MethodMayBeStatic")
@NotNull
private String getThisAlias() {
return "$this";
}
@NotNull
String getLabelPrefix() {
String ident = getSimpleIdent(call);
String labelPrefix = ident != null ? ident : "inline$";
if (labelPrefix.endsWith("$")) {
return labelPrefix;
}
return labelPrefix + "$";
}
@Nullable
private static JsExpression getThisReplacement(JsInvocation call) {
if (isCallInvocation(call)) {
return call.getArguments().get(0);
}
if (hasCallerQualifier(call)) {
return getCallerQualifier(call);
}
return null;
}
private static boolean hasThisReference(JsBlock body) {
List<JsLiteral.JsThisRef> thisRefs = collectInstances(JsLiteral.JsThisRef.class, body);
return !thisRefs.isEmpty();
}
public static boolean canBeExpression(JsFunction function) {
return canBeExpression(function.getBody());
}
private static boolean canBeExpression(JsBlock body) {
List<JsStatement> statements = body.getStatements();
return statements.size() == 1 && statements.get(0) instanceof JsReturn;
}
private static JsExpression asExpression(JsBlock body) {
assert canBeExpression(body);
List<JsStatement> statements = body.getStatements();
JsReturn returnStatement = (JsReturn) statements.get(0);
return returnStatement.getExpression();
}
}

View File

@@ -0,0 +1,274 @@
/*
* Copyright 2008 Google Inc.
*
* 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.js.inline
import com.google.dart.compiler.backend.js.ast.*
import com.google.dart.compiler.util.AstUtil
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.js.inline.clean.removeDefaultInitializers
import org.jetbrains.kotlin.js.inline.context.InliningContext
import org.jetbrains.kotlin.js.inline.context.NamingContext
import org.jetbrains.kotlin.js.inline.util.*
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.newVar
import org.jetbrains.kotlin.js.translate.utils.ast.*
import kotlin.platform.platformStatic
class FunctionInlineMutator private (private val call: JsInvocation, private val inliningContext: InliningContext) {
private val invokedFunction: JsFunction
private val isResultNeeded: Boolean
private val namingContext: NamingContext
private val body: JsBlock
private var resultExpr: JsExpression? = null
private var breakLabel: JsLabel? = null
private val currentStatement = inliningContext.statementContext.getCurrentNode()
init {
val functionContext = inliningContext.functionContext
invokedFunction = functionContext.getFunctionDefinition(call)
body = invokedFunction.getBody().deepCopy()
isResultNeeded = isResultNeeded(call)
namingContext = inliningContext.newNamingContext()
}
private fun process() {
val arguments = getArguments()
val parameters = getParameters()
replaceThis()
removeDefaultInitializers(arguments, parameters, body)
aliasArgumentsIfNeeded(namingContext, arguments, parameters)
renameLocalNames(namingContext, invokedFunction)
removeStatementsAfterTopReturn()
processReturns()
namingContext.applyRenameTo(body)
resultExpr = resultExpr?.let {
namingContext.applyRenameTo(it) as JsExpression
}
}
private fun replaceThis() {
if (!hasThisReference(body)) return
var thisReplacement = getThisReplacement(call)
if (thisReplacement == null || thisReplacement is JsLiteral.JsThisRef) return
if (thisReplacement!!.needToAlias()) {
val thisName = namingContext.getFreshName(getThisAlias())
namingContext.newVar(thisName, thisReplacement)
thisReplacement = thisName.makeRef()
}
replaceThisReference(body, thisReplacement!!)
}
private fun removeStatementsAfterTopReturn() {
val statements = body.getStatements()
val statementsSize = statements.size()
for (i in 0..statementsSize - 1) {
val statement = statements.get(i)
if (statement is JsReturn) {
statements.subList(i + 1, statementsSize).clear()
break
}
}
}
private fun processReturns() {
if (currentStatement is JsReturn && currentStatement.getExpression() === call) {
inliningContext.statementContext.removeMe()
return
}
val returnCount = collectInstances(javaClass<JsReturn>(), body).size()
if (returnCount == 0) {
// TODO return Unit (KT-5647)
resultExpr = JsLiteral.UNDEFINED
return
}
if (returnCount == 1) {
val statements = body.getStatements()
val lastTopLevelStatement = statements[statements.lastIndex]
if (lastTopLevelStatement is JsReturn) {
resultExpr = lastTopLevelStatement.getExpression()
statements.remove(statements.lastIndex)
return
}
}
doReplaceReturns(returnCount)
}
private fun doReplaceReturns(returnCount: Int) {
val returnOnTop = ContainerUtil.findInstance(body.getStatements(), javaClass<JsReturn>())
val hasReturnOnTopLevel = returnOnTop != null
val needBreakLabel = !(returnCount == 0 || returnCount == 1 && hasReturnOnTopLevel)
var breakLabelRef: JsNameRef? = null
if (needBreakLabel) {
val breakName = namingContext.getFreshName(getBreakLabel())
breakLabelRef = breakName.makeRef()
breakLabel = JsLabel(breakName)
}
variableReferenceForResult?.let {
resultExpr = it
}
assert(resultExpr == null || resultExpr is JsNameRef)
replaceReturns(body, resultExpr as? JsNameRef, breakLabelRef)
}
private val variableReferenceForResult: JsNameRef?
get() {
if (!isResultNeeded) return null
val existingReference = when (currentStatement) {
is JsExpressionStatement -> {
val expression = currentStatement.getExpression() as? JsBinaryOperation
expression?.variableReferenceForResult
}
is JsVars -> currentStatement.variableReferenceForResult
else -> null
}
if (existingReference != null) return existingReference
val resultName = namingContext.getFreshName(getResultLabel())
namingContext.newVar(resultName, null)
return resultName.makeRef()
}
private val JsBinaryOperation.variableReferenceForResult: JsNameRef?
get() {
if (operator !== JsBinaryOperator.ASG || arg2 !== call) return null
return arg1 as? JsNameRef
}
private val JsVars.variableReferenceForResult: JsNameRef?
get() {
val vars = getVars()
val variable = vars.first()
// var a = expr1 + call() is ok, but we don't want to reuse 'a' for result,
// as it means to replace every 'return expr2' to 'a = expr1 + expr2'.
// If there is more than one return, expr1 copies are undesirable.
if (variable.initExpression !== call || vars.size() > 1) return null
val varName = variable.getName()
with (inliningContext.statementContext) {
removeMe()
addPrevious(newVar(varName, null))
}
return varName.makeRef()
}
private fun getArguments(): List<JsExpression> {
val arguments = call.getArguments()
if (isCallInvocation(call)) {
return arguments.subList(1, arguments.size())
}
return arguments
}
private fun isResultNeeded(call: JsInvocation): Boolean {
return currentStatement !is JsExpressionStatement || call != currentStatement.getExpression()
}
private fun getParameters(): List<JsParameter> {
return invokedFunction.getParameters()
}
private fun getResultLabel(): String {
return getLabelPrefix() + "result"
}
private fun getBreakLabel(): String {
return getLabelPrefix() + "break"
}
private fun getThisAlias(): String {
return "\$this"
}
fun getLabelPrefix(): String {
val ident = getSimpleIdent(call)
val labelPrefix = ident ?: "inline$"
if (labelPrefix.endsWith("$")) {
return labelPrefix
}
return labelPrefix + "$"
}
companion object {
platformStatic
public fun getInlineableCallReplacement(call: JsInvocation, inliningContext: InliningContext): InlineableResult {
val mutator = FunctionInlineMutator(call, inliningContext)
mutator.process()
var inlineableBody: JsStatement = mutator.body
val breakLabel = mutator.breakLabel
if (breakLabel != null) {
breakLabel.setStatement(inlineableBody)
inlineableBody = breakLabel
}
return InlineableResult(inlineableBody, mutator.resultExpr)
}
platformStatic
private fun getThisReplacement(call: JsInvocation): JsExpression? {
if (isCallInvocation(call)) {
return call.getArguments().get(0)
}
if (hasCallerQualifier(call)) {
return getCallerQualifier(call)
}
return null
}
private fun hasThisReference(body: JsBlock): Boolean {
val thisRefs = collectInstances(javaClass<JsLiteral.JsThisRef>(), body)
return !thisRefs.isEmpty()
}
platformStatic
public fun canBeExpression(function: JsFunction): Boolean {
return canBeExpression(function.getBody())
}
private fun canBeExpression(body: JsBlock): Boolean {
val statements = body.getStatements()
return statements.size() == 1 && statements.get(0) is JsReturn
}
}
}

View File

@@ -184,8 +184,8 @@ public class JsInliner extends JsVisitorWithContextImpl {
JsExpression resultExpression = inlineableResult.getResultExpression();
JsContext<JsStatement> statementContext = inliningContext.getStatementContext();
// body of inline function can contain call to lambdas that need to be inlined
JsStatement statement = accept(inlineableBody);
assert inlineableBody == statement;
JsStatement inlineableBodyWithLambdasInlined = accept(inlineableBody);
assert inlineableBody == inlineableBodyWithLambdasInlined;
statementContext.addPrevious(flattenStatement(inlineableBody));
@@ -199,7 +199,17 @@ public class JsInliner extends JsVisitorWithContextImpl {
}
resultExpression = accept(resultExpression);
context.replaceMe(resultExpression);
JsStatement currentStatement = statementContext.getCurrentNode();
if (currentStatement instanceof JsExpressionStatement &&
((JsExpressionStatement) currentStatement).getExpression() == call &&
(resultExpression == null || !canHaveSideEffect(resultExpression))
) {
statementContext.removeMe();
}
else {
context.replaceMe(resultExpression);
}
}
@NotNull

View File

@@ -30,16 +30,15 @@ class NamingContext(
) {
private val renamings = IdentityHashMap<JsName, JsExpression>()
private val declarations = ArrayList<JsVars>()
private var renamingApplied = false
private var addedDeclarations = false
public fun applyRenameTo(target: JsNode): JsNode {
if (renamingApplied) throw RuntimeException("RenamingContext has been applied already")
if (!addedDeclarations) {
statementContext.addPrevious(declarations)
addedDeclarations = true
}
val result = replaceNames(target, renamings)
statementContext.addPrevious(declarations)
renamingApplied = true
return result
return replaceNames(target, renamings)
}
public fun replaceName(name: JsName, replacement: JsExpression) {

View File

@@ -68,8 +68,11 @@ public fun collectNamedFunctions(scope: JsNode): IdentityHashMap<JsName, JsFunct
return namedFunctions
}
public fun collectInstances<T : JsNode>(klass: Class<T>, scope: JsNode): List<T> {
return with(InstanceCollector(klass)) {
kotlin.jvm.overloads
public fun collectInstances<T : JsNode>(
klass: Class<T>, scope: JsNode, visitNestedDeclarations: Boolean = false
): List<T> {
return with(InstanceCollector(klass, visitNestedDeclarations)) {
accept(scope)
collected
}

View File

@@ -16,13 +16,27 @@
package org.jetbrains.kotlin.js.inline.util.collectors
import com.google.dart.compiler.backend.js.ast.JsFunction
import com.google.dart.compiler.backend.js.ast.JsNode
import com.google.dart.compiler.backend.js.ast.JsObjectLiteral
import com.google.dart.compiler.backend.js.ast.RecursiveJsVisitor
import java.util.ArrayList
class InstanceCollector<T : JsNode>(val klass: Class<T>) : RecursiveJsVisitor() {
class InstanceCollector<T : JsNode>(val klass: Class<T>, val visitNestedDeclarations: Boolean) : RecursiveJsVisitor() {
public val collected: MutableList<T> = ArrayList()
override fun visitFunction(x: JsFunction) {
if (visitNestedDeclarations) {
visitElement(x)
}
}
override fun visitObjectLiteral(x: JsObjectLiteral) {
if (visitNestedDeclarations) {
visitElement(x)
}
}
override fun visitElement(node: JsNode) {
if (klass.isInstance(node)) {
collected.add(klass.cast(node)!!)

View File

@@ -16,10 +16,7 @@
package org.jetbrains.kotlin.js.inline.util
import com.google.dart.compiler.backend.js.ast.JsExpression
import com.google.dart.compiler.backend.js.ast.JsName
import com.google.dart.compiler.backend.js.ast.JsNameRef
import com.google.dart.compiler.backend.js.ast.JsNode
import com.google.dart.compiler.backend.js.ast.*
import org.jetbrains.kotlin.js.inline.util.rewriters.NameReplacingVisitor
import org.jetbrains.kotlin.js.inline.util.rewriters.ReturnReplacingVisitor
import org.jetbrains.kotlin.js.inline.util.rewriters.ThisReplacingVisitor
@@ -30,8 +27,20 @@ public fun <T : JsNode> replaceNames(node: T, replaceMap: IdentityHashMap<JsName
return NameReplacingVisitor(replaceMap).accept(node)!!
}
public fun replaceReturns(scope: JsNode, resultRef: JsNameRef?, breakLabel: JsNameRef?): JsNode {
return ReturnReplacingVisitor(resultRef, breakLabel).accept(scope)!!
public fun replaceReturns(scope: JsBlock, resultRef: JsNameRef?, breakLabel: JsNameRef?): JsNode {
val visitor = ReturnReplacingVisitor(resultRef, breakLabel)
val withReturnReplaced = visitor.accept(scope)!!
if (breakLabel != null) {
val statements = scope.getStatements()
val last = statements.last() as? JsBreak
if (last?.getLabel()?.getName() === breakLabel.getName()) {
statements.remove(statements.lastIndex)
}
}
return withReturnReplaced
}
public fun replaceThisReference<T : JsNode>(node: T, replacement: JsExpression) {

View File

@@ -0,0 +1,25 @@
/*
* 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.js.test.semantics;
import org.jetbrains.kotlin.js.test.AbstractSingleFileTranslationWithDirectivesTest;
public abstract class AbstractInlineSizeReductionTest extends AbstractSingleFileTranslationWithDirectivesTest {
public AbstractInlineSizeReductionTest() {
super("inlineSizeReduction/");
}
}

View File

@@ -281,12 +281,6 @@ public class InlineJsTestGenerated extends AbstractInlineJsTest {
doTest(fileName);
}
@TestMetadata("noAdditionalVarsCreated.kt")
public void testNoAdditionalVarsCreated() throws Exception {
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inline/cases/noAdditionalVarsCreated.kt");
doTest(fileName);
}
@TestMetadata("noInlineLambda.kt")
public void testNoInlineLambda() throws Exception {
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inline/cases/noInlineLambda.kt");

View File

@@ -0,0 +1,79 @@
/*
* 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.js.test.semantics;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.JetTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("js/js.translator/testData/inlineSizeReduction/cases")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class InlineSizeReductionTestGenerated extends AbstractInlineSizeReductionTest {
public void testAllFilesPresentInCases() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/inlineSizeReduction/cases"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("lastBreak.kt")
public void testLastBreak() throws Exception {
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inlineSizeReduction/cases/lastBreak.kt");
doTest(fileName);
}
@TestMetadata("oneTopLevelReturn.kt")
public void testOneTopLevelReturn() throws Exception {
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inlineSizeReduction/cases/oneTopLevelReturn.kt");
doTest(fileName);
}
@TestMetadata("returnInlineCall.kt")
public void testReturnInlineCall() throws Exception {
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inlineSizeReduction/cases/returnInlineCall.kt");
doTest(fileName);
}
@TestMetadata("simpleReturnFunction.kt")
public void testSimpleReturnFunction() throws Exception {
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inlineSizeReduction/cases/simpleReturnFunction.kt");
doTest(fileName);
}
@TestMetadata("this.kt")
public void testThis() throws Exception {
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inlineSizeReduction/cases/this.kt");
doTest(fileName);
}
@TestMetadata("valAssignment.kt")
public void testValAssignment() throws Exception {
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inlineSizeReduction/cases/valAssignment.kt");
doTest(fileName);
}
@TestMetadata("valDeclaration.kt")
public void testValDeclaration() throws Exception {
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inlineSizeReduction/cases/valDeclaration.kt");
doTest(fileName);
}
}

View File

@@ -21,10 +21,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import static org.jetbrains.kotlin.js.inline.util.UtilPackage.collectInstances;
import static org.jetbrains.kotlin.test.InTextDirectivesUtils.findLinesWithPrefixesRemoved;
@@ -91,7 +88,7 @@ public class DirectiveTestUtils {
}
};
private abstract static class CountNodesDirective<T extends JsNode> extends DirectiveHandler {
private static class CountNodesDirective<T extends JsNode> extends DirectiveHandler {
@NotNull
private final Class<T> klass;
@@ -107,8 +104,8 @@ public class DirectiveTestUtils {
String countStr = arguments.getNamedArgument("count");
int expectedCount = Integer.valueOf(countStr);
JsNode scope = AstSearchUtil.getFunction(ast, functionName);
List<T> nodes = collectInstances(klass, scope);
JsFunction function = AstSearchUtil.getFunction(ast, functionName);
List<T> nodes = collectInstances(klass, function.getBody());
int actualCount = 0;
for (T node : nodes) {
@@ -121,7 +118,9 @@ public class DirectiveTestUtils {
assertEquals(message, expectedCount, actualCount);
}
protected abstract int getActualCountFor(@NotNull T node, @NotNull ArgumentsHelper arguments);
protected int getActualCountFor(@NotNull T node, @NotNull ArgumentsHelper arguments) {
return 1;
}
}
private static final DirectiveHandler COUNT_LABELS = new CountNodesDirective<JsLabel>("CHECK_LABELS_COUNT", JsLabel.class) {
@@ -137,12 +136,9 @@ public class DirectiveTestUtils {
}
};
private static final DirectiveHandler COUNT_VARS = new CountNodesDirective<JsVars>("CHECK_VARS_COUNT", JsVars.class) {
@Override
protected int getActualCountFor(@NotNull JsVars node, @NotNull ArgumentsHelper arguments) {
return node.getVars().size();
}
};
private static final DirectiveHandler COUNT_VARS = new CountNodesDirective<JsVars.JsVar>("CHECK_VARS_COUNT", JsVars.JsVar.class);
private static final DirectiveHandler COUNT_BREAKS = new CountNodesDirective<JsBreak>("CHECK_BREAKS_COUNT", JsBreak.class);
private static final DirectiveHandler HAS_INLINE_METADATA = new DirectiveHandler("CHECK_HAS_INLINE_METADATA") {
@Override
@@ -164,16 +160,23 @@ public class DirectiveTestUtils {
}
};
private static final List<DirectiveHandler> DIRECTIVE_HANDLERS = Arrays.asList(
FUNCTION_CONTAINS_NO_CALLS,
FUNCTION_NOT_CALLED,
FUNCTION_CALLED_IN_SCOPE,
FUNCTION_NOT_CALLED_IN_SCOPE,
FUNCTIONS_HAVE_SAME_LINES,
COUNT_LABELS,
COUNT_VARS,
COUNT_BREAKS,
HAS_INLINE_METADATA,
HAS_NO_INLINE_METADATA
);
public static void processDirectives(@NotNull JsNode ast, @NotNull String sourceCode) throws Exception {
FUNCTION_CONTAINS_NO_CALLS.process(ast, sourceCode);
FUNCTION_NOT_CALLED.process(ast, sourceCode);
FUNCTION_CALLED_IN_SCOPE.process(ast, sourceCode);
FUNCTION_NOT_CALLED_IN_SCOPE.process(ast, sourceCode);
FUNCTIONS_HAVE_SAME_LINES.process(ast, sourceCode);
COUNT_LABELS.process(ast, sourceCode);
COUNT_VARS.process(ast, sourceCode);
HAS_INLINE_METADATA.process(ast, sourceCode);
HAS_NO_INLINE_METADATA.process(ast, sourceCode);
for (DirectiveHandler handler : DIRECTIVE_HANDLERS) {
handler.process(ast, sourceCode);
}
}
public static void checkFunctionContainsNoCalls(JsNode node, String functionName) throws Exception {

View File

@@ -0,0 +1,37 @@
package foo
// CHECK_NOT_CALLED: f1
// CHECK_NOT_CALLED: f2
// CHECK_BREAKS_COUNT: function=test count=3
var even = arrayListOf<Int>()
var odd = arrayListOf<Int>()
inline fun f2(x: Int): Unit {
if (x % 2 == 0) {
even.add(x)
return
}
odd.add(x)
return
}
inline fun f1(x: Boolean, y: Int, z: Int): Unit {
if (x) {
return f2(y)
}
return f2(z)
}
fun test(x: Boolean, y: Int, z: Int): Unit = f1(x, y, z)
fun box(): String {
test(true, 2, 1)
test(false, 2, 1)
assertEquals(listOf(2), even)
assertEquals(listOf(1), odd)
return "OK"
}

View File

@@ -0,0 +1,30 @@
package foo
// CHECK_CONTAINS_NO_CALLS: test
// CHECK_VARS_COUNT: function=test count=0
var log = ""
inline fun run1(fn: ()->Int): Int {
log += "1;"
return 1 + fn()
}
inline fun run2(fn: ()->Int): Int {
log += "2;"
return 2 + run1(fn)
}
inline fun run3(fn: ()->Int): Int {
log += "3;"
return 3 + run2(fn)
}
fun test(x: Int): Int = run3 { x }
fun box(): String {
assertEquals(7, test(1))
assertEquals("3;2;1;", log)
return "OK"
}

View File

@@ -0,0 +1,22 @@
package foo
// CHECK_CONTAINS_NO_CALLS: test
// CHECK_VARS_COUNT: function=test count=0
inline fun sign(x: Int): Int {
if (x < 0) return -1
if (x == 0) return 0
return 1
}
fun test(x: Int): Int = sign(x)
fun box(): String {
assertEquals(-1, test(-2))
assertEquals(0, test(0))
assertEquals(1, test(2))
return "OK"
}

View File

@@ -1,7 +1,7 @@
package foo
// CHECK_CONTAINS_NO_CALLS: test
// COUNT_VARS: function=test count=0
// CHECK_VARS_COUNT: function=test count=0
// A copy of stdlib run function.
// Copied to not to depend on run implementation.

View File

@@ -0,0 +1,17 @@
package foo
// CHECK_CONTAINS_NO_CALLS: test
// CHECK_VARS_COUNT: function=test count=0
class A(val x: Int)
inline fun A.run(fn: A.()->Int) = fn()
fun test(a: A): Int = a.run { x }
fun box(): String {
assertEquals(1, test(A(1)))
assertEquals(2, test(A(2)))
return "OK"
}

View File

@@ -0,0 +1,21 @@
package foo
// CHECK_CONTAINS_NO_CALLS: test
// CHECK_VARS_COUNT: function=test count=1
inline fun sum(x: Int, y: Int): Int {
if (x == 0 || y == 0) return 0
return x * y
}
fun test(x: Int, y: Int): Int {
val sum: Int
sum = sum(x, y)
return sum
}
fun box(): String {
return "OK"
}

View File

@@ -0,0 +1,20 @@
package foo
// CHECK_CONTAINS_NO_CALLS: test
// CHECK_VARS_COUNT: function=test count=1
inline fun sum(x: Int, y: Int): Int {
if (x == 0 || y == 0) return 0
return x * y
}
fun test(x: Int, y: Int): Int {
val sum = sum(x, y)
return sum
}
fun box(): String {
return "OK"
}