Conversation
09a2327 to
fc3f917
Compare
|
The following will work on @madebr do you want to take a look? |
| marker = match_marker("// VTABLE: TEST 0x1234 S p a c e s") | ||
| assert marker is not None | ||
| assert marker.extra == "S p a c e s" | ||
| assert marker.extras == ("S", "p", "a", "c", "e", "s") |
There was a problem hiding this comment.
Could this be a problem in some way? a key-value approach might need a way of escaping spaces, but that could also be overkill
|
Does the compiler drop unused types from the PDB? If not, users could just create whatever type/shape is needed: struct cKeyboardStruct {
_DIOBJECTDATAFORMAT x[0x100]
};Building a struct-name to leaf-id map doesn't add too much performance overhead. I ran a quick test yesterday with an extra regex search on each |
Unfortunately, yes. The following makes it to the PDB: struct cKeyboardStruct {
_DIOBJECTDATAFORMAT x[0x100];
};
static cKeyboardStruct* keyboardStruct = NULL;but the last line is necessary, which can have an impact on binary matching. |
|
But even if we could make the "define your own struct in C++" approach work, it would get rid of the array type creation logic at most. The remainder of this change would still be needed. |
|
Rough idea I have not thought through: // VTABLE: HELLO 0x1000 KEYVAL
// VTABLE: HOWDY 0x2000 KEYVAL
// VTABLE: GREET 0x3000 KEYVAL
// symbol = asdadfafadfadf
// name = Pizza:`vftable'{for XYZ}
// base_class = XYZBasically using the "extra" field to indicate that we're parsing the following comments differently. |
True. I'm just looking for any way to reduce the surface area. And if we need to do this for a variable that has no PDB struct to attach to, we are stuck. |
This approach looks promising to me. Some thoughts:
Edit: I read your comment again and now I think I understand where you are going with the Edit 2: I wouldn't provide the symbol via key-value since that is against the general annotation pattern we have established: where |
|
We don't need // FUNCTION: TEST 0x1234
// @test = 100
// @emote = 😎
// NameOf::TheFunctionbut we know But what if there is no "completion token"? Are these still valid annotations? //// Completed by attributes only:
// GLOBAL: TEST 0x2000
// @name = MyGlobalVariable
//// Attribute and comment completion token in conflict:
// GLOBAL: TEST 0x3000
// @name = OtherVariable
// RealNameOfTheVariable
//// Attribute and code completion token in conflict:
// GLOBAL: TEST 0x3000
// @name = OtherOtherVariable
int CodeVariable = 5;So it may be that I've introduced something that will take more effort to solve than the specific ask for the DirectInput keyboard struct. |
|
Going full circle, how about one-line key-value with double quotes and backslash escaping, similar to JSON? (I have briefly considered proper JSON, but I think it's overkill and adds noise). This could look like Main benefit: This keeps syntax and parser complexity lower and is capable of representing all strings. We could use existing JSON logic to parse only the string, then we would already have support for other escape sequences like |
|
I implemented the JSON key-value approach. datacmp on LEGO1 will now pass with the following addition: |
| # TODO: Discuss if we want to split the file instead, and if so, what can/should be pulled out | ||
| # pylint:disable=too-many-lines | ||
|
|
There was a problem hiding this comment.
I think we can easily extract all the read_(leaf_type) functions and their companion regex strings into their own module.
| marker = match_marker("// VTABLE: TEST 0x1234 S p a c e s") | ||
| assert marker is not None | ||
| assert marker.extra == "S p a c e s" |
There was a problem hiding this comment.
semantics no longer relevant or desired. New semantics are covered by other tests.
| # Trailing spaces removed | ||
| marker = match_marker("// VTABLE: TEST 0x8888 spaces ") | ||
| assert marker is not None | ||
| assert marker.extra == "spaces" |
There was a problem hiding this comment.
Covered by other tests
There was a problem hiding this comment.
drive-by: better type safety. Functionality wise, this was only adapted to the signature change of match_variables.
| logger.warning( | ||
| 'Legacy VTABLE base class annotation used above %s:%i. Change to `// VTABLE: %s 0x%x BASE_CLASS="%s"`.', | ||
| self.filename.name, | ||
| self.line_number, | ||
| marker.module, | ||
| marker.offset, | ||
| base_class, | ||
| ) |
There was a problem hiding this comment.
We can move this to a second PR if you prefer, or even split into three (implement key-value, implement orig type annotation, change VTABLE annotation with legacy support).
If we keep everything in here, we should change the PR title.
There was a problem hiding this comment.
I think a split makes sense if it's not too much trouble.
| for match in SINGLE_MARKER_EXTRA_REGEX.finditer(raw_extra): | ||
| raw_value = match.group("value") | ||
| if raw_value is None: | ||
| extra_flags.add(match.group("key")) | ||
| else: | ||
| try: | ||
| value = json.loads(raw_value) | ||
| assert isinstance( | ||
| value, str | ||
| ), "This assertion should never fail since the regex checks the presence of double quotes" | ||
| extra_strings.append((match.group("key"), value)) | ||
| except JSONDecodeError as e: | ||
| logging.warning( | ||
| "Invalid JSON in extra (last part) of annotation '%s'", | ||
| raw_extra, | ||
| exc_info=e, | ||
| ) | ||
| return None |
There was a problem hiding this comment.
Not sure if this would rather belong into match_msvc.py architecturally. Benefit would be that we could make decomplint failures out of these. The current approach has the benefit that all regex shenanigans are localised to this file.
In the LEGO1 example, we find a `_DIOBJECTDATAFORMAT *` and deduce that it points to some instance, but it actually points to an array (`_DIOBJECTDATAFORMAT[0x100]`). This cannot be reconstructed from the PDB.
60f3b57 to
7de69a2
Compare
datacmpdatacmp
| } | ||
|
|
||
|
|
||
| @dataclass |
There was a problem hiding this comment.
generates __eq__ which I need in a test
| orig_addr: int, | ||
| report: ReccmpReportProtocol = reccmp_report_nop, |
There was a problem hiding this comment.
I think we should just use the existing logger and not drag the report protocol into here. reccmp-cvdump fails with a circular import because you can't import from reccmp.compare and not get .core and all its dependencies.
| expected_leaf_fragment = f"class name = {name}," | ||
| potential_hits = [ | ||
| self.get(key) | ||
| for key, (leaf, _) in self._raw.items() | ||
| if expected_leaf_fragment in leaf | ||
| ] |
There was a problem hiding this comment.
We should cache this structure so we only pay for it once. Or not at all, if there are no name lookups.
| # The same entry may appear multiple times (e.g. due to forward refs), so we deduplicate by key | ||
| # and also filter again by the name just to be sure | ||
| actual_hits = dict((hit.key, hit) for hit in potential_hits if hit.name == name) |
There was a problem hiding this comment.
We don't currently do anything with the "WARNING: UDT mismatch" reports, but would that explain why we have some duplicate struct names? Maybe we could treat these cases (where the leaf does not have the FORWARD REF flag but the leaf id does not match the embedded UDT) as forward refs. Not sure how correct an approach that is.
There was a problem hiding this comment.
The forward refs were the reason I introduced this code since the string match catches them. I don't have any new insight about the UDT mismatch. The warning about double matches is merely a precaution. I'd keep the other handling as it is.
| ) -> TypeInfo | None: | ||
| """ | ||
| Searches the type database for `name`. | ||
| Also supports arrays with decimal length (e.g. `MyType[20]`); |
There was a problem hiding this comment.
Maybe this should be two functions: one to get the CvdumpTypeKey from the name, and one to create a synthetic array type where you pass in the key and number of elements you want. That would shift the user-provided type string parsing back to the caller.
|
Since we are adding new options to the markers anyway, would it be better to have "create synthetic matches from this variable" as one of the options and do it in the |
The difficulty is that we only find out the recomp address of the synthetic match when |
|
Maybe a better alternative: The requirement would be that there is a different, valid global covering the
We could then repeat this step until the number of matched globals no longer increases, which would cover recursive structures. |
The current approach is flawed: In the LEGO1 example, we find a
_DIOBJECTDATAFORMAT *and deduce that it points to some instance, but it actually points to an array (_DIOBJECTDATAFORMAT[0x100]). This cannot be reconstructed from the PDB.Tasks:
// GLOBAL