diff --git a/compiler/testData/codegen/boxInline/property/property.kt b/compiler/testData/codegen/boxInline/property/property.kt new file mode 100644 index 00000000000..3265987a72b --- /dev/null +++ b/compiler/testData/codegen/boxInline/property/property.kt @@ -0,0 +1,162 @@ +// PROPERTY_NOT_USED: p1 +// PROPERTY_NOT_READ_FROM: p2 +// PROPERTY_NOT_WRITTEN_TO: p3 +// CHECK_NOT_CALLED: get_p4 +// CHECK_NOT_CALLED: set_p4 +// CHECK_NOT_CALLED: get_p5 +// CHECK_NOT_CALLED: set_p6 +// PROPERTY_NOT_USED: p7 +// PROPERTY_NOT_READ_FROM: p8 +// PROPERTY_NOT_WRITTEN_TO: p9 +// CHECK_NOT_CALLED: get_p10_s8ev3o$ +// CHECK_NOT_CALLED: set_p10_rksjo2$ +// CHECK_NOT_CALLED: get_p11_s8ev3o$ +// CHECK_NOT_CALLED: set_p12_rksjo2$ +// CHECK_NOT_CALLED: get_p13 +// CHECK_NOT_CALLED: set_p13 +// CHECK_NOT_CALLED: get_p14 +// CHECK_NOT_CALLED: set_p15 + +// FILE: 1.kt +package test + +var a = 0 + +inline var p1: Int + get() = a + 10000 + set(v: Int) { + a = v + 100 + } + +var p2: Int + inline get() = a + 20000 + set(v: Int) { + a = v + 200 + } + +var p3: Int + get() = a + 30000 + inline set(v: Int) { + a = v + 300 + } + +inline var Int.p4: Int + get() = this * 100 + a + 40000 + set(v: Int) { + a = this + v + 400 + } + +var Int.p5: Int + inline get() = this * 100 + a + 50000 + set(v: Int) { + a = this + v + 500 + } + +var Int.p6: Int + get() = this * 100 + a + 60000 + inline set(v: Int) { + a = this + v + 600 + } + +class A { + inline var p7: Int + get() = a + 70000 + set(v: Int) { + a = v + 700 + } + + var p8: Int + inline get() = a + 80000 + set(v: Int) { + a = v + 800 + } + + var p9: Int + get() = a + 90000 + inline set(v: Int) { + a = v + 900 + } + + inline var Int.p10: Int + get() = this * 100 + a + 100000 + set(v: Int) { + a = this + v + 1000 + } + + var Int.p11: Int + inline get() = this * 100 + a + 110000 + set(v: Int) { + a = this + v + 1100 + } + + var Int.p12: Int + get() = this * 100 + a + 120000 + inline set(v: Int) { + a = this + v + 1200 + } +} + +inline var A.p13: Int + get() = a + 130000 + set(v: Int) { + a = v + 1300 + } + +var A.p14: Int + inline get() = a + 140000 + set(v: Int) { + a = v + 1400 + } + +var A.p15: Int + get() = a + 150000 + inline set(v: Int) { + a = v + 1500 + } + +// FILE: 2.kt +import test.* + +fun box(): String { + p1 = 1 + if (p1 != 10101) return "test1: $p1" + p2 = 2 + if (p2 != 20202) return "test2: $p2" + p3 = 3 + if (p3 != 30303) return "test3: $p3" + + 4000000.p4 = 4 + if (4000000.p4 != 404040404) return "test4: ${4000000.p4}" + 5000000.p5 = 5 + if (5000000.p5 != 505050505) return "test5: ${5000000.p5}" + 6000000.p6 = 6 + if (6000000.p6 != 606060606) return "test6: ${6000000.p6}" + + + val a = A() + + a.p7 = 7 + if (a.p7 != 70707) return "test7: ${a.p7}" + a.p8 = 8 + if (a.p8 != 80808) return "test8: ${a.p8}" + a.p9 = 9 + if (a.p9 != 90909) return "test9: ${a.p9}" + + with (a) { + 10000000.p10 = 10 + if (10000000.p10 != 1010101010) return "test10: ${10000000.p10}" + 11000000.p11 = 11 + if (11000000.p11 != 1111111111) return "test11: ${11000000.p11}" + 12000000.p12 = 12 + if (12000000.p12 != 1212121212) return "test12: ${12000000.p12}" + } + + a.p13 = 13 + if (a.p13 != 131313) return "test13: ${a.p13}" + a.p14 = 14 + if (a.p14 != 141414) return "test14: ${a.p14}" + a.p15 = 15 + if (a.p15 != 151515) return "test15: ${a.p15}" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/property/reifiedVal.kt b/compiler/testData/codegen/boxInline/property/reifiedVal.kt index 52b0a4182e0..f88d11b18fc 100644 --- a/compiler/testData/codegen/boxInline/property/reifiedVal.kt +++ b/compiler/testData/codegen/boxInline/property/reifiedVal.kt @@ -1,9 +1,10 @@ // WITH_RUNTIME +// WITH_REFLECT // FILE: 1.kt package test inline val T.value: String - get() = T::class.java.simpleName + get() = T::class.simpleName!! // FILE: 2.kt import test.* @@ -11,5 +12,5 @@ import test.* class OK fun box(): String { - return OK().value + return OK().value ?: "fail" } \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/property/reifiedVar.kt b/compiler/testData/codegen/boxInline/property/reifiedVar.kt index 814a03fbbcb..ace575f220f 100644 --- a/compiler/testData/codegen/boxInline/property/reifiedVar.kt +++ b/compiler/testData/codegen/boxInline/property/reifiedVar.kt @@ -1,11 +1,12 @@ // WITH_RUNTIME +// WITH_REFLECT // FILE: 1.kt package test var bvalue: String = "" inline var T.value: String - get() = T::class.java.simpleName + bvalue + get() = T::class.simpleName!! + bvalue set(p: String) { bvalue = p } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java index 27b5f293c60..ff06d0bb37f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java @@ -1733,6 +1733,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/property"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("property.kt") + public void testProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/property.kt"); + doTest(fileName); + } + @TestMetadata("reifiedVal.kt") public void testReifiedVal() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/reifiedVal.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java index 66b64073174..5a0d992fdf2 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -1733,6 +1733,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/property"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("property.kt") + public void testProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/property.kt"); + doTest(fileName); + } + @TestMetadata("reifiedVal.kt") public void testReifiedVal() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/reifiedVal.kt"); diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index fb65933630b..85b15a6fd44 100755 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -133,6 +133,7 @@ import org.jetbrains.kotlin.jps.incremental.AbstractProtoComparisonTest import org.jetbrains.kotlin.js.test.semantics.AbstractBoxJsTest import org.jetbrains.kotlin.js.test.semantics.AbstractJsCodegenBoxTest import org.jetbrains.kotlin.js.test.semantics.AbstractNonLocalReturnsTest +import org.jetbrains.kotlin.js.test.semantics.AbstractPropertyAccessorsInlineTests import org.jetbrains.kotlin.jvm.compiler.* import org.jetbrains.kotlin.jvm.runtime.AbstractJvm8RuntimeDescriptorLoaderTest import org.jetbrains.kotlin.jvm.runtime.AbstractJvmRuntimeDescriptorLoaderTest @@ -1188,6 +1189,10 @@ fun main(args: Array) { testClass { model("codegen/boxInline/nonLocalReturns/", targetBackend = TargetBackend.JS) } + + testClass() { + model("codegen/boxInline/property/", targetBackend = TargetBackend.JS) + } } } diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt index c23038f341d..c1f731f53e4 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt @@ -33,6 +33,12 @@ var JsInvocation.descriptor: CallableDescriptor? by MetadataProperty(default = n var JsInvocation.psiElement: PsiElement? by MetadataProperty(default = null) +var JsNameRef.inlineStrategy: InlineStrategy? by MetadataProperty(default = null) + +var JsNameRef.descriptor: CallableDescriptor? by MetadataProperty(default = null) + +var JsNameRef.psiElement: PsiElement? by MetadataProperty(default = null) + var JsFunction.isLocal: Boolean by MetadataProperty(default = false) var JsParameter.hasDefaultValue: Boolean by MetadataProperty(default = false) diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/JsInliner.java b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/JsInliner.java index bb57336e53e..19cc94c4596 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/JsInliner.java +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/JsInliner.java @@ -23,6 +23,8 @@ import kotlin.jvm.functions.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.CallableDescriptor; +import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor; +import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor; import org.jetbrains.kotlin.diagnostics.DiagnosticSink; import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor; @@ -43,6 +45,7 @@ import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.flattenStatemen public class JsInliner extends JsVisitorWithContextImpl { private final Map functions; + private final Map accessors; private final Stack inliningContexts = new Stack(); private final Set processedFunctions = CollectionUtilsKt.IdentitySet(); private final Set inProcessFunctions = CollectionUtilsKt.IdentitySet(); @@ -65,7 +68,8 @@ public class JsInliner extends JsVisitorWithContextImpl { public static JsProgram process(@NotNull TranslationContext context) { JsProgram program = context.program(); Map functions = CollectUtilsKt.collectNamedFunctions(program); - JsInliner inliner = new JsInliner(functions, new FunctionReader(context), context.bindingTrace()); + Map accessors = CollectUtilsKt.collectAccessors(program); + JsInliner inliner = new JsInliner(functions, accessors, new FunctionReader(context), context.bindingTrace()); inliner.accept(program); RemoveUnusedFunctionDefinitionsKt.removeUnusedFunctionDefinitions(program, functions); return program; @@ -73,14 +77,85 @@ public class JsInliner extends JsVisitorWithContextImpl { private JsInliner( @NotNull Map functions, + @NotNull Map accessors, @NotNull FunctionReader functionReader, @NotNull DiagnosticSink trace ) { this.functions = functions; + this.accessors = accessors; this.functionReader = functionReader; this.trace = trace; } + @Override + public boolean visit(@NotNull JsNameRef x, @NotNull JsContext ctx) { + JsInvocation dummy = tryCreatePropertyGetterInvocation(x); + if (dummy != null) { + return visit(dummy, ctx); + } + return super.visit(x, ctx); + } + + @Override + public void endVisit(@NotNull JsNameRef x, @NotNull JsContext ctx) { + JsInvocation dummy = tryCreatePropertyGetterInvocation(x); + if (dummy != null) { + endVisit(dummy, ctx); + } + super.visit(x, ctx); + } + + @Override + public boolean visit(@NotNull JsBinaryOperation x, @NotNull JsContext ctx) { + JsInvocation dummy = tryCreatePropertySetterInvocation(x); + if (dummy != null) { + return visit(dummy, ctx); + } + return super.visit(x, ctx); + } + + @Override + public void endVisit(@NotNull JsBinaryOperation x, @NotNull JsContext ctx) { + JsInvocation dummy = tryCreatePropertySetterInvocation(x); + if (dummy != null) { + // Prevent FunctionInlineMutator from creating a variable for the result (there is none, because assignment is a statement) + // TODO is there a better way to achieve this? + getInliningContext().getStatementContext().replaceMe(new JsExpressionStatement(dummy)); + endVisit(dummy, ctx); + } + super.visit(x, ctx); + } + + @Nullable + private static JsInvocation tryCreatePropertyGetterInvocation(@NotNull JsNameRef x) { + if (MetadataProperties.getInlineStrategy(x) != null && MetadataProperties.getDescriptor(x) instanceof PropertyGetterDescriptor) { + JsInvocation dummyInvocation = new JsInvocation(x); + copyInlineMetadata(x, dummyInvocation); + return dummyInvocation; + } + return null; + } + + @Nullable + private static JsInvocation tryCreatePropertySetterInvocation(@NotNull JsBinaryOperation x) { + if (!x.getOperator().isAssignment() || !(x.getArg1() instanceof JsNameRef)) return null; + JsNameRef name = (JsNameRef) x.getArg1(); + if (MetadataProperties.getInlineStrategy(name) != null && + MetadataProperties.getDescriptor(name) instanceof PropertySetterDescriptor) { + + JsInvocation dummyInvocation = new JsInvocation(name, x.getArg2()); + copyInlineMetadata(name, dummyInvocation); + return dummyInvocation; + } + return null; + } + + private static void copyInlineMetadata(@NotNull JsNameRef from, @NotNull JsInvocation to) { + MetadataProperties.setInlineStrategy(to, MetadataProperties.getInlineStrategy(from)); + MetadataProperties.setDescriptor(to, MetadataProperties.getDescriptor(from)); + MetadataProperties.setPsiElement(to, MetadataProperties.getPsiElement(from)); + } + @Override public boolean visit(@NotNull JsFunction function, @NotNull JsContext context) { inliningContexts.push(new JsInliningContext(function)); @@ -248,6 +323,12 @@ public class JsInliner extends JsVisitorWithContextImpl { protected JsFunction lookUpStaticFunction(@Nullable JsName functionName) { return functions.get(functionName); } + + @Nullable + @Override + protected JsFunction lookUpStaticFunctionByTag(@NotNull String functionTag) { + return accessors.get(functionTag); + } }; } diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/FunctionContext.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/FunctionContext.kt index bd29af35519..7de37c92534 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/FunctionContext.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/FunctionContext.kt @@ -20,6 +20,7 @@ import com.google.dart.compiler.backend.js.ast.* import com.google.dart.compiler.backend.js.ast.metadata.descriptor import org.jetbrains.kotlin.js.inline.FunctionReader import org.jetbrains.kotlin.js.inline.util.* +import org.jetbrains.kotlin.js.translate.context.Namer abstract class FunctionContext( private val function: JsFunction, @@ -27,6 +28,8 @@ abstract class FunctionContext( ) { protected abstract fun lookUpStaticFunction(functionName: JsName?): JsFunction? + protected abstract fun lookUpStaticFunctionByTag(functionTag: String): JsFunction? + fun getFunctionDefinition(call: JsInvocation): JsFunction { return getFunctionDefinitionImpl(call)!! } @@ -71,7 +74,10 @@ abstract class FunctionContext( */ private fun getFunctionDefinitionImpl(call: JsInvocation): JsFunction? { val descriptor = call.descriptor - if (descriptor != null && descriptor in functionReader) return functionReader[descriptor] + if (descriptor != null) { + if (descriptor in functionReader) return functionReader[descriptor] + lookUpStaticFunctionByTag(Namer.getFunctionTag(descriptor))?.let { return it } + } /** remove ending `()` */ val callQualifier: JsExpression = if (isCallInvocation(call)) { @@ -84,7 +90,9 @@ abstract class FunctionContext( /** process cases 2, 3 */ val qualifier = callQualifier.transitiveStaticRef return when (qualifier) { - is JsInvocation -> lookUpStaticFunction(getSimpleName(qualifier)!!)?.let { if (isFunctionCreator(it)) it else null } + is JsInvocation -> { + lookUpStaticFunction(getSimpleName(qualifier)!!)?.let { if (isFunctionCreator(it)) it else null } + } is JsNameRef -> lookUpStaticFunction(qualifier.name) is JsFunction -> qualifier else -> null diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt index daa8998c364..fec5bdfa096 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt @@ -22,6 +22,7 @@ import com.google.dart.compiler.backend.js.ast.metadata.staticRef import org.jetbrains.kotlin.js.inline.util.collectors.InstanceCollector import org.jetbrains.kotlin.js.translate.expression.InlineMetadata import org.jetbrains.kotlin.js.translate.utils.JsAstUtils +import org.jetbrains.kotlin.js.translate.utils.name import java.util.* fun collectFunctionReferencesInside(scope: JsNode): List = @@ -179,6 +180,21 @@ fun collectNamedFunctionsAndMetadata(scope: JsNode): Map { + val accessors = hashMapOf() + + scope.accept(object : RecursiveJsVisitor() { + override fun visitInvocation(invocation: JsInvocation) { + InlineMetadata.decompose(invocation)?.let { + accessors[it.tag.value] = it.function + } + super.visitInvocation(invocation) + } + }) + + return accessors +} + fun collectInstances(klass: Class, scope: JsNode): List { return with(InstanceCollector(klass, visitNestedDeclarations = false)) { accept(scope) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index bea53869903..bedfa991201 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -4505,6 +4505,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { doTest(fileName); } + @TestMetadata("property.kt") + public void testProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineMultiModule/property.kt"); + doTest(fileName); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineMultiModule/simple.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/PropertyAccessorsInlineTestsGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/PropertyAccessorsInlineTestsGenerated.java new file mode 100644 index 00000000000..8a1db4b805a --- /dev/null +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/PropertyAccessorsInlineTestsGenerated.java @@ -0,0 +1,68 @@ +/* + * Copyright 2010-2016 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.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +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("compiler/testData/codegen/boxInline/property") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class PropertyAccessorsInlineTestsGenerated extends AbstractPropertyAccessorsInlineTests { + public void testAllFilesPresentInProperty() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/property"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("property.kt") + public void testProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/property.kt"); + doTest(fileName); + } + + @TestMetadata("reifiedVal.kt") + public void testReifiedVal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/reifiedVal.kt"); + doTest(fileName); + } + + @TestMetadata("reifiedVar.kt") + public void testReifiedVar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/reifiedVar.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/simple.kt"); + doTest(fileName); + } + + @TestMetadata("simpleExtension.kt") + public void testSimpleExtension() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/simpleExtension.kt"); + doTest(fileName); + } +} diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt index 7a6c44de25e..4ff74ccaa57 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt @@ -29,6 +29,8 @@ abstract class BorrowedInlineTest(relativePath: String) : BasicBoxTest( abstract class AbstractNonLocalReturnsTest : BorrowedInlineTest("nonLocalReturns/") +abstract class AbstractPropertyAccessorsInlineTests : BorrowedInlineTest("property/") + abstract class AbstractBoxJsTest() : BasicBoxTest( BasicBoxTest.TEST_DATA_DIR_PATH + "box/", BasicBoxTest.TEST_DATA_DIR_PATH + "out/box/" diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java index a535a1256b7..20802242275 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java @@ -53,6 +53,27 @@ public class DirectiveTestUtils { } }; + private static final DirectiveHandler PROPERTY_NOT_USED = new DirectiveHandler("PROPERTY_NOT_USED") { + @Override + void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception { + checkPropertyNotUsed(ast, arguments.getFirst(), false, false); + } + }; + + private static final DirectiveHandler PROPERTY_NOT_READ_FROM = new DirectiveHandler("PROPERTY_NOT_READ_FROM") { + @Override + void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception { + checkPropertyNotUsed(ast, arguments.getFirst(), false, true); + } + }; + + private static final DirectiveHandler PROPERTY_NOT_WRITTEN_TO = new DirectiveHandler("PROPERTY_NOT_WRITTEN_TO") { + @Override + void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception { + checkPropertyNotUsed(ast, arguments.getFirst(), true, false); + } + }; + private static final DirectiveHandler FUNCTION_CALLED_IN_SCOPE = new DirectiveHandler("CHECK_CALLED_IN_SCOPE") { @Override void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception { @@ -232,6 +253,9 @@ public class DirectiveTestUtils { private static final List DIRECTIVE_HANDLERS = Arrays.asList( FUNCTION_CONTAINS_NO_CALLS, FUNCTION_NOT_CALLED, + PROPERTY_NOT_USED, + PROPERTY_NOT_READ_FROM, + PROPERTY_NOT_WRITTEN_TO, FUNCTION_CALLED_IN_SCOPE, FUNCTION_NOT_CALLED_IN_SCOPE, FUNCTIONS_HAVE_SAME_LINES, @@ -261,6 +285,17 @@ public class DirectiveTestUtils { assertEquals(errorMessage, 0, callsCount); } + public static void checkPropertyNotUsed(JsNode node, String propertyName, boolean isGetAllowed, boolean isSetAllowed) throws Exception { + PropertyReferenceCollector counter = PropertyReferenceCollector.Companion.collect(node); + if (!isGetAllowed) { + assertFalse("inline property getter for `" + propertyName + "` is called", counter.hasUnqualifiedReads(propertyName)); + } + if (!isSetAllowed) { + assertFalse("inline property setter for `" + propertyName + "` is called", counter.hasUnqualifiedWrites(propertyName)); + } + } + + public static void checkFunctionNotCalled(JsNode node, String functionName) throws Exception { CallCounter counter = CallCounter.countCalls(node); int functionCalledCount = counter.getQualifiedCallsCount(functionName); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/PropertyReferenceCollector.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/PropertyReferenceCollector.kt new file mode 100644 index 00000000000..6220bebe886 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/PropertyReferenceCollector.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2016 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.utils + +import com.google.dart.compiler.backend.js.ast.* +import org.jetbrains.kotlin.js.translate.utils.JsAstUtils + +class PropertyReferenceCollector : RecursiveJsVisitor() { + + private val identReadSet = hashSetOf() + private val identWriteSet = hashSetOf() + + fun hasUnqualifiedReads(expectedIdent: String) = expectedIdent in identReadSet + fun hasUnqualifiedWrites(expectedIdent: String) = expectedIdent in identWriteSet + + override fun visitNameRef(nameRef: JsNameRef) { + super.visitNameRef(nameRef) + identReadSet.add(nameRef.ident) + } + + override fun visitBinaryExpression(x: JsBinaryOperation) { + var assignmentToProperty = false + JsAstUtils.decomposeAssignment(x)?.let { (left, right) -> + (left as? JsNameRef)?.let { nameRef -> + assignmentToProperty = true + identWriteSet.add(nameRef.ident) + nameRef.qualifier?.accept(this) + right.accept(this) + } + } + if (!assignmentToProperty) { + super.visitBinaryExpression(x) + } + } + + companion object { + fun collect(node: JsNode): PropertyReferenceCollector { + val visitor = PropertyReferenceCollector() + node.accept(visitor) + return visitor + } + } +} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/VariableCallCases.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/VariableCallCases.kt index ce28943a58d..26d37271aff 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/VariableCallCases.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/VariableCallCases.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer.getCapturedVarAccessor import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator import org.jetbrains.kotlin.js.translate.utils.JsAstUtils +import org.jetbrains.kotlin.js.translate.reference.buildReifiedTypeArgs import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils @@ -120,7 +121,8 @@ object DefaultVariableAccessCase : VariableAccessCase() { override fun VariableAccessInfo.extensionReceiver(): JsExpression { val functionRef = ReferenceTranslator.translateAsValueReference(getAccessDescriptorIfNeeded(), context) - return JsInvocation(functionRef, extensionReceiver!!, *additionalArguments.toTypedArray()) + val reifiedTypeArguments = resolvedCall.typeArguments.buildReifiedTypeArgs(context) + return JsInvocation(functionRef, reifiedTypeArguments + listOf(extensionReceiver!!) + additionalArguments) } override fun VariableAccessInfo.bothReceivers(): JsExpression { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.java index 97cb28dbde7..7039986866b 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.java @@ -135,7 +135,7 @@ public final class FunctionTranslator extends AbstractTranslator { List jsParameters = new SmartList(); Map aliases = new HashMap(); - for (TypeParameterDescriptor type : descriptor.getTypeParameters()) { + for (TypeParameterDescriptor type : getTypeParameters(descriptor)) { if (type.isReified()) { JsName paramNameForType = context().getNameForDescriptor(type); jsParameters.add(new JsParameter(paramNameForType)); @@ -158,6 +158,13 @@ public final class FunctionTranslator extends AbstractTranslator { return jsParameters; } + private static List getTypeParameters(FunctionDescriptor functionDescriptor) { + if (functionDescriptor instanceof PropertyAccessorDescriptor) { + return ((PropertyAccessorDescriptor) functionDescriptor).getCorrespondingProperty().getTypeParameters(); + } + return functionDescriptor.getTypeParameters(); + } + public static void addParameters(List list, FunctionDescriptor descriptor, TranslationContext context) { for (ValueParameterDescriptor valueParameter : descriptor.getValueParameters()) { JsParameter jsParameter = new JsParameter(context.getNameForDescriptor(valueParameter)); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/InlineMetadata.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/InlineMetadata.kt index 295cf5ec67f..dc756bd8787 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/InlineMetadata.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/InlineMetadata.kt @@ -19,8 +19,6 @@ package org.jetbrains.kotlin.js.translate.expression import com.google.dart.compiler.backend.js.ast.* import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.js.translate.context.Namer -import org.jetbrains.kotlin.js.translate.context.TranslationContext -import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.* private val METADATA_PROPERTIES_COUNT = 2 diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt index 12d74dce450..d35300fa86a 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt @@ -185,7 +185,7 @@ class CallArgumentTranslator private constructor( if (CallExpressionTranslator.shouldBeInlined(callDescriptor)) { val typeArgs = resolvedCall.typeArguments - return typeArgs.addReifiedTypeArgsTo(result, context) + return result.copy(reifiedArguments = typeArgs.buildReifiedTypeArgs(context)) } return result @@ -330,10 +330,9 @@ class CallArgumentTranslator private constructor( } -private fun Map.addReifiedTypeArgsTo( - info: CallArgumentTranslator.ArgumentsInfo, +public fun Map.buildReifiedTypeArgs( context: TranslationContext -): CallArgumentTranslator.ArgumentsInfo { +): List { val reifiedTypeArguments = SmartList() val patternTranslator = PatternTranslator.newInstance(context) @@ -349,5 +348,5 @@ private fun Map.addReifiedTypeArgsTo( reifiedTypeArguments.add(isCheckCallable) } - return info.copy(reifiedArguments = reifiedTypeArguments) + return reifiedTypeArguments } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java index 74b0e67ad53..d83ce2fc93c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java @@ -72,7 +72,7 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl } public static boolean shouldBeInlined(@NotNull CallableDescriptor descriptor) { - if (descriptor instanceof SimpleFunctionDescriptor) { + if (descriptor instanceof SimpleFunctionDescriptor || descriptor instanceof PropertyAccessorDescriptor) { return InlineUtil.isInline(descriptor); } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/VariableAccessTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/VariableAccessTranslator.java index 8e294ed2a8b..54fe7145f66 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/VariableAccessTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/VariableAccessTranslator.java @@ -16,10 +16,12 @@ package org.jetbrains.kotlin.js.translate.reference; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.descriptors.VariableDescriptor; +import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator; import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; @@ -27,6 +29,9 @@ import org.jetbrains.kotlin.psi.KtReferenceExpression; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall; +import org.jetbrains.kotlin.resolve.inline.InlineUtil; + +import static org.jetbrains.kotlin.js.translate.utils.InlineUtils.setInlineCallMetadata; public class VariableAccessTranslator extends AbstractTranslator implements AccessTranslator { public static VariableAccessTranslator newInstance( @@ -39,19 +44,23 @@ public class VariableAccessTranslator extends AbstractTranslator implements Acce resolvedCall = ((VariableAsFunctionResolvedCall) resolvedCall).getVariableCall(); } assert resolvedCall.getResultingDescriptor() instanceof VariableDescriptor; - return new VariableAccessTranslator(context, (ResolvedCall) resolvedCall, receiver); + return new VariableAccessTranslator(context, referenceExpression, (ResolvedCall) resolvedCall, + receiver); } private final ResolvedCall resolvedCall; + private final KtReferenceExpression referenceExpression; private final JsExpression receiver; private VariableAccessTranslator( @NotNull TranslationContext context, + @NotNull KtReferenceExpression referenceExpression, @NotNull ResolvedCall resolvedCall, @Nullable JsExpression receiver ) { super(context); + this.referenceExpression = referenceExpression; this.receiver = receiver; this.resolvedCall = resolvedCall; } @@ -59,29 +68,56 @@ public class VariableAccessTranslator extends AbstractTranslator implements Acce @NotNull @Override public JsExpression translateAsGet() { - return CallTranslator.INSTANCE.translateGet(context(), resolvedCall, receiver); + JsExpression e = CallTranslator.INSTANCE.translateGet(context(), resolvedCall, receiver); + CallableDescriptor original = resolvedCall.getResultingDescriptor().getOriginal(); + if (original instanceof PropertyDescriptor) { + PropertyGetterDescriptor getter = ((PropertyDescriptor) original).getGetter(); + if (InlineUtil.isInline(getter)) { + if (e instanceof JsNameRef) { + // Get was translated as a name reference + setInlineCallMetadata((JsNameRef) e, referenceExpression, getter); + } else { + setInlineCallMetadata(e, referenceExpression, getter, context()); + } + } + } + return e; } @NotNull @Override public JsExpression translateAsSet(@NotNull JsExpression setTo) { - return CallTranslator.INSTANCE.translateSet(context(), resolvedCall, setTo, receiver); + JsExpression e = CallTranslator.INSTANCE.translateSet(context(), resolvedCall, setTo, receiver); + CallableDescriptor original = resolvedCall.getResultingDescriptor().getOriginal(); + if (original instanceof PropertyDescriptor) { + PropertySetterDescriptor setter = ((PropertyDescriptor)original).getSetter(); + if (InlineUtil.isInline(setter)) { + if (e instanceof JsBinaryOperation && ((JsBinaryOperation) e).getOperator().isAssignment()) { + // Set was translated as an assignment + setInlineCallMetadata((JsNameRef) (((JsBinaryOperation) e).getArg1()), referenceExpression, setter); + } else { + setInlineCallMetadata(e, referenceExpression, setter, context()); + } + } + } + return e; } @NotNull @Override public AccessTranslator getCached() { JsExpression cachedReceiver = receiver != null ? context().cacheExpressionIfNeeded(receiver) : null; - return new CachedVariableAccessTranslator(context(), resolvedCall, cachedReceiver); + return new CachedVariableAccessTranslator(context(), referenceExpression, resolvedCall, cachedReceiver); } private static class CachedVariableAccessTranslator extends VariableAccessTranslator implements AccessTranslator { public CachedVariableAccessTranslator( @NotNull TranslationContext context, + @NotNull KtReferenceExpression referenceExpression, @NotNull ResolvedCall resolvedCall, @Nullable JsExpression cachedReceiver ) { - super(context, resolvedCall, cachedReceiver); + super(context, referenceExpression, resolvedCall, cachedReceiver); } @NotNull diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java index a818018802c..23391a7d111 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator; import org.jetbrains.kotlin.js.translate.context.Namer; import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable; import org.jetbrains.kotlin.js.translate.context.TranslationContext; +import org.jetbrains.kotlin.js.translate.expression.InlineMetadata; import org.jetbrains.kotlin.js.translate.general.Translation; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; @@ -37,6 +38,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument; import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver; +import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.types.KotlinType; import java.util.ArrayList; @@ -58,15 +60,21 @@ public final class TranslationUtils { public static JsPropertyInitializer translateFunctionAsEcma5PropertyDescriptor(@NotNull JsFunction function, @NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) { + JsExpression functionExpression = function; + if (InlineUtil.isInline(descriptor)) { + InlineMetadata metadata = InlineMetadata.compose(function, descriptor); + functionExpression = metadata.getFunctionWithMetadata(); + } + if (DescriptorUtils.isExtension(descriptor) || descriptor instanceof PropertyAccessorDescriptor && shouldAccessViaFunctions(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()) ) { - return translateExtensionFunctionAsEcma5DataDescriptor(function, descriptor, context); + return translateExtensionFunctionAsEcma5DataDescriptor(functionExpression, descriptor, context); } else { JsStringLiteral getOrSet = context.program().getStringLiteral(getAccessorFunctionName(descriptor)); - return new JsPropertyInitializer(getOrSet, function); + return new JsPropertyInitializer(getOrSet, functionExpression); } } @@ -82,9 +90,9 @@ public final class TranslationUtils { } @NotNull - private static JsPropertyInitializer translateExtensionFunctionAsEcma5DataDescriptor(@NotNull JsFunction function, + private static JsPropertyInitializer translateExtensionFunctionAsEcma5DataDescriptor(@NotNull JsExpression functionExpression, @NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) { - JsObjectLiteral meta = createDataDescriptor(function, ModalityKt.isOverridable(descriptor), false); + JsObjectLiteral meta = createDataDescriptor(functionExpression, ModalityKt.isOverridable(descriptor), false); return new JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), meta); } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/inlineUtils.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/inlineUtils.kt index d6c477012f7..3b30a2b50af 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/inlineUtils.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/inlineUtils.kt @@ -44,10 +44,9 @@ import org.jetbrains.kotlin.resolve.inline.InlineStrategy fun setInlineCallMetadata( expression: JsExpression, psiElement: KtExpression, - resolvedCall: ResolvedCall<*>, + descriptor: CallableDescriptor, context: TranslationContext ) { - val descriptor = PsiUtils.getFunctionDescriptor(resolvedCall) assert(CallExpressionTranslator.shouldBeInlined(descriptor)) { "Expected descriptor of callable, that should be inlined, but got: $descriptor" } @@ -69,6 +68,23 @@ fun setInlineCallMetadata( visitor.accept(expression) } +fun setInlineCallMetadata( + expression: JsExpression, + psiElement: KtExpression, + resolvedCall: ResolvedCall<*>, + context: TranslationContext +) = setInlineCallMetadata(expression, psiElement, PsiUtils.getFunctionDescriptor(resolvedCall), context) + +fun setInlineCallMetadata( + nameRef: JsNameRef, + psiElement: KtExpression, + descriptor: CallableDescriptor +) { + nameRef.descriptor = descriptor + nameRef.inlineStrategy = InlineStrategy.IN_PLACE + nameRef.psiElement = psiElement +} + fun TranslationContext.aliasedName(descriptor: CallableDescriptor): JsName { val alias = getAliasForDescriptor(descriptor) val aliasName = (alias as? JsNameRef)?.name diff --git a/js/js.translator/testData/box/inlineMultiModule/property.kt b/js/js.translator/testData/box/inlineMultiModule/property.kt new file mode 100644 index 00000000000..09ee5fc057f --- /dev/null +++ b/js/js.translator/testData/box/inlineMultiModule/property.kt @@ -0,0 +1,165 @@ +// MODULE: lib +// FILE: lib.kt + +package test + +var a = 0 + +inline var p1: Int + get() = a + 10000 + set(v) { + a = v + 100 + } + +var p2: Int + inline get() = a + 20000 + set(v) { + a = v + 200 + } + +var p3: Int + get() = a + 30000 + inline set(v) { + a = v + 300 + } + +inline var Int.p4: Int + get() = this * 100 + a + 40000 + set(v) { + a = this + v + 400 + } + +var Int.p5: Int + inline get() = this * 100 + a + 50000 + set(v) { + a = this + v + 500 + } + +var Int.p6: Int + get() = this * 100 + a + 60000 + inline set(v) { + a = this + v + 600 + } + +class A { + inline var p7: Int + get() = a + 70000 + set(v) { + a = v + 700 + } + + var p8: Int + inline get() = a + 80000 + set(v) { + a = v + 800 + } + + var p9: Int + get() = a + 90000 + inline set(v) { + a = v + 900 + } + + inline var Int.p10: Int + get() = this * 100 + a + 100000 + set(v) { + a = this + v + 1000 + } + + var Int.p11: Int + inline get() = this * 100 + a + 110000 + set(v) { + a = this + v + 1100 + } + + var Int.p12: Int + get() = this * 100 + a + 120000 + inline set(v) { + a = this + v + 1200 + } +} + +inline var A.p13: Int + get() = a + 130000 + set(v) { + a = v + 1300 + } + +var A.p14: Int + inline get() = a + 140000 + set(v) { + a = v + 1400 + } + +var A.p15: Int + get() = a + 150000 + inline set(v) { + a = v + 1500 + } + +// MODULE: main(lib) +// FILE: main.kt +// PROPERTY_NOT_USED: p1 +// PROPERTY_NOT_READ_FROM: p2 +// PROPERTY_NOT_WRITTEN_TO: p3 +// CHECK_NOT_CALLED: imported$get_p4 +// CHECK_NOT_CALLED: imported$set_p4 +// CHECK_NOT_CALLED: imported$get_p5 +// CHECK_NOT_CALLED: imported$set_p6 +// PROPERTY_NOT_USED: p7 +// PROPERTY_NOT_READ_FROM: p8 +// PROPERTY_NOT_WRITTEN_TO: p9 +// CHECK_NOT_CALLED: imported$A$get_A$p10 +// CHECK_NOT_CALLED: imported$A$set_A$p10 +// CHECK_NOT_CALLED: imported$A$get_A$p11 +// CHECK_NOT_CALLED: imported$A$set_A$p12 +// CHECK_NOT_CALLED: imported$get_p13 +// CHECK_NOT_CALLED: imported$set_p13 +// CHECK_NOT_CALLED: imported$get_p14 +// CHECK_NOT_CALLED: imported$set_p15 + +import test.* + +fun box(): String { + p1 = 1 + if (p1 != 10101) return "test1: $p1" + p2 = 2 + if (p2 != 20202) return "test2: $p2" + p3 = 3 + if (p3 != 30303) return "test3: $p3" + + 4000000.p4 = 4 + if (4000000.p4 != 404040404) return "test4: ${4000000.p4}" + 5000000.p5 = 5 + if (5000000.p5 != 505050505) return "test5: ${5000000.p5}" + 6000000.p6 = 6 + if (6000000.p6 != 606060606) return "test6: ${6000000.p6}" + + + val a = A() + + a.p7 = 7 + if (a.p7 != 70707) return "test7: ${a.p7}" + a.p8 = 8 + if (a.p8 != 80808) return "test8: ${a.p8}" + a.p9 = 9 + if (a.p9 != 90909) return "test9: ${a.p9}" + + with (a) { + 10000000.p10 = 10 + if (10000000.p10 != 1010101010) return "test10: ${10000000.p10}" + 11000000.p11 = 11 + if (11000000.p11 != 1111111111) return "test11: ${11000000.p11}" + 12000000.p12 = 12 + if (12000000.p12 != 1212121212) return "test12: ${12000000.p12}" + } + + a.p13 = 13 + if (a.p13 != 131313) return "test13: ${a.p13}" + a.p14 = 14 + if (a.p14 != 141414) return "test14: ${a.p14}" + a.p15 = 15 + if (a.p15 != 151515) return "test15: ${a.p15}" + + return "OK" +} \ No newline at end of file