[JS IR] Introduce the GENERATE_INLINE_ANONYMOUS_FUNCTIONS feature flag
This commit is contained in:
+6
@@ -178,6 +178,12 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
|
|||||||
@Argument(value = "-Xir-new-ir2js", description = "New fragment-based ir2js")
|
@Argument(value = "-Xir-new-ir2js", description = "New fragment-based ir2js")
|
||||||
var irNewIr2Js: Boolean by FreezableVar(true)
|
var irNewIr2Js: Boolean by FreezableVar(true)
|
||||||
|
|
||||||
|
@Argument(
|
||||||
|
value = "-Xir-generate-inline-anonymous-functions",
|
||||||
|
description = "Lambda expressions that capture values are translated into in-line anonymous JavaScript functions"
|
||||||
|
)
|
||||||
|
var irGenerateInlineAnonymousFunctions: Boolean by FreezableVar(false)
|
||||||
|
|
||||||
@Argument(
|
@Argument(
|
||||||
value = "-Xinclude",
|
value = "-Xinclude",
|
||||||
valueDescription = "<path>",
|
valueDescription = "<path>",
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
|||||||
configurationJs.put(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE, arguments.allowKotlinPackage)
|
configurationJs.put(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE, arguments.allowKotlinPackage)
|
||||||
configurationJs.put(CLIConfigurationKeys.RENDER_DIAGNOSTIC_INTERNAL_NAME, arguments.renderInternalDiagnosticNames)
|
configurationJs.put(CLIConfigurationKeys.RENDER_DIAGNOSTIC_INTERNAL_NAME, arguments.renderInternalDiagnosticNames)
|
||||||
configurationJs.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, arguments.irPropertyLazyInitialization)
|
configurationJs.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, arguments.irPropertyLazyInitialization)
|
||||||
|
configurationJs.put(JSConfigurationKeys.GENERATE_INLINE_ANONYMOUS_FUNCTIONS, arguments.irGenerateInlineAnonymousFunctions)
|
||||||
|
|
||||||
if (!checkKotlinPackageUsage(environmentForJS.configuration, sourcesFiles)) return ExitCode.COMPILATION_ERROR
|
if (!checkKotlinPackageUsage(environmentForJS.configuration, sourcesFiles)) return ExitCode.COMPILATION_ERROR
|
||||||
|
|
||||||
|
|||||||
+8
-2
@@ -28,12 +28,16 @@ import org.jetbrains.kotlin.ir.types.classifierOrNull
|
|||||||
import org.jetbrains.kotlin.ir.types.isUnit
|
import org.jetbrains.kotlin.ir.types.isUnit
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.visitors.*
|
import org.jetbrains.kotlin.ir.visitors.*
|
||||||
|
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
class InteropCallableReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass {
|
class InteropCallableReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass {
|
||||||
|
|
||||||
|
val generateInlineAnonymousFunctions: Boolean
|
||||||
|
get() = context.configuration.getBoolean(JSConfigurationKeys.GENERATE_INLINE_ANONYMOUS_FUNCTIONS)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A factory that creates [IrFunctionExpression] for lambdas being constructed.
|
* A factory that creates [IrFunctionExpression] for lambdas being constructed.
|
||||||
*
|
*
|
||||||
@@ -121,7 +125,8 @@ class InteropCallableReferenceLowering(val context: JsIrBackendContext) : BodyLo
|
|||||||
|
|
||||||
val closureUsageAnalyser = ClosureUsageAnalyser()
|
val closureUsageAnalyser = ClosureUsageAnalyser()
|
||||||
|
|
||||||
irFile.acceptChildrenVoid(closureUsageAnalyser) // TODO: Only do this if lambda inlining is enabled with a feature flag!
|
if (generateInlineAnonymousFunctions)
|
||||||
|
irFile.acceptChildrenVoid(closureUsageAnalyser)
|
||||||
|
|
||||||
val callableReferenceClassTransformer = CallableReferenceClassTransformer(
|
val callableReferenceClassTransformer = CallableReferenceClassTransformer(
|
||||||
ctorToFactoryMap,
|
ctorToFactoryMap,
|
||||||
@@ -317,8 +322,9 @@ class InteropCallableReferenceLowering(val context: JsIrBackendContext) : BodyLo
|
|||||||
// This allows us to avoid allocating a new object each time the lambda is created.
|
// This allows us to avoid allocating a new object each time the lambda is created.
|
||||||
liftLambda(ctorToFreeFunctionMap, lambdaInfo)
|
liftLambda(ctorToFreeFunctionMap, lambdaInfo)
|
||||||
} else if (
|
} else if (
|
||||||
|
generateInlineAnonymousFunctions
|
||||||
// If the lambda constructor is called from more than one place, don't inline.
|
// If the lambda constructor is called from more than one place, don't inline.
|
||||||
closureUsageAnalyser.getLambdaConstructorCalls(lambdaClass.primaryConstructor!!.symbol).size == 1
|
&& closureUsageAnalyser.getLambdaConstructorCalls(lambdaClass.primaryConstructor!!.symbol).size == 1
|
||||||
// In-line anonymous functions that capture variables declared in loops are dangerous.
|
// In-line anonymous functions that capture variables declared in loops are dangerous.
|
||||||
// See https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example
|
// See https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example
|
||||||
&& !closureUsageAnalyser.lambdaCapturesVariablesDeclaredInLoops(lambdaClass)
|
&& !closureUsageAnalyser.lambdaCapturesVariablesDeclaredInLoops(lambdaClass)
|
||||||
|
|||||||
+2
@@ -17,6 +17,8 @@ where advanced options include:
|
|||||||
Print declarations' reachability info to stdout during performing DCE
|
Print declarations' reachability info to stdout during performing DCE
|
||||||
-Xir-dce-runtime-diagnostic={log|exception}
|
-Xir-dce-runtime-diagnostic={log|exception}
|
||||||
Enable runtime diagnostics when performing DCE instead of removing declarations
|
Enable runtime diagnostics when performing DCE instead of removing declarations
|
||||||
|
-Xir-generate-inline-anonymous-functions
|
||||||
|
Lambda expressions that capture values are translated into in-line anonymous JavaScript functions
|
||||||
-Xir-minimized-member-names Perform minimization for names of members
|
-Xir-minimized-member-names Perform minimization for names of members
|
||||||
-Xir-module-name=<name> Specify a compilation module name for IR backend
|
-Xir-module-name=<name> Specify a compilation module name for IR backend
|
||||||
-Xir-new-ir2js New fragment-based ir2js
|
-Xir-new-ir2js New fragment-based ir2js
|
||||||
|
|||||||
+5
@@ -112,6 +112,11 @@ object JsEnvironmentConfigurationDirectives : SimpleDirectivesContainer() {
|
|||||||
applicability = DirectiveApplicability.Global
|
applicability = DirectiveApplicability.Global
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val GENERATE_INLINE_ANONYMOUS_FUNCTIONS by directive(
|
||||||
|
description = "translate lambdas into in-line anonymous functions",
|
||||||
|
applicability = DirectiveApplicability.Global
|
||||||
|
)
|
||||||
|
|
||||||
val SAFE_EXTERNAL_BOOLEAN by directive(
|
val SAFE_EXTERNAL_BOOLEAN by directive(
|
||||||
description = "",
|
description = "",
|
||||||
applicability = DirectiveApplicability.Global
|
applicability = DirectiveApplicability.Global
|
||||||
|
|||||||
+4
@@ -120,6 +120,10 @@ fun createCompilerConfiguration(module: TestModule, configurators: List<Abstract
|
|||||||
configuration.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, true)
|
configuration.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (JsEnvironmentConfigurationDirectives.GENERATE_INLINE_ANONYMOUS_FUNCTIONS in module.directives) {
|
||||||
|
configuration.put(JSConfigurationKeys.GENERATE_INLINE_ANONYMOUS_FUNCTIONS, true)
|
||||||
|
}
|
||||||
|
|
||||||
if (module.frontendKind == FrontendKinds.FIR) {
|
if (module.frontendKind == FrontendKinds.FIR) {
|
||||||
configuration[CommonConfigurationKeys.USE_FIR] = true
|
configuration[CommonConfigurationKeys.USE_FIR] = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,9 @@ public class JSConfigurationKeys {
|
|||||||
public static final CompilerConfigurationKey<Boolean> PROPERTY_LAZY_INITIALIZATION =
|
public static final CompilerConfigurationKey<Boolean> PROPERTY_LAZY_INITIALIZATION =
|
||||||
CompilerConfigurationKey.create("perform lazy initialization for properties");
|
CompilerConfigurationKey.create("perform lazy initialization for properties");
|
||||||
|
|
||||||
|
public static final CompilerConfigurationKey<Boolean> GENERATE_INLINE_ANONYMOUS_FUNCTIONS =
|
||||||
|
CompilerConfigurationKey.create("translate lambdas into in-line anonymous functions");
|
||||||
|
|
||||||
public static final CompilerConfigurationKey<Boolean> WASM_ENABLE_ARRAY_RANGE_CHECKS =
|
public static final CompilerConfigurationKey<Boolean> WASM_ENABLE_ARRAY_RANGE_CHECKS =
|
||||||
CompilerConfigurationKey.create("enable array range checks");
|
CompilerConfigurationKey.create("enable array range checks");
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ fun main(args: Array<String>) {
|
|||||||
generateTestGroupSuiteWithJUnit5(args) {
|
generateTestGroupSuiteWithJUnit5(args) {
|
||||||
testGroup("js/js.tests/tests-gen", "js/js.translator/testData", testRunnerMethodName = "runTest0") {
|
testGroup("js/js.tests/tests-gen", "js/js.translator/testData", testRunnerMethodName = "runTest0") {
|
||||||
testClass<AbstractBoxJsTest> {
|
testClass<AbstractBoxJsTest> {
|
||||||
model("box/", pattern = "^([^_](.+))\\.kt$")
|
model("box/", pattern = "^([^_](.+))\\.kt$", excludeDirs = listOf("closure/inlineAnonymousFunctions"))
|
||||||
}
|
}
|
||||||
|
|
||||||
testClass<AbstractSourceMapGenerationSmokeTest> {
|
testClass<AbstractSourceMapGenerationSmokeTest> {
|
||||||
|
|||||||
@@ -65,6 +65,12 @@ abstract class AbstractJsIrTest(
|
|||||||
::JsIrRecompiledArtifactsIdentityHandler,
|
::JsIrRecompiledArtifactsIdentityHandler,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
forTestsMatching("${JsEnvironmentConfigurator.TEST_DATA_DIR_PATH}/box/closure/inlineAnonymousFunctions/*") {
|
||||||
|
defaultDirectives {
|
||||||
|
+JsEnvironmentConfigurationDirectives.GENERATE_INLINE_ANONYMOUS_FUNCTIONS
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-8
@@ -22,7 +22,7 @@ import java.util.regex.Pattern;
|
|||||||
public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||||
@Test
|
@Test
|
||||||
public void testAllFilesPresentInBox() throws Exception {
|
public void testAllFilesPresentInBox() throws Exception {
|
||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS, true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS, true, "closure/inlineAnonymousFunctions");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
@@ -347,7 +347,7 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
|||||||
public class Closure {
|
public class Closure {
|
||||||
@Test
|
@Test
|
||||||
public void testAllFilesPresentInClosure() throws Exception {
|
public void testAllFilesPresentInClosure() throws Exception {
|
||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/closure"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS, true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/closure"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS, true, "inlineAnonymousFunctions");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -356,12 +356,6 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
|||||||
runTest("js/js.translator/testData/box/closure/closureArrayListInstance.kt");
|
runTest("js/js.translator/testData/box/closure/closureArrayListInstance.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@TestMetadata("closureCodeSize.kt")
|
|
||||||
public void testClosureCodeSize() throws Exception {
|
|
||||||
runTest("js/js.translator/testData/box/closure/closureCodeSize.kt");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("closureFunctionAsArgument.kt")
|
@TestMetadata("closureFunctionAsArgument.kt")
|
||||||
public void testClosureFunctionAsArgument() throws Exception {
|
public void testClosureFunctionAsArgument() throws Exception {
|
||||||
|
|||||||
+52
-6
@@ -356,12 +356,6 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
|||||||
runTest("js/js.translator/testData/box/closure/closureArrayListInstance.kt");
|
runTest("js/js.translator/testData/box/closure/closureArrayListInstance.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@TestMetadata("closureCodeSize.kt")
|
|
||||||
public void testClosureCodeSize() throws Exception {
|
|
||||||
runTest("js/js.translator/testData/box/closure/closureCodeSize.kt");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("closureFunctionAsArgument.kt")
|
@TestMetadata("closureFunctionAsArgument.kt")
|
||||||
public void testClosureFunctionAsArgument() throws Exception {
|
public void testClosureFunctionAsArgument() throws Exception {
|
||||||
@@ -631,6 +625,58 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
|||||||
public void testWrappedVariableInExtensionFun() throws Exception {
|
public void testWrappedVariableInExtensionFun() throws Exception {
|
||||||
runTest("js/js.translator/testData/box/closure/wrappedVariableInExtensionFun.kt");
|
runTest("js/js.translator/testData/box/closure/wrappedVariableInExtensionFun.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@TestMetadata("js/js.translator/testData/box/closure/inlineAnonymousFunctions")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
public class InlineAnonymousFunctions {
|
||||||
|
@Test
|
||||||
|
public void testAllFilesPresentInInlineAnonymousFunctions() throws Exception {
|
||||||
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/closure/inlineAnonymousFunctions"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("closureCodeSize.kt")
|
||||||
|
public void testClosureCodeSize() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/closure/inlineAnonymousFunctions/closureCodeSize.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("closureInWithInsideWith.kt")
|
||||||
|
public void testClosureInWithInsideWith() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/closure/inlineAnonymousFunctions/closureInWithInsideWith.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("inlineChain.kt")
|
||||||
|
public void testInlineChain() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/closure/inlineAnonymousFunctions/inlineChain.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("lambdaChain.kt")
|
||||||
|
public void testLambdaChain() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/closure/inlineAnonymousFunctions/lambdaChain.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("lambdaParameters.kt")
|
||||||
|
public void testLambdaParameters() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/closure/inlineAnonymousFunctions/lambdaParameters.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("localParameterInCallback.kt")
|
||||||
|
public void testLocalParameterInCallback() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/closure/inlineAnonymousFunctions/localParameterInCallback.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("twiceRegeneratedAnonymousObject.kt")
|
||||||
|
public void testTwiceRegeneratedAnonymousObject() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/closure/inlineAnonymousFunctions/twiceRegeneratedAnonymousObject.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
|
|||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
// KT-4237 With in with
|
||||||
|
|
||||||
|
package foo
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val ok = "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
class B
|
||||||
|
|
||||||
|
fun <T> with(o: T, body: T.() -> Unit) {
|
||||||
|
o.body()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var o = ""
|
||||||
|
|
||||||
|
with(A()) {
|
||||||
|
with(B()) {
|
||||||
|
o = ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
// FILE: 1.kt
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
interface A {
|
||||||
|
fun run()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun testNested(crossinline f: (String) -> Unit) {
|
||||||
|
object : A {
|
||||||
|
override fun run() {
|
||||||
|
f("OK")
|
||||||
|
}
|
||||||
|
}.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun test(crossinline f: (String) -> Unit) {
|
||||||
|
testNested { it -> { f(it) }()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: 2.kt
|
||||||
|
|
||||||
|
import test.*
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var result = "fail"
|
||||||
|
test { it -> result = it }
|
||||||
|
return result
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
// FILE: 1.kt
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
inline fun <T> inlineFun(arg: T, f: (T) -> Unit) {
|
||||||
|
f(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: 2.kt
|
||||||
|
|
||||||
|
import test.*
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val param = "start"
|
||||||
|
var result = "fail"
|
||||||
|
inlineFun("1") { c ->
|
||||||
|
{
|
||||||
|
inlineFun("2") { a ->
|
||||||
|
{
|
||||||
|
{
|
||||||
|
result = param + c + a
|
||||||
|
}()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (result == "start12") "OK" else "fail: $result"
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
fun <T> bar(x: T, y: (T) -> Boolean): Boolean = y(x) && jsTypeOf(x.asDynamic()) != "number"
|
||||||
|
|
||||||
|
fun typeOf(x: dynamic) = js("typeof x")
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val f = { x: Char ->
|
||||||
|
val a: Char = x
|
||||||
|
val b: Any = x
|
||||||
|
typeOf(a) == "number" && typeOf(b) == "object"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!f('Q')) return "fail1"
|
||||||
|
if (!bar('W', f)) return "fail2"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
// KJS_WITH_FULL_RUNTIME
|
||||||
|
package foo
|
||||||
|
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val oneTwo = Array(2) {
|
||||||
|
it + 1
|
||||||
|
}
|
||||||
|
val a = ArrayList<() -> Int>()
|
||||||
|
for (i in oneTwo) {
|
||||||
|
for (l in 1..2) {
|
||||||
|
val j = l
|
||||||
|
a.add({
|
||||||
|
var res = 0
|
||||||
|
for (t in 0..2) {
|
||||||
|
res += i * j
|
||||||
|
}
|
||||||
|
res
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var sum = 0
|
||||||
|
for (f in a) {
|
||||||
|
sum += f()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sum != 27) return "fail: $sum"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
Vendored
+26
@@ -0,0 +1,26 @@
|
|||||||
|
// WITH_STDLIB
|
||||||
|
// WITH_COROUTINES
|
||||||
|
// FILE: 1.kt
|
||||||
|
package test
|
||||||
|
|
||||||
|
inline fun foo(crossinline x: () -> Unit) = suspend {
|
||||||
|
try { } finally {
|
||||||
|
// This object is regenerated twice (normal return & "catch Throwable, execute finally, and rethrow")
|
||||||
|
// It doesn't *need* to be, but this should work regardless.
|
||||||
|
{ x() }()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: 2.kt
|
||||||
|
import helpers.*
|
||||||
|
import kotlin.coroutines.*
|
||||||
|
import kotlin.coroutines.intrinsics.*
|
||||||
|
import test.*
|
||||||
|
|
||||||
|
var result = "fail"
|
||||||
|
fun box(): String {
|
||||||
|
suspend {
|
||||||
|
foo { result = "OK" }()
|
||||||
|
}.startCoroutine(EmptyContinuation)
|
||||||
|
return result
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user