KT-5248 Don't wrap variable if it is captured only in inlined closures
Remove non-escaping Ref's on bytecode postprocessing pass.
This commit is contained in:
@@ -1919,11 +1919,6 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
return asmType(varType);
|
||||
}
|
||||
|
||||
private static boolean isSharedVarType(@NotNull Type type) {
|
||||
return type.getSort() == Type.OBJECT && type.getInternalName().startsWith(REF_TYPE_PREFIX);
|
||||
}
|
||||
|
||||
|
||||
private void putDescriptorIntoFrameMap(@NotNull KtElement statement) {
|
||||
if (statement instanceof KtDestructuringDeclaration) {
|
||||
KtDestructuringDeclaration multiDeclaration = (KtDestructuringDeclaration) statement;
|
||||
|
||||
@@ -1359,9 +1359,7 @@ public abstract class StackValue {
|
||||
default:
|
||||
PrimitiveType primitiveType = AsmUtil.asmPrimitiveTypeToLangPrimitiveType(type);
|
||||
if (primitiveType == null) throw new UnsupportedOperationException();
|
||||
|
||||
String typeName = primitiveType.getTypeName().getIdentifier();
|
||||
return Type.getObjectType(REF_TYPE_PREFIX + typeName + "Ref");
|
||||
return sharedTypeForPrimitive(primitiveType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -21,6 +21,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor;
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer;
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantCoercionToUnitTransformer;
|
||||
import org.jetbrains.kotlin.codegen.optimization.captured.CapturedVarsOptimizationMethodTransformer;
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.UtilKt;
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.RedundantNullCheckMethodTransformer;
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer;
|
||||
@@ -33,6 +34,7 @@ public class OptimizationMethodVisitor extends TransformationMethodVisitor {
|
||||
private static final MethodTransformer MANDATORY_METHOD_TRANSFORMER = new FixStackWithLabelNormalizationMethodTransformer();
|
||||
|
||||
private static final MethodTransformer[] OPTIMIZATION_TRANSFORMERS = new MethodTransformer[] {
|
||||
new CapturedVarsOptimizationMethodTransformer(),
|
||||
new RedundantNullCheckMethodTransformer(),
|
||||
new RedundantBoxingMethodTransformer(),
|
||||
new RedundantCoercionToUnitTransformer(),
|
||||
|
||||
+2
-16
@@ -21,6 +21,7 @@ import kotlin.collections.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue;
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.UtilKt;
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
@@ -48,7 +49,7 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
||||
|
||||
adaptLocalVariableTableForBoxedValues(node, frames);
|
||||
|
||||
applyVariablesRemapping(node, buildVariablesRemapping(valuesToOptimize, node));
|
||||
UtilKt.remapLocalVariables(node, buildVariablesRemapping(valuesToOptimize, node));
|
||||
|
||||
adaptInstructionsForBoxedValues(node, valuesToOptimize);
|
||||
}
|
||||
@@ -221,21 +222,6 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
||||
return remapping;
|
||||
}
|
||||
|
||||
private static void applyVariablesRemapping(@NotNull MethodNode node, @NotNull int[] remapping) {
|
||||
for (AbstractInsnNode insn : node.instructions.toArray()) {
|
||||
if (insn instanceof VarInsnNode) {
|
||||
((VarInsnNode) insn).var = remapping[((VarInsnNode) insn).var];
|
||||
}
|
||||
if (insn instanceof IincInsnNode) {
|
||||
((IincInsnNode) insn).var = remapping[((IincInsnNode) insn).var];
|
||||
}
|
||||
}
|
||||
|
||||
for (LocalVariableNode localVariableNode : node.localVariables) {
|
||||
localVariableNode.index = remapping[localVariableNode.index];
|
||||
}
|
||||
}
|
||||
|
||||
private static void adaptInstructionsForBoxedValues(
|
||||
@NotNull MethodNode node,
|
||||
@NotNull RedundantBoxedValuesCollection values
|
||||
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* 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.captured
|
||||
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.removeEmptyCatchBlocks
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.removeUnusedLocalVariables
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||
|
||||
class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
Transformer(internalClassName, methodNode).run()
|
||||
}
|
||||
|
||||
// Tracks proper usages of objects corresponding to captured variables.
|
||||
//
|
||||
// The 'kotlin.jvm.internal.Ref.*' instance can be replaced with a local variable,
|
||||
// if all of the following conditions are satisfied:
|
||||
// * It is created inside a current method.
|
||||
// * The only permitted operations on it are:
|
||||
// - store to a local variable
|
||||
// - ALOAD, ASTORE
|
||||
// - DUP, POP
|
||||
// - GETFIELD <owner>.element, PUTFIELD <owner>.element
|
||||
// * There's a corresponding local variable definition,
|
||||
// and all ALOAD/ASTORE instructions operate on that particular local variable.
|
||||
// * Its 'element' field is initialized at start of local variable visibility range.
|
||||
//
|
||||
// Note that for code that doesn't create Ref objects explicitly these conditions are true,
|
||||
// unless the Ref object escapes to a local class constructor (including local classes for lambdas).
|
||||
//
|
||||
private class CapturedVarDescriptor(val newInsn: TypeInsnNode, val refType: Type, val valueType: Type) : ReferenceValueDescriptor {
|
||||
var hazard = false
|
||||
|
||||
var initCallInsn: MethodInsnNode? = null
|
||||
var localVar: LocalVariableNode? = null
|
||||
var localVarIndex = -1
|
||||
val astoreInsns: MutableCollection<VarInsnNode> = LinkedHashSet()
|
||||
val aloadInsns: MutableCollection<VarInsnNode> = LinkedHashSet()
|
||||
val stackInsns: MutableCollection<AbstractInsnNode> = LinkedHashSet()
|
||||
val getFieldInsns: MutableCollection<FieldInsnNode> = LinkedHashSet()
|
||||
val putFieldInsns: MutableCollection<FieldInsnNode> = LinkedHashSet()
|
||||
|
||||
fun canRewrite(): Boolean =
|
||||
!hazard &&
|
||||
initCallInsn != null &&
|
||||
localVar != null &&
|
||||
localVarIndex >= 0
|
||||
|
||||
override fun onUseAsTainted() {
|
||||
hazard = true
|
||||
}
|
||||
}
|
||||
|
||||
private class Transformer(private val internalClassName: String, private val methodNode: MethodNode) {
|
||||
private val refValues = ArrayList<CapturedVarDescriptor>()
|
||||
private val refValuesByNewInsn = LinkedHashMap<TypeInsnNode, CapturedVarDescriptor>()
|
||||
private val insns = methodNode.instructions.toArray()
|
||||
private lateinit var frames: Array<out Frame<BasicValue>?>
|
||||
|
||||
val hasRewritableRefValues: Boolean
|
||||
get() = refValues.isNotEmpty()
|
||||
|
||||
fun run() {
|
||||
createRefValues()
|
||||
if (!hasRewritableRefValues) return
|
||||
|
||||
analyze()
|
||||
if (!hasRewritableRefValues) return
|
||||
|
||||
rewrite()
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.getIndex() = methodNode.instructions.indexOf(this)
|
||||
|
||||
private fun createRefValues() {
|
||||
for (insn in insns) {
|
||||
if (insn.opcode == Opcodes.NEW && insn is TypeInsnNode) {
|
||||
val type = Type.getObjectType(insn.desc)
|
||||
if (AsmTypes.isSharedVarType(type)) {
|
||||
val valueType = REF_TYPE_TO_ELEMENT_TYPE[type.internalName] ?: continue
|
||||
val refValue = CapturedVarDescriptor(insn, type, valueType)
|
||||
refValues.add(refValue)
|
||||
refValuesByNewInsn[insn] = refValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class Interpreter : ReferenceTrackingInterpreter() {
|
||||
override fun newOperation(insn: AbstractInsnNode): BasicValue =
|
||||
refValuesByNewInsn[insn]?.let { descriptor ->
|
||||
ProperTrackedReferenceValue(descriptor.refType, descriptor)
|
||||
}
|
||||
?: super.newOperation(insn)
|
||||
|
||||
override fun processRefValueUsage(value: TrackedReferenceValue, insn: AbstractInsnNode, position: Int) {
|
||||
for (descriptor in value.descriptors) {
|
||||
if (descriptor !is CapturedVarDescriptor) throw AssertionError("Unexpected descriptor: $descriptor")
|
||||
when {
|
||||
insn.opcode == Opcodes.ALOAD ->
|
||||
descriptor.aloadInsns.add(insn as VarInsnNode)
|
||||
insn.opcode == Opcodes.ASTORE ->
|
||||
descriptor.astoreInsns.add(insn as VarInsnNode)
|
||||
insn.opcode == Opcodes.GETFIELD && insn is FieldInsnNode && insn.name == REF_ELEMENT_FIELD && position == 0 ->
|
||||
descriptor.getFieldInsns.add(insn)
|
||||
insn.opcode == Opcodes.PUTFIELD && insn is FieldInsnNode && insn.name == REF_ELEMENT_FIELD && position == 0 ->
|
||||
descriptor.putFieldInsns.add(insn)
|
||||
insn.opcode == Opcodes.INVOKESPECIAL && insn is MethodInsnNode && insn.name == INIT_METHOD_NAME && position == 0 ->
|
||||
if (descriptor.initCallInsn != null && descriptor.initCallInsn != insn)
|
||||
descriptor.hazard = true
|
||||
else
|
||||
descriptor.initCallInsn = insn
|
||||
insn.opcode == Opcodes.DUP ->
|
||||
descriptor.stackInsns.add(insn)
|
||||
else ->
|
||||
descriptor.hazard = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun analyze() {
|
||||
frames = MethodTransformer.analyze(internalClassName, methodNode, Interpreter())
|
||||
trackPops()
|
||||
assignLocalVars()
|
||||
|
||||
refValues.removeAll { !it.canRewrite() }
|
||||
}
|
||||
|
||||
private fun trackPops() {
|
||||
for (i in insns.indices) {
|
||||
val frame = frames[i] ?: continue
|
||||
val insn = insns[i]
|
||||
|
||||
when (insn.opcode) {
|
||||
Opcodes.POP -> {
|
||||
frame.top()?.getCapturedVarOrNull()?.run { stackInsns.add(insn) }
|
||||
}
|
||||
Opcodes.POP2 -> {
|
||||
val top = frame.top()
|
||||
if (top?.size == 1) {
|
||||
top.getCapturedVarOrNull()?.hazard = true
|
||||
frame.peek(1)?.getCapturedVarOrNull()?.hazard = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BasicValue.getCapturedVarOrNull() =
|
||||
safeAs<ProperTrackedReferenceValue>()?.descriptor?.safeAs<CapturedVarDescriptor>()
|
||||
|
||||
private fun assignLocalVars() {
|
||||
for (localVar in methodNode.localVariables) {
|
||||
val type = Type.getType(localVar.desc)
|
||||
if (!AsmTypes.isSharedVarType(type)) continue
|
||||
|
||||
val startFrame = frames[localVar.start.getIndex()] ?: continue
|
||||
|
||||
val refValue = startFrame.getLocal(localVar.index) as? ProperTrackedReferenceValue ?: continue
|
||||
val descriptor = refValue.descriptor as? CapturedVarDescriptor ?: continue
|
||||
|
||||
if (descriptor.hazard) continue
|
||||
|
||||
if (descriptor.localVar == null) {
|
||||
descriptor.localVar = localVar
|
||||
}
|
||||
else {
|
||||
descriptor.hazard = true
|
||||
}
|
||||
}
|
||||
|
||||
for (refValue in refValues) {
|
||||
if (refValue.hazard) continue
|
||||
val localVar = refValue.localVar ?: continue
|
||||
|
||||
if (refValue.valueType.size != 1) {
|
||||
refValue.localVarIndex = methodNode.maxLocals
|
||||
methodNode.maxLocals += 2
|
||||
localVar.index = refValue.localVarIndex
|
||||
}
|
||||
else {
|
||||
refValue.localVarIndex = localVar.index
|
||||
}
|
||||
|
||||
val startIndex = localVar.start.getIndex()
|
||||
val initFieldInsns = refValue.putFieldInsns.filter { it.getIndex() < startIndex }
|
||||
if (initFieldInsns.size != 1) {
|
||||
refValue.hazard = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun rewrite() {
|
||||
for (refValue in refValues) {
|
||||
if (!refValue.canRewrite()) continue
|
||||
|
||||
rewriteRefValue(refValue)
|
||||
}
|
||||
|
||||
methodNode.removeEmptyCatchBlocks()
|
||||
methodNode.removeUnusedLocalVariables()
|
||||
}
|
||||
|
||||
private fun rewriteRefValue(capturedVar: CapturedVarDescriptor) {
|
||||
methodNode.instructions.run {
|
||||
capturedVar.localVar!!.let {
|
||||
it.signature = null
|
||||
it.desc = capturedVar.valueType.descriptor
|
||||
}
|
||||
|
||||
remove(capturedVar.newInsn)
|
||||
remove(capturedVar.initCallInsn!!)
|
||||
capturedVar.stackInsns.forEach { remove(it) }
|
||||
capturedVar.aloadInsns.forEach { remove(it) }
|
||||
capturedVar.astoreInsns.forEach { remove(it) }
|
||||
|
||||
capturedVar.getFieldInsns.forEach {
|
||||
set(it, VarInsnNode(capturedVar.valueType.getOpcode(Opcodes.ILOAD), capturedVar.localVarIndex))
|
||||
}
|
||||
|
||||
capturedVar.putFieldInsns.forEach {
|
||||
set(it, VarInsnNode(capturedVar.valueType.getOpcode(Opcodes.ISTORE), capturedVar.localVarIndex))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal const val REF_ELEMENT_FIELD = "element"
|
||||
internal const val INIT_METHOD_NAME = "<init>"
|
||||
|
||||
internal val REF_TYPE_TO_ELEMENT_TYPE = HashMap<String, Type>().apply {
|
||||
put(AsmTypes.OBJECT_REF_TYPE.internalName, AsmTypes.OBJECT_TYPE)
|
||||
PrimitiveType.values().forEach {
|
||||
put(AsmTypes.sharedTypeForPrimitive(it).internalName, AsmTypes.valueTypeForPrimitive(it))
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.captured
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
|
||||
abstract class ReferenceTrackingInterpreter : OptimizationBasicInterpreter() {
|
||||
override fun merge(v: BasicValue, w: BasicValue): BasicValue =
|
||||
when {
|
||||
v is ProperTrackedReferenceValue && w is ProperTrackedReferenceValue ->
|
||||
if (v.descriptor == w.descriptor)
|
||||
v
|
||||
else
|
||||
TaintedTrackedReferenceValue(
|
||||
getTaintedValueType(v.type, w.type),
|
||||
setOf(v.descriptor, w.descriptor)
|
||||
)
|
||||
|
||||
v is TrackedReferenceValue || w is TrackedReferenceValue ->
|
||||
TaintedTrackedReferenceValue(
|
||||
getTaintedValueType(v.type, w.type),
|
||||
v.referenceValueDescriptors + w.referenceValueDescriptors
|
||||
)
|
||||
|
||||
else ->
|
||||
super.merge(v, w)
|
||||
}
|
||||
|
||||
private val BasicValue.referenceValueDescriptors: Set<ReferenceValueDescriptor>
|
||||
get() = if (this is TrackedReferenceValue) this.descriptors else emptySet()
|
||||
|
||||
private fun getTaintedValueType(type1: Type?, type2: Type?): Type =
|
||||
when {
|
||||
type1 == null || type2 == null -> AsmTypes.OBJECT_TYPE
|
||||
type1 == type2 -> type1
|
||||
else -> AsmTypes.OBJECT_TYPE
|
||||
}
|
||||
|
||||
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? =
|
||||
if (value is TrackedReferenceValue) {
|
||||
checkRefValuesUsages(insn, listOf(value))
|
||||
value
|
||||
}
|
||||
else {
|
||||
super.copyOperation(insn, value)
|
||||
}
|
||||
|
||||
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? {
|
||||
checkRefValuesUsages(insn, listOf(value))
|
||||
return super.unaryOperation(insn, value)
|
||||
}
|
||||
|
||||
override fun binaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue): BasicValue? {
|
||||
checkRefValuesUsages(insn, listOf(value1, value2))
|
||||
return super.binaryOperation(insn, value1, value2)
|
||||
}
|
||||
|
||||
override fun ternaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue, value3: BasicValue): BasicValue? {
|
||||
checkRefValuesUsages(insn, listOf(value1, value2, value3))
|
||||
return super.ternaryOperation(insn, value1, value2, value3)
|
||||
}
|
||||
|
||||
override fun naryOperation(insn: AbstractInsnNode, values: List<BasicValue>): BasicValue? {
|
||||
checkRefValuesUsages(insn, values)
|
||||
return super.naryOperation(insn, values)
|
||||
}
|
||||
|
||||
protected open fun checkRefValuesUsages(insn: AbstractInsnNode, values: List<BasicValue>) {
|
||||
values.forEach { value ->
|
||||
if (value is TaintedTrackedReferenceValue) {
|
||||
value.descriptors.forEach { it.onUseAsTainted() }
|
||||
}
|
||||
}
|
||||
|
||||
values.forEachIndexed { pos, value ->
|
||||
if (value is TrackedReferenceValue) {
|
||||
processRefValueUsage(value, insn, pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun processRefValueUsage(value: TrackedReferenceValue, insn: AbstractInsnNode, position: Int)
|
||||
}
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.captured
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
interface ReferenceValueDescriptor {
|
||||
fun onUseAsTainted()
|
||||
}
|
||||
|
||||
abstract class TrackedReferenceValue(type: Type): StrictBasicValue(type) {
|
||||
abstract val descriptors: Set<ReferenceValueDescriptor>
|
||||
}
|
||||
|
||||
class ProperTrackedReferenceValue(type: Type, val descriptor: ReferenceValueDescriptor) : TrackedReferenceValue(type) {
|
||||
override val descriptors: Set<ReferenceValueDescriptor>
|
||||
get() = setOf(descriptor)
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other === this ||
|
||||
other is ProperTrackedReferenceValue && other.descriptor == this.descriptor
|
||||
|
||||
override fun hashCode(): Int =
|
||||
descriptor.hashCode()
|
||||
|
||||
override fun toString(): String =
|
||||
"[$descriptor]"
|
||||
}
|
||||
|
||||
class TaintedTrackedReferenceValue(type: Type, override val descriptors: Set<ReferenceValueDescriptor>) : TrackedReferenceValue(type) {
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other === this ||
|
||||
other is TaintedTrackedReferenceValue && other.descriptors == this.descriptors
|
||||
|
||||
override fun hashCode(): Int =
|
||||
descriptors.hashCode()
|
||||
|
||||
override fun toString(): String =
|
||||
"!$descriptors"
|
||||
}
|
||||
@@ -17,8 +17,10 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization.common
|
||||
|
||||
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
|
||||
val AbstractInsnNode.isMeaningful: Boolean get() =
|
||||
@@ -77,6 +79,62 @@ fun MethodNode.removeEmptyCatchBlocks() {
|
||||
}
|
||||
}
|
||||
|
||||
fun MethodNode.removeUnusedLocalVariables() {
|
||||
val used = BooleanArray(maxLocals) { false }
|
||||
for (insn in instructions) {
|
||||
when (insn) {
|
||||
is VarInsnNode -> {
|
||||
val varIndex = insn.`var`
|
||||
used[varIndex] = true
|
||||
if (insn.isSize2LoadStoreOperation()) {
|
||||
used[varIndex + 1] = true
|
||||
}
|
||||
}
|
||||
is IincInsnNode ->
|
||||
used[insn.`var`] = true
|
||||
}
|
||||
}
|
||||
for (localVar in localVariables) {
|
||||
val varIndex = localVar.index
|
||||
used[varIndex] = true
|
||||
val type = Type.getType(localVar.desc)
|
||||
if (type.size == 2) {
|
||||
used[varIndex + 1] = true
|
||||
}
|
||||
}
|
||||
|
||||
if (used.all { it }) return
|
||||
|
||||
val remapping = IntArray(maxLocals) { 0 }
|
||||
var lastUnused = 0
|
||||
for (i in remapping.indices) {
|
||||
remapping[i] = lastUnused
|
||||
if (used[i]) {
|
||||
lastUnused++
|
||||
}
|
||||
}
|
||||
|
||||
remapLocalVariables(remapping)
|
||||
}
|
||||
|
||||
private fun VarInsnNode.isSize2LoadStoreOperation() =
|
||||
opcode == LLOAD || opcode == DLOAD || opcode == LSTORE || opcode == DSTORE
|
||||
|
||||
fun MethodNode.remapLocalVariables(remapping: IntArray) {
|
||||
for (insn in instructions.toArray()) {
|
||||
when (insn) {
|
||||
is VarInsnNode ->
|
||||
insn.`var` = remapping[insn.`var`]
|
||||
is IincInsnNode ->
|
||||
insn.`var` = remapping[insn.`var`]
|
||||
}
|
||||
}
|
||||
|
||||
for (localVariableNode in localVariables) {
|
||||
localVariableNode.index = remapping[localVariableNode.index]
|
||||
}
|
||||
}
|
||||
|
||||
inline fun AbstractInsnNode.findNextOrNull(predicate: (AbstractInsnNode) -> Boolean): AbstractInsnNode? {
|
||||
var finger = this.next
|
||||
while (finger != null && !predicate(finger)) {
|
||||
@@ -125,3 +183,10 @@ fun AbstractInsnNode.isLoadOperation(): Boolean = getOpcode() in Opcodes.ILOAD..
|
||||
val AbstractInsnNode?.insnText get() = InlineCodegenUtil.getInsnText(this)
|
||||
val AbstractInsnNode?.debugText get() =
|
||||
if (this == null) "<null>" else "${this.javaClass.simpleName}: $insnText"
|
||||
|
||||
internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.isInsn(opcode: Int, condition: T.() -> Boolean): Boolean =
|
||||
takeInsnIf(opcode, condition) != null
|
||||
|
||||
internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.takeInsnIf(opcode: Int, condition: T.() -> Boolean): T? =
|
||||
takeIf { it.opcode == opcode }?.safeAs<T>()?.takeIf { it.condition() }
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.resolve.jvm;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -83,6 +84,40 @@ public class AsmTypes {
|
||||
return Type.getObjectType("kotlin/reflect/" + className);
|
||||
}
|
||||
|
||||
public static boolean isSharedVarType(@NotNull Type type) {
|
||||
return type.getSort() == Type.OBJECT && type.getInternalName().startsWith(REF_TYPE_PREFIX);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Type sharedTypeForPrimitive(@NotNull PrimitiveType primitiveType) {
|
||||
String typeName = primitiveType.getTypeName().getIdentifier();
|
||||
return Type.getObjectType(REF_TYPE_PREFIX + typeName + "Ref");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Type valueTypeForPrimitive(PrimitiveType primitiveType) {
|
||||
switch (primitiveType) {
|
||||
case BOOLEAN:
|
||||
return Type.BOOLEAN_TYPE;
|
||||
case CHAR:
|
||||
return Type.CHAR_TYPE;
|
||||
case BYTE:
|
||||
return Type.BYTE_TYPE;
|
||||
case SHORT:
|
||||
return Type.SHORT_TYPE;
|
||||
case INT:
|
||||
return Type.INT_TYPE;
|
||||
case FLOAT:
|
||||
return Type.FLOAT_TYPE;
|
||||
case LONG:
|
||||
return Type.LONG_TYPE;
|
||||
case DOUBLE:
|
||||
return Type.DOUBLE_TYPE;
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Type getType(@NotNull Class<?> javaClass) {
|
||||
Type type = TYPES_MAP.get(javaClass);
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
inline fun runCrossInline(crossinline f: () -> Unit) {
|
||||
f()
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
var x = ""
|
||||
runCrossInline { x = "OK" }
|
||||
return x
|
||||
}
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
var x = ""
|
||||
run { x = "OK" }
|
||||
return x
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
var x = ""
|
||||
run {
|
||||
x += "O"
|
||||
x += "K"
|
||||
}
|
||||
return x
|
||||
}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
var x = 0
|
||||
run { x++ }
|
||||
run { ++x }
|
||||
return if (x == 2) "OK" else "Fail: $x"
|
||||
}
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
class Host(var value: String) {
|
||||
operator fun get(i: Int, j: Int, k: Int) = value
|
||||
|
||||
operator fun set(i: Int, j: Int, k: Int, newValue: String) {
|
||||
value = newValue
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
var x = Host("")
|
||||
run {
|
||||
x[0, 0, 0] += "O"
|
||||
x[0, 0, 0] += "K"
|
||||
}
|
||||
return x.value
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
var xl = 0L // Long, size 2
|
||||
var xi = 0 // Int, size 1
|
||||
var xd = 0.0 // Double, size 2
|
||||
|
||||
run {
|
||||
xl++
|
||||
xd += 1.0
|
||||
xi++
|
||||
}
|
||||
|
||||
run {
|
||||
run {
|
||||
xl++
|
||||
xd += 1.0
|
||||
xi++
|
||||
}
|
||||
}
|
||||
|
||||
if (xi != 2) return "fail: xi=$xi"
|
||||
if (xl != 2L) return "fail: xl=$xl"
|
||||
if (xd != 2.0) return "fail: xd=$xd"
|
||||
return "OK"
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
run {
|
||||
run {
|
||||
var x = 0
|
||||
run { ++x }
|
||||
if (x == 0) return "fail"
|
||||
}
|
||||
|
||||
run {
|
||||
var x = 0
|
||||
run { x++ }
|
||||
if (x == 0) return "fail"
|
||||
}
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
import kotlin.coroutines.experimental.*
|
||||
import kotlin.coroutines.experimental.intrinsics.*
|
||||
|
||||
|
||||
class Controller {
|
||||
var result = ""
|
||||
|
||||
suspend fun <T> suspendWithResult(value: T): T = suspendCoroutineOrReturn { c ->
|
||||
c.resume(value)
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(c: suspend Controller.() -> Unit): String {
|
||||
val controller = Controller()
|
||||
c.startCoroutine(controller, EmptyContinuation)
|
||||
return controller.result
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val value = builder {
|
||||
var r = ""
|
||||
|
||||
for (x in listOf("O", "$", "K")) {
|
||||
if (x == "$") continue
|
||||
run {
|
||||
r += suspendWithResult(x)
|
||||
}
|
||||
}
|
||||
run {
|
||||
r += "."
|
||||
}
|
||||
result = r
|
||||
}
|
||||
|
||||
if (value != "OK.") return "fail: suspend in for body: $value"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun test() {
|
||||
var x = 0
|
||||
run {
|
||||
run {
|
||||
run {
|
||||
++x
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0 NEW
|
||||
// 0 GETFIELD
|
||||
// 0 PUTFIELD
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun test() {
|
||||
var x = 0
|
||||
run { ++x }
|
||||
}
|
||||
|
||||
// 0 NEW
|
||||
// 0 GETFIELD
|
||||
// 0 PUTFIELD
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun test() {
|
||||
var x = 0
|
||||
run {
|
||||
val obj = object {
|
||||
fun foo() { ++x }
|
||||
}
|
||||
obj.foo()
|
||||
}
|
||||
}
|
||||
|
||||
// 1 NEW kotlin/jvm/internal/Ref\$IntRef
|
||||
// 2 GETFIELD kotlin/jvm/internal/Ref\$IntRef\.element
|
||||
// 2 PUTFIELD kotlin/jvm/internal/Ref\$IntRef\.element
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun runNoInline(f: () -> Unit) = f()
|
||||
|
||||
fun test() {
|
||||
var x = 0
|
||||
runNoInline { ++x }
|
||||
}
|
||||
|
||||
// 1 NEW kotlin/jvm/internal/Ref\$IntRef
|
||||
// 2 GETFIELD kotlin/jvm/internal/Ref\$IntRef\.element
|
||||
// 2 PUTFIELD kotlin/jvm/internal/Ref\$IntRef\.element
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun runNoInline(f: () -> Unit) = f()
|
||||
|
||||
fun test() {
|
||||
var x = 0
|
||||
run {
|
||||
run {
|
||||
runNoInline {
|
||||
run {
|
||||
++x
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1 NEW kotlin/jvm/internal/Ref\$IntRef
|
||||
// 2 GETFIELD kotlin/jvm/internal/Ref\$IntRef\.element
|
||||
// 2 PUTFIELD kotlin/jvm/internal/Ref\$IntRef\.element
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
var xl = 0L // Long, size 2
|
||||
var xi = 0 // Int, size 1
|
||||
var xd = 0.0 // Double, size 2
|
||||
|
||||
run {
|
||||
xl++
|
||||
xd += 1.0
|
||||
xi++
|
||||
}
|
||||
|
||||
run {
|
||||
run {
|
||||
xl++
|
||||
xd += 1.0
|
||||
xi++
|
||||
}
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// 0 NEW
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
run {
|
||||
run {
|
||||
var x1 = 0
|
||||
run { ++x1 }
|
||||
if (x1 == 0) return "fail"
|
||||
}
|
||||
|
||||
run {
|
||||
var x2 = 0
|
||||
run { x2++ }
|
||||
if (x2 == 0) return "fail"
|
||||
}
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
|
||||
// Shared variable slots (x1, x2):
|
||||
// 5 ILOAD 0
|
||||
// 4 ISTORE 0
|
||||
|
||||
// Temporary variable slots for 'x2++':
|
||||
// 1 ILOAD 1
|
||||
// 1 ISTORE 1
|
||||
|
||||
// 0 NEW
|
||||
// 0 GETFIELD
|
||||
// 0 PUTFIELD
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun add(x: Int, y: Int) = x + y
|
||||
|
||||
fun test() {
|
||||
var x = 0
|
||||
run {
|
||||
x += add(1, try { 1 } catch (e: Throwable) { 42 })
|
||||
}
|
||||
}
|
||||
|
||||
// 0 NEW
|
||||
// 0 GETFIELD
|
||||
// 0 PUTFIELD
|
||||
compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInCrossinline.txt
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
@kotlin.Metadata
|
||||
public final class CapturedInCrossinlineKt {
|
||||
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||
public final static method runCrossInline(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function0): void
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
@kotlin.Metadata
|
||||
public final class CapturedInInlineOnlyAssignKt {
|
||||
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
@kotlin.Metadata
|
||||
public final class CapturedInInlineOnlyCAOKt {
|
||||
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
@kotlin.Metadata
|
||||
public final class CapturedInInlineOnlyIncrDecrKt {
|
||||
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
@kotlin.Metadata
|
||||
public final class CapturedInInlineOnlyIndexedCAOKt {
|
||||
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class Host {
|
||||
private @org.jetbrains.annotations.NotNull field value: java.lang.String
|
||||
public method <init>(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
|
||||
public final @org.jetbrains.annotations.NotNull method get(p0: int, p1: int, p2: int): java.lang.String
|
||||
public final @org.jetbrains.annotations.NotNull method getValue(): java.lang.String
|
||||
public final method set(p0: int, p1: int, p2: int, @org.jetbrains.annotations.NotNull p3: java.lang.String): void
|
||||
public final method setValue(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
|
||||
}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
@kotlin.Metadata
|
||||
public final class CapturedVarsOfSize2Kt {
|
||||
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
@kotlin.Metadata
|
||||
public final class SharedSlotsWithCapturedVarsKt {
|
||||
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||
}
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
@kotlin.Metadata
|
||||
public final class Controller {
|
||||
private @org.jetbrains.annotations.NotNull field result: java.lang.String
|
||||
public method <init>(): void
|
||||
public final @org.jetbrains.annotations.NotNull method getResult(): java.lang.String
|
||||
public final method setResult(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
|
||||
public final @org.jetbrains.annotations.Nullable method suspendWithResult(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class CoroutineUtilKt {
|
||||
public final static @org.jetbrains.annotations.NotNull method handleExceptionContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.experimental.Continuation
|
||||
public final static @org.jetbrains.annotations.NotNull method handleResultContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.experimental.Continuation
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public class EmptyContinuation {
|
||||
public final static field Companion: EmptyContinuation.Companion
|
||||
private final @org.jetbrains.annotations.NotNull field context: kotlin.coroutines.experimental.CoroutineContext
|
||||
inner class EmptyContinuation/Companion
|
||||
public @synthetic.kotlin.jvm.GeneratedByJvmOverloads method <init>(): void
|
||||
public method <init>(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.CoroutineContext): void
|
||||
public synthetic method <init>(p0: kotlin.coroutines.experimental.CoroutineContext, p1: int, p2: kotlin.jvm.internal.DefaultConstructorMarker): void
|
||||
public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.experimental.CoroutineContext
|
||||
public method resume(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void
|
||||
public method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final static class EmptyContinuation/Companion {
|
||||
inner class EmptyContinuation/Companion
|
||||
private method <init>(): void
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class WithCoroutinesKt {
|
||||
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||
public final static @org.jetbrains.annotations.NotNull method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function2): java.lang.String
|
||||
}
|
||||
+57
@@ -3767,6 +3767,63 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class CapturedVarsOptimization extends AbstractIrBlackBoxCodegenTest {
|
||||
public void testAllFilesPresentInCapturedVarsOptimization() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/closures/capturedVarsOptimization"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInCrossinline.kt")
|
||||
public void testCapturedInCrossinline() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInCrossinline.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInInlineOnlyAssign.kt")
|
||||
public void testCapturedInInlineOnlyAssign() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyAssign.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInInlineOnlyCAO.kt")
|
||||
public void testCapturedInInlineOnlyCAO() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyCAO.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInInlineOnlyIncrDecr.kt")
|
||||
public void testCapturedInInlineOnlyIncrDecr() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIncrDecr.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInInlineOnlyIndexedCAO.kt")
|
||||
public void testCapturedInInlineOnlyIndexedCAO() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIndexedCAO.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedVarsOfSize2.kt")
|
||||
public void testCapturedVarsOfSize2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedVarsOfSize2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("sharedSlotsWithCapturedVars.kt")
|
||||
public void testSharedSlotsWithCapturedVars() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("withCoroutines.kt")
|
||||
public void testWithCoroutines() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/withCoroutines.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/closures/closureInsideClosure")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -3767,6 +3767,63 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class CapturedVarsOptimization extends AbstractBlackBoxCodegenTest {
|
||||
public void testAllFilesPresentInCapturedVarsOptimization() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/closures/capturedVarsOptimization"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInCrossinline.kt")
|
||||
public void testCapturedInCrossinline() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInCrossinline.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInInlineOnlyAssign.kt")
|
||||
public void testCapturedInInlineOnlyAssign() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyAssign.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInInlineOnlyCAO.kt")
|
||||
public void testCapturedInInlineOnlyCAO() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyCAO.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInInlineOnlyIncrDecr.kt")
|
||||
public void testCapturedInInlineOnlyIncrDecr() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIncrDecr.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInInlineOnlyIndexedCAO.kt")
|
||||
public void testCapturedInInlineOnlyIndexedCAO() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIndexedCAO.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedVarsOfSize2.kt")
|
||||
public void testCapturedVarsOfSize2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedVarsOfSize2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("sharedSlotsWithCapturedVars.kt")
|
||||
public void testSharedSlotsWithCapturedVars() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("withCoroutines.kt")
|
||||
public void testWithCoroutines() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/withCoroutines.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/closures/closureInsideClosure")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -648,6 +648,63 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class CapturedVarsOptimization extends AbstractBytecodeTextTest {
|
||||
public void testAllFilesPresentInCapturedVarsOptimization() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/capturedVarsOptimization"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInChainOfInlineFuns.kt")
|
||||
public void testCapturedInChainOfInlineFuns() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInChainOfInlineFuns.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInInlineOnly.kt")
|
||||
public void testCapturedInInlineOnly() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInInlineOnly.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInLocalObject.kt")
|
||||
public void testCapturedInLocalObject() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInLocalObject.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInNoInlineOnly.kt")
|
||||
public void testCapturedInNoInlineOnly() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInNoInlineOnly.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInNoInlneInsideChainOfInlineFuns.kt")
|
||||
public void testCapturedInNoInlneInsideChainOfInlineFuns() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInNoInlneInsideChainOfInlineFuns.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedVarsOfSize2.kt")
|
||||
public void testCapturedVarsOfSize2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedVarsOfSize2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("sharedSlotsWithCapturedVars.kt")
|
||||
public void testSharedSlotsWithCapturedVars() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("withStackNormalization.kt")
|
||||
public void testWithStackNormalization() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/withStackNormalization.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeText/checkcast")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -23,8 +23,7 @@ inline fun foo(f: () -> Unit) {
|
||||
// RESULT: 1: I
|
||||
frame = main:7, FrameSharedVarLocalVarKt {frameSharedVarLocalVar}
|
||||
local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} (sp = frameSharedVarLocalVar.kt, 3)
|
||||
local = var1: kotlin.jvm.internal.Ref$IntRef = {kotlin.jvm.internal.Ref$IntRef@uniqueID}1 (sp = frameSharedVarLocalVar.kt, 4)
|
||||
field = element: int = 1 (sp = Ref.!EXT!)
|
||||
local = var1: int = 1 (sp = frameSharedVarLocalVar.kt, 4)
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
|
||||
+57
@@ -4452,6 +4452,63 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class CapturedVarsOptimization extends AbstractJsCodegenBoxTest {
|
||||
public void testAllFilesPresentInCapturedVarsOptimization() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/closures/capturedVarsOptimization"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInCrossinline.kt")
|
||||
public void testCapturedInCrossinline() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInCrossinline.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInInlineOnlyAssign.kt")
|
||||
public void testCapturedInInlineOnlyAssign() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyAssign.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInInlineOnlyCAO.kt")
|
||||
public void testCapturedInInlineOnlyCAO() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyCAO.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInInlineOnlyIncrDecr.kt")
|
||||
public void testCapturedInInlineOnlyIncrDecr() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIncrDecr.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedInInlineOnlyIndexedCAO.kt")
|
||||
public void testCapturedInInlineOnlyIndexedCAO() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIndexedCAO.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedVarsOfSize2.kt")
|
||||
public void testCapturedVarsOfSize2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedVarsOfSize2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("sharedSlotsWithCapturedVars.kt")
|
||||
public void testSharedSlotsWithCapturedVars() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("withCoroutines.kt")
|
||||
public void testWithCoroutines() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/withCoroutines.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/closures/closureInsideClosure")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user