提交 c0a83c3c 编写于 作者: D Dmitry Petrov

KT-19251 Process uninitialized stores in mandatory bytecode pass

See
https://youtrack.jetbrains.com/issue/KT-19251
https://github.com/puniverse/quasar/issues/280
https://bugs.openjdk.java.net/browse/JDK-8046233

Inline function calls (as well as try/catch expressions) in constructor
arguments produce bytecode that spills stack, and stores uninitialized
objects (created by 'NEW C', but not initialized by 'C.<init>') to
local variables. Such bytecode is valid according to the JVM spec, but
confuses Quasar (and other bytecode postprocessing tools),
and fails to verify under some (buggy) versions of JDK 8.

In order to avoid that, we apply 'processUnitializedStores' already
implemented for coroutines. It moves 'NEW' instructions after the
constructor arguments evaluation, producing code like

<initialize class C using Class.forName>
<evaluate constructor arguments>
<store constructor arguments to variables>
NEW C
DUP
<load constructor arguments from variables>
INVOKESPECIAL C.<init>(...)

NB some other expressions, such as break/continue in the constructor
arguments, also can produce "weird" bytecode: object is created by a
'NEW C' instruction, but later (conditionally) POPped from stack and
left uninitialized. This, as we know, also can screw bytecode
postprocessing. However, it looks like we can get away with it ATM.
Otherwise it looks like we'd have to analyze constructor arguments, see
if the evaluation can "jump out", and perform argument linearization in
codegen.
上级 3d8486e8
......@@ -16,10 +16,8 @@
package org.jetbrains.kotlin.codegen.coroutines
import org.jetbrains.kotlin.codegen.optimization.common.CustomFramesMethodAnalyzer
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
import org.jetbrains.kotlin.codegen.optimization.common.insnListOf
import org.jetbrains.kotlin.codegen.optimization.common.*
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.*
......@@ -71,124 +69,161 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter
* - restore constructor arguments
*/
internal fun processUninitializedStores(methodNode: MethodNode) {
val interpreter = UninitializedNewValueMarkerInterpreter()
val frames = CustomFramesMethodAnalyzer("fake", methodNode, interpreter, ::UninitializedNewValueFrame).analyze()
for ((index, insn) in methodNode.instructions.toArray().withIndex()) {
val frame = frames[index] ?: continue
val uninitializedValue = frame.getUninitializedValueForConstructorCall(insn) ?: continue
val copyUsages: Set<AbstractInsnNode> = interpreter.uninitializedValuesToCopyUsages[uninitializedValue.newInsn]!!
assert(copyUsages.size > 0) { "At least DUP copy operation expected" }
// Value generated by NEW wasn't store to local/field (only DUPed)
if (copyUsages.size == 1) continue
UninitializedStoresProcessor(methodNode, forCoroutines = true).run()
}
(copyUsages + uninitializedValue.newInsn).forEach {
methodNode.instructions.remove(it)
}
class UninitializedStoresProcessor(private val methodNode: MethodNode, private val forCoroutines: Boolean) {
private val isInSpecialMethod = methodNode.name == "<init>" || methodNode.name == "<clinit>"
fun run() {
val interpreter = UninitializedNewValueMarkerInterpreter()
val frames = CustomFramesMethodAnalyzer(
"fake", methodNode, interpreter,
this::UninitializedNewValueFrame
).analyze()
for ((index, insn) in methodNode.instructions.toArray().withIndex()) {
val frame = frames[index] ?: continue
val uninitializedValue = frame.getUninitializedValueForConstructorCall(insn) ?: continue
val newInsn = uninitializedValue.newInsn
val copyUsages: Set<AbstractInsnNode> = interpreter.uninitializedValuesToCopyUsages[newInsn]!!
assert(copyUsages.isNotEmpty()) { "At least DUP copy operation expected" }
// Value generated by NEW wasn't store to local/field (only DUPed)
if (copyUsages.size == 1) continue
methodNode.instructions.run {
removeAll(copyUsages)
if (forCoroutines) {
remove(newInsn)
}
else {
// Replace 'NEW C' instruction with "manual" initialization of class 'C':
// LDC [typeName for C]
// INVOKESTATIC java/lang/Class.forName (Ljava/lang/String;)Ljava/lang/Class;
// POP
val typeNameForClass = newInsn.desc.replace('/', '.')
insertBefore(newInsn, LdcInsnNode(typeNameForClass))
insertBefore(
newInsn,
MethodInsnNode(
Opcodes.INVOKESTATIC,
"java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;",
false
)
)
set(newInsn, InsnNode(Opcodes.POP))
}
}
val indexOfConstructorArgumentFromTopOfStack = Type.getArgumentTypes((insn as MethodInsnNode).desc).size
val storedTypes = arrayListOf<Type>()
var nextVarIndex = methodNode.maxLocals
val indexOfConstructorArgumentFromTopOfStack = Type.getArgumentTypes((insn as MethodInsnNode).desc).size
val storedTypes = arrayListOf<Type>()
var nextVarIndex = methodNode.maxLocals
for (i in 0 until indexOfConstructorArgumentFromTopOfStack) {
val value = frame.getStack(frame.stackSize - 1 - i)
val type = value.type
methodNode.instructions.insertBefore(insn, VarInsnNode(type.getOpcode(Opcodes.ISTORE), nextVarIndex))
nextVarIndex += type.size
storedTypes.add(type)
}
methodNode.maxLocals = Math.max(methodNode.maxLocals, nextVarIndex)
for (i in 0 until indexOfConstructorArgumentFromTopOfStack) {
val value = frame.getStack(frame.stackSize - 1 - i)
val type = value.type
methodNode.instructions.insertBefore(insn, VarInsnNode(type.getOpcode(Opcodes.ISTORE), nextVarIndex))
nextVarIndex += type.size
storedTypes.add(type)
}
methodNode.maxLocals = Math.max(methodNode.maxLocals, nextVarIndex)
methodNode.instructions.insertBefore(insn, insnListOf(
TypeInsnNode(Opcodes.NEW, uninitializedValue.newInsn.desc),
InsnNode(Opcodes.DUP)
))
methodNode.instructions.insertBefore(insn, insnListOf(
TypeInsnNode(Opcodes.NEW, newInsn.desc),
InsnNode(Opcodes.DUP)
))
for (type in storedTypes.reversed()) {
nextVarIndex -= type.size
methodNode.instructions.insertBefore(insn, VarInsnNode(type.getOpcode(Opcodes.ILOAD), nextVarIndex))
for (type in storedTypes.reversed()) {
nextVarIndex -= type.size
methodNode.instructions.insertBefore(insn, VarInsnNode(type.getOpcode(Opcodes.ILOAD), nextVarIndex))
}
}
}
}
private class UninitializedNewValue(
val newInsn: TypeInsnNode, val internalName: String
) : StrictBasicValue(Type.getObjectType(internalName)) {
override fun toString() = "UninitializedNewValue(internalName='$internalName')"
}
private class UninitializedNewValueFrame(nLocals: Int, nStack: Int) : Frame<BasicValue>(nLocals, nStack) {
override fun execute(insn: AbstractInsnNode, interpreter: Interpreter<BasicValue>?) {
val replaceTopValueWithInitialized = getUninitializedValueForConstructorCall(insn) != null
private inner class UninitializedNewValueFrame(nLocals: Int, nStack: Int) : Frame<BasicValue>(nLocals, nStack) {
override fun execute(insn: AbstractInsnNode, interpreter: Interpreter<BasicValue>?) {
val replaceTopValueWithInitialized = getUninitializedValueForConstructorCall(insn) != null
super.execute(insn, interpreter)
super.execute(insn, interpreter)
if (replaceTopValueWithInitialized) {
// Drop top value
val value = pop() as UninitializedNewValue
if (replaceTopValueWithInitialized) {
// Drop top value
val value = pop() as UninitializedNewValue
// uninitialized value become initialized after <init> call
push(StrictBasicValue(value.type))
// uninitialized value become initialized after <init> call
push(StrictBasicValue(value.type))
}
}
}
}
/**
* @return value generated by NEW that used as 0-th argument of constructor call or null if current instruction is not constructor call
*/
private fun Frame<BasicValue>.getUninitializedValueForConstructorCall(
insn: AbstractInsnNode
): UninitializedNewValue? {
if (!insn.isConstructorCall()) return null
assert(insn.opcode == Opcodes.INVOKESPECIAL) { "Expected opcode Opcodes.INVOKESPECIAL for <init>, but ${insn.opcode} found" }
val paramsCountIncludingReceiver = Type.getArgumentTypes((insn as MethodInsnNode).desc).size + 1
val newValue = getStack(stackSize - (paramsCountIncludingReceiver + 1)) as? UninitializedNewValue ?: error("Expected value generated with NEW")
/**
* @return value generated by NEW that used as 0-th argument of constructor call or null if current instruction is not constructor call
*/
private fun Frame<BasicValue>.getUninitializedValueForConstructorCall(insn: AbstractInsnNode): UninitializedNewValue? {
if (!insn.isConstructorCall()) return null
assert(insn.opcode == Opcodes.INVOKESPECIAL) { "Expected opcode Opcodes.INVOKESPECIAL for <init>, but ${insn.opcode} found" }
val paramsCountIncludingReceiver = Type.getArgumentTypes((insn as MethodInsnNode).desc).size + 1
val newValue = peek(paramsCountIncludingReceiver) as? UninitializedNewValue ?:
if (isInSpecialMethod)
return null
else
error("Expected value generated with NEW")
assert(peek(paramsCountIncludingReceiver - 1) is UninitializedNewValue) {
"Next value after NEW should be one generated by DUP"
}
assert(getStack(stackSize - paramsCountIncludingReceiver) is UninitializedNewValue) {
"Next value after NEW should be one generated by DUP"
return newValue
}
return newValue
}
private class UninitializedNewValue(
val newInsn: TypeInsnNode,
val internalName: String
) : StrictBasicValue(Type.getObjectType(internalName)) {
override fun toString() = "UninitializedNewValue(internalName='$internalName')"
}
private fun AbstractInsnNode.isConstructorCall() = this is MethodInsnNode && this.name == "<init>"
private class UninitializedNewValueMarkerInterpreter : OptimizationBasicInterpreter() {
val uninitializedValuesToCopyUsages = hashMapOf<AbstractInsnNode, MutableSet<AbstractInsnNode>>()
override fun newOperation(insn: AbstractInsnNode): BasicValue? {
if (insn.opcode == Opcodes.NEW) {
uninitializedValuesToCopyUsages.getOrPut(insn) { mutableSetOf() }
return UninitializedNewValue(insn as TypeInsnNode, insn.desc)
}
return super.newOperation(insn)
}
private fun AbstractInsnNode.isConstructorCall() = this is MethodInsnNode && this.name == "<init>"
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? {
if (value is UninitializedNewValue) {
uninitializedValuesToCopyUsages[value.newInsn]!!.add(insn)
return value
private class UninitializedNewValueMarkerInterpreter : OptimizationBasicInterpreter() {
val uninitializedValuesToCopyUsages = hashMapOf<AbstractInsnNode, MutableSet<AbstractInsnNode>>()
override fun newOperation(insn: AbstractInsnNode): BasicValue? {
if (insn.opcode == Opcodes.NEW) {
uninitializedValuesToCopyUsages.getOrPut(insn) { mutableSetOf() }
return UninitializedNewValue(insn as TypeInsnNode, insn.desc)
}
return super.newOperation(insn)
}
return super.copyOperation(insn, value)
}
override fun merge(v: BasicValue, w: BasicValue): BasicValue {
if (v === w) return v
if (v === StrictBasicValue.UNINITIALIZED_VALUE || w === StrictBasicValue.UNINITIALIZED_VALUE) {
return StrictBasicValue.UNINITIALIZED_VALUE
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? {
if (value is UninitializedNewValue) {
uninitializedValuesToCopyUsages[value.newInsn]!!.add(insn)
return value
}
return super.copyOperation(insn, value)
}
if (v is UninitializedNewValue || w is UninitializedNewValue) {
if ((v as? UninitializedNewValue)?.newInsn !== (w as? UninitializedNewValue)?.newInsn) {
// Merge of two different ANEW result is possible, but such values should not be used further
override fun merge(v: BasicValue, w: BasicValue): BasicValue {
if (v === w) return v
if (v === StrictBasicValue.UNINITIALIZED_VALUE || w === StrictBasicValue.UNINITIALIZED_VALUE) {
return StrictBasicValue.UNINITIALIZED_VALUE
}
return v
}
if (v is UninitializedNewValue || w is UninitializedNewValue) {
if ((v as? UninitializedNewValue)?.newInsn !== (w as? UninitializedNewValue)?.newInsn) {
// Merge of two different ANEW result is possible, but such values should not be used further
return StrictBasicValue.UNINITIALIZED_VALUE
}
return super.merge(v, w)
return v
}
return super.merge(v, w)
}
}
}
......@@ -17,9 +17,7 @@
package org.jetbrains.kotlin.codegen.optimization;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.codegen.ClassBuilder;
import org.jetbrains.kotlin.codegen.ClassBuilderFactory;
import org.jetbrains.kotlin.codegen.ClassBuilderMode;
import org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
......
......@@ -17,9 +17,9 @@
package org.jetbrains.kotlin.codegen.optimization
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
import org.jetbrains.kotlin.codegen.optimization.boxing.StackPeepholeOptimizationsTransformer
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.boxing.PopBackwardPropagationTransformer
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.boxing.StackPeepholeOptimizationsTransformer
import org.jetbrains.kotlin.codegen.optimization.common.prepareForEmitting
import org.jetbrains.kotlin.codegen.optimization.nullCheck.RedundantNullCheckMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.transformer.CompositeMethodTransformer
......@@ -35,11 +35,10 @@ class OptimizationMethodVisitor(
signature: String?,
exceptions: Array<String>?
) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) {
override fun performTransformations(methodNode: MethodNode) {
MANDATORY_METHOD_TRANSFORMER.transform("fake", methodNode)
normalizationMethodTransformer.transform("fake", methodNode)
if (canBeOptimized(methodNode) && !disableOptimization) {
OPTIMIZATION_TRANSFORMER.transform("fake", methodNode)
optimizationTransformer.transform("fake", methodNode)
}
methodNode.prepareForEmitting()
}
......@@ -47,12 +46,13 @@ class OptimizationMethodVisitor(
companion object {
private val MEMORY_LIMIT_BY_METHOD_MB = 50
private val MANDATORY_METHOD_TRANSFORMER = CompositeMethodTransformer(
val normalizationMethodTransformer = CompositeMethodTransformer(
FixStackWithLabelNormalizationMethodTransformer(),
UninitializedStoresMethodTransformer(),
MethodVerifier("AFTER mandatory stack transformations")
)
private val OPTIMIZATION_TRANSFORMER = CompositeMethodTransformer(
val optimizationTransformer = CompositeMethodTransformer(
CapturedVarsOptimizationMethodTransformer(),
RedundantNullCheckMethodTransformer(),
RedundantCheckCastEliminationMethodTransformer(),
......
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen.optimization
import org.jetbrains.kotlin.codegen.coroutines.UninitializedStoresProcessor
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
import org.jetbrains.org.objectweb.asm.tree.MethodNode
class UninitializedStoresMethodTransformer : MethodTransformer() {
override fun transform(internalClassName: String, methodNode: MethodNode) {
UninitializedStoresProcessor(methodNode, forCoroutines = false).run()
}
}
\ No newline at end of file
......@@ -214,3 +214,6 @@ internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.isInsn(opcod
internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.takeInsnIf(opcode: Int, condition: T.() -> Boolean): T? =
takeIf { it.opcode == opcode }?.safeAs<T>()?.takeIf { it.condition() }
fun InsnList.removeAll(nodes: Collection<AbstractInsnNode>) {
for (node in nodes) remove(node)
}
\ No newline at end of file
......@@ -18,8 +18,15 @@ package org.jetbrains.kotlin.codegen.optimization.transformer
import org.jetbrains.org.objectweb.asm.tree.MethodNode
open class CompositeMethodTransformer(private vararg val transformers: MethodTransformer) : MethodTransformer() {
open class CompositeMethodTransformer(private val transformers: List<MethodTransformer>) : MethodTransformer() {
constructor(vararg transformers: MethodTransformer?) : this(transformers.filterNotNull())
override fun transform(internalClassName: String, methodNode: MethodNode) {
transformers.forEach { it.transform(internalClassName, methodNode) }
}
companion object {
inline fun build(builder: MutableList<MethodTransformer>.() -> Unit) =
CompositeMethodTransformer(ArrayList<MethodTransformer>().apply { builder() })
}
}
\ No newline at end of file
......@@ -179,7 +179,6 @@ class GenerationState @JvmOverloads constructor(
val generateParametersMetadata: Boolean = configuration.getBoolean(JVMConfigurationKeys.PARAMETERS_METADATA)
val shouldInlineConstVals = languageVersionSettings.supportsFeature(LanguageFeature.InlineConstVals)
init {
......
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// 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>")
}
}
}
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: test.kt
fun box(): String {
Outer().Inner(
logged("i;", 1.let { it }),
logged("j;", 2.let { it })
)
val result = log.toString()
if (result != "Foo.<clinit>;i;j;Foo.<init>;Inner.<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
open class Foo {
init {
log.append("Foo.<init>;")
}
companion object {
init {
log.append("Foo.<clinit>;")
}
}
}
class Outer {
inner class Inner(val x: Int, val y: Int) : Foo() {
init {
log.append("Inner.<init>;")
}
}
}
\ No newline at end of file
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: test.kt
fun box(): String {
class Local(val i: Int, val j: Int) : Foo() {
init {
log.append("Local.<init>;")
}
}
Local(
logged("i;", 1.let { it }),
logged("j;", 2.let { it })
)
val result = log.toString()
if (result != "Foo.<clinit>;i;j;Foo.<init>;Local.<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
open class Foo {
init {
log.append("Foo.<init>;")
}
companion object {
init {
log.append("Foo.<clinit>;")
}
}
}
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: test.kt
fun box(): String {
Foo(logged("i", 1), logged("j", 2))
val result = log.toString()
if (result != "<clinit>ij<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: Int) {
init {
log.append("<init>")
}
companion object {
init {
log.append("<clinit>")
}
}
}
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: test.kt
fun box(): String {
Foo(
logged("i", try { 1 } catch (e: Exception) { 42 }),
logged("j", 2)
)
val result = log.toString()
if (result != "<clinit>ij<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: Int) {
init {
log.append("<init>")
}
companion object {
init {
log.append("<clinit>")
}
}
}
......@@ -4275,6 +4275,45 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
}
}
@TestMetadata("compiler/testData/codegen/box/constructorCall")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ConstructorCall extends AbstractIrBlackBoxCodegenTest {
public void testAllFilesPresentInConstructorCall() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/constructorCall"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("inlineFunInConstructorCallEvaluationOrder.kt")
public void testInlineFunInConstructorCallEvaluationOrder() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallEvaluationOrder.kt");
doTest(fileName);
}
@TestMetadata("inlineFunInInnerClassConstructorCall.kt")
public void testInlineFunInInnerClassConstructorCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInInnerClassConstructorCall.kt");
doTest(fileName);
}
@TestMetadata("inlineFunInLocalClassConstructorCall.kt")
public void testInlineFunInLocalClassConstructorCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInLocalClassConstructorCall.kt");
doTest(fileName);
}
@TestMetadata("regularConstructorCallEvaluationOrder.kt")
public void testRegularConstructorCallEvaluationOrder() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/regularConstructorCallEvaluationOrder.kt");
doTest(fileName);
}
@TestMetadata("tryCatchInConstructorCallEvaluationOrder.kt")
public void testTryCatchInConstructorCallEvaluationOrder() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/tryCatchInConstructorCallEvaluationOrder.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/controlStructures")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
......
......@@ -4275,6 +4275,45 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
}
}
@TestMetadata("compiler/testData/codegen/box/constructorCall")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ConstructorCall extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInConstructorCall() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/constructorCall"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("inlineFunInConstructorCallEvaluationOrder.kt")
public void testInlineFunInConstructorCallEvaluationOrder() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallEvaluationOrder.kt");
doTest(fileName);
}
@TestMetadata("inlineFunInInnerClassConstructorCall.kt")
public void testInlineFunInInnerClassConstructorCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInInnerClassConstructorCall.kt");
doTest(fileName);
}
@TestMetadata("inlineFunInLocalClassConstructorCall.kt")
public void testInlineFunInLocalClassConstructorCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInLocalClassConstructorCall.kt");
doTest(fileName);
}
@TestMetadata("regularConstructorCallEvaluationOrder.kt")
public void testRegularConstructorCallEvaluationOrder() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/regularConstructorCallEvaluationOrder.kt");
doTest(fileName);
}
@TestMetadata("tryCatchInConstructorCallEvaluationOrder.kt")
public void testTryCatchInConstructorCallEvaluationOrder() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/tryCatchInConstructorCallEvaluationOrder.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/controlStructures")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
......
......@@ -4275,6 +4275,45 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
}
}
@TestMetadata("compiler/testData/codegen/box/constructorCall")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ConstructorCall extends AbstractLightAnalysisModeTest {
public void testAllFilesPresentInConstructorCall() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/constructorCall"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("inlineFunInConstructorCallEvaluationOrder.kt")
public void testInlineFunInConstructorCallEvaluationOrder() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallEvaluationOrder.kt");
doTest(fileName);
}
@TestMetadata("inlineFunInInnerClassConstructorCall.kt")
public void testInlineFunInInnerClassConstructorCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInInnerClassConstructorCall.kt");
doTest(fileName);
}
@TestMetadata("inlineFunInLocalClassConstructorCall.kt")
public void testInlineFunInLocalClassConstructorCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInLocalClassConstructorCall.kt");
doTest(fileName);
}
@TestMetadata("regularConstructorCallEvaluationOrder.kt")
public void testRegularConstructorCallEvaluationOrder() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/regularConstructorCallEvaluationOrder.kt");
doTest(fileName);
}
@TestMetadata("tryCatchInConstructorCallEvaluationOrder.kt")
public void testTryCatchInConstructorCallEvaluationOrder() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/tryCatchInConstructorCallEvaluationOrder.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/controlStructures")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
......
......@@ -4905,6 +4905,15 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
}
}
@TestMetadata("compiler/testData/codegen/box/constructorCall")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ConstructorCall extends AbstractJsCodegenBoxTest {
public void testAllFilesPresentInConstructorCall() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/constructorCall"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
}
}
@TestMetadata("compiler/testData/codegen/box/controlStructures")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册