public RemapInfo doRemap(int index) { int remappedIndex; if (index < params.getArgsSizeOnStack()) { ParameterInfo info = params.getParameterByDeclarationSlot(index); StackValue remapped = remapValues[index]; if (info.isSkipped || remapped == null) { return new RemapInfo(info); } if (info.isRemapped()) { return new RemapInfo(remapped, info, REMAPPED); } else { remappedIndex = ((StackValue.Local) remapped).index; } } else { remappedIndex = actualParamsSize - params.getArgsSizeOnStack() + index; // captured params not used directly in this inlined method, they used in // closure } return new RemapInfo( StackValue.local(remappedIndex + additionalShift, AsmTypes.OBJECT_TYPE), null, SHIFT); }
@Override public void putCapturedValueOnStack( @NotNull StackValue stackValue, @NotNull Type valueType, int paramIndex) { if (shouldPutValue(stackValue.type, stackValue, null)) { stackValue.put(stackValue.type, codegen.v); } putCapturedInLocal(stackValue.type, stackValue, null, paramIndex); }
@Override public void putValueIfNeeded( @Nullable ValueParameterDescriptor valueParameterDescriptor, @NotNull Type parameterType, @NotNull StackValue value) { if (shouldPutValue(parameterType, value, valueParameterDescriptor)) { value.put(parameterType, codegen.v); } afterParameterPut(parameterType, value, valueParameterDescriptor); }
private void putParameterOnStack(ParameterInfo... infos) { int[] index = new int[infos.length]; for (int i = 0; i < infos.length; i++) { ParameterInfo info = infos[i]; if (!info.isSkippedOrRemapped()) { index[i] = codegen.getFrameMap().enterTemp(info.getType()); } else { index[i] = -1; } } for (int i = infos.length - 1; i >= 0; i--) { ParameterInfo info = infos[i]; if (!info.isSkippedOrRemapped()) { Type type = info.type; StackValue.local(index[i], type).store(StackValue.onStack(type), codegen.v); } } }
@NotNull public List<CapturedParamDesc> getCapturedVars() { // lazy initialization cause it would be calculated after object creation if (capturedVars == null) { capturedVars = new ArrayList<CapturedParamDesc>(); if (closure.getCaptureThis() != null) { Type type = typeMapper.mapType(closure.getCaptureThis()); EnclosedValueDescriptor descriptor = new EnclosedValueDescriptor( AsmUtil.CAPTURED_THIS_FIELD, /* descriptor = */ null, StackValue.field( type, closureClassType, AsmUtil.CAPTURED_THIS_FIELD, false, StackValue.LOCAL_0), type); capturedVars.add(getCapturedParamInfo(descriptor)); } if (closure.getCaptureReceiverType() != null) { Type type = typeMapper.mapType(closure.getCaptureReceiverType()); EnclosedValueDescriptor descriptor = new EnclosedValueDescriptor( AsmUtil.CAPTURED_RECEIVER_FIELD, /* descriptor = */ null, StackValue.field( type, closureClassType, AsmUtil.CAPTURED_RECEIVER_FIELD, false, StackValue.LOCAL_0), type); capturedVars.add(getCapturedParamInfo(descriptor)); } for (EnclosedValueDescriptor descriptor : closure.getCaptureVariables().values()) { capturedVars.add(getCapturedParamInfo(descriptor)); } } return capturedVars; }
public void visitVarInsn(int opcode, int var, InstructionAdapter mv) { RemapInfo remapInfo = remap(var); StackValue value = remapInfo.value; if (value instanceof StackValue.Local) { if (remapInfo.parameterInfo != null) { // All remapped value parameters can't be rewritten except case of default ones. // On remapping default parameter to actual value there is only one instruction that writes // to it according to mask value // but if such parameter remapped then it passed and this mask branch code never executed // TODO add assertion about parameter default value: descriptor is required opcode = value.type.getOpcode( InlineCodegenUtil.isStoreInstruction(opcode) ? Opcodes.ISTORE : Opcodes.ILOAD); } mv.visitVarInsn(opcode, ((StackValue.Local) value).index); if (remapInfo.parameterInfo != null) { StackValue.coerce(value.type, remapInfo.parameterInfo.type, mv); } } else { assert remapInfo.parameterInfo != null : "Non local value should have parameter info"; value.put(remapInfo.parameterInfo.type, mv); } }
public LocalVarRemapper(Parameters params, int additionalShift) { this.additionalShift = additionalShift; this.params = params; remapValues = new StackValue[params.getArgsSizeOnStack()]; int realSize = 0; for (ParameterInfo info : params) { Integer shift = params.getDeclarationSlot(info); if (!info.isSkippedOrRemapped()) { remapValues[shift] = StackValue.local(realSize, AsmTypes.OBJECT_TYPE); realSize += info.getType().getSize(); } else { remapValues[shift] = info.isRemapped() ? info.getRemapValue() : null; } } actualParamsSize = realSize; }
public static void putStackValuesIntoLocals( List<Type> directOrder, int shift, InstructionAdapter iv, String descriptor) { Type[] actualParams = Type.getArgumentTypes(descriptor); assert actualParams.length == directOrder.size() : "Number of expected and actual params should be equals!"; int size = 0; for (Type next : directOrder) { size += next.getSize(); } shift += size; int index = directOrder.size(); for (Type next : Lists.reverse(directOrder)) { shift -= next.getSize(); Type typeOnStack = actualParams[--index]; if (!typeOnStack.equals(next)) { StackValue.onStack(typeOnStack).put(next, iv); } iv.store(shift, next); } }
private List<CapturedParamInfo> extractParametersMappingAndPatchConstructor( @NotNull MethodNode constructor, @NotNull ParametersBuilder capturedParamBuilder, @NotNull ParametersBuilder constructorParamBuilder, @NotNull final AnonymousObjectGeneration anonymousObjectGen, @NotNull FieldRemapper parentFieldRemapper) { CapturedParamOwner owner = new CapturedParamOwner() { @Override public Type getType() { return Type.getObjectType(anonymousObjectGen.getOwnerInternalName()); } }; Set<LambdaInfo> capturedLambdas = new LinkedHashSet<LambdaInfo>(); // captured var of inlined parameter List<CapturedParamInfo> constructorAdditionalFakeParams = new ArrayList<CapturedParamInfo>(); Map<Integer, LambdaInfo> indexToLambda = anonymousObjectGen.getLambdasToInline(); Set<Integer> capturedParams = new HashSet<Integer>(); // load captured parameters and patch instruction list (NB: there is also could be object // fields) AbstractInsnNode cur = constructor.instructions.getFirst(); 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 (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( owner, fieldName, newFieldName, Type.getType(fieldNode.desc), lambdaInfo != null, null); if (lambdaInfo != null) { info.setLambda(lambdaInfo); capturedLambdas.add(lambdaInfo); } 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; } } } } cur = cur.getNext(); } constructorParamBuilder.addThis(oldObjectType, false); String constructorDesc = anonymousObjectGen.getConstructorDesc(); if (constructorDesc == null) { // in case of anonymous object with empty closure constructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE); } Type[] types = Type.getArgumentTypes(constructorDesc); for (Type type : types) { LambdaInfo info = indexToLambda.get(constructorParamBuilder.getNextValueParameterIndex()); ParameterInfo parameterInfo = constructorParamBuilder.addNextParameter(type, info != null, null); parameterInfo.setLambda(info); if (capturedParams.contains(parameterInfo.getIndex())) { parameterInfo.setCaptured(true); } else { // otherwise it's super constructor parameter } } // 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<String, LambdaInfo>(); // captured var of inlined parameter List<CapturedParamDesc> allRecapturedParameters = new ArrayList<CapturedParamDesc>(); boolean addCapturedNotAddOuter = parentFieldRemapper.isRoot() || (parentFieldRemapper instanceof InlinedLambdaRemapper && parentFieldRemapper.getParent().isRoot()); Map<String, CapturedParamInfo> alreadyAdded = new HashMap<String, CapturedParamInfo>(); for (LambdaInfo info : capturedLambdas) { if (addCapturedNotAddOuter) { for (CapturedParamDesc desc : info.getCapturedVars()) { String key = desc.getFieldName() + "$$$" + desc.getType().getClassName(); CapturedParamInfo alreadyAddedParam = alreadyAdded.get(key); CapturedParamInfo recapturedParamInfo = capturedParamBuilder.addCapturedParam( desc, alreadyAddedParam != null ? alreadyAddedParam.getNewFieldName() : getNewFieldName(desc.getFieldName(), false)); StackValue composed = StackValue.field( desc.getType(), oldObjectType, /*TODO owner type*/ recapturedParamInfo.getNewFieldName(), false, StackValue.LOCAL_0); recapturedParamInfo.setRemapValue(composed); allRecapturedParameters.add(desc); constructorParamBuilder .addCapturedParam(recapturedParamInfo, recapturedParamInfo.getNewFieldName()) .setRemapValue(composed); if (alreadyAddedParam != null) { recapturedParamInfo.setSkipInConstructor(true); } if (isThis0(desc.getFieldName())) { alreadyAdded.put(key, recapturedParamInfo); } } } capturedLambdasToInline.put(info.getLambdaClassType().getInternalName(), info); } if (parentFieldRemapper instanceof InlinedLambdaRemapper && !capturedLambdas.isEmpty() && !addCapturedNotAddOuter) { // lambda with non InlinedLambdaRemapper already have outer FieldRemapper parent = parentFieldRemapper.getParent(); assert parent instanceof RegeneratedLambdaFieldRemapper; final Type ownerType = Type.getObjectType(parent.getLambdaInternalName()); CapturedParamDesc desc = new CapturedParamDesc( new CapturedParamOwner() { @Override public Type getType() { return ownerType; } }, InlineCodegenUtil.THIS, ownerType); CapturedParamInfo recapturedParamInfo = capturedParamBuilder.addCapturedParam( desc, InlineCodegenUtil.THIS$0 /*outer lambda/object*/); StackValue composed = StackValue.LOCAL_0; recapturedParamInfo.setRemapValue(composed); allRecapturedParameters.add(desc); constructorParamBuilder .addCapturedParam(recapturedParamInfo, recapturedParamInfo.getNewFieldName()) .setRemapValue(composed); } anonymousObjectGen.setAllRecapturedParameters(allRecapturedParameters); anonymousObjectGen.setCapturedLambdasToInline(capturedLambdasToInline); return constructorAdditionalFakeParams; }
private void generateConstructorAndFields( @NotNull ClassBuilder classBuilder, @NotNull ParametersBuilder allCapturedBuilder, @NotNull ParametersBuilder constructorInlineBuilder, @NotNull AnonymousObjectGeneration anonymousObjectGen, @NotNull FieldRemapper parentRemapper, @NotNull List<CapturedParamInfo> constructorAdditionalFakeParams) { List<Type> descTypes = new ArrayList<Type>(); Parameters constructorParams = constructorInlineBuilder.buildParameters(); int[] capturedIndexes = new int[constructorParams.getReal().size() + constructorParams.getCaptured().size()]; int index = 0; int size = 0; // complex processing cause it could have super constructor call params for (ParameterInfo info : constructorParams) { if (!info.isSkipped()) { // not inlined if (info.isCaptured() || info instanceof CapturedParamInfo) { capturedIndexes[index] = size; index++; } if (size != 0) { // skip this descTypes.add(info.getType()); } size += info.getType().getSize(); } } String constructorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, descTypes.toArray(new Type[descTypes.size()])); // TODO for inline method make public class anonymousObjectGen.setNewConstructorDescriptor(constructorDescriptor); MethodVisitor constructorVisitor = classBuilder.newMethod( NO_ORIGIN, AsmUtil.NO_FLAG_PACKAGE_PRIVATE, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY); // initialize captured fields List<NewJavaField> newFieldsWithSkipped = TransformationUtilsKt.getNewFieldsToGenerate(allCapturedBuilder.listCaptured()); List<FieldInfo> fieldInfoWithSkipped = TransformationUtilsKt.transformToFieldInfo(newLambdaType, 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); } 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); if (fake.getLambda() != null) { // set remap value to skip this fake (captured with lambda already skipped) StackValue composed = StackValue.field( fake.getType(), oldObjectType, fake.getNewFieldName(), false, StackValue.LOCAL_0); fake.setRemapValue(composed); } } inlineMethodAndUpdateGlobalResult( anonymousObjectGen, parentRemapper, capturedFieldInitializer, constructor, constructorInlineBuilder, true); constructorVisitor.visitEnd(); AsmUtil.genClosureFields( TransformationUtilsKt.toNameTypePair( TransformationUtilsKt.filterSkipped(newFieldsWithSkipped)), classBuilder); }