Convert AnonymousObjectTransformer.java to Kotlin

This commit is contained in:
Mikhael Bogdanov
2017-05-05 17:58:51 +02:00
parent 36c43b630e
commit 3fe152aad3
@@ -14,420 +14,392 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.codegen.inline; package org.jetbrains.kotlin.codegen.inline
import com.intellij.util.ArrayUtil; import com.intellij.util.ArrayUtil
import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.AsmUtil; import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.ClassBuilder; import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.FieldInfo; import org.jetbrains.kotlin.codegen.coroutines.COROUTINE_IMPL_ASM_TYPE
import org.jetbrains.kotlin.codegen.StackValue; import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.isThis0
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.Companion.NO_ORIGIN
import org.jetbrains.org.objectweb.asm.*; import org.jetbrains.org.objectweb.asm.*
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.tree.*; import org.jetbrains.org.objectweb.asm.tree.*
import java.util.*
import java.util.*; class AnonymousObjectTransformer(
transformationInfo: AnonymousObjectTransformationInfo,
private val inliningContext: InliningContext,
private val isSameModule: Boolean
) : ObjectTransformer<AnonymousObjectTransformationInfo>(transformationInfo, inliningContext.state) {
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.isThis0; private val oldObjectType = Type.getObjectType(transformationInfo.oldClassName)
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjectTransformationInfo> { private val fieldNames = hashMapOf<String, MutableList<String>>()
private final InliningContext inliningContext;
private final Type oldObjectType;
private final boolean isSameModule;
private final Map<String, List<String>> fieldNames = new HashMap<>();
private MethodNode constructor; private var constructor: MethodNode? = null
private String sourceInfo; private var sourceInfo: String? = null
private String debugInfo; private var debugInfo: String? = null
private SourceMapper sourceMapper; private var sourceMapper: SourceMapper? = null
public AnonymousObjectTransformer( override fun doTransform(parentRemapper: FieldRemapper): InlineResult {
@NotNull AnonymousObjectTransformationInfo transformationInfo, val innerClassNodes = ArrayList<InnerClassNode>()
@NotNull InliningContext inliningContext, val classBuilder = createRemappingClassBuilderViaFactory(inliningContext)
boolean isSameModule val methodsToTransform = ArrayList<MethodNode>()
) {
super(transformationInfo, inliningContext.state);
this.isSameModule = isSameModule;
this.inliningContext = inliningContext;
this.oldObjectType = Type.getObjectType(transformationInfo.getOldClassName());
}
@Override createClassReader().accept(object : ClassVisitor(InlineCodegenUtil.API, classBuilder.visitor) {
@NotNull override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<String>) {
public InlineResult doTransform(@NotNull FieldRemapper parentRemapper) { classBuilder.defineClass(null, version, access, name, signature, superName, interfaces)
List<InnerClassNode> innerClassNodes = new ArrayList<>(); if (COROUTINE_IMPL_ASM_TYPE.internalName == superName) {
ClassBuilder classBuilder = createRemappingClassBuilderViaFactory(inliningContext); inliningContext.isContinuation = true
List<MethodNode> methodsToTransform = new ArrayList<>();
createClassReader().accept(new ClassVisitor(InlineCodegenUtil.API, classBuilder.getVisitor()) {
@Override
public void visit(int version, int access, @NotNull String name, String signature, String superName, String[] interfaces) {
classBuilder.defineClass(null, version, access, name, signature, superName, interfaces);
if(CoroutineCodegenUtilKt.COROUTINE_IMPL_ASM_TYPE.getInternalName().equals(superName)) {
inliningContext.setContinuation(true);
} }
} }
@Override override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) {
public void visitInnerClass(@NotNull String name, String outerName, String innerName, int access) { innerClassNodes.add(InnerClassNode(name, outerName, innerName, access))
innerClassNodes.add(new InnerClassNode(name, outerName, innerName, access));
} }
@Override override fun visitMethod(
public MethodVisitor visitMethod( access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?
int access, @NotNull String name, @NotNull String desc, String signature, String[] exceptions ): MethodVisitor {
) { val node = MethodNode(access, name, desc, signature, exceptions)
MethodNode node = new MethodNode(access, name, desc, signature, exceptions); if (name == "<init>") {
if (name.equals("<init>")) {
if (constructor != null) { if (constructor != null) {
throw new RuntimeException("Lambda, SAM or anonymous object should have only one constructor"); throw RuntimeException("Lambda, SAM or anonymous object should have only one constructor")
} }
constructor = node; constructor = node
} }
else { else {
methodsToTransform.add(node); methodsToTransform.add(node)
} }
return node; return node
} }
@Override override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
public FieldVisitor visitField(int access, @NotNull String name, @NotNull String desc, String signature, Object value) { addUniqueField(name)
addUniqueField(name);
if (InlineCodegenUtil.isCapturedFieldName(name)) { if (InlineCodegenUtil.isCapturedFieldName(name)) {
return null; return null
} }
else { else {
return classBuilder.newField(JvmDeclarationOrigin.NO_ORIGIN, access, name, desc, signature, value); return classBuilder.newField(JvmDeclarationOrigin.NO_ORIGIN, access, name, desc, signature, value)
} }
} }
@Override override fun visitSource(source: String, debug: String?) {
public void visitSource(String source, String debug) { sourceInfo = source
sourceInfo = source; debugInfo = debug
debugInfo = debug;
} }
@Override override fun visitEnd() {}
public void visitEnd() { }, ClassReader.SKIP_FRAMES)
}
}, ClassReader.SKIP_FRAMES);
if (!inliningContext.isInliningLambda) { if (!inliningContext.isInliningLambda) {
if (debugInfo != null && !debugInfo.isEmpty()) { if (debugInfo != null && !debugInfo!!.isEmpty()) {
sourceMapper = SourceMapper.Companion.createFromSmap(SMAPParser.parse(debugInfo)); sourceMapper = SourceMapper.createFromSmap(SMAPParser.parse(debugInfo!!))
} }
else { else {
//seems we can't do any clever mapping cause we don't know any about original class name //seems we can't do any clever mapping cause we don't know any about original class name
sourceMapper = IdenticalSourceMapper.INSTANCE; sourceMapper = IdenticalSourceMapper
} }
if (sourceInfo != null && !InlineCodegenUtil.GENERATE_SMAP) { if (sourceInfo != null && !InlineCodegenUtil.GENERATE_SMAP) {
classBuilder.visitSource(sourceInfo, debugInfo); classBuilder.visitSource(sourceInfo!!, debugInfo)
} }
} }
else { else {
if (sourceInfo != null) { if (sourceInfo != null) {
classBuilder.visitSource(sourceInfo, debugInfo); classBuilder.visitSource(sourceInfo!!, debugInfo)
} }
sourceMapper = IdenticalSourceMapper.INSTANCE; sourceMapper = IdenticalSourceMapper
} }
ParametersBuilder allCapturedParamBuilder = ParametersBuilder.newBuilder(); val allCapturedParamBuilder = ParametersBuilder.newBuilder()
ParametersBuilder constructorParamBuilder = ParametersBuilder.newBuilder(); val constructorParamBuilder = ParametersBuilder.newBuilder()
List<CapturedParamInfo> additionalFakeParams = val additionalFakeParams = extractParametersMappingAndPatchConstructor(
extractParametersMappingAndPatchConstructor(constructor, allCapturedParamBuilder, constructorParamBuilder, constructor!!, allCapturedParamBuilder, constructorParamBuilder,transformationInfo, parentRemapper
transformationInfo, parentRemapper); )
List<DeferredMethodVisitor> deferringMethods = new ArrayList<>(); val deferringMethods = ArrayList<DeferredMethodVisitor>()
generateConstructorAndFields(classBuilder, allCapturedParamBuilder, constructorParamBuilder, parentRemapper, additionalFakeParams); generateConstructorAndFields(classBuilder, allCapturedParamBuilder, constructorParamBuilder, parentRemapper, additionalFakeParams)
for (MethodNode next : methodsToTransform) { for (next in methodsToTransform) {
DeferredMethodVisitor deferringVisitor = newMethod(classBuilder, next); val deferringVisitor = newMethod(classBuilder, next)
InlineResult funResult = val funResult = inlineMethodAndUpdateGlobalResult(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, false)
inlineMethodAndUpdateGlobalResult(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, false);
Type returnType = Type.getReturnType(next.desc); val returnType = Type.getReturnType(next.desc)
if (!AsmUtil.isPrimitive(returnType)) { if (!AsmUtil.isPrimitive(returnType)) {
String oldFunReturnType = returnType.getInternalName(); val oldFunReturnType = returnType.internalName
String newFunReturnType = funResult.getChangedTypes().get(oldFunReturnType); val newFunReturnType = funResult.getChangedTypes()[oldFunReturnType]
if (newFunReturnType != null) { if (newFunReturnType != null) {
inliningContext.typeRemapper.addAdditionalMappings(oldFunReturnType, newFunReturnType); inliningContext.typeRemapper.addAdditionalMappings(oldFunReturnType, newFunReturnType)
} }
} }
deferringMethods.add(deferringVisitor); deferringMethods.add(deferringVisitor)
} }
for (DeferredMethodVisitor method : deferringMethods) { deferringMethods.forEach { method ->
InlineCodegenUtil.removeFinallyMarkers(method.getIntermediate()); InlineCodegenUtil.removeFinallyMarkers(method.intermediate)
method.visitEnd(); method.visitEnd()
} }
SourceMapper.Companion.flushToClassBuilder(sourceMapper, classBuilder); SourceMapper.flushToClassBuilder(sourceMapper!!, classBuilder)
ClassVisitor visitor = classBuilder.getVisitor(); val visitor = classBuilder.visitor
for (InnerClassNode node : innerClassNodes) { innerClassNodes.forEach {
visitor.visitInnerClass(node.name, node.outerName, node.innerName, node.access); node -> visitor.visitInnerClass(node.name, node.outerName, node.innerName, node.access)
} }
writeOuterInfo(visitor); writeOuterInfo(visitor)
classBuilder.done(); classBuilder.done()
return transformationResult; return transformationResult
} }
private void writeOuterInfo(@NotNull ClassVisitor visitor) { private fun writeOuterInfo(visitor: ClassVisitor) {
InlineCallSiteInfo info = inliningContext.getCallSiteInfo(); val info = inliningContext.callSiteInfo
visitor.visitOuterClass(info.getOwnerClassName(), info.getFunctionName(), info.getFunctionDesc()); visitor.visitOuterClass(info.ownerClassName, info.functionName, info.functionDesc)
} }
@NotNull private fun inlineMethodAndUpdateGlobalResult(
private InlineResult inlineMethodAndUpdateGlobalResult( parentRemapper: FieldRemapper,
@NotNull FieldRemapper parentRemapper, deferringVisitor: MethodVisitor,
@NotNull MethodVisitor deferringVisitor, next: MethodNode,
@NotNull MethodNode next, allCapturedParamBuilder: ParametersBuilder,
@NotNull ParametersBuilder allCapturedParamBuilder, isConstructor: Boolean
boolean isConstructor ): InlineResult {
) { val funResult = inlineMethod(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, isConstructor)
InlineResult funResult = inlineMethod(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, isConstructor); transformationResult.merge(funResult)
transformationResult.merge(funResult); transformationResult.reifiedTypeParametersUsages.mergeAll(funResult.reifiedTypeParametersUsages)
transformationResult.getReifiedTypeParametersUsages().mergeAll(funResult.getReifiedTypeParametersUsages()); return funResult
return funResult;
} }
@NotNull private fun inlineMethod(
private InlineResult inlineMethod( parentRemapper: FieldRemapper,
@NotNull FieldRemapper parentRemapper, deferringVisitor: MethodVisitor,
@NotNull MethodVisitor deferringVisitor, sourceNode: MethodNode,
@NotNull MethodNode sourceNode, capturedBuilder: ParametersBuilder,
@NotNull ParametersBuilder capturedBuilder, isConstructor: Boolean
boolean isConstructor ): InlineResult {
) { val typeParametersToReify = inliningContext.reifiedTypeInliner.reifyInstructions(sourceNode)
ReifiedTypeParametersUsages typeParametersToReify = inliningContext.reifiedTypeInliner.reifyInstructions(sourceNode); val parameters = if (isConstructor) capturedBuilder.buildParameters() else getMethodParametersWithCaptured(capturedBuilder, sourceNode)
Parameters parameters =
isConstructor ? capturedBuilder.buildParameters() : getMethodParametersWithCaptured(capturedBuilder, sourceNode);
RegeneratedLambdaFieldRemapper remapper = new RegeneratedLambdaFieldRemapper( val remapper = RegeneratedLambdaFieldRemapper(
oldObjectType.getInternalName(), transformationInfo.getNewClassName(), parameters, oldObjectType.internalName, transformationInfo.newClassName, parameters,
transformationInfo.getCapturedLambdasToInline(), parentRemapper, isConstructor transformationInfo.capturedLambdasToInline, parentRemapper, isConstructor
); )
MethodInliner inliner = new MethodInliner( val inliner = MethodInliner(
sourceNode, sourceNode,
parameters, parameters,
inliningContext.subInline(transformationInfo.getNameGenerator()), inliningContext.subInline(transformationInfo.nameGenerator),
remapper, remapper,
isSameModule, isSameModule,
"Transformer for " + transformationInfo.getOldClassName(), "Transformer for " + transformationInfo.oldClassName,
sourceMapper, sourceMapper!!,
new InlineCallSiteInfo( InlineCallSiteInfo(
transformationInfo.getOldClassName(), transformationInfo.oldClassName,
sourceNode.name, sourceNode.name,
isConstructor ? transformationInfo.getNewConstructorDescriptor() : sourceNode.desc if (isConstructor) transformationInfo.newConstructorDescriptor else sourceNode.desc
), ), null
null )
);
InlineResult result = inliner.doInline(deferringVisitor, new LocalVarRemapper(parameters, 0), false, LabelOwner.NOT_APPLICABLE); val result = inliner.doInline(deferringVisitor, LocalVarRemapper(parameters, 0), false, LabelOwner.NOT_APPLICABLE)
result.getReifiedTypeParametersUsages().mergeAll(typeParametersToReify); result.reifiedTypeParametersUsages.mergeAll(typeParametersToReify)
deferringVisitor.visitMaxs(-1, -1); deferringVisitor.visitMaxs(-1, -1)
return result; return result
} }
private void generateConstructorAndFields( private fun generateConstructorAndFields(
@NotNull ClassBuilder classBuilder, classBuilder: ClassBuilder,
@NotNull ParametersBuilder allCapturedBuilder, allCapturedBuilder: ParametersBuilder,
@NotNull ParametersBuilder constructorInlineBuilder, constructorInlineBuilder: ParametersBuilder,
@NotNull FieldRemapper parentRemapper, parentRemapper: FieldRemapper,
@NotNull List<CapturedParamInfo> constructorAdditionalFakeParams constructorAdditionalFakeParams: List<CapturedParamInfo>
) { ) {
List<Type> descTypes = new ArrayList<>(); val descTypes = ArrayList<Type>()
Parameters constructorParams = constructorInlineBuilder.buildParameters(); val constructorParams = constructorInlineBuilder.buildParameters()
int[] capturedIndexes = new int[constructorParams.getParameters().size()]; val capturedIndexes = IntArray(constructorParams.parameters.size)
int index = 0; var index = 0
int size = 0; var size = 0
//complex processing cause it could have super constructor call params //complex processing cause it could have super constructor call params
for (ParameterInfo info : constructorParams) { for (info in constructorParams) {
if (!info.isSkipped) { //not inlined if (!info.isSkipped) { //not inlined
if (info.isCaptured() || info instanceof CapturedParamInfo) { if (info.isCaptured || info is CapturedParamInfo) {
capturedIndexes[index] = size; capturedIndexes[index] = size
index++; index++
} }
if (size != 0) { //skip this if (size != 0) { //skip this
descTypes.add(info.getType()); descTypes.add(info.getType())
} }
size += info.getType().getSize(); size += info.getType().size
} }
} }
String constructorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, descTypes.toArray(new Type[descTypes.size()])); val constructorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, *descTypes.toTypedArray())
//TODO for inline method make public class //TODO for inline method make public class
transformationInfo.setNewConstructorDescriptor(constructorDescriptor); transformationInfo.newConstructorDescriptor = constructorDescriptor
MethodVisitor constructorVisitor = classBuilder.newMethod( val constructorVisitor = classBuilder.newMethod(
NO_ORIGIN, constructor.access, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY NO_ORIGIN, constructor!!.access, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY
); )
Label newBodyStartLabel = new Label(); val newBodyStartLabel = Label()
constructorVisitor.visitLabel(newBodyStartLabel); constructorVisitor.visitLabel(newBodyStartLabel)
//initialize captured fields //initialize captured fields
List<NewJavaField> newFieldsWithSkipped = TransformationUtilsKt.getNewFieldsToGenerate(allCapturedBuilder.listCaptured()); val newFieldsWithSkipped = getNewFieldsToGenerate(allCapturedBuilder.listCaptured())
List<FieldInfo> fieldInfoWithSkipped = val fieldInfoWithSkipped = transformToFieldInfo(Type.getObjectType(transformationInfo.newClassName), newFieldsWithSkipped)
TransformationUtilsKt.transformToFieldInfo(Type.getObjectType(transformationInfo.getNewClassName()), newFieldsWithSkipped);
int paramIndex = 0; var paramIndex = 0
InstructionAdapter capturedFieldInitializer = new InstructionAdapter(constructorVisitor); val capturedFieldInitializer = InstructionAdapter(constructorVisitor)
for (int i = 0; i < fieldInfoWithSkipped.size(); i++) { for (i in fieldInfoWithSkipped.indices) {
FieldInfo fieldInfo = fieldInfoWithSkipped.get(i); val fieldInfo = fieldInfoWithSkipped[i]
if (!newFieldsWithSkipped.get(i).getSkip()) { if (!newFieldsWithSkipped[i].skip) {
AsmUtil.genAssignInstanceFieldFromParam(fieldInfo, capturedIndexes[paramIndex], capturedFieldInitializer); AsmUtil.genAssignInstanceFieldFromParam(fieldInfo, capturedIndexes[paramIndex], capturedFieldInitializer)
} }
paramIndex++; paramIndex++
} }
//then transform constructor //then transform constructor
//HACK: in inlinining into constructor we access original captured fields with field access not local var //HACK: in inlinining into constructor we access original captured fields with field access not local var
//but this fields added to general params (this assumes local var access) not captured one, //but this fields added to general params (this assumes local var access) not captured one,
//so we need to add them to captured params //so we need to add them to captured params
for (CapturedParamInfo info : constructorAdditionalFakeParams) { for (info in constructorAdditionalFakeParams) {
CapturedParamInfo fake = constructorInlineBuilder.addCapturedParamCopy(info); val fake = constructorInlineBuilder.addCapturedParamCopy(info)
if (fake.getLambda() != null) { if (fake.lambda != null) {
//set remap value to skip this fake (captured with lambda already skipped) //set remap value to skip this fake (captured with lambda already skipped)
StackValue composed = StackValue.field( val composed = StackValue.field(
fake.getType(), fake.getType(),
oldObjectType, oldObjectType,
fake.getNewFieldName(), fake.newFieldName,
false, false,
StackValue.LOCAL_0 StackValue.LOCAL_0
); )
fake.setRemapValue(composed); fake.remapValue = composed
} }
} }
MethodNode intermediateMethodNode = val intermediateMethodNode = MethodNode(constructor!!.access, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY)
new MethodNode(constructor.access, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY); inlineMethodAndUpdateGlobalResult(parentRemapper, intermediateMethodNode, constructor!!, constructorInlineBuilder, true)
inlineMethodAndUpdateGlobalResult(parentRemapper, intermediateMethodNode, constructor, constructorInlineBuilder, true); InlineCodegenUtil.removeFinallyMarkers(intermediateMethodNode)
InlineCodegenUtil.removeFinallyMarkers(intermediateMethodNode);
AbstractInsnNode first = intermediateMethodNode.instructions.getFirst(); val first = intermediateMethodNode.instructions.first
Label oldStartLabel = first instanceof LabelNode ? ((LabelNode) first).getLabel() : null; val oldStartLabel = if (first is LabelNode) first.label else null
intermediateMethodNode.accept(new MethodBodyVisitor(capturedFieldInitializer) { intermediateMethodNode.accept(object : MethodBodyVisitor(capturedFieldInitializer) {
@Override override fun visitLocalVariable(
public void visitLocalVariable( name: String, desc: String, signature: String?, start: Label, end: Label, index: Int
@NotNull String name, @NotNull String desc, String signature, @NotNull Label start, @NotNull Label end, int index
) { ) {
if (oldStartLabel == start) { super.visitLocalVariable(
start = newBodyStartLabel;//patch for jack&jill name, desc, signature,
} //patch for jack&jill
super.visitLocalVariable(name, desc, signature, start, end, index); if (oldStartLabel === start) newBodyStartLabel else start,
end, index
)
} }
}); })
constructorVisitor.visitEnd(); constructorVisitor.visitEnd()
AsmUtil.genClosureFields( AsmUtil.genClosureFields(
TransformationUtilsKt.toNameTypePair(TransformationUtilsKt.filterSkipped(newFieldsWithSkipped)), classBuilder toNameTypePair(filterSkipped(newFieldsWithSkipped)), classBuilder
); )
} }
@NotNull private fun getMethodParametersWithCaptured(capturedBuilder: ParametersBuilder, sourceNode: MethodNode): Parameters {
private Parameters getMethodParametersWithCaptured(@NotNull ParametersBuilder capturedBuilder, @NotNull MethodNode sourceNode) { val builder = ParametersBuilder.initializeBuilderFrom(oldObjectType, sourceNode.desc)
ParametersBuilder builder = ParametersBuilder.initializeBuilderFrom(oldObjectType, sourceNode.desc); for (param in capturedBuilder.listCaptured()) {
for (CapturedParamInfo param : capturedBuilder.listCaptured()) { builder.addCapturedParamCopy(param)
builder.addCapturedParamCopy(param);
} }
return builder.buildParameters(); return builder.buildParameters()
} }
@NotNull private fun newMethod(builder: ClassBuilder, original: MethodNode): DeferredMethodVisitor {
private static DeferredMethodVisitor newMethod(@NotNull ClassBuilder builder, @NotNull MethodNode original) { return DeferredMethodVisitor(
return new DeferredMethodVisitor( MethodNode(
new MethodNode(
original.access, original.name, original.desc, original.signature, original.access, original.name, original.desc, original.signature,
ArrayUtil.toStringArray(original.exceptions) ArrayUtil.toStringArray(original.exceptions)
),
() -> builder.newMethod(
NO_ORIGIN, original.access, original.name, original.desc, original.signature,
ArrayUtil.toStringArray(original.exceptions)
) )
); ) {
builder.newMethod(
NO_ORIGIN, original.access, original.name, original.desc, original.signature,
ArrayUtil.toStringArray(original.exceptions)
)
}
} }
@NotNull private fun extractParametersMappingAndPatchConstructor(
private List<CapturedParamInfo> extractParametersMappingAndPatchConstructor( constructor: MethodNode,
@NotNull MethodNode constructor, capturedParamBuilder: ParametersBuilder,
@NotNull ParametersBuilder capturedParamBuilder, constructorParamBuilder: ParametersBuilder,
@NotNull ParametersBuilder constructorParamBuilder, transformationInfo: AnonymousObjectTransformationInfo,
@NotNull AnonymousObjectTransformationInfo transformationInfo, parentFieldRemapper: FieldRemapper
@NotNull FieldRemapper parentFieldRemapper ): List<CapturedParamInfo> {
) { val capturedLambdas = LinkedHashSet<LambdaInfo>() //captured var of inlined parameter
Set<LambdaInfo> capturedLambdas = new LinkedHashSet<>(); //captured var of inlined parameter val constructorAdditionalFakeParams = ArrayList<CapturedParamInfo>()
List<CapturedParamInfo> constructorAdditionalFakeParams = new ArrayList<>(); val indexToLambda = transformationInfo.lambdasToInline
Map<Integer, LambdaInfo> indexToLambda = transformationInfo.getLambdasToInline(); val capturedParams = HashSet<Int>()
Set<Integer> capturedParams = new HashSet<>();
//load captured parameters and patch instruction list (NB: there is also could be object fields) //load captured parameters and patch instruction list (NB: there is also could be object fields)
AbstractInsnNode cur = constructor.instructions.getFirst(); var cur: AbstractInsnNode? = constructor.instructions.first
while (cur != null) { while (cur != null) {
if (cur instanceof FieldInsnNode) { if (cur is FieldInsnNode) {
FieldInsnNode fieldNode = (FieldInsnNode) cur; val fieldNode = cur as FieldInsnNode?
String fieldName = fieldNode.name; val fieldName = fieldNode!!.name
if (fieldNode.getOpcode() == Opcodes.PUTFIELD && InlineCodegenUtil.isCapturedFieldName(fieldName)) { if (fieldNode.opcode == Opcodes.PUTFIELD && InlineCodegenUtil.isCapturedFieldName(fieldName)) {
boolean isPrevVarNode = fieldNode.getPrevious() instanceof VarInsnNode; val isPrevVarNode = fieldNode.previous is VarInsnNode
boolean isPrevPrevVarNode = isPrevVarNode && fieldNode.getPrevious().getPrevious() instanceof VarInsnNode; val isPrevPrevVarNode = isPrevVarNode && fieldNode.previous.previous is VarInsnNode
if (isPrevPrevVarNode) { if (isPrevPrevVarNode) {
VarInsnNode node = (VarInsnNode) fieldNode.getPrevious().getPrevious(); val node = fieldNode.previous.previous as VarInsnNode
if (node.var == 0) { if (node.`var` == 0) {
VarInsnNode previous = (VarInsnNode) fieldNode.getPrevious(); val previous = fieldNode.previous as VarInsnNode
int varIndex = previous.var; val varIndex = previous.`var`
LambdaInfo lambdaInfo = indexToLambda.get(varIndex); val lambdaInfo = indexToLambda[varIndex]
String newFieldName = val newFieldName = if (isThis0(fieldName) && shouldRenameThis0(parentFieldRemapper, indexToLambda.values))
isThis0(fieldName) && shouldRenameThis0(parentFieldRemapper, indexToLambda.values()) getNewFieldName(fieldName, true)
? getNewFieldName(fieldName, true) else
: fieldName; fieldName
CapturedParamInfo info = capturedParamBuilder.addCapturedParam( val info = capturedParamBuilder.addCapturedParam(
Type.getObjectType(transformationInfo.getOldClassName()), fieldName, newFieldName, Type.getObjectType(transformationInfo.oldClassName), fieldName, newFieldName,
Type.getType(fieldNode.desc), lambdaInfo != null, null Type.getType(fieldNode.desc), lambdaInfo != null, null
); )
if (lambdaInfo != null) { if (lambdaInfo != null) {
info.setLambda(lambdaInfo); info.lambda = lambdaInfo
capturedLambdas.add(lambdaInfo); capturedLambdas.add(lambdaInfo)
} }
constructorAdditionalFakeParams.add(info); constructorAdditionalFakeParams.add(info)
capturedParams.add(varIndex); capturedParams.add(varIndex)
constructor.instructions.remove(previous.getPrevious()); constructor.instructions.remove(previous.previous)
constructor.instructions.remove(previous); constructor.instructions.remove(previous)
AbstractInsnNode temp = cur; val temp = cur
cur = cur.getNext(); cur = cur.next
constructor.instructions.remove(temp); constructor.instructions.remove(temp)
continue; continue
} }
} }
} }
} }
cur = cur.getNext(); cur = cur.next
} }
constructorParamBuilder.addThis(oldObjectType, false); constructorParamBuilder.addThis(oldObjectType, false)
String constructorDesc = transformationInfo.getConstructorDesc(); var constructorDesc = transformationInfo.constructorDesc
if (constructorDesc == null) { if (constructorDesc == null) {
// in case of anonymous object with empty closure // in case of anonymous object with empty closure
constructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE); constructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE)
} }
Type[] types = Type.getArgumentTypes(constructorDesc); val types = Type.getArgumentTypes(constructorDesc!!)
for (Type type : types) { for (type in types) {
LambdaInfo info = indexToLambda.get(constructorParamBuilder.getNextParameterOffset()); val info = indexToLambda[constructorParamBuilder.nextParameterOffset]
ParameterInfo parameterInfo = constructorParamBuilder.addNextParameter(type, info != null); val parameterInfo = constructorParamBuilder.addNextParameter(type, info != null)
parameterInfo.setLambda(info); parameterInfo.lambda = info
if (capturedParams.contains(parameterInfo.getIndex())) { if (capturedParams.contains(parameterInfo.index)) {
parameterInfo.setCaptured(true); parameterInfo.isCaptured = true
} }
else { else {
//otherwise it's super constructor parameter //otherwise it's super constructor parameter
@@ -436,102 +408,96 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
//For all inlined lambdas add their captured parameters //For all inlined lambdas add their captured parameters
//TODO: some of such parameters could be skipped - we should perform additional analysis //TODO: some of such parameters could be skipped - we should perform additional analysis
Map<String, LambdaInfo> capturedLambdasToInline = new HashMap<>(); //captured var of inlined parameter val capturedLambdasToInline = HashMap<String, LambdaInfo>() //captured var of inlined parameter
List<CapturedParamDesc> allRecapturedParameters = new ArrayList<>(); val allRecapturedParameters = ArrayList<CapturedParamDesc>()
boolean addCapturedNotAddOuter = val addCapturedNotAddOuter = parentFieldRemapper.isRoot || parentFieldRemapper is InlinedLambdaRemapper && parentFieldRemapper.getParent().isRoot
parentFieldRemapper.isRoot() || val alreadyAdded = HashMap<String, CapturedParamInfo>()
(parentFieldRemapper instanceof InlinedLambdaRemapper && parentFieldRemapper.getParent().isRoot()); for (info in capturedLambdas) {
Map<String, CapturedParamInfo> alreadyAdded = new HashMap<>();
for (LambdaInfo info : capturedLambdas) {
if (addCapturedNotAddOuter) { if (addCapturedNotAddOuter) {
for (CapturedParamDesc desc : info.getCapturedVars()) { for (desc in info.capturedVars) {
String key = desc.getFieldName() + "$$$" + desc.getType().getClassName(); val key = desc.fieldName + "$$$" + desc.type.className
CapturedParamInfo alreadyAddedParam = alreadyAdded.get(key); val alreadyAddedParam = alreadyAdded[key]
CapturedParamInfo recapturedParamInfo = capturedParamBuilder.addCapturedParam( val recapturedParamInfo = capturedParamBuilder.addCapturedParam(
desc, desc,
alreadyAddedParam != null ? alreadyAddedParam.getNewFieldName() : getNewFieldName(desc.getFieldName(), false), alreadyAddedParam?.newFieldName ?: getNewFieldName(desc.fieldName, false),
alreadyAddedParam != null alreadyAddedParam != null
); )
StackValue composed = StackValue.field( val composed = StackValue.field(
desc.getType(), desc.type,
oldObjectType, /*TODO owner type*/ oldObjectType, /*TODO owner type*/
recapturedParamInfo.getNewFieldName(), recapturedParamInfo.newFieldName,
false, false,
StackValue.LOCAL_0 StackValue.LOCAL_0
); )
recapturedParamInfo.setRemapValue(composed); recapturedParamInfo.remapValue = composed
allRecapturedParameters.add(desc); allRecapturedParameters.add(desc)
constructorParamBuilder.addCapturedParam(recapturedParamInfo, recapturedParamInfo.getNewFieldName()) constructorParamBuilder.addCapturedParam(recapturedParamInfo, recapturedParamInfo.newFieldName).remapValue = composed
.setRemapValue(composed);
if (isThis0(desc.getFieldName())) { if (isThis0(desc.fieldName)) {
alreadyAdded.put(key, recapturedParamInfo); alreadyAdded.put(key, recapturedParamInfo)
} }
} }
} }
capturedLambdasToInline.put(info.getLambdaClassType().getInternalName(), info); capturedLambdasToInline.put(info.lambdaClassType.internalName, info)
} }
if (parentFieldRemapper instanceof InlinedLambdaRemapper && !capturedLambdas.isEmpty() && !addCapturedNotAddOuter) { if (parentFieldRemapper is InlinedLambdaRemapper && !capturedLambdas.isEmpty() && !addCapturedNotAddOuter) {
//lambda with non InlinedLambdaRemapper already have outer //lambda with non InlinedLambdaRemapper already have outer
FieldRemapper parent = parentFieldRemapper.getParent(); val parent = parentFieldRemapper.getParent()
assert parent instanceof RegeneratedLambdaFieldRemapper; assert(parent is RegeneratedLambdaFieldRemapper)
Type ownerType = Type.getObjectType(parent.getLambdaInternalName()); val ownerType = Type.getObjectType(parent.lambdaInternalName)
CapturedParamDesc desc = new CapturedParamDesc(ownerType, InlineCodegenUtil.THIS, ownerType); val desc = CapturedParamDesc(ownerType, InlineCodegenUtil.THIS, ownerType)
CapturedParamInfo recapturedParamInfo = val recapturedParamInfo = capturedParamBuilder.addCapturedParam(desc, InlineCodegenUtil.`THIS$0`/*outer lambda/object*/, false)
capturedParamBuilder.addCapturedParam(desc, InlineCodegenUtil.THIS$0/*outer lambda/object*/, false); val composed = StackValue.LOCAL_0
StackValue composed = StackValue.LOCAL_0; recapturedParamInfo.remapValue = composed
recapturedParamInfo.setRemapValue(composed); allRecapturedParameters.add(desc)
allRecapturedParameters.add(desc);
constructorParamBuilder.addCapturedParam(recapturedParamInfo, recapturedParamInfo.getNewFieldName()).setRemapValue(composed); constructorParamBuilder.addCapturedParam(recapturedParamInfo, recapturedParamInfo.newFieldName).remapValue = composed
} }
transformationInfo.setAllRecapturedParameters(allRecapturedParameters); transformationInfo.allRecapturedParameters = allRecapturedParameters
transformationInfo.setCapturedLambdasToInline(capturedLambdasToInline); transformationInfo.capturedLambdasToInline = capturedLambdasToInline
return constructorAdditionalFakeParams; return constructorAdditionalFakeParams
} }
private static boolean shouldRenameThis0(@NotNull FieldRemapper parentFieldRemapper, @NotNull Collection<LambdaInfo> values) { private fun shouldRenameThis0(parentFieldRemapper: FieldRemapper, values: Collection<LambdaInfo>): Boolean {
if (isFirstDeclSiteLambdaFieldRemapper(parentFieldRemapper)) { if (isFirstDeclSiteLambdaFieldRemapper(parentFieldRemapper)) {
for (LambdaInfo value : values) { for (value in values) {
for (CapturedParamDesc desc : value.getCapturedVars()) { for (desc in value.capturedVars) {
if (isThis0(desc.getFieldName())) { if (isThis0(desc.fieldName)) {
return true; return true
} }
} }
} }
} }
return false; return false
} }
@NotNull private fun getNewFieldName(oldName: String, originalField: Boolean): String {
private String getNewFieldName(@NotNull String oldName, boolean originalField) { if (InlineCodegenUtil.`THIS$0` == oldName) {
if (InlineCodegenUtil.THIS$0.equals(oldName)) {
if (!originalField) { if (!originalField) {
return oldName; return oldName
} }
else { else {
//rename original 'this$0' in declaration site lambda (inside inline function) to use this$0 only for outer lambda/object access on call site //rename original 'this$0' in declaration site lambda (inside inline function) to use this$0 only for outer lambda/object access on call site
return addUniqueField(oldName + InlineCodegenUtil.INLINE_FUN_THIS_0_SUFFIX); return addUniqueField(oldName + InlineCodegenUtil.INLINE_FUN_THIS_0_SUFFIX)
} }
} }
return addUniqueField(oldName + InlineCodegenUtil.INLINE_TRANSFORMATION_SUFFIX); return addUniqueField(oldName + InlineCodegenUtil.INLINE_TRANSFORMATION_SUFFIX)
} }
@NotNull private fun addUniqueField(name: String): String {
private String addUniqueField(@NotNull String name) { val existNames = fieldNames.getOrPut(name) { LinkedList<String>() }
List<String> existNames = fieldNames.computeIfAbsent(name, unused -> new LinkedList<>()); val suffix = if (existNames.isEmpty()) "" else "$" + existNames.size
String suffix = existNames.isEmpty() ? "" : "$" + existNames.size(); val newName = name + suffix
String newName = name + suffix; existNames.add(newName)
existNames.add(newName); return newName
return newName;
} }
private static boolean isFirstDeclSiteLambdaFieldRemapper(@NotNull FieldRemapper parentRemapper) { private fun isFirstDeclSiteLambdaFieldRemapper(parentRemapper: FieldRemapper): Boolean {
return !(parentRemapper instanceof RegeneratedLambdaFieldRemapper) && !(parentRemapper instanceof InlinedLambdaRemapper); return parentRemapper !is RegeneratedLambdaFieldRemapper && parentRemapper !is InlinedLambdaRemapper
} }
} }