<commit_before>package org.jetbrains.debugger.memory.tracking;

import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.engine.jdi.ThreadReferenceProxy;
import com.sun.jdi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.stream.Collectors;

class HashBasedTracking extends InstanceTrackingStrategy {
  private final ReferenceType myClassType;
  private Set<Integer> myHashesSet;

  HashBasedTracking(@NotNull SuspendContextImpl suspendContext,
                    @NotNull ReferenceType classType,
                    @NotNull List<ObjectReference> initialInstances) {
    myClassType = classType;
    myHashesSet = toSetOfHashes(evalHashCodes(suspendContext, initialInstances));
  }

  @NotNull
  @Override
  protected List<ObjectReference> updateImpl(@NotNull SuspendContextImpl suspendContext,
                                             @NotNull List<ObjectReference> references) {
    Map<ObjectReference, Optional<Integer>> ref2hash = evalHashCodes(suspendContext, references);
    List<ObjectReference> newInstances = new ArrayList<>();

    for(ObjectReference ref : references) {
      Optional<Integer> hash = ref2hash.get(ref);
      if(hash.isPresent() && myHashesSet.contains(hash.get())) {
        newInstances.add(ref);
      }
    }

    return newInstances;
  }

  @NotNull
  private Map<ObjectReference, Optional<Integer>> evalHashCodes(@NotNull SuspendContextImpl suspendContext,
                                                                @NotNull List<ObjectReference> references) {
    Method hashCodeMethod = getHashCodeMethod(myClassType);
    Map<ObjectReference, Optional<Integer>> result = new HashMap<>();
    ThreadReferenceProxy threadProxy = suspendContext.getThread();
    ThreadReference thread = threadProxy != null ? threadProxy.getThreadReference() : null;
    if(thread == null) {
      return result;
    }

    for (ObjectReference ref : references) {
      Optional<Integer> hash = Optional.empty();
      try {
        Value value = ref.invokeMethod(thread, hashCodeMethod, Collections.emptyList(), 0);
        hash = Optional.of(((IntegerValue)value).value());
      } catch (InvalidTypeException | ClassNotLoadedException |
          IncompatibleThreadStateException | InvocationException ignored) {
      }

      result.put(ref, hash);
    }

    return result;
  }

  private Set<Integer> toSetOfHashes(@NotNull Map<ObjectReference, Optional<Integer>> ref2hashCode) {
    return ref2hashCode.values().stream()
        .filter(Optional::isPresent)
        .map(Optional::get)
        .collect(Collectors.toCollection(TreeSet::new));<commit_msg>Fix bugs in hashCode-based instances tracker<commit_after>package org.jetbrains.debugger.memory.tracking;

import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.engine.jdi.ThreadReferenceProxy;
import com.sun.jdi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.stream.Collectors;

class HashBasedTracking extends InstanceTrackingStrategy {
  private final ReferenceType myClassType;
  private Set<Integer> myHashesSet;

  HashBasedTracking(@NotNull SuspendContextImpl suspendContext,
                    @NotNull ReferenceType classType,
                    @NotNull List<ObjectReference> initialInstances) {
    myClassType = classType;
    myHashesSet = toSetOfHashes(evalHashCodes(suspendContext, initialInstances));
  }

  @NotNull
  @Override
  protected List<ObjectReference> updateImpl(@NotNull SuspendContextImpl suspendContext,
                                             @NotNull List<ObjectReference> references) {
    Map<ObjectReference, Optional<Integer>> ref2hash = evalHashCodes(suspendContext, references);
    List<ObjectReference> newInstances = new ArrayList<>();

    for(ObjectReference ref : references) {
      Optional<Integer> hash = ref2hash.get(ref);
      if(hash.isPresent() && !myHashesSet.contains(hash.get())) {
        newInstances.add(ref);
      }
    }

    myHashesSet = toSetOfHashes(ref2hash);
    return newInstances;
  }

  @NotNull
  private Map<ObjectReference, Optional<Integer>> evalHashCodes(@NotNull SuspendContextImpl suspendContext,
                                                                @NotNull List<ObjectReference> references) {
    Method hashCodeMethod = getHashCodeMethod(myClassType);
    Map<ObjectReference, Optional<Integer>> result = new HashMap<>();
    ThreadReferenceProxy threadProxy = suspendContext.getThread();
    ThreadReference thread = threadProxy != null ? threadProxy.getThreadReference() : null;
    if(thread == null) {
      return result;
    }

    for (ObjectReference ref : references) {
      Optional<Integer> hash = Optional.empty();
      try {
        Value value = ref.invokeMethod(thread, hashCodeMethod, Collections.emptyList(), 0);
        hash = Optional.of(((IntegerValue)value).value());
      } catch (InvalidTypeException | ClassNotLoadedException |
          IncompatibleThreadStateException | InvocationException ignored) {
      }

      result.put(ref, hash);
    }

    return result;
  }

  private Set<Integer> toSetOfHashes(@NotNull Map<ObjectReference, Optional<Integer>> ref2hashCode) {
    return ref2hashCode.values().stream()
        .filter(Optional::isPresent)
        .map(Optional::get)
        .collect(Collectors.toCollection(HashSet::new));
{'fraction_non_alphanumeric': 0.09789954337899544, 'fraction_numerical': 0.002009132420091324, 'mean_word_length': 3.5785953177257523, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1056378', 'n_tokens_mistral': 1430, 'n_tokens_neox': 1410, 'n_words': 284}