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);
|
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) {
|
private void putDescriptorIntoFrameMap(@NotNull KtElement statement) {
|
||||||
if (statement instanceof KtDestructuringDeclaration) {
|
if (statement instanceof KtDestructuringDeclaration) {
|
||||||
KtDestructuringDeclaration multiDeclaration = (KtDestructuringDeclaration) statement;
|
KtDestructuringDeclaration multiDeclaration = (KtDestructuringDeclaration) statement;
|
||||||
|
|||||||
@@ -1359,9 +1359,7 @@ public abstract class StackValue {
|
|||||||
default:
|
default:
|
||||||
PrimitiveType primitiveType = AsmUtil.asmPrimitiveTypeToLangPrimitiveType(type);
|
PrimitiveType primitiveType = AsmUtil.asmPrimitiveTypeToLangPrimitiveType(type);
|
||||||
if (primitiveType == null) throw new UnsupportedOperationException();
|
if (primitiveType == null) throw new UnsupportedOperationException();
|
||||||
|
return sharedTypeForPrimitive(primitiveType);
|
||||||
String typeName = primitiveType.getTypeName().getIdentifier();
|
|
||||||
return Type.getObjectType(REF_TYPE_PREFIX + typeName + "Ref");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
@@ -21,6 +21,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor;
|
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor;
|
||||||
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer;
|
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer;
|
||||||
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantCoercionToUnitTransformer;
|
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.common.UtilKt;
|
||||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.RedundantNullCheckMethodTransformer;
|
import org.jetbrains.kotlin.codegen.optimization.nullCheck.RedundantNullCheckMethodTransformer;
|
||||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer;
|
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 MANDATORY_METHOD_TRANSFORMER = new FixStackWithLabelNormalizationMethodTransformer();
|
||||||
|
|
||||||
private static final MethodTransformer[] OPTIMIZATION_TRANSFORMERS = new MethodTransformer[] {
|
private static final MethodTransformer[] OPTIMIZATION_TRANSFORMERS = new MethodTransformer[] {
|
||||||
|
new CapturedVarsOptimizationMethodTransformer(),
|
||||||
new RedundantNullCheckMethodTransformer(),
|
new RedundantNullCheckMethodTransformer(),
|
||||||
new RedundantBoxingMethodTransformer(),
|
new RedundantBoxingMethodTransformer(),
|
||||||
new RedundantCoercionToUnitTransformer(),
|
new RedundantCoercionToUnitTransformer(),
|
||||||
|
|||||||
+2
-16
@@ -21,6 +21,7 @@ import kotlin.collections.CollectionsKt;
|
|||||||
import kotlin.jvm.functions.Function1;
|
import kotlin.jvm.functions.Function1;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue;
|
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.kotlin.codegen.optimization.transformer.MethodTransformer;
|
||||||
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.Type;
|
||||||
@@ -48,7 +49,7 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
|
|
||||||
adaptLocalVariableTableForBoxedValues(node, frames);
|
adaptLocalVariableTableForBoxedValues(node, frames);
|
||||||
|
|
||||||
applyVariablesRemapping(node, buildVariablesRemapping(valuesToOptimize, node));
|
UtilKt.remapLocalVariables(node, buildVariablesRemapping(valuesToOptimize, node));
|
||||||
|
|
||||||
adaptInstructionsForBoxedValues(node, valuesToOptimize);
|
adaptInstructionsForBoxedValues(node, valuesToOptimize);
|
||||||
}
|
}
|
||||||
@@ -221,21 +222,6 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
return remapping;
|
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(
|
private static void adaptInstructionsForBoxedValues(
|
||||||
@NotNull MethodNode node,
|
@NotNull MethodNode node,
|
||||||
@NotNull RedundantBoxedValuesCollection values
|
@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
|
package org.jetbrains.kotlin.codegen.optimization.common
|
||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil
|
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.Opcodes.*
|
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.*
|
||||||
|
|
||||||
val AbstractInsnNode.isMeaningful: Boolean get() =
|
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? {
|
inline fun AbstractInsnNode.findNextOrNull(predicate: (AbstractInsnNode) -> Boolean): AbstractInsnNode? {
|
||||||
var finger = this.next
|
var finger = this.next
|
||||||
while (finger != null && !predicate(finger)) {
|
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?.insnText get() = InlineCodegenUtil.getInsnText(this)
|
||||||
val AbstractInsnNode?.debugText get() =
|
val AbstractInsnNode?.debugText get() =
|
||||||
if (this == null) "<null>" else "${this.javaClass.simpleName}: $insnText"
|
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;
|
package org.jetbrains.kotlin.resolve.jvm;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.kotlin.builtins.PrimitiveType;
|
||||||
import org.jetbrains.org.objectweb.asm.Type;
|
import org.jetbrains.org.objectweb.asm.Type;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -83,6 +84,40 @@ public class AsmTypes {
|
|||||||
return Type.getObjectType("kotlin/reflect/" + className);
|
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
|
@NotNull
|
||||||
public static Type getType(@NotNull Class<?> javaClass) {
|
public static Type getType(@NotNull Class<?> javaClass) {
|
||||||
Type type = TYPES_MAP.get(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")
|
@TestMetadata("compiler/testData/codegen/box/closures/closureInsideClosure")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@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")
|
@TestMetadata("compiler/testData/codegen/box/closures/closureInsideClosure")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@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")
|
@TestMetadata("compiler/testData/codegen/bytecodeText/checkcast")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ inline fun foo(f: () -> Unit) {
|
|||||||
// RESULT: 1: I
|
// RESULT: 1: I
|
||||||
frame = main:7, FrameSharedVarLocalVarKt {frameSharedVarLocalVar}
|
frame = main:7, FrameSharedVarLocalVarKt {frameSharedVarLocalVar}
|
||||||
local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} (sp = frameSharedVarLocalVar.kt, 3)
|
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)
|
local = var1: int = 1 (sp = frameSharedVarLocalVar.kt, 4)
|
||||||
field = element: int = 1 (sp = Ref.!EXT!)
|
|
||||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
|
||||||
Process finished with exit code 0
|
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")
|
@TestMetadata("compiler/testData/codegen/box/closures/closureInsideClosure")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user