Skip to content

Commit

Permalink
Add additional comments to LvtNamer
Browse files Browse the repository at this point in the history
  • Loading branch information
DenWav committed Jul 26, 2023
1 parent 993084e commit e09736a
Showing 1 changed file with 69 additions and 31 deletions.
100 changes: 69 additions & 31 deletions src/main/java/io/papermc/codebook/lvt/LvtNamer.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,17 @@ public static void fillNames(
return;
}

if (method.parentClass().name().endsWith("VerticalGradientCondition")) {
LvtNamer.class.getName();
}

boolean isLambdaMethod = false;
// Determine if this method exists as a lambda expression inside another method
// If it does, we need to keep track of the LVTs we inherit
@Nullable AsmMethodData outerMethod = null;
int @Nullable [] outerMethodParamLvtIndices = null;
final @Nullable List<MethodClosure<MethodData>> lambdaCalls = method.get(HypoHydration.LAMBDA_CALLS);
// Only track synthetic, non-synthetic means a method reference which does not behave as a closure (does not
// capture LVT)
if (lambdaCalls != null && method.isSynthetic()) {
for (final MethodClosure<MethodData> lambdaCall : lambdaCalls) {
// lambdaCall.getClosure() -> The lambda method
// lambdaCall.getContainingMethod() -> The outer method
if (lambdaCall.getClosure().equals(method)) {
outerMethod = (AsmMethodData) lambdaCall.getContainingMethod();
if (outerMethod.equals(method)) {
Expand All @@ -82,7 +83,6 @@ public static void fillNames(
continue;
}
outerMethodParamLvtIndices = lambdaCall.getParamLvtIndices();
isLambdaMethod = true;
// there can only be 1 outer method
break;
}
Expand All @@ -97,6 +97,9 @@ public static void fillNames(
}

final ClassData parentClass = method.parentClass();
// A method cannot be both a lambda expression and a local class, so if we've already determined an outer
// method,
// there's nothing to do here.
localCheck:
if (outerMethod == null) {
final @Nullable List<MethodClosure<ClassData>> localClasses = parentClass.get(HypoHydration.LOCAL_CLASSES);
Expand All @@ -106,9 +109,11 @@ public static void fillNames(

final MethodClosure<ClassData> localClass = localClasses.get(0);
outerMethod = (AsmMethodData) localClass.getContainingMethod();
outerMethodParamLvtIndices = localClass.getParamLvtIndices();
// local classes don't capture as lvt, so don't assign `outerMethodParamLvtIndices`
}

// This method (`fillNames`) will ensure the outer method has names defined in its scope first.
// If the scope is already computed this is a no-op
if (outerMethod != null) {
fillNames(context, outerMethod, mappings);
}
Expand All @@ -118,6 +123,8 @@ public static void fillNames(
final @Nullable MethodMapping methodMapping =
getMethodMapping(classMapping, method.name(), method.descriptorText());

// We inherit names from our outer scope, if it exists. These names will be included in our scope for any
// potential inner scopes (other nested lambdas or local classes) that are present in this method too
final Set<String> scopedNames;
if (outerMethod != null) {
final @Nullable Set<String> outerScope = outerMethod.get(SCOPED_NAMES);
Expand All @@ -126,10 +133,18 @@ public static void fillNames(
scopedNames = new HashSet<>();
}

// Keep track of which parameter LVT slots do have names already
// These are names we won't want to override, so remember which LVT slot we used
// We probably aren't going to use this whole array, we just have enough slots for if we have names already for
// all parameters
// therefore `paramIndex` is the "size" of this array in terms of how many slots are actually used
int paramIndex = 0;
final int[] paramLvtsWithNames = new int[method.params().size()];
Arrays.fill(paramLvtsWithNames, -1);

// Keep track of method parameter mappings that are present, and add them to the current scope
// These are names which we can assume to be trusted
// TODO: We cannot trust these names in local classes or lambda expressions
for (int i = 0; i < method.params().size(); i++) {
final int paramLvtIndex = toLvtIndex(i, method);
final @Nullable MethodParameterMapping paramMapping = getParameterMapping(methodMapping, paramLvtIndex);
Expand All @@ -139,6 +154,7 @@ public static void fillNames(
}
}

// If there's no LVT table there's nothing for us to process
if (node.localVariables == null) {
method.store(SCOPED_NAMES, scopedNames);
return;
Expand All @@ -153,14 +169,16 @@ public static void fillNames(

// set our captured lvt names, if possible
// only applies to lambda methods, not local classes
if (outerMethodParamLvtIndices != null && isLambdaMethod) {
if (outerMethodParamLvtIndices != null) {
// param counts are typically low (< 20), so not doing anything clever here
final List<LocalVariableNode> outerLvts = outerMethod.getNode().localVariables;
for (final LocalVariableNode outerLvt : outerLvts) {
final int ourLvtIndex = find(outerMethodParamLvtIndices, outerLvt.index);
if (ourLvtIndex != -1 && find(ourCapturedLvts, ourLvtIndex) == -1) {
ourCapturedLvts[ourCapturedLvtIndex++] = ourLvtIndex;
for (final LocalVariableNode ourLvt : node.localVariables) {
// we can apply this name to any matching LVT slot, since duplicates
// are guaranteed to be in different scopes
if (ourLvt.index == ourLvtIndex && ourLvt.desc.equals(outerLvt.desc)) {
ourLvt.name = outerLvt.name;
}
Expand All @@ -169,12 +187,35 @@ public static void fillNames(
}
}

// Consider parameters we already have names for as "captured".
// Capture in this context just means we've already captured a name for a variable and should therefore leave it
// alone
for (int i = 0; i < paramIndex; i++) {
// Safeguard to prevent us from marking a captured LVT twice
if (find(ourCapturedLvts, paramLvtsWithNames[i]) == -1) {
ourCapturedLvts[ourCapturedLvtIndex++] = paramLvtsWithNames[i];
}
}

// Keep track of which names (and their slots) we've used
// This means if we come across a slot again with the same type, we can
// re-use the existing name.
//
// This is ideal because sometimes the different scopes are actually for the same variable
// by merging these cases we can prevent names from appearing in code with de-dupe numbers
// when there are actually no duplicates.
//
// Examples:
// Thing thing;
// if (flag) {
// thing = createThing();
// } else {
// thing = null;
// }
//
// This will (probably) result in 2 separate scopes, and the same variable will have 2 separate
// LVT entries for the same slot. By re-using the name we can prevent this from being created
// as `thing1` or `thing2` for no reason.
int usedNameIndex = 0;
final @Nullable UsedLvtName[] usedNames = new UsedLvtName[node.localVariables.size()];

Expand All @@ -184,6 +225,7 @@ public static void fillNames(
continue;
}
if (ourCapturedLvtIndex != -1) {
// Check if we've already set this name, if so, skip it
if (find(ourCapturedLvts, lvt.index, ourCapturedLvtIndex) != -1) {
continue;
}
Expand All @@ -202,6 +244,7 @@ public static void fillNames(
usedNames[usedNameIndex++] = new UsedLvtName(lvt.name, lvt.desc, lvt.index);
scopedNames.add(suggestedName);

// Also update the parameters table if this LVT slot is a parameter
final int paramIndexFromLvt = fromLvtIndex(lvt.index, method);
if (paramIndexFromLvt != -1 && node.parameters != null && node.parameters.size() > paramIndexFromLvt) {
node.parameters.get(paramIndexFromLvt).name = suggestedName;
Expand All @@ -217,39 +260,30 @@ private static int find(final int[] array, final int value) {
return find(array, value, array.length);
}

private static int find(final int[] array, final int value, final int w) {
for (int i = 0; i < array.length; i++) {
private static int find(final int[] array, final int value, final int len) {
for (int i = 0; i < len; i++) {
if (array[i] == value) {
return i;
}
}
return -1;
}

/*
* Transform the given parameter index into an LVT index.
*/
private static int toLvtIndex(final int index, final MethodData method) {
if (index == -1) {
return -1;
}

int currentIndex = 0;
int currentLvtIndex = method.isStatic() ? 0 : 1;

for (final JvmType param : method.params()) {
if (currentIndex == index) {
return currentLvtIndex;
}

currentIndex++;
currentLvtIndex++;
if (param == PrimitiveType.LONG || param == PrimitiveType.DOUBLE) {
currentLvtIndex++;
}
}

return -1;
return _getLvtIndex(index, method, false);
}

/*
* Transform the given LVT index into a parameter index.
*/
private static int fromLvtIndex(final int lvtIndex, final MethodData method) {
return _getLvtIndex(lvtIndex, method, true);
}

private static int _getLvtIndex(final int lvtIndex, final MethodData method, final boolean findParam) {
if (lvtIndex == 0) {
return 0;
}
Expand All @@ -259,7 +293,11 @@ private static int fromLvtIndex(final int lvtIndex, final MethodData method) {

for (final JvmType param : method.params()) {
if (currentLvtIndex == lvtIndex) {
return currentIndex;
if (findParam) {
return currentIndex;
} else {
return currentLvtIndex;
}
}

currentIndex++;
Expand Down

0 comments on commit e09736a

Please sign in to comment.