JVM: keep track of which regenerated field is for which parameter

This commit is contained in:
pyos
2022-04-28 15:46:59 +02:00
committed by Mikhael Bogdanov
parent 60c27affbb
commit 64e8bc31e2
4 changed files with 45 additions and 58 deletions
@@ -129,13 +129,13 @@ class AnonymousObjectTransformer(
val allCapturedParamBuilder = ParametersBuilder.newBuilder() val allCapturedParamBuilder = ParametersBuilder.newBuilder()
val constructorParamBuilder = ParametersBuilder.newBuilder() val constructorParamBuilder = ParametersBuilder.newBuilder()
val additionalFakeParams = extractParametersMappingAndPatchConstructor( extractParametersMappingAndPatchConstructor(
constructor!!, allCapturedParamBuilder, constructorParamBuilder, transformationInfo, parentRemapper constructor!!, allCapturedParamBuilder, constructorParamBuilder, transformationInfo, parentRemapper
) )
val deferringMethods = ArrayList<DeferredMethodVisitor>() val deferringMethods = ArrayList<DeferredMethodVisitor>()
generateConstructorAndFields(classBuilder, allCapturedParamBuilder, constructorParamBuilder, parentRemapper, additionalFakeParams) generateConstructorAndFields(classBuilder, constructorParamBuilder, parentRemapper)
val coroutineTransformer = CoroutineTransformer( val coroutineTransformer = CoroutineTransformer(
inliningContext, inliningContext,
@@ -341,10 +341,8 @@ class AnonymousObjectTransformer(
private fun generateConstructorAndFields( private fun generateConstructorAndFields(
classBuilder: ClassBuilder, classBuilder: ClassBuilder,
allCapturedBuilder: ParametersBuilder,
constructorInlineBuilder: ParametersBuilder, constructorInlineBuilder: ParametersBuilder,
parentRemapper: FieldRemapper, parentRemapper: FieldRemapper
constructorAdditionalFakeParams: List<CapturedParamInfo>
) { ) {
val constructorParams = constructorInlineBuilder.buildParameters() val constructorParams = constructorInlineBuilder.buildParameters()
val constructorParamTypes = constructorParams.filter { !it.isSkipped }.map { it.type }.drop(1) val constructorParamTypes = constructorParams.filter { !it.isSkipped }.map { it.type }.drop(1)
@@ -359,47 +357,24 @@ class AnonymousObjectTransformer(
constructorVisitor.visitLabel(newBodyStartLabel) constructorVisitor.visitLabel(newBodyStartLabel)
//initialize captured fields //initialize captured fields
val capturedIndexes = IntArray(constructorParams.parameters.size) var nextParamOffset = 0
var index = 0 for (param in constructorParams) {
var size = 0 if (param.isSkipped) continue
for (info in constructorParams) { val info = param.fieldEquivalent ?: param
if (!info.isSkipped) { //not inlined if (info is CapturedParamInfo && !info.isSkipInConstructor) {
if (info.isCaptured || info is CapturedParamInfo) {
capturedIndexes[index] = size
index++
}
size += info.type.size
}
}
for ((paramIndex, info) in allCapturedBuilder.listCaptured().filter { it.functionalArgument !is LambdaInfo }.withIndex()) {
if (!info.isSkipInConstructor) {
val desc = info.type.descriptor val desc = info.type.descriptor
val access = AsmUtil.NO_FLAG_PACKAGE_PRIVATE or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_FINAL val access = AsmUtil.NO_FLAG_PACKAGE_PRIVATE or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_FINAL
classBuilder.newField(NO_ORIGIN, access, info.newFieldName, desc, null, null) classBuilder.newField(NO_ORIGIN, access, info.newFieldName, desc, null, null)
constructorVisitor.visitVarInsn(Opcodes.ALOAD, 0) constructorVisitor.visitVarInsn(Opcodes.ALOAD, 0)
constructorVisitor.visitVarInsn(info.type.getOpcode(Opcodes.ILOAD), capturedIndexes[paramIndex]) constructorVisitor.visitVarInsn(info.type.getOpcode(Opcodes.ILOAD), nextParamOffset)
constructorVisitor.visitFieldInsn(Opcodes.PUTFIELD, transformationInfo.newClassName, info.newFieldName, desc) constructorVisitor.visitFieldInsn(Opcodes.PUTFIELD, transformationInfo.newClassName, info.newFieldName, desc)
} }
nextParamOffset += param.type.size
} }
//then transform constructor for (param in constructorParams) {
//HACK: in inlining into constructor we access original captured fields with field access not local var val info = param.fieldEquivalent ?: continue
//but this fields added to general params (this assumes local var access) not captured one, constructorInlineBuilder.addCapturedParamCopy(info)
//so we need to add them to captured params
for (info in constructorAdditionalFakeParams) {
val fake = constructorInlineBuilder.addCapturedParamCopy(info)
if (fake.functionalArgument is LambdaInfo) {
//set remap value to skip this fake (captured with lambda already skipped)
val composed = StackValue.field(
fake.type,
oldObjectType,
fake.newFieldName,
false,
StackValue.LOCAL_0
)
fake.remapValue = composed
}
} }
val intermediateMethodNode = MethodNode(constructor!!.access, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY) val intermediateMethodNode = MethodNode(constructor!!.access, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY)
@@ -457,11 +432,10 @@ class AnonymousObjectTransformer(
constructorParamBuilder: ParametersBuilder, constructorParamBuilder: ParametersBuilder,
transformationInfo: AnonymousObjectTransformationInfo, transformationInfo: AnonymousObjectTransformationInfo,
parentFieldRemapper: FieldRemapper parentFieldRemapper: FieldRemapper
): List<CapturedParamInfo> { ) {
val capturedParams = HashMap<Int, CapturedParamInfo>()
val capturedLambdas = LinkedHashSet<LambdaInfo>() //captured var of inlined parameter val capturedLambdas = LinkedHashSet<LambdaInfo>() //captured var of inlined parameter
val constructorAdditionalFakeParams = ArrayList<CapturedParamInfo>()
val indexToFunctionalArgument = transformationInfo.functionalArguments val indexToFunctionalArgument = transformationInfo.functionalArguments
val capturedParams = HashSet<Int>()
// Possible cases where we need to add each lambda's captures separately: // Possible cases where we need to add each lambda's captures separately:
// //
@@ -508,9 +482,7 @@ class AnonymousObjectTransformer(
if (functionalArgument is LambdaInfo) { if (functionalArgument is LambdaInfo) {
capturedLambdas.add(functionalArgument) capturedLambdas.add(functionalArgument)
} }
constructorAdditionalFakeParams.add(info) capturedParams[varIndex] = info
capturedParams.add(varIndex)
toDelete.add(parameterAload.previous) toDelete.add(parameterAload.previous)
toDelete.add(parameterAload) toDelete.add(parameterAload)
toDelete.add(fieldNode) toDelete.add(fieldNode)
@@ -521,11 +493,17 @@ class AnonymousObjectTransformer(
val paramTypes = transformationInfo.constructorDesc?.let { Type.getArgumentTypes(it) } ?: emptyArray() val paramTypes = transformationInfo.constructorDesc?.let { Type.getArgumentTypes(it) } ?: emptyArray()
for (type in paramTypes) { for (type in paramTypes) {
val info = indexToFunctionalArgument[constructorParamBuilder.nextParameterOffset] val functionalArgument = indexToFunctionalArgument[constructorParamBuilder.nextParameterOffset]
val isCaptured = capturedParams.contains(constructorParamBuilder.nextParameterOffset) val fieldEquivalent = capturedParams[constructorParamBuilder.nextParameterOffset]
val parameterInfo = constructorParamBuilder.addNextParameter(type, info is LambdaInfo) val parameterInfo = constructorParamBuilder.addNextParameter(type, functionalArgument is LambdaInfo)
parameterInfo.functionalArgument = info parameterInfo.functionalArgument = functionalArgument
parameterInfo.isCaptured = isCaptured parameterInfo.fieldEquivalent = fieldEquivalent
if (functionalArgument is LambdaInfo && parameterInfo.fieldEquivalent == null) {
// TODO: check if this is enough to support lambdas that have no field equivalent because they are only used
// in the constructor - see `LocalClassContext` in `LocalDeclarationsLowering`.
// TODO: these lambdas' captures should have no fields either.
capturedLambdas.add(functionalArgument)
}
} }
//For all inlined lambdas add their captured parameters //For all inlined lambdas add their captured parameters
@@ -576,8 +554,6 @@ class AnonymousObjectTransformer(
transformationInfo.allRecapturedParameters = allRecapturedParameters transformationInfo.allRecapturedParameters = allRecapturedParameters
transformationInfo.capturedLambdasToInline = capturedLambdas.associateBy { it.lambdaClassType.internalName } transformationInfo.capturedLambdasToInline = capturedLambdas.associateBy { it.lambdaClassType.internalName }
return constructorAdditionalFakeParams
} }
private fun addUniqueField(name: String): String { private fun addUniqueField(name: String): String {
@@ -32,7 +32,14 @@ class CapturedParamInfo : ParameterInfo {
val containingLambdaName: String val containingLambdaName: String
get() = desc.containingLambdaName get() = desc.containingLambdaName
constructor(desc: CapturedParamDesc, newFieldName: String, skipped: Boolean, index: Int, remapIndex: Int) : super( constructor(
desc: CapturedParamDesc,
newFieldName: String,
skipped: Boolean,
index: Int,
remapIndex: Int,
skipInConstructor: Boolean
) : super(
desc.type, desc.type,
skipped, skipped,
index, index,
@@ -41,10 +48,10 @@ class CapturedParamInfo : ParameterInfo {
) { ) {
this.desc = desc this.desc = desc
this.newFieldName = newFieldName this.newFieldName = newFieldName
this.isSkipInConstructor = false this.isSkipInConstructor = skipInConstructor
} }
constructor( private constructor(
desc: CapturedParamDesc, desc: CapturedParamDesc,
newFieldName: String, newFieldName: String,
skipped: Boolean, skipped: Boolean,
@@ -27,8 +27,10 @@ open class ParameterInfo(
val declarationIndex: Int, val declarationIndex: Int,
val typeOnStack: Type = type val typeOnStack: Type = type
) { ) {
// Parameters of an anonymous object constructor are all represented as locals, but some of them
// are also stored in fields.
var fieldEquivalent: CapturedParamInfo? = null
var isCaptured: Boolean = false
var functionalArgument: FunctionalArgument? = null var functionalArgument: FunctionalArgument? = null
val isSkippedOrRemapped: Boolean val isSkippedOrRemapped: Boolean
@@ -37,13 +37,15 @@ class ParametersBuilder private constructor() {
} }
fun addCapturedParam(original: CapturedParamInfo, newFieldName: String): CapturedParamInfo { fun addCapturedParam(original: CapturedParamInfo, newFieldName: String): CapturedParamInfo {
val info = CapturedParamInfo(original.desc, newFieldName, original.isSkipped, nextParameterOffset, original.index) val info = CapturedParamInfo(
original.desc, newFieldName, original.isSkipped, nextParameterOffset, original.index, original.isSkipInConstructor
)
info.functionalArgument = original.functionalArgument info.functionalArgument = original.functionalArgument
return addParameter(info) return addParameter(info)
} }
fun addCapturedParam(desc: CapturedParamDesc, newFieldName: String, skipInConstructor: Boolean): CapturedParamInfo { fun addCapturedParam(desc: CapturedParamDesc, newFieldName: String, skipInConstructor: Boolean): CapturedParamInfo {
return addParameter(CapturedParamInfo(desc, newFieldName, false, nextParameterOffset, null, skipInConstructor, -1)) return addParameter(CapturedParamInfo(desc, newFieldName, false, nextParameterOffset, -1, skipInConstructor))
} }
fun addCapturedParamCopy(copyFrom: CapturedParamInfo): CapturedParamInfo { fun addCapturedParamCopy(copyFrom: CapturedParamInfo): CapturedParamInfo {
@@ -59,7 +61,7 @@ class ParametersBuilder private constructor() {
original: ParameterInfo? original: ParameterInfo?
): CapturedParamInfo { ): CapturedParamInfo {
val info = CapturedParamInfo( val info = CapturedParamInfo(
CapturedParamDesc(containingLambdaType, fieldName, type), newFieldName, skipped, nextParameterOffset, original?.index ?: -1 CapturedParamDesc(containingLambdaType, fieldName, type), newFieldName, skipped, nextParameterOffset, original?.index ?: -1, false
) )
if (original != null) { if (original != null) {
info.functionalArgument = original.functionalArgument info.functionalArgument = original.functionalArgument