Skip to content

Commit

Permalink
Dynamic LVT upgrade patch
Browse files Browse the repository at this point in the history
  • Loading branch information
Su5eD committed Jul 26, 2024
1 parent 23cd480 commit 6fa7e5a
Show file tree
Hide file tree
Showing 26 changed files with 230 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.sinytra.adapter.patch.analysis.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.locals.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.selector.AnnotationHandle;
import org.sinytra.adapter.patch.analysis.selector.AnnotationValueHandle;
import org.sinytra.adapter.patch.api.MethodContext;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.sinytra.adapter.patch.analysis;
package org.sinytra.adapter.patch.analysis.locals;

import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package org.sinytra.adapter.patch.analysis;
package org.sinytra.adapter.patch.analysis.locals;

import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.sinytra.adapter.patch.analysis.params.EnhancedParamsDiff;
import org.sinytra.adapter.patch.analysis.params.ParamsDiffSnapshot;
import org.sinytra.adapter.patch.api.MethodContext;
import org.sinytra.adapter.patch.api.MethodTransform;
import org.sinytra.adapter.patch.transformer.operation.param.TransformParameters;
import org.sinytra.adapter.patch.util.AdapterUtil;
Expand All @@ -18,6 +23,25 @@

public final class LocalVarAnalyzer {

public record CapturedLocalsInfo(AdapterUtil.CapturedLocals capturedLocals, ParamsDiffSnapshot diff, List<Type> availableTypes) {}

@Nullable
public static CapturedLocalsInfo getCapturedLocals(MethodContext methodContext) {
AdapterUtil.CapturedLocals capturedLocals = AdapterUtil.getCapturedLocals(methodContext.getMixinMethod(), methodContext);
if (capturedLocals == null) {
return null;
}
// Get available local variables at the injection point in the target method
List<MethodContext.LocalVariable> available = methodContext.getTargetMethodLocals(capturedLocals.target());
if (available == null) {
return null;
}
List<Type> availableTypes = available.stream().map(MethodContext.LocalVariable::type).toList();
// Compare expected and available params
ParamsDiffSnapshot diff = EnhancedParamsDiff.createLayered(capturedLocals.expected(), availableTypes);
return new CapturedLocalsInfo(capturedLocals, diff, availableTypes);
}

public static InsnList findInitializerInsns(MethodNode methodNode, int index) {
InsnList insns = new InsnList();
outer:
Expand All @@ -39,7 +63,7 @@ public static InsnList findInitializerInsns(MethodNode methodNode, int index) {

public record CapturedLocalsUsage(LocalVariableLookup targetTable, Int2IntMap usageCount, Int2ObjectMap<InsnList> varInsnLists) {}

public record CapturedLocalsTransform(List<Integer> used, MethodTransform remover) {
public record CapturedLocalsTransform(List<Integer> used, MethodTransform remover, List<LocalVariableNode> usedLocalNodes) {
public CapturedLocalsUsage getUsage(AdapterUtil.CapturedLocals capturedLocals) {
LocalVariableLookup targetTable = new LocalVariableLookup(capturedLocals.target().methodNode());
Int2ObjectMap<InsnList> varInsnLists = new Int2ObjectOpenHashMap<>();
Expand All @@ -57,6 +81,7 @@ public static CapturedLocalsTransform analyzeCapturedLocals(AdapterUtil.Captured
int paramLocalStart = capturedLocals.paramLocalStart();
LocalVariableLookup table = capturedLocals.lvt();
List<Integer> used = new ArrayList<>();
List<LocalVariableNode> usedLocalNodes = new ArrayList<>();
for (AbstractInsnNode insn : methodNode.instructions) {
if (insn instanceof VarInsnNode varInsn) {
LocalVariableNode node = table.getByIndexOrNull(varInsn.var);
Expand All @@ -66,6 +91,7 @@ public static CapturedLocalsTransform analyzeCapturedLocals(AdapterUtil.Captured
int ordinal = table.getOrdinal(node);
if (ordinal >= paramLocalStart && ordinal <= capturedLocals.paramLocalEnd()) {
used.add(ordinal - 1); // Subtract 1, which represents the CI param
usedLocalNodes.add(node);
}
}
}
Expand All @@ -76,7 +102,7 @@ public static CapturedLocalsTransform analyzeCapturedLocals(AdapterUtil.Captured
.boxed().sorted(Collections.reverseOrder())
.forEach(b::remove))
.build();
return new CapturedLocalsTransform(used, remover);
return new CapturedLocalsTransform(used, remover, usedLocalNodes);
}

public static void findVariableInitializerInsns(MethodNode methodNode, boolean isStatic, int index, Int2ObjectMap<InsnList> varInsnLists, Int2IntMap usageCount) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.sinytra.adapter.patch.analysis;
package org.sinytra.adapter.patch.analysis.locals;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.sinytra.adapter.patch.analysis;
package org.sinytra.adapter.patch.analysis.locals;

import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.sinytra.adapter.patch.analysis.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.locals.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.selector.AnnotationHandle;
import org.sinytra.adapter.patch.analysis.selector.AnnotationValueHandle;
import org.sinytra.adapter.patch.util.MethodQualifier;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.sinytra.adapter.patch.analysis.LocalVarAnalyzer;
import org.sinytra.adapter.patch.analysis.locals.LocalVarAnalyzer;
import org.sinytra.adapter.patch.analysis.params.EnhancedParamsDiff;
import org.sinytra.adapter.patch.analysis.params.LayeredParamsDiffSnapshot;
import org.sinytra.adapter.patch.analysis.params.SimpleParamsDiffSnapshot;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.sinytra.adapter.patch.analysis.*;
import org.sinytra.adapter.patch.analysis.locals.LocalVarAnalyzer;
import org.sinytra.adapter.patch.analysis.locals.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.selector.AnnotationHandle;
import org.sinytra.adapter.patch.analysis.selector.AnnotationValueHandle;
import org.sinytra.adapter.patch.api.*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.sinytra.adapter.patch.LVTOffsets;
import org.sinytra.adapter.patch.analysis.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.params.EnhancedParamsDiff;
import org.sinytra.adapter.patch.analysis.locals.LocalVarAnalyzer;
import org.sinytra.adapter.patch.analysis.locals.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.params.ParamsDiffSnapshot;
import org.sinytra.adapter.patch.analysis.params.SimpleParamsDiffSnapshot;
import org.sinytra.adapter.patch.analysis.selector.AnnotationHandle;
Expand Down Expand Up @@ -153,27 +153,19 @@ private Patch.Result offsetVariableIndex(ClassNode classNode, MethodNode methodN

@Nullable
private ParamsDiffSnapshot compareParameters(ClassNode classNode, MethodNode methodNode, MethodContext methodContext) {
AdapterUtil.CapturedLocals capturedLocals = AdapterUtil.getCapturedLocals(methodNode, methodContext);
if (capturedLocals == null) {
LocalVarAnalyzer.CapturedLocalsInfo info = LocalVarAnalyzer.getCapturedLocals(methodContext);
if (info == null) {
return null;
}

// Get available local variables at the injection point in the target method
List<MethodContext.LocalVariable> available = methodContext.getTargetMethodLocals(capturedLocals.target());
if (available == null) {
return null;
}
List<Type> availableTypes = available.stream().map(MethodContext.LocalVariable::type).toList();
// Compare expected and available params
ParamsDiffSnapshot diff = EnhancedParamsDiff.createLayered(capturedLocals.expected(), availableTypes);
ParamsDiffSnapshot diff = info.diff();
if (diff.isEmpty()) {
// No changes required
return null;
}
AdapterUtil.CapturedLocals capturedLocals = info.capturedLocals();
// Replacements are only partially supported, as most would require LVT fixups and converters
if (!diff.replacements().isEmpty() && areReplacedParamsUsed(diff.replacements(), methodNode)) {
// Check if we can rearrange parameters
SimpleParamsDiffSnapshot rearrange = rearrangeParameters(capturedLocals.expected(), availableTypes);
SimpleParamsDiffSnapshot rearrange = rearrangeParameters(capturedLocals.expected(), info.availableTypes());
if (rearrange == null) {
LOGGER.debug(MIXINPATCH, "Tried to replace local variables in mixin method {}.{} using {}", classNode.name, methodNode.name + methodNode.desc, diff.replacements());
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.sinytra.adapter.patch.transformer.dynfix;

import com.mojang.datafixers.util.Pair;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
import org.sinytra.adapter.patch.analysis.locals.LocalVarAnalyzer;
import org.sinytra.adapter.patch.api.MethodContext;
import org.sinytra.adapter.patch.api.MixinConstants;
import org.sinytra.adapter.patch.api.Patch;
import org.sinytra.adapter.patch.api.PatchAuditTrail;
import org.sinytra.adapter.patch.util.AdapterUtil;

import java.util.ArrayList;
import java.util.List;

public class DynFixLocalCaptureUpgrade implements DynamicFixer<DynFixLocalCaptureUpgrade.Data> {
public record Data(AdapterUtil.CapturedLocals capturedLocals, LocalVarAnalyzer.CapturedLocalsTransform transform) {}

@Override
@Nullable
public DynFixLocalCaptureUpgrade.Data prepare(MethodContext methodContext) {
if (methodContext.findDirtyInjectionTarget() == null) {
return null;
}
MethodNode methodNode = methodContext.getMixinMethod();
if (!methodContext.capturesLocals()) {
return null;
}
Type[] paramTypes = Type.getArgumentTypes(methodNode.desc);
List<Pair<AnnotationNode, Type>> localAnnotations = AdapterUtil.getAnnotatedParameters(methodNode, paramTypes, MixinConstants.LOCAL, Pair::of);
if (!localAnnotations.isEmpty()) {
return null;
}

LocalVarAnalyzer.CapturedLocalsInfo info = LocalVarAnalyzer.getCapturedLocals(methodContext);
if (info == null || info.diff().isEmpty()) {
return null;
}

LocalVarAnalyzer.CapturedLocalsTransform transform = LocalVarAnalyzer.analyzeCapturedLocals(info.capturedLocals(), methodNode);
List<Type> availableTypes = new ArrayList<>(info.availableTypes());
for (LocalVariableNode node : transform.usedLocalNodes()) {
Type expected = Type.getType(node.desc);
List<Type> available = availableTypes.stream().filter(expected::equals).toList();
if (available.size() != 1) {
return null;
}
availableTypes.remove(available.getFirst());
}

return new Data(info.capturedLocals(), transform);
}

@Override
@Nullable
public FixResult apply(ClassNode classNode, MethodNode methodNode, MethodContext methodContext, PatchAuditTrail auditTrail, Data data) {
Patch.Result result = data.transform().remover().apply(methodContext);
if (result == Patch.Result.PASS) {
return null;
}

int start = data.capturedLocals().paramLocalStart();
Type[] args = Type.getArgumentTypes(methodNode.desc);

for (int i = start; i < args.length; i++) {
methodNode.visitParameterAnnotation(i, MixinConstants.LOCAL, false);
}

auditTrail.recordAudit(this, methodContext, "Upgrade captured locals");

return FixResult.of(result, PatchAuditTrail.Match.FULL);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.sinytra.adapter.patch.analysis.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.locals.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.MethodCallAnalyzer;
import org.sinytra.adapter.patch.analysis.MethodLabelComparator;
import org.sinytra.adapter.patch.analysis.selector.AnnotationHandle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.sinytra.adapter.patch.analysis.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.locals.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.MethodCallAnalyzer;
import org.sinytra.adapter.patch.analysis.params.EnhancedParamsDiff;
import org.sinytra.adapter.patch.analysis.params.LayeredParamsDiffSnapshot;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
@SuppressWarnings({"rawtypes", "unchecked"})
public class DynamicInjectionPointPatch implements MethodTransform {
private static final Logger LOGGER = LogUtils.getLogger();
private static final List<DynamicFixer<?>> PASSIVE = List.of(
new DynFixLocalCaptureUpgrade()
);
private static final List<DynamicFixer<?>> PREPATCH = List.of(
new DynFixResolveAmbigousTarget()
);
Expand All @@ -28,13 +31,30 @@ public class DynamicInjectionPointPatch implements MethodTransform {

@Override
public Patch.Result apply(ClassNode classNode, MethodNode methodNode, MethodContext methodContext, PatchContext context) {
if (methodContext.failsDirtyInjectionCheck() && methodContext.findCleanInjectionTarget() != null) {
if (methodContext.findCleanInjectionTarget() == null) {
return Patch.Result.PASS;
}

PatchAuditTrail auditTrail = context.environment().auditTrail();
Patch.Result result = Patch.Result.PASS;

for (DynamicFixer fix : PASSIVE) {
Object data = fix.prepare(methodContext);
if (data != null) {
auditTrail.recordResult(methodContext, PatchAuditTrail.Match.NONE);
DynamicFixer.FixResult fixResult = fix.apply(classNode, methodNode, methodContext, auditTrail, data);
if (fixResult != null) {
auditTrail.recordResult(methodContext, fixResult.match());
result = result.or(fixResult.result());
}
}
}

if (methodContext.failsDirtyInjectionCheck()) {
LOGGER.debug(MIXINPATCH, "Considering method {}.{}", classNode.name, methodNode.name);

PatchAuditTrail auditTrail = context.environment().auditTrail();
auditTrail.recordResult(methodContext, PatchAuditTrail.Match.NONE);

Patch.Result result = Patch.Result.PASS;
for (DynamicFixer fix : PREPATCH) {
Object data = fix.prepare(methodContext);
if (data != null) {
Expand All @@ -55,8 +75,8 @@ public Patch.Result apply(ClassNode classNode, MethodNode methodNode, MethodCont
}
}
}
return result;
}
return Patch.Result.PASS;

return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.sinytra.adapter.patch.analysis.LocalVarAnalyzer;
import org.sinytra.adapter.patch.analysis.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.locals.LocalVarAnalyzer;
import org.sinytra.adapter.patch.analysis.locals.LocalVariableLookup;
import org.sinytra.adapter.patch.api.*;
import org.sinytra.adapter.patch.transformer.operation.param.TransformParameters;
import org.sinytra.adapter.patch.util.AdapterUtil;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
import org.objectweb.asm.commons.InstructionAdapter;
import org.objectweb.asm.tree.*;
import org.sinytra.adapter.patch.PatchInstance;
import org.sinytra.adapter.patch.analysis.LVTSnapshot;
import org.sinytra.adapter.patch.analysis.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.locals.LVTSnapshot;
import org.sinytra.adapter.patch.analysis.locals.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.params.SimpleParamsDiffSnapshot;
import org.sinytra.adapter.patch.analysis.selector.AnnotationHandle;
import org.sinytra.adapter.patch.api.*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import org.sinytra.adapter.patch.api.Patch;
import org.sinytra.adapter.patch.api.PatchContext;
import org.sinytra.adapter.patch.analysis.selector.AnnotationHandle;
import org.sinytra.adapter.patch.analysis.LVTSnapshot;
import org.sinytra.adapter.patch.analysis.locals.LVTSnapshot;
import org.sinytra.adapter.patch.fixes.ModifyArgsOffsetTransformer;
import org.sinytra.adapter.patch.util.AdapterUtil;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.sinytra.adapter.patch.api.MethodContext;
import org.sinytra.adapter.patch.api.Patch;
import org.sinytra.adapter.patch.api.PatchContext;
import org.sinytra.adapter.patch.analysis.LVTSnapshot;
import org.sinytra.adapter.patch.analysis.locals.LVTSnapshot;
import org.sinytra.adapter.patch.util.AdapterUtil;
import org.slf4j.Logger;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.ParameterNode;
import org.sinytra.adapter.patch.analysis.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.locals.LocalVariableLookup;
import org.sinytra.adapter.patch.api.MethodContext;
import org.sinytra.adapter.patch.api.Patch;
import org.sinytra.adapter.patch.api.PatchContext;
import org.sinytra.adapter.patch.analysis.LVTSnapshot;
import org.sinytra.adapter.patch.analysis.locals.LVTSnapshot;
import org.sinytra.adapter.patch.util.AdapterUtil;
import org.slf4j.Logger;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import org.sinytra.adapter.patch.api.MethodContext;
import org.sinytra.adapter.patch.api.Patch;
import org.sinytra.adapter.patch.api.PatchContext;
import org.sinytra.adapter.patch.analysis.LVTSnapshot;
import org.sinytra.adapter.patch.analysis.locals.LVTSnapshot;
import org.sinytra.adapter.patch.util.AdapterUtil;

import java.util.List;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.sinytra.adapter.patch.analysis.LocalVariableLookup;
import org.sinytra.adapter.patch.analysis.locals.LocalVariableLookup;
import org.sinytra.adapter.patch.api.MethodContext;
import org.sinytra.adapter.patch.api.Patch;
import org.sinytra.adapter.patch.api.PatchContext;
Expand Down
Loading

0 comments on commit 6fa7e5a

Please sign in to comment.