GP-1969: Add 'Model' provider for inspecting object-based traces.

This commit is contained in:
Dan 2022-06-15 15:41:38 -04:00
parent eb0a23aecc
commit 2a4b4f9bcf
59 changed files with 6131 additions and 269 deletions

View file

@ -414,7 +414,8 @@ public interface TargetObjectSchema {
*
* <p>
* If this is the schema of the root object, then this gives the schema of the object at the
* given path in the model.
* given path in the model. This will always give a non-null result, though that result might be
* {@link EnumerableTargetObjectSchema#VOID}.
*
* @param path the relative path from an object having this schema to the desired successor
* @return the schema for the successor

View file

@ -1,81 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.dbg.util;
import java.util.List;
import java.util.Set;
public enum AllPathsMatcher implements PathPredicates {
INSTANCE;
@Override
public PathPredicates or(PathPredicates that) {
return this;
}
@Override
public boolean matches(List<String> path) {
return true;
}
@Override
public boolean successorCouldMatch(List<String> path, boolean strict) {
return true;
}
@Override
public boolean ancestorMatches(List<String> path, boolean strict) {
if (path.isEmpty() && strict) {
return false;
}
return true;
}
@Override
public Set<String> getNextKeys(List<String> path) {
return Set.of("", "[]");
}
@Override
public Set<String> getNextNames(List<String> path) {
return Set.of("");
}
@Override
public Set<String> getNextIndices(List<String> path) {
return Set.of("");
}
@Override
public List<String> getSingletonPath() {
return null;
}
@Override
public PathPattern getSingletonPattern() {
return null;
}
@Override
public PathPredicates applyKeys(List<String> keys) {
throw new UnsupportedOperationException();
}
@Override
public boolean isEmpty() {
return false;
}
}

View file

@ -38,6 +38,21 @@ public class PathMatcher implements PathPredicates {
return String.format("<PathMatcher\n %s\n>", StringUtils.join(patterns, "\n "));
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof PathMatcher)) {
return false;
}
PathMatcher that = (PathMatcher) obj;
if (!Objects.equals(this.patterns, that.patterns)) {
return false;
}
return true;
}
@Override
public PathPredicates or(PathPredicates that) {
PathMatcher result = new PathMatcher();
@ -82,6 +97,11 @@ public class PathMatcher implements PathPredicates {
return anyPattern(p -> p.ancestorMatches(path, strict));
}
@Override
public boolean ancestorCouldMatchRight(List<String> path, boolean strict) {
return anyPattern(p -> p.ancestorCouldMatchRight(path, strict));
}
@Override
public List<String> getSingletonPath() {
if (patterns.size() != 1) {
@ -98,12 +118,7 @@ public class PathMatcher implements PathPredicates {
return patterns.iterator().next();
}
@Override
public Set<String> getNextKeys(List<String> path) {
Set<String> result = new HashSet<>();
for (PathPattern pattern : patterns) {
result.addAll(pattern.getNextKeys(path));
}
protected void coalesceWilds(Set<String> result) {
if (result.contains("")) {
result.removeIf(PathUtils::isName);
result.add("");
@ -112,6 +127,15 @@ public class PathMatcher implements PathPredicates {
result.removeIf(PathUtils::isIndex);
result.add("[]");
}
}
@Override
public Set<String> getNextKeys(List<String> path) {
Set<String> result = new HashSet<>();
for (PathPattern pattern : patterns) {
result.addAll(pattern.getNextKeys(path));
}
coalesceWilds(result);
return result;
}
@ -139,6 +163,16 @@ public class PathMatcher implements PathPredicates {
return result;
}
@Override
public Set<String> getPrevKeys(List<String> path) {
Set<String> result = new HashSet<>();
for (PathPattern pattern : patterns) {
result.addAll(pattern.getPrevKeys(path));
}
coalesceWilds(result);
return result;
}
@Override
public boolean isEmpty() {
return patterns.isEmpty();
@ -152,4 +186,13 @@ public class PathMatcher implements PathPredicates {
}
return result;
}
@Override
public PathMatcher removeRight(int count) {
PathMatcher result = new PathMatcher();
for (PathPattern pat : patterns) {
pat.doRemoveRight(count, result);
}
return result;
}
}

View file

@ -48,13 +48,25 @@ public class PathPattern implements PathPredicates {
return String.format("<PathPattern %s>", PathUtils.toString(pattern));
}
/**
* Convert this pattern to a string as in {@link PathPredicates#parse(String)}.
*
* @return the string
*/
public String toPatternString() {
return PathUtils.toString(pattern);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof PathPattern)) {
return false;
}
PathPattern that = (PathPattern) obj;
return Objects.equals(this.pattern, that.pattern);
if (!Objects.equals(this.pattern, that.pattern)) {
return false;
}
return true;
}
@Override
@ -95,6 +107,17 @@ public class PathPattern implements PathPredicates {
return true;
}
protected boolean matchesBackTo(List<String> path, int length) {
int patternMax = pattern.size() - 1;
int pathMax = path.size() - 1;
for (int i = 0; i < length; i++) {
if (!PathPredicates.keyMatches(pattern.get(patternMax - i), path.get(pathMax - i))) {
return false;
}
}
return true;
}
@Override
public boolean matches(List<String> path) {
if (path.size() != pattern.size()) {
@ -125,6 +148,17 @@ public class PathPattern implements PathPredicates {
return matchesUpTo(path, pattern.size());
}
@Override
public boolean ancestorCouldMatchRight(List<String> path, boolean strict) {
if (path.size() > pattern.size()) {
return false;
}
if (strict && path.size() == pattern.size()) {
return false;
}
return matchesBackTo(path, path.size());
}
protected static boolean containsWildcards(List<String> pattern) {
for (String pat : pattern) {
if (isWildcard(pat)) {
@ -142,6 +176,20 @@ public class PathPattern implements PathPredicates {
return pattern;
}
/**
* Return the pattern as a list of key patterns
*
* @return the list of key patterns
*/
public List<String> asPath() {
return pattern;
}
/**
* Count the number of wildcard keys in this pattern
*
* @return the count
*/
public int countWildcards() {
return (int) pattern.stream().filter(k -> isWildcard(k)).count();
}
@ -192,6 +240,17 @@ public class PathPattern implements PathPredicates {
return Set.of();
}
@Override
public Set<String> getPrevKeys(List<String> path) {
if (path.size() >= pattern.size()) {
return Set.of();
}
if (!matchesBackTo(path, path.size())) {
return Set.of();
}
return Set.of(pattern.get(pattern.size() - 1 - path.size()));
}
@Override
public boolean isEmpty() {
return false;
@ -254,4 +313,18 @@ public class PathPattern implements PathPredicates {
}
return result;
}
public void doRemoveRight(int count, PathMatcher result) {
if (count > pattern.size()) {
return;
}
result.addPattern(pattern.subList(0, pattern.size() - count));
}
@Override
public PathMatcher removeRight(int count) {
PathMatcher result = new PathMatcher();
doRemoveRight(count, result);
return result;
}
}

View file

@ -54,10 +54,6 @@ public interface PathPredicates {
return new PathPattern(PathUtils.parse(pattern));
}
static PathPredicates all() {
return AllPathsMatcher.INSTANCE;
}
PathPredicates or(PathPredicates that);
/**
@ -95,6 +91,18 @@ public interface PathPredicates {
*/
boolean ancestorMatches(List<String> path, boolean strict);
/**
* Check if the given path <em>could</em> have a matching ancestor, right to left
*
* <p>
* This essentially checks if the given path is a viable postfix to the matcher.
*
* @param path the path (postfix) to check
* @param strict true to exclude the case where {@link #matches(List)} would return true
* @return true if an ancestor could match, false otherwise
*/
boolean ancestorCouldMatchRight(List<String> path, boolean strict);
/**
* Get the patterns for the next possible key
*
@ -130,6 +138,17 @@ public interface PathPredicates {
*/
Set<String> getNextIndices(List<String> path);
/**
* Get the patterns for the previous possible key (right-to-left matching)
*
* <p>
* If an ancestor of the given path cannot match this pattern, the empty set is returned.
*
* @param path the successor path
* @return a set of patterns where indices are enclosed in brackets ({@code [])
*/
Set<String> getPrevKeys(List<String> path);
/**
* If this predicate is known to match only one path, i.e., no wildcards, get that path
*
@ -144,6 +163,14 @@ public interface PathPredicates {
*/
PathPattern getSingletonPattern();
/**
* Remove count elements from the right
*
* @param count the number of elements to remove
* @return the resulting predicates
*/
PathPredicates removeRight(int count);
default NavigableMap<List<String>, ?> getCachedValues(TargetObject seed) {
return getCachedValues(List.of(), seed);
}

View file

@ -0,0 +1,41 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.dbg.util;
import static org.junit.Assert.assertEquals;
import java.util.Set;
import org.junit.Test;
public class PathPredicatesTest {
@Test
public void testGetPrevKeys() {
PathPredicates pred = PathPredicates.parse("Processes[0].Threads[].Stack");
assertEquals(Set.of("Stack"), pred.getPrevKeys(PathUtils.parse("")));
assertEquals(Set.of("[]"), pred.getPrevKeys(PathUtils.parse("Stack")));
assertEquals(Set.of("Threads"), pred.getPrevKeys(PathUtils.parse("[].Stack")));
assertEquals(Set.of("[0]"), pred.getPrevKeys(PathUtils.parse("Threads[].Stack")));
assertEquals(Set.of("Processes"), pred.getPrevKeys(PathUtils.parse("[0].Threads[].Stack")));
assertEquals(Set.of(), pred.getPrevKeys(PathUtils.parse("Processes[0].Threads[].Stack")));
assertEquals(Set.of(),
pred.getPrevKeys(PathUtils.parse("Foo.Processes[0].Threads[].Stack")));
assertEquals(Set.of(), pred.getPrevKeys(PathUtils.parse("Foo")));
assertEquals(Set.of(), pred.getPrevKeys(PathUtils.parse("[]")));
}
}