Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix var hoisting issue when no previous store #8122

Merged
merged 2 commits into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ public static String extractSuperClass(ClassNode classNode) {
return Strings.getClassName(classNode.superName);
}

public static boolean isStore(int opcode) {
return opcode >= Opcodes.ISTORE && opcode <= Opcodes.ASTORE;
}

/** Wraps ASM's {@link org.objectweb.asm.Type} with associated generic types */
public static class Type {
private final org.objectweb.asm.Type mainType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import static com.datadog.debugger.instrumentation.ASMHelper.invokeVirtual;
import static com.datadog.debugger.instrumentation.ASMHelper.isFinalField;
import static com.datadog.debugger.instrumentation.ASMHelper.isStaticField;
import static com.datadog.debugger.instrumentation.ASMHelper.isStore;
import static com.datadog.debugger.instrumentation.ASMHelper.ldc;
import static com.datadog.debugger.instrumentation.ASMHelper.newInstance;
import static com.datadog.debugger.instrumentation.Types.CAPTURED_CONTEXT_TYPE;
Expand Down Expand Up @@ -68,7 +69,7 @@
import org.slf4j.LoggerFactory;

public class CapturedContextInstrumentor extends Instrumentor {
private static final Logger log = LoggerFactory.getLogger(CapturedContextInstrumentor.class);
private static final Logger LOGGER = LoggerFactory.getLogger(CapturedContextInstrumentor.class);
private final boolean captureSnapshot;
private final Limits limits;
private final LabelNode contextInitLabel = new LabelNode();
Expand Down Expand Up @@ -639,8 +640,8 @@ private void rewriteLocalVarInsn(LocalVariableNode localVar, int oldSlot, int ne
// 10: astore_1
// 11: aload_1
// range for slot 1 starts at 11
// javac always starts the range right after the init of the local var, so we can just look for
// the previous instruction
// javac often starts the range right after the init of the local var, so we can just look for
// the previous instruction. But not always, and we put an arbitrary limit to 10 instructions
// for kotlinc, many instructions can separate the init and the range start
// ex:
// LocalVariableTable:
Expand All @@ -656,18 +657,26 @@ private void rewriteLocalVarInsn(LocalVariableNode localVar, int oldSlot, int ne
private static void rewritePreviousStoreInsn(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might need to update the comment above

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

LocalVariableNode localVar, int oldSlot, int newSlot) {
AbstractInsnNode previous = localVar.start.getPrevious();
while (previous != null
&& (!(previous instanceof VarInsnNode) || ((VarInsnNode) previous).var != oldSlot)) {
int processed = 0;
// arbitrary fixing limit to 10 previous instructions to look at
while (previous != null && !isVarStoreForSlot(previous, oldSlot) && processed < 10) {
previous = previous.getPrevious();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happen if getPrevious return null?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah right I missed one test in the while. it's working for isVarStoreForLost method

processed++;
}
if (previous != null) {
if (isVarStoreForSlot(previous, oldSlot)) {
VarInsnNode varInsnNode = (VarInsnNode) previous;
if (varInsnNode.var == oldSlot) {
varInsnNode.var = newSlot;
}
}
}

private static boolean isVarStoreForSlot(AbstractInsnNode node, int slotIdx) {
return node instanceof VarInsnNode
&& isStore(node.getOpcode())
&& ((VarInsnNode) node).var == slotIdx;
}

private void createInProbeFinallyHandler(LabelNode inProbeStartLabel, LabelNode inProbeEndLabel) {
LabelNode handlerLabel = new LabelNode();
InsnList handler = new InsnList();
Expand Down Expand Up @@ -1234,7 +1243,7 @@ private static void addInheritedStaticFields(
FieldNode fieldNode =
new FieldNode(field.getModifiers(), field.getName(), desc, null, field);
results.add(fieldNode);
log.debug("Adding static inherited field {}", fieldNode.name);
LOGGER.debug("Adding static inherited field {}", fieldNode.name);
fieldCount++;
if (fieldCount > limits.maxFieldCount) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
import java.lang.reflect.Modifier;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -134,6 +133,17 @@ public void methodProbe() throws IOException, URISyntaxException {
assertEquals("CapturedSnapshot01.main", snapshot.getStack().get(0).getFunction());
}

@Test
public void localVarHoistingNoPreviousStore() throws IOException, URISyntaxException {
final String CLASS_NAME = "com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper";
TestSnapshotListener listener = installSingleProbe(CLASS_NAME, "detectEncoding", null);
Class<?> testClass =
loadClass(
CLASS_NAME,
getClass().getResource("/classfiles/ByteSourceJsonBootstrapper.classfile").getFile());
assertNotNull(testClass);
}

@Test
public void singleLineProbe() throws IOException, URISyntaxException {
final String CLASS_NAME = "CapturedSnapshot01";
Expand Down Expand Up @@ -219,12 +229,8 @@ public void oldClass1_1() throws Exception {
when(config.isDebuggerVerifyByteCode()).thenReturn(true);
Class<?> testClass =
loadClass(
CLASS_NAME,
Paths.get(
CapturedSnapshotTest.class
.getResource("/classfiles/BooleanUtils.classfile")
.toURI())
.toString());
CLASS_NAME, getClass().getResource("/classfiles/BooleanUtils.classfile").getFile());

boolean result = Reflect.onClass(testClass).call("toBoolean", Boolean.TRUE).get();
assertTrue(result);
assertOneSnapshot(listener);
Expand Down
Binary file not shown.
Loading