mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GP-1969: Add 'Model' provider for inspecting object-based traces.
This commit is contained in:
parent
eb0a23aecc
commit
2a4b4f9bcf
59 changed files with 6131 additions and 269 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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("[]")));
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue