Provide configurable constructor call normalization
Three modes: - 'disable' (default): normalize constructor calls in coroutines only (required because uninitialized objects can't be stored in fields), don't insert additional code for forced class initialization; - 'enable': normalize constructor calls, don't insert additional code for forced class initialization; - 'preserve-class-initialization': normalize constructor calls, insert additional code for forced class initialization.
This commit is contained in:
@@ -137,7 +137,8 @@ public class FunctionCodegen {
|
|||||||
state,
|
state,
|
||||||
CoroutineCodegenUtilKt.<FunctionDescriptor>unwrapInitialDescriptorForSuspendFunction(functionDescriptor),
|
CoroutineCodegenUtilKt.<FunctionDescriptor>unwrapInitialDescriptorForSuspendFunction(functionDescriptor),
|
||||||
function,
|
function,
|
||||||
v.getThisName()
|
v.getThisName(),
|
||||||
|
state.getConstructorCallNormalizationMode()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -145,6 +145,8 @@ class CoroutineCodegenForLambda private constructor(
|
|||||||
) {
|
) {
|
||||||
private val builtIns = funDescriptor.builtIns
|
private val builtIns = funDescriptor.builtIns
|
||||||
|
|
||||||
|
private val constructorCallNormalizationMode = outerExpressionCodegen.state.constructorCallNormalizationMode
|
||||||
|
|
||||||
private lateinit var constructorToUseFromInvoke: Method
|
private lateinit var constructorToUseFromInvoke: Method
|
||||||
|
|
||||||
private val createCoroutineDescriptor =
|
private val createCoroutineDescriptor =
|
||||||
@@ -313,6 +315,7 @@ class CoroutineCodegenForLambda private constructor(
|
|||||||
mv, access, name, desc, null, null,
|
mv, access, name, desc, null, null,
|
||||||
obtainClassBuilderForCoroutineState = { v },
|
obtainClassBuilderForCoroutineState = { v },
|
||||||
element = element,
|
element = element,
|
||||||
|
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||||
containingClassInternalName = v.thisName,
|
containingClassInternalName = v.thisName,
|
||||||
isForNamedFunction = false
|
isForNamedFunction = false
|
||||||
)
|
)
|
||||||
|
|||||||
+2
-1
@@ -54,6 +54,7 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
private val containingClassInternalName: String,
|
private val containingClassInternalName: String,
|
||||||
obtainClassBuilderForCoroutineState: () -> ClassBuilder,
|
obtainClassBuilderForCoroutineState: () -> ClassBuilder,
|
||||||
private val isForNamedFunction: Boolean,
|
private val isForNamedFunction: Boolean,
|
||||||
|
private val shouldPreserveClassInitialization: Boolean,
|
||||||
private val element: KtElement,
|
private val element: KtElement,
|
||||||
// It's only matters for named functions, may differ from '!isStatic(access)' in case of DefaultImpls
|
// It's only matters for named functions, may differ from '!isStatic(access)' in case of DefaultImpls
|
||||||
private val needDispatchReceiver: Boolean = false,
|
private val needDispatchReceiver: Boolean = false,
|
||||||
@@ -99,7 +100,7 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
// If we don't do this, then relevant frames will not be analyzed, that is unexpected from point of view of next steps (e.g. variable spilling)
|
// If we don't do this, then relevant frames will not be analyzed, that is unexpected from point of view of next steps (e.g. variable spilling)
|
||||||
removeUnreachableSuspensionPointsAndExitPoints(methodNode, suspensionPoints)
|
removeUnreachableSuspensionPointsAndExitPoints(methodNode, suspensionPoints)
|
||||||
|
|
||||||
processUninitializedStores(methodNode)
|
UninitializedStoresProcessor(methodNode, shouldPreserveClassInitialization).run()
|
||||||
|
|
||||||
spillVariables(suspensionPoints, methodNode)
|
spillVariables(suspensionPoints, methodNode)
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
|||||||
import org.jetbrains.kotlin.codegen.FunctionGenerationStrategy
|
import org.jetbrains.kotlin.codegen.FunctionGenerationStrategy
|
||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
|
import org.jetbrains.kotlin.config.JVMConstructorCallNormalizationMode
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtFunction
|
import org.jetbrains.kotlin.psi.KtFunction
|
||||||
@@ -34,7 +35,8 @@ class SuspendFunctionGenerationStrategy(
|
|||||||
state: GenerationState,
|
state: GenerationState,
|
||||||
private val originalSuspendDescriptor: FunctionDescriptor,
|
private val originalSuspendDescriptor: FunctionDescriptor,
|
||||||
private val declaration: KtFunction,
|
private val declaration: KtFunction,
|
||||||
private val containingClassInternalName: String
|
private val containingClassInternalName: String,
|
||||||
|
private val constructorCallNormalizationMode: JVMConstructorCallNormalizationMode
|
||||||
) : FunctionGenerationStrategy.CodegenBased(state) {
|
) : FunctionGenerationStrategy.CodegenBased(state) {
|
||||||
|
|
||||||
private lateinit var transformer: CoroutineTransformerMethodVisitor
|
private lateinit var transformer: CoroutineTransformerMethodVisitor
|
||||||
@@ -59,6 +61,7 @@ class SuspendFunctionGenerationStrategy(
|
|||||||
mv, access, name, desc, null, null, containingClassInternalName, this::classBuilderForCoroutineState,
|
mv, access, name, desc, null, null, containingClassInternalName, this::classBuilderForCoroutineState,
|
||||||
isForNamedFunction = true,
|
isForNamedFunction = true,
|
||||||
element = declaration,
|
element = declaration,
|
||||||
|
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||||
needDispatchReceiver = originalSuspendDescriptor.dispatchReceiverParameter != null,
|
needDispatchReceiver = originalSuspendDescriptor.dispatchReceiverParameter != null,
|
||||||
internalNameForDispatchReceiver = containingClassInternalNameOrNull()
|
internalNameForDispatchReceiver = containingClassInternalNameOrNull()
|
||||||
).also {
|
).also {
|
||||||
|
|||||||
+19
-20
@@ -71,11 +71,10 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter
|
|||||||
* - generate NEW/DUP
|
* - generate NEW/DUP
|
||||||
* - restore constructor arguments
|
* - restore constructor arguments
|
||||||
*/
|
*/
|
||||||
internal fun processUninitializedStores(methodNode: MethodNode) {
|
class UninitializedStoresProcessor(
|
||||||
UninitializedStoresProcessor(methodNode).run()
|
private val methodNode: MethodNode,
|
||||||
}
|
private val shouldPreserveClassInitialization: Boolean
|
||||||
|
) {
|
||||||
class UninitializedStoresProcessor(private val methodNode: MethodNode) {
|
|
||||||
// <init> method is "special", because it will invoke <init> from this class or from a base class for #0
|
// <init> method is "special", because it will invoke <init> from this class or from a base class for #0
|
||||||
//
|
//
|
||||||
// <clinit> method is "special", because <clinit> for singleton objects is generated as:
|
// <clinit> method is "special", because <clinit> for singleton objects is generated as:
|
||||||
@@ -108,21 +107,21 @@ class UninitializedStoresProcessor(private val methodNode: MethodNode) {
|
|||||||
methodNode.instructions.run {
|
methodNode.instructions.run {
|
||||||
removeAll(removableUsages)
|
removeAll(removableUsages)
|
||||||
|
|
||||||
// Replace 'NEW C' instruction with "manual" initialization of class 'C':
|
if (shouldPreserveClassInitialization) {
|
||||||
// LDC [typeName for C]
|
// Replace 'NEW C' instruction with "manual" initialization of class 'C':
|
||||||
// INVOKESTATIC java/lang/Class.forName (Ljava/lang/String;)Ljava/lang/Class;
|
// LDC [typeName for C]
|
||||||
// POP
|
// INVOKESTATIC java/lang/Class.forName (Ljava/lang/String;)Ljava/lang/Class;
|
||||||
val typeNameForClass = newInsn.desc.replace('/', '.')
|
// POP
|
||||||
insertBefore(newInsn, LdcInsnNode(typeNameForClass))
|
val typeNameForClass = newInsn.desc.replace('/', '.')
|
||||||
insertBefore(
|
insertBefore(newInsn, LdcInsnNode(typeNameForClass))
|
||||||
newInsn,
|
insertBefore(newInsn, MethodInsnNode(
|
||||||
MethodInsnNode(
|
Opcodes.INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false
|
||||||
Opcodes.INVOKESTATIC,
|
))
|
||||||
"java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;",
|
set(newInsn, InsnNode(Opcodes.POP))
|
||||||
false
|
}
|
||||||
)
|
else {
|
||||||
)
|
remove(newInsn)
|
||||||
set(newInsn, InsnNode(Opcodes.POP))
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val indexOfConstructorArgumentFromTopOfStack = Type.getArgumentTypes((insn as MethodInsnNode).desc).size
|
val indexOfConstructorArgumentFromTopOfStack = Type.getArgumentTypes((insn as MethodInsnNode).desc).size
|
||||||
|
|||||||
+9
-2
@@ -20,16 +20,23 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilder;
|
import org.jetbrains.kotlin.codegen.ClassBuilder;
|
||||||
import org.jetbrains.kotlin.codegen.DelegatingClassBuilder;
|
import org.jetbrains.kotlin.codegen.DelegatingClassBuilder;
|
||||||
|
import org.jetbrains.kotlin.config.JVMConstructorCallNormalizationMode;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
||||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
public class OptimizationClassBuilder extends DelegatingClassBuilder {
|
public class OptimizationClassBuilder extends DelegatingClassBuilder {
|
||||||
private final ClassBuilder delegate;
|
private final ClassBuilder delegate;
|
||||||
private final boolean disableOptimization;
|
private final boolean disableOptimization;
|
||||||
|
private final JVMConstructorCallNormalizationMode constructorCallNormalizationMode;
|
||||||
|
|
||||||
public OptimizationClassBuilder(@NotNull ClassBuilder delegate, boolean disableOptimization) {
|
public OptimizationClassBuilder(
|
||||||
|
@NotNull ClassBuilder delegate,
|
||||||
|
boolean disableOptimization,
|
||||||
|
JVMConstructorCallNormalizationMode constructorCallNormalizationMode
|
||||||
|
) {
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.disableOptimization = disableOptimization;
|
this.disableOptimization = disableOptimization;
|
||||||
|
this.constructorCallNormalizationMode = constructorCallNormalizationMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -50,7 +57,7 @@ public class OptimizationClassBuilder extends DelegatingClassBuilder {
|
|||||||
) {
|
) {
|
||||||
return new OptimizationMethodVisitor(
|
return new OptimizationMethodVisitor(
|
||||||
super.newMethod(origin, access, name, desc, signature, exceptions),
|
super.newMethod(origin, access, name, desc, signature, exceptions),
|
||||||
disableOptimization,
|
disableOptimization, constructorCallNormalizationMode,
|
||||||
access, name, desc, signature, exceptions
|
access, name, desc, signature, exceptions
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-2
@@ -19,19 +19,28 @@ package org.jetbrains.kotlin.codegen.optimization;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactory;
|
import org.jetbrains.kotlin.codegen.ClassBuilderFactory;
|
||||||
import org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory;
|
import org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory;
|
||||||
|
import org.jetbrains.kotlin.config.JVMConstructorCallNormalizationMode;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
||||||
|
|
||||||
public class OptimizationClassBuilderFactory extends DelegatingClassBuilderFactory {
|
public class OptimizationClassBuilderFactory extends DelegatingClassBuilderFactory {
|
||||||
private final boolean disableOptimization;
|
private final boolean disableOptimization;
|
||||||
|
private final JVMConstructorCallNormalizationMode constructorCallNormalizationMode;
|
||||||
|
|
||||||
public OptimizationClassBuilderFactory(ClassBuilderFactory delegate, boolean disableOptimization) {
|
public OptimizationClassBuilderFactory(
|
||||||
|
ClassBuilderFactory delegate,
|
||||||
|
boolean disableOptimization,
|
||||||
|
JVMConstructorCallNormalizationMode constructorCallNormalizationMode
|
||||||
|
) {
|
||||||
super(delegate);
|
super(delegate);
|
||||||
this.disableOptimization = disableOptimization;
|
this.disableOptimization = disableOptimization;
|
||||||
|
this.constructorCallNormalizationMode = constructorCallNormalizationMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public OptimizationClassBuilder newClassBuilder(@NotNull JvmDeclarationOrigin origin) {
|
public OptimizationClassBuilder newClassBuilder(@NotNull JvmDeclarationOrigin origin) {
|
||||||
return new OptimizationClassBuilder(getDelegate().newClassBuilder(origin), disableOptimization);
|
return new OptimizationClassBuilder(
|
||||||
|
getDelegate().newClassBuilder(origin), disableOptimization, constructorCallNormalizationMode
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-1
@@ -23,23 +23,31 @@ import org.jetbrains.kotlin.codegen.optimization.boxing.StackPeepholeOptimizatio
|
|||||||
import org.jetbrains.kotlin.codegen.optimization.common.prepareForEmitting
|
import org.jetbrains.kotlin.codegen.optimization.common.prepareForEmitting
|
||||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.RedundantNullCheckMethodTransformer
|
import org.jetbrains.kotlin.codegen.optimization.nullCheck.RedundantNullCheckMethodTransformer
|
||||||
import org.jetbrains.kotlin.codegen.optimization.transformer.CompositeMethodTransformer
|
import org.jetbrains.kotlin.codegen.optimization.transformer.CompositeMethodTransformer
|
||||||
|
import org.jetbrains.kotlin.config.JVMConstructorCallNormalizationMode
|
||||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
|
||||||
class OptimizationMethodVisitor(
|
class OptimizationMethodVisitor(
|
||||||
delegate: MethodVisitor,
|
delegate: MethodVisitor,
|
||||||
private val disableOptimization: Boolean,
|
private val disableOptimization: Boolean,
|
||||||
|
private val constructorCallNormalizationMode: JVMConstructorCallNormalizationMode,
|
||||||
access: Int,
|
access: Int,
|
||||||
name: String,
|
name: String,
|
||||||
desc: String,
|
desc: String,
|
||||||
signature: String?,
|
signature: String?,
|
||||||
exceptions: Array<String>?
|
exceptions: Array<String>?
|
||||||
) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) {
|
) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) {
|
||||||
|
private val constructorCallNormalizationTransformer =
|
||||||
|
UninitializedStoresMethodTransformer(constructorCallNormalizationMode)
|
||||||
|
|
||||||
override fun performTransformations(methodNode: MethodNode) {
|
override fun performTransformations(methodNode: MethodNode) {
|
||||||
normalizationMethodTransformer.transform("fake", methodNode)
|
normalizationMethodTransformer.transform("fake", methodNode)
|
||||||
|
constructorCallNormalizationTransformer.transform("fake", methodNode)
|
||||||
|
|
||||||
if (canBeOptimized(methodNode) && !disableOptimization) {
|
if (canBeOptimized(methodNode) && !disableOptimization) {
|
||||||
optimizationTransformer.transform("fake", methodNode)
|
optimizationTransformer.transform("fake", methodNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
methodNode.prepareForEmitting()
|
methodNode.prepareForEmitting()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +56,6 @@ class OptimizationMethodVisitor(
|
|||||||
|
|
||||||
val normalizationMethodTransformer = CompositeMethodTransformer(
|
val normalizationMethodTransformer = CompositeMethodTransformer(
|
||||||
FixStackWithLabelNormalizationMethodTransformer(),
|
FixStackWithLabelNormalizationMethodTransformer(),
|
||||||
UninitializedStoresMethodTransformer(),
|
|
||||||
MethodVerifier("AFTER mandatory stack transformations")
|
MethodVerifier("AFTER mandatory stack transformations")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+9
-2
@@ -18,10 +18,17 @@ package org.jetbrains.kotlin.codegen.optimization
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.UninitializedStoresProcessor
|
import org.jetbrains.kotlin.codegen.coroutines.UninitializedStoresProcessor
|
||||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||||
|
import org.jetbrains.kotlin.config.JVMConstructorCallNormalizationMode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
|
||||||
class UninitializedStoresMethodTransformer : MethodTransformer() {
|
class UninitializedStoresMethodTransformer(
|
||||||
|
private val mode: JVMConstructorCallNormalizationMode
|
||||||
|
) : MethodTransformer() {
|
||||||
|
|
||||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||||
UninitializedStoresProcessor(methodNode).run()
|
if (mode.isEnabled) {
|
||||||
|
UninitializedStoresProcessor(methodNode, mode.shouldPreserveClassInitialization).run()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -181,10 +181,15 @@ class GenerationState @JvmOverloads constructor(
|
|||||||
|
|
||||||
val shouldInlineConstVals = languageVersionSettings.supportsFeature(LanguageFeature.InlineConstVals)
|
val shouldInlineConstVals = languageVersionSettings.supportsFeature(LanguageFeature.InlineConstVals)
|
||||||
|
|
||||||
|
val constructorCallNormalizationMode = configuration.get(JVMConfigurationKeys.CONSTRUCTOR_CALL_NORMALIZATION_MODE,
|
||||||
|
JVMConstructorCallNormalizationMode.DEFAULT)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
val disableOptimization = configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false)
|
||||||
|
|
||||||
this.interceptedBuilderFactory = builderFactory
|
this.interceptedBuilderFactory = builderFactory
|
||||||
.wrapWith(
|
.wrapWith(
|
||||||
{ OptimizationClassBuilderFactory(it, configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false)) },
|
{ OptimizationClassBuilderFactory(it, disableOptimization, constructorCallNormalizationMode) },
|
||||||
{ BuilderFactoryForDuplicateSignatureDiagnostics(
|
{ BuilderFactoryForDuplicateSignatureDiagnostics(
|
||||||
it, this.bindingContext, diagnostics, this.moduleName,
|
it, this.bindingContext, diagnostics, this.moduleName,
|
||||||
shouldGenerate = { !shouldOnlyCollectSignatures(it) }
|
shouldGenerate = { !shouldOnlyCollectSignatures(it) }
|
||||||
|
|||||||
+11
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.cli.common.arguments
|
|||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
import org.jetbrains.kotlin.config.AnalysisFlag
|
import org.jetbrains.kotlin.config.AnalysisFlag
|
||||||
|
import org.jetbrains.kotlin.config.JVMConstructorCallNormalizationMode
|
||||||
import org.jetbrains.kotlin.config.JvmTarget
|
import org.jetbrains.kotlin.config.JvmTarget
|
||||||
import org.jetbrains.kotlin.utils.Jsr305State
|
import org.jetbrains.kotlin.utils.Jsr305State
|
||||||
import org.jetbrains.kotlin.utils.ReportLevel
|
import org.jetbrains.kotlin.utils.ReportLevel
|
||||||
@@ -108,6 +109,16 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
|||||||
@Argument(value = "-Xno-optimize", description = "Disable optimizations")
|
@Argument(value = "-Xno-optimize", description = "Disable optimizations")
|
||||||
var noOptimize: Boolean by FreezableVar(false)
|
var noOptimize: Boolean by FreezableVar(false)
|
||||||
|
|
||||||
|
@Argument(
|
||||||
|
value = "-Xnormalize-constructor-calls",
|
||||||
|
valueDescription = "{disable|enable|preserve-class-initialization}",
|
||||||
|
description = "Normalize constructor calls " +
|
||||||
|
"(disable: don't normalize; enable: normalize; " +
|
||||||
|
"preserve-class-initialization: normalize preserving class initialization order), " +
|
||||||
|
"default is disable"
|
||||||
|
)
|
||||||
|
var constructorCallNormalizationMode: String? by FreezableVar(JVMConstructorCallNormalizationMode.DEFAULT.description)
|
||||||
|
|
||||||
@Argument(value = "-Xreport-perf", description = "Report detailed performance statistics")
|
@Argument(value = "-Xreport-perf", description = "Report detailed performance statistics")
|
||||||
var reportPerf: Boolean by FreezableVar(false)
|
var reportPerf: Boolean by FreezableVar(false)
|
||||||
|
|
||||||
|
|||||||
@@ -349,6 +349,16 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
|||||||
configuration.put(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, arguments.noParamAssertions)
|
configuration.put(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, arguments.noParamAssertions)
|
||||||
configuration.put(JVMConfigurationKeys.NO_EXCEPTION_ON_EXPLICIT_EQUALS_FOR_BOXED_NULL, arguments.noExceptionOnExplicitEqualsForBoxedNull);
|
configuration.put(JVMConfigurationKeys.NO_EXCEPTION_ON_EXPLICIT_EQUALS_FOR_BOXED_NULL, arguments.noExceptionOnExplicitEqualsForBoxedNull);
|
||||||
configuration.put(JVMConfigurationKeys.DISABLE_OPTIMIZATION, arguments.noOptimize)
|
configuration.put(JVMConfigurationKeys.DISABLE_OPTIMIZATION, arguments.noOptimize)
|
||||||
|
|
||||||
|
val constructorCallNormalizationMode = JVMConstructorCallNormalizationMode.fromStringOrNull(arguments.constructorCallNormalizationMode)
|
||||||
|
if (constructorCallNormalizationMode == null) {
|
||||||
|
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||||
|
.report(ERROR, "Unknown constructor call normalization mode: ${arguments.constructorCallNormalizationMode}, " +
|
||||||
|
"supported modes: ${JVMConstructorCallNormalizationMode.values().map { it.description }}")
|
||||||
|
}
|
||||||
|
configuration.put(JVMConfigurationKeys.CONSTRUCTOR_CALL_NORMALIZATION_MODE,
|
||||||
|
constructorCallNormalizationMode ?: JVMConstructorCallNormalizationMode.DEFAULT)
|
||||||
|
|
||||||
configuration.put(JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS, arguments.inheritMultifileParts)
|
configuration.put(JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS, arguments.inheritMultifileParts)
|
||||||
configuration.put(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK, arguments.skipRuntimeVersionCheck)
|
configuration.put(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK, arguments.skipRuntimeVersionCheck)
|
||||||
configuration.put(JVMConfigurationKeys.USE_FAST_CLASS_FILES_READING, !arguments.useOldClassFilesReading)
|
configuration.put(JVMConfigurationKeys.USE_FAST_CLASS_FILES_READING, !arguments.useOldClassFilesReading)
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ public class JVMConfigurationKeys {
|
|||||||
CompilerConfigurationKey.create("disable not-null call receiver assertions");
|
CompilerConfigurationKey.create("disable not-null call receiver assertions");
|
||||||
public static final CompilerConfigurationKey<Boolean> DISABLE_PARAM_ASSERTIONS =
|
public static final CompilerConfigurationKey<Boolean> DISABLE_PARAM_ASSERTIONS =
|
||||||
CompilerConfigurationKey.create("disable not-null parameter assertions");
|
CompilerConfigurationKey.create("disable not-null parameter assertions");
|
||||||
|
public static final CompilerConfigurationKey<JVMConstructorCallNormalizationMode> CONSTRUCTOR_CALL_NORMALIZATION_MODE =
|
||||||
|
CompilerConfigurationKey.create("constructor call normalization mode");
|
||||||
public static final CompilerConfigurationKey<Boolean> NO_EXCEPTION_ON_EXPLICIT_EQUALS_FOR_BOXED_NULL =
|
public static final CompilerConfigurationKey<Boolean> NO_EXCEPTION_ON_EXPLICIT_EQUALS_FOR_BOXED_NULL =
|
||||||
CompilerConfigurationKey.create("do not throw NPE on explicit 'equals' call for null receiver of platform boxed primitive type");
|
CompilerConfigurationKey.create("do not throw NPE on explicit 'equals' call for null receiver of platform boxed primitive type");
|
||||||
public static final CompilerConfigurationKey<Boolean> DISABLE_OPTIMIZATION =
|
public static final CompilerConfigurationKey<Boolean> DISABLE_OPTIMIZATION =
|
||||||
|
|||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.config
|
||||||
|
|
||||||
|
enum class JVMConstructorCallNormalizationMode(
|
||||||
|
val description: String,
|
||||||
|
val isEnabled: Boolean,
|
||||||
|
val shouldPreserveClassInitialization: Boolean
|
||||||
|
) {
|
||||||
|
DISABLE("disable", false, false),
|
||||||
|
ENABLE("enable", true, false),
|
||||||
|
PRESERVE_CLASS_INITIALIZATION("preserve-class-initialization", true, true)
|
||||||
|
;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmField
|
||||||
|
val DEFAULT = DISABLE
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun fromStringOrNull(string: String?) = values().find { it.description == string }
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
@@ -5,6 +5,8 @@ where advanced options include:
|
|||||||
or all modules on the module path if <module> is ALL-MODULE-PATH
|
or all modules on the module path if <module> is ALL-MODULE-PATH
|
||||||
-Xbuild-file=<path> Path to the .xml build file to compile
|
-Xbuild-file=<path> Path to the .xml build file to compile
|
||||||
-Xcompile-java Reuse javac analysis and compile Java source files
|
-Xcompile-java Reuse javac analysis and compile Java source files
|
||||||
|
-Xnormalize-constructor-calls={disable|enable|preserve-class-initialization}
|
||||||
|
Normalize constructor calls (disable: don't normalize; enable: normalize; preserve-class-initialization: normalize preserving class initialization order), default is disable
|
||||||
-Xdump-declarations-to=<path> Path to JSON file to dump Java to Kotlin declaration mappings
|
-Xdump-declarations-to=<path> Path to JSON file to dump Java to Kotlin declaration mappings
|
||||||
-Xmultifile-parts-inherit Compile multifile classes as a hierarchy of parts and facade
|
-Xmultifile-parts-inherit Compile multifile classes as a hierarchy of parts and facade
|
||||||
-Xmodule-path=<path> Paths where to find Java 9+ modules
|
-Xmodule-path=<path> Paths where to find Java 9+ modules
|
||||||
|
|||||||
compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallWithDisabledNormalization.kt
Vendored
+41
@@ -0,0 +1,41 @@
|
|||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: CONSTRUCTOR_CALL_NORMALIZATION_MODE=disable
|
||||||
|
// FILE: test.kt
|
||||||
|
fun box(): String {
|
||||||
|
Foo(
|
||||||
|
logged("i", 1.let { it }),
|
||||||
|
logged("j",
|
||||||
|
Foo(
|
||||||
|
logged("k", 2.let { it }),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = log.toString()
|
||||||
|
if (result != "<clinit>ik<init>j<init>") return "Fail: '$result'"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: util.kt
|
||||||
|
val log = StringBuilder()
|
||||||
|
|
||||||
|
fun <T> logged(msg: String, value: T): T {
|
||||||
|
log.append(msg)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: Foo.kt
|
||||||
|
class Foo(i: Int, j: Foo?) {
|
||||||
|
init {
|
||||||
|
log.append("<init>")
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
init {
|
||||||
|
log.append("<clinit>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+41
@@ -0,0 +1,41 @@
|
|||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: CONSTRUCTOR_CALL_NORMALIZATION_MODE=enable
|
||||||
|
// FILE: test.kt
|
||||||
|
fun box(): String {
|
||||||
|
Foo(
|
||||||
|
logged("i", 1.let { it }),
|
||||||
|
logged("j",
|
||||||
|
Foo(
|
||||||
|
logged("k", 2.let { it }),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = log.toString()
|
||||||
|
if (result != "ik<clinit><init>j<init>") return "Fail: '$result'"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: util.kt
|
||||||
|
val log = StringBuilder()
|
||||||
|
|
||||||
|
fun <T> logged(msg: String, value: T): T {
|
||||||
|
log.append(msg)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: Foo.kt
|
||||||
|
class Foo(i: Int, j: Foo?) {
|
||||||
|
init {
|
||||||
|
log.append("<init>")
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
init {
|
||||||
|
log.append("<clinit>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+41
@@ -0,0 +1,41 @@
|
|||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
// WITH_RUNTIME
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: CONSTRUCTOR_CALL_NORMALIZATION_MODE=preserve-class-initialization
|
||||||
|
// FILE: test.kt
|
||||||
|
fun box(): String {
|
||||||
|
Foo(
|
||||||
|
logged("i", 1.let { it }),
|
||||||
|
logged("j",
|
||||||
|
Foo(
|
||||||
|
logged("k", 2.let { it }),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = log.toString()
|
||||||
|
if (result != "<clinit>ik<init>j<init>") return "Fail: '$result'"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: util.kt
|
||||||
|
val log = StringBuilder()
|
||||||
|
|
||||||
|
fun <T> logged(msg: String, value: T): T {
|
||||||
|
log.append(msg)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: Foo.kt
|
||||||
|
class Foo(i: Int, j: Foo?) {
|
||||||
|
init {
|
||||||
|
log.append("<init>")
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
init {
|
||||||
|
log.append("<clinit>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+1
-1
@@ -49,7 +49,7 @@ fun box(): String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logger.toString() != "A.<clinit>;args;A.<init>;") {
|
if (logger.toString() != "args;A.<clinit>;A.<init>;") {
|
||||||
return "Fail: '$logger'"
|
return "Fail: '$logger'"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys;
|
|||||||
import org.jetbrains.kotlin.cli.common.output.outputUtils.OutputUtilsKt;
|
import org.jetbrains.kotlin.cli.common.output.outputUtils.OutputUtilsKt;
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRootsKt;
|
|
||||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
||||||
import org.jetbrains.kotlin.config.*;
|
import org.jetbrains.kotlin.config.*;
|
||||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
|
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
|
||||||
@@ -200,6 +199,8 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
|
|||||||
private static final List<Class<?>> FLAG_CLASSES = ImmutableList.of(CLIConfigurationKeys.class, JVMConfigurationKeys.class);
|
private static final List<Class<?>> FLAG_CLASSES = ImmutableList.of(CLIConfigurationKeys.class, JVMConfigurationKeys.class);
|
||||||
|
|
||||||
private static final Pattern BOOLEAN_FLAG_PATTERN = Pattern.compile("([+-])(([a-zA-Z_0-9]*)\\.)?([a-zA-Z_0-9]*)");
|
private static final Pattern BOOLEAN_FLAG_PATTERN = Pattern.compile("([+-])(([a-zA-Z_0-9]*)\\.)?([a-zA-Z_0-9]*)");
|
||||||
|
private static final Pattern CONSTRUCTOR_CALL_NORMALIZATION_MODE_FLAG_PATTERN = Pattern.compile(
|
||||||
|
"CONSTRUCTOR_CALL_NORMALIZATION_MODE=([a-zA-Z_0-9]*)");
|
||||||
|
|
||||||
private static void updateConfigurationWithFlags(@NotNull CompilerConfiguration configuration, @NotNull List<String> flags) {
|
private static void updateConfigurationWithFlags(@NotNull CompilerConfiguration configuration, @NotNull List<String> flags) {
|
||||||
for (String flag : flags) {
|
for (String flag : flags) {
|
||||||
@@ -210,6 +211,15 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
|
|||||||
String flagName = m.group(4);
|
String flagName = m.group(4);
|
||||||
|
|
||||||
tryApplyBooleanFlag(configuration, flag, flagEnabled, flagNamespace, flagName);
|
tryApplyBooleanFlag(configuration, flag, flagEnabled, flagNamespace, flagName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m = CONSTRUCTOR_CALL_NORMALIZATION_MODE_FLAG_PATTERN.matcher(flag);
|
||||||
|
if (m.matches()) {
|
||||||
|
String flagValueString = m.group(1);
|
||||||
|
JVMConstructorCallNormalizationMode mode = JVMConstructorCallNormalizationMode.fromStringOrNull(flagValueString);
|
||||||
|
assert mode != null : "Wrong CONSTRUCTOR_CALL_NORMALIZATION_MODE value: " + flagValueString;
|
||||||
|
configuration.put(JVMConfigurationKeys.CONSTRUCTOR_CALL_NORMALIZATION_MODE, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
@@ -4307,6 +4307,24 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineFunInConstructorCallWithDisabledNormalization.kt")
|
||||||
|
public void testInlineFunInConstructorCallWithDisabledNormalization() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallWithDisabledNormalization.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineFunInConstructorCallWithEnabledNormalization.kt")
|
||||||
|
public void testInlineFunInConstructorCallWithEnabledNormalization() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallWithEnabledNormalization.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineFunInConstructorCallWithStrictNormalization.kt")
|
||||||
|
public void testInlineFunInConstructorCallWithStrictNormalization() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallWithStrictNormalization.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("inlineFunInInnerClassConstructorCall.kt")
|
@TestMetadata("inlineFunInInnerClassConstructorCall.kt")
|
||||||
public void testInlineFunInInnerClassConstructorCall() throws Exception {
|
public void testInlineFunInInnerClassConstructorCall() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInInnerClassConstructorCall.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInInnerClassConstructorCall.kt");
|
||||||
|
|||||||
@@ -4307,6 +4307,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineFunInConstructorCallWithDisabledNormalization.kt")
|
||||||
|
public void testInlineFunInConstructorCallWithDisabledNormalization() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallWithDisabledNormalization.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineFunInConstructorCallWithEnabledNormalization.kt")
|
||||||
|
public void testInlineFunInConstructorCallWithEnabledNormalization() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallWithEnabledNormalization.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineFunInConstructorCallWithStrictNormalization.kt")
|
||||||
|
public void testInlineFunInConstructorCallWithStrictNormalization() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallWithStrictNormalization.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("inlineFunInInnerClassConstructorCall.kt")
|
@TestMetadata("inlineFunInInnerClassConstructorCall.kt")
|
||||||
public void testInlineFunInInnerClassConstructorCall() throws Exception {
|
public void testInlineFunInInnerClassConstructorCall() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInInnerClassConstructorCall.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInInnerClassConstructorCall.kt");
|
||||||
|
|||||||
@@ -4307,6 +4307,24 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineFunInConstructorCallWithDisabledNormalization.kt")
|
||||||
|
public void testInlineFunInConstructorCallWithDisabledNormalization() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallWithDisabledNormalization.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineFunInConstructorCallWithEnabledNormalization.kt")
|
||||||
|
public void testInlineFunInConstructorCallWithEnabledNormalization() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallWithEnabledNormalization.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineFunInConstructorCallWithStrictNormalization.kt")
|
||||||
|
public void testInlineFunInConstructorCallWithStrictNormalization() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallWithStrictNormalization.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("inlineFunInInnerClassConstructorCall.kt")
|
@TestMetadata("inlineFunInInnerClassConstructorCall.kt")
|
||||||
public void testInlineFunInInnerClassConstructorCall() throws Exception {
|
public void testInlineFunInInnerClassConstructorCall() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInInnerClassConstructorCall.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInInnerClassConstructorCall.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user