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