GP-810 - Gnu Demangler - Fixed some failures when parsing function member pointers and array pointers/references; Fixed the parser not handling cast operators for function pointers

This commit is contained in:
dragonmacher 2021-04-19 18:37:05 -04:00
parent 6a8788acbc
commit 071eb82103
14 changed files with 898 additions and 159 deletions

View file

@ -129,7 +129,7 @@ public class DemanglerCmd extends BackgroundCommand {
". Message: " + message); ". Message: " + message);
} }
Msg.error(this, getStatusMsg()); Msg.error(this, getStatusMsg(), e);
} }
public String getResult() { public String getResult() {

View file

@ -18,6 +18,8 @@ package ghidra.app.util.demangler;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.StringUtils;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.Namespace;
@ -40,9 +42,6 @@ public abstract class AbstractDemangledFunctionDefinitionDataType extends Demang
protected boolean isTrailingUnaligned; protected boolean isTrailingUnaligned;
protected boolean isTrailingRestrict; protected boolean isTrailingRestrict;
/** display parens in front of parameter list */
protected boolean displayFunctionPointerParens = true;
AbstractDemangledFunctionDefinitionDataType(String mangled, String originalDemangled) { AbstractDemangledFunctionDefinitionDataType(String mangled, String originalDemangled) {
super(mangled, originalDemangled, DEFAULT_NAME_PREFIX + nextId()); super(mangled, originalDemangled, DEFAULT_NAME_PREFIX + nextId());
} }
@ -134,10 +133,6 @@ public abstract class AbstractDemangledFunctionDefinitionDataType extends Demang
isTrailingRestrict = true; isTrailingRestrict = true;
} }
public void setDisplayFunctionPointerParens(boolean b) {
this.displayFunctionPointerParens = b;
}
/** /**
* Adds a parameters to the end of the parameter list for this demangled function * Adds a parameters to the end of the parameter list for this demangled function
* @param parameter the new parameter to add * @param parameter the new parameter to add
@ -158,13 +153,8 @@ public abstract class AbstractDemangledFunctionDefinitionDataType extends Demang
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
StringBuilder buffer1 = new StringBuilder(); StringBuilder buffer1 = new StringBuilder();
String s = getConventionPointerNameString(name); String s = getConventionPointerNameString(name);
if (s.contains(" ") || s.isEmpty()) {
// spaces--add parens
addFunctionPointerParens(buffer1, s); addFunctionPointerParens(buffer1, s);
}
else { // this allows the '__cdecl' in templates to not have parens
buffer1.append(s);
}
buffer1.append('('); buffer1.append('(');
for (int i = 0; i < parameters.size(); ++i) { for (int i = 0; i < parameters.size(); ++i) {
@ -234,26 +224,28 @@ public abstract class AbstractDemangledFunctionDefinitionDataType extends Demang
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
buffer.append(callingConvention == null ? EMPTY_STRING : callingConvention); buffer.append(callingConvention == null ? EMPTY_STRING : callingConvention);
StringBuilder typeBuffer = new StringBuilder();
int pointerLevels = getPointerLevels(); int pointerLevels = getPointerLevels();
if (pointerLevels > 0) { if (pointerLevels > 0) {
if (callingConvention != null) {
buffer.append(SPACE);
}
addParentName(buffer); addParentName(typeBuffer);
for (int i = 0; i < pointerLevels; ++i) { for (int i = 0; i < pointerLevels; ++i) {
buffer.append(getTypeString()); typeBuffer.append(getTypeString());
} }
} }
if ((modifier != null) && (modifier.length() != 0)) { if (!StringUtils.isBlank(typeBuffer)) {
if (buffer.length() > 2) {
if (!StringUtils.isBlank(callingConvention)) {
buffer.append(SPACE); buffer.append(SPACE);
} }
buffer.append(modifier);
buffer.append(typeBuffer);
} }
addModifier(buffer);
if (isConstPointer) { if (isConstPointer) {
buffer.append(CONST); buffer.append(CONST);
} }
@ -266,7 +258,7 @@ public abstract class AbstractDemangledFunctionDefinitionDataType extends Demang
} }
if (name != null) { if (name != null) {
if ((buffer.length() > 2) && (buffer.charAt(buffer.length() - 1) != SPACE)) { if ((buffer.length() > 0) && (buffer.charAt(buffer.length() - 1) != SPACE)) {
buffer.append(SPACE); buffer.append(SPACE);
} }
buffer.append(name); buffer.append(name);
@ -275,11 +267,29 @@ public abstract class AbstractDemangledFunctionDefinitionDataType extends Demang
return buffer.toString(); return buffer.toString();
} }
protected void addFunctionPointerParens(StringBuilder buffer, String s) { private void addModifier(StringBuilder buffer) {
if (!displayFunctionPointerParens) { if (StringUtils.isBlank(modifier)) {
return; return;
} }
//
// Guilty knowledge: in many cases the 'modifier' is the same as the type string. Further,
// when we print signatures, we will print the type string if there are pointer levels. To
// prevent duplication, do not print the modifier when it matches the type string and we
// will be printing the type string (which is printed when there are pointer levels).
//
if (modifier.equals(getTypeString()) &&
getPointerLevels() > 0) {
return;
}
if (buffer.length() > 2) {
buffer.append(SPACE);
}
buffer.append(modifier);
}
protected void addFunctionPointerParens(StringBuilder buffer, String s) {
buffer.append('(').append(s).append(')'); buffer.append('(').append(s).append(')');
} }
@ -310,15 +320,7 @@ public abstract class AbstractDemangledFunctionDefinitionDataType extends Demang
fddt.setReturnType(returnType.getDataType(dataTypeManager)); fddt.setReturnType(returnType.getDataType(dataTypeManager));
} }
if (parameters.size() != 1 || setParameters(fddt, dataTypeManager);
!(parameters.get(0).getDataType(dataTypeManager) instanceof VoidDataType)) {
ParameterDefinition[] params = new ParameterDefinition[parameters.size()];
for (int i = 0; i < parameters.size(); ++i) {
params[i] = new ParameterDefinitionImpl(null,
parameters.get(i).getDataType(dataTypeManager), null);
}
fddt.setArguments(params);
}
DataType dt = DemangledDataType.findDataType(dataTypeManager, namespace, getName()); DataType dt = DemangledDataType.findDataType(dataTypeManager, namespace, getName());
if (dt == null || !(dt instanceof FunctionDefinitionDataType)) { if (dt == null || !(dt instanceof FunctionDefinitionDataType)) {
@ -327,4 +329,27 @@ public abstract class AbstractDemangledFunctionDefinitionDataType extends Demang
return new PointerDataType(dt, dataTypeManager); return new PointerDataType(dt, dataTypeManager);
} }
private void setParameters(FunctionDefinitionDataType fddt, DataTypeManager dataTypeManager) {
if (hasSingleVoidParameter(dataTypeManager)) {
return;
}
ParameterDefinition[] params = new ParameterDefinition[parameters.size()];
for (int i = 0; i < parameters.size(); ++i) {
params[i] = new ParameterDefinitionImpl(null,
parameters.get(i).getDataType(dataTypeManager), null);
}
fddt.setArguments(params);
}
private boolean hasSingleVoidParameter(DataTypeManager dataTypeManager) {
if (parameters.size() != 1) {
return false;
}
DemangledDataType parameter = parameters.get(0);
DataType dt = parameter.getDataType(dataTypeManager);
return dt instanceof VoidDataType;
}
} }

View file

@ -20,6 +20,8 @@ import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum; import ghidra.program.model.data.Enum;
@ -404,6 +406,9 @@ public class DemangledDataType extends DemangledType {
} }
static Structure createPlaceHolderStructure(String dtName, Demangled namespace) { static Structure createPlaceHolderStructure(String dtName, Demangled namespace) {
if (StringUtils.isBlank(dtName)) {
throw new IllegalArgumentException("Name cannot be blank");
}
StructureDataType structDT = new StructureDataType(dtName, 0); StructureDataType structDT = new StructureDataType(dtName, 0);
structDT.setDescription("PlaceHolder Structure"); structDT.setDescription("PlaceHolder Structure");
structDT.setCategoryPath(getDemanglerCategoryPath(dtName, namespace)); structDT.setCategoryPath(getDemanglerCategoryPath(dtName, namespace));

View file

@ -229,25 +229,8 @@ public class DemangledFunction extends DemangledObject {
buffer.append('<').append(templatedConstructorType).append('>'); buffer.append('<').append(templatedConstructorType).append('>');
} }
Iterator<DemangledDataType> paramIterator = parameters.iterator(); addParameters(buffer, format);
buffer.append('(');
String pad = format ? pad(buffer.length()) : "";
if (!paramIterator.hasNext()) {
buffer.append("void");
}
while (paramIterator.hasNext()) {
buffer.append(paramIterator.next().getSignature());
if (paramIterator.hasNext()) {
buffer.append(',');
if (format) {
buffer.append('\n');
}
buffer.append(pad);
}
}
buffer.append(')');
buffer.append(storageClass == null ? "" : " " + storageClass); buffer.append(storageClass == null ? "" : " " + storageClass);
if (returnType instanceof DemangledFunctionPointer) { if (returnType instanceof DemangledFunctionPointer) {
@ -303,6 +286,29 @@ public class DemangledFunction extends DemangledObject {
return buffer.toString(); return buffer.toString();
} }
protected void addParameters(StringBuilder buffer, boolean format) {
Iterator<DemangledDataType> paramIterator = parameters.iterator();
buffer.append('(');
int padLength = format ? buffer.length() : 0;
String pad = StringUtils.rightPad("", padLength);
if (!paramIterator.hasNext()) {
buffer.append("void");
}
while (paramIterator.hasNext()) {
buffer.append(paramIterator.next().getSignature());
if (paramIterator.hasNext()) {
buffer.append(',');
if (format) {
buffer.append('\n');
}
buffer.append(pad);
}
}
buffer.append(')');
}
@Override @Override
public String getNamespaceName() { public String getNamespaceName() {
return getName() + getParameterString(); return getName() + getParameterString();

View file

@ -32,4 +32,10 @@ public class DemangledFunctionIndirect extends AbstractDemangledFunctionDefiniti
protected String getTypeString() { protected String getTypeString() {
return EMPTY_STRING; return EMPTY_STRING;
} }
@Override
protected void addFunctionPointerParens(StringBuilder buffer, String s) {
// do not display pointer parens
buffer.append(s);
}
} }

View file

@ -20,12 +20,34 @@ package ghidra.app.util.demangler;
*/ */
public class DemangledFunctionPointer extends AbstractDemangledFunctionDefinitionDataType { public class DemangledFunctionPointer extends AbstractDemangledFunctionDefinitionDataType {
/** display parens in front of parameter list */
private boolean displayFunctionPointerSyntax = true;
public DemangledFunctionPointer(String mangled, String originalDemangled) { public DemangledFunctionPointer(String mangled, String originalDemangled) {
super(mangled, originalDemangled); super(mangled, originalDemangled);
incrementPointerLevels(); // a function pointer is 1 level by default
} }
@Override @Override
protected String getTypeString() { protected String getTypeString() {
return "*"; return "*";
} }
/**
* Signals whether to display function pointer syntax when there is no function name, which
* is '{@code (*)}', such as found in this example '{@code void (*)()}'. the default is true
* @param b true to display nameless function pointer syntax; false to not display
*/
public void setDisplayDefaultFunctionPointerSyntax(boolean b) {
this.displayFunctionPointerSyntax = b;
}
@Override
protected void addFunctionPointerParens(StringBuilder buffer, String s) {
if (!displayFunctionPointerSyntax) {
return;
}
buffer.append('(').append(s).append(')');
}
} }

View file

@ -34,4 +34,9 @@ public class DemangledLambda extends DemangledFunction {
public String toString() { public String toString() {
return getName(); return getName();
} }
@Override
protected void addParameters(StringBuilder buffer, boolean format) {
// no parameter display for lambdas; the name currently shows the parameters
}
} }

View file

@ -68,7 +68,7 @@ public abstract class DemangledObject implements Demangled {
protected String basedName; protected String basedName;
protected String memberScope; protected String memberScope;
private String signature; private String plateComment;
DemangledObject(String mangled, String originalDemangled) { DemangledObject(String mangled, String originalDemangled) {
this.mangled = mangled; this.mangled = mangled;
@ -248,15 +248,6 @@ public abstract class DemangledObject implements Demangled {
return getName(); return getName();
} }
/**
* Sets the signature. Calling this method will
* override the auto-generated signature.
* @param signature the signature
*/
public void setSignature(String signature) {
this.signature = signature;
}
@Override @Override
public String toString() { public String toString() {
return getSignature(false); return getSignature(false);
@ -332,19 +323,29 @@ public abstract class DemangledObject implements Demangled {
return true; return true;
} }
/**
* Sets the plate comment to be used if the {@link #getOriginalDemangled()} string is not
* available
*
* @param plateComment the plate comment text
*/
public void setBackupPlateComment(String plateComment) {
this.plateComment = plateComment;
}
/**
* Creates descriptive text that is intended to be used as documentation. The text defaults
* to the original demangled text. If that is not available, then any text set by
* {@link #setBackupPlateComment(String)} will be used. The last choice for this text is
* the signature generated by {@link #getSignature(boolean)}.
*
* @return the text
*/
protected String generatePlateComment() { protected String generatePlateComment() {
if (originalDemangled != null) { if (originalDemangled != null) {
return originalDemangled; return originalDemangled;
} }
return (signature == null) ? getSignature(true) : signature; return (plateComment == null) ? getSignature(true) : plateComment;
}
protected String pad(int len) {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < len; i++) {
buffer.append(' ');
}
return buffer.toString();
} }
protected Symbol applyDemangledName(Address addr, boolean setPrimary, protected Symbol applyDemangledName(Address addr, boolean setPrimary,
@ -461,8 +462,7 @@ public abstract class DemangledObject implements Demangled {
break; break;
} }
catch (InvalidInputException e) { catch (InvalidInputException e) {
Msg.error(DemangledObject.class, Msg.error(DemangledObject.class, "Failed to create namespace: " + e.getMessage());
"Failed to create namespace: " + e.getMessage());
break; break;
} }
@ -479,7 +479,6 @@ public abstract class DemangledObject implements Demangled {
NamespaceUtils.getNamespaceQualifiedName(namespace, namespaceName, false)); NamespaceUtils.getNamespaceQualifiedName(namespace, namespaceName, false));
break; break;
} }
} }
return namespace; return namespace;
} }

View file

@ -15,6 +15,8 @@
*/ */
package ghidra.app.util.demangler; package ghidra.app.util.demangler;
import org.apache.commons.lang3.StringUtils;
import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.Namespace;
/** /**
@ -53,6 +55,10 @@ public class DemangledType implements Demangled {
@Override @Override
public void setName(String name) { public void setName(String name) {
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("Name cannot be blank");
}
demangledName = name; demangledName = name;
this.name = name; this.name = name;
if (name != null) { if (name != null) {

View file

@ -117,9 +117,6 @@ public class GnuDemangler implements Demangler {
dfunc.setNamespace(demangledObject.getNamespace()); dfunc.setNamespace(demangledObject.getNamespace());
demangledObject = dfunc; demangledObject = dfunc;
} }
else {
demangledObject.setSignature(demangled);
}
if (isDwarf) { if (isDwarf) {
DemangledAddressTable dat = DemangledAddressTable dat =

View file

@ -93,16 +93,17 @@ public class GnuDemanglerParser {
/* /*
* Sample: bob(short (&)[7]) * Sample: bob(short (&)[7])
* bob(int const[8] (*) [12]) * bob(int const[8] (*) [12])
* _S_ref(array<float, 3ul> const (&) [64])
* _S_ptr(Foo<Bar> const* const (&) [3])
* {lambda(long&, unsigned int)#1} const (&) [4]
* *
* Pattern: name[optional '*']<space>(*|&)[optional spaces][optional value] * Pattern: <space>[optional const with optional '[',']', number, '*', '&' <space>]
* (*|&)[optional spaces]brackets with optional characters inside
* *
* Parts: * Parts:
* -a word (capture group 1) * -optional const text (e.g., const[8]) (non-capture group)
* -followed by an optional pointer '*' * -followed by '()' that contain a '&' or a '*' (capture group 1)
* -followed by a space * -followed by one or more '[]' with optional interior text (capture group 2)
* -*optional: any other text (e.g., const[8]) (non-capture group)
* -followed by '()' that contain a '&' or a '*' (capture group 2)
* -followed by one or more '[]' with optional interior text (capture group 3)
* *
* Group Samples: * Group Samples:
* short (&)[7] * short (&)[7]
@ -117,7 +118,8 @@ public class GnuDemanglerParser {
* *
*/ */
private static final Pattern ARRAY_POINTER_REFERENCE_PATTERN = private static final Pattern ARRAY_POINTER_REFERENCE_PATTERN =
Pattern.compile("([\\w:]+)\\*?\\s(?:.*)\\(([&*])\\)\\s*((?:\\[.*?\\])+)"); Pattern.compile(
"\\s(?:const[\\[\\]\\d\\*&]{0,4}\\s)*\\(([&*])\\)\\s*((?:\\[.*?\\])+)");
/* /*
* Sample: bob(short (&)[7]) * Sample: bob(short (&)[7])
@ -309,6 +311,16 @@ public class GnuDemanglerParser {
return Pattern.compile(operatorPrefix + parameters + trailing); return Pattern.compile(operatorPrefix + parameters + trailing);
} }
/**
* Pattern to catch literal strings of the form:
*
* -1l
* 2l
* 0u
* 4294967295u
*/
private static final Pattern LITERAL_NUMBER_PATTERN = Pattern.compile("-*\\d+[ul]{0,1}");
private String mangledSource; private String mangledSource;
private String demangledSource; private String demangledSource;
@ -353,6 +365,13 @@ public class GnuDemanglerParser {
return operatorHandler; return operatorHandler;
} }
// Note: this really is a 'special handler' check that used to be handled above. However,
// some demangled operator strings begin with this text. If we do this check above,
// then we will not correctly handle those operators.
if (mangledSource.startsWith("_ZZ")) {
return new ItemInNamespaceHandler(demangled);
}
return null; return null;
} }
@ -397,9 +416,6 @@ public class GnuDemanglerParser {
return new ItemInNamespaceHandler(demangled, prefix, type); return new ItemInNamespaceHandler(demangled, prefix, type);
} }
if (mangled.startsWith("_ZZ")) {
return new ItemInNamespaceHandler(demangled);
}
return null; return null;
} }
@ -420,12 +436,15 @@ public class GnuDemanglerParser {
// following that text in the original string. We want the name to be the full lambda // following that text in the original string. We want the name to be the full lambda
// text, without spaces. // text, without spaces.
// //
LambdaName lambdaName = getLambdaName(demangled); String prefix = signatureParts.getRawParameterPrefix();
int lambdaStart = prefix.length() - LAMBDA_START.length(); // strip off '{lambda'
String lambdaText = demangled.substring(lambdaStart);
LambdaName lambdaName = getLambdaName(lambdaText);
String uniqueName = lambdaName.getFullText(); String uniqueName = lambdaName.getFullText();
String escapedLambda = removeBadSpaces(uniqueName); String escapedLambda = removeBadSpaces(uniqueName);
simpleName = simpleName.replace(LAMBDA_START, escapedLambda); simpleName = simpleName.replace(LAMBDA_START, escapedLambda);
function = new DemangledLambda(mangledSource, demangled, null); function = new DemangledLambda(mangledSource, demangled, null);
function.setSignature(lambdaName.getFullText()); function.setBackupPlateComment(lambdaName.getFullText());
} }
// //
@ -437,16 +456,8 @@ public class GnuDemanglerParser {
function.addParameter(parameter); function.addParameter(parameter);
} }
// For GNU, we cannot leave the return type as null, because the DemangleCmd will fill in
// pointer to the class to accommodate windows demangling
DemangledDataType defaultReturnType =
new DemangledDataType(mangledSource, demangled, "undefined");
function.setReturnType(defaultReturnType);
String returnType = signatureParts.getReturnType(); String returnType = signatureParts.getReturnType();
if (returnType != null) { setReturnType(demangled, function, returnType);
setReturnType(function, returnType);
}
if (demangled.endsWith(CONST)) { if (demangled.endsWith(CONST)) {
function.setConst(true); function.setConst(true);
@ -455,27 +466,54 @@ public class GnuDemanglerParser {
return function; return function;
} }
private void setReturnType(DemangledFunction function, String returnType) { private void setReturnType(String demangled, DemangledFunction function, String returnType) {
if (DECLTYPE_RETURN_TYPE_PATTERN.matcher(returnType).matches()) { String updatedReturnType = returnType;
if (returnType != null && DECLTYPE_RETURN_TYPE_PATTERN.matcher(returnType).matches()) {
// Not sure yet if there is any information we wish to recover from this pattern. // Not sure yet if there is any information we wish to recover from this pattern.
// Sample: decltype (functionName({parm#1}, (float)[42c80000])) // Sample: decltype (functionName({parm#1}, (float)[42c80000]))
updatedReturnType = null;
}
if (updatedReturnType != null) {
function.setReturnType(parseReturnType(updatedReturnType));
return; return;
} }
function.setReturnType(parseReturnType(returnType)); // For GNU, we cannot leave the return type as null, because the DemangleCmd will fill in
// pointer to the class to accommodate windows demangling
DemangledDataType defaultReturnType =
new DemangledDataType(mangledSource, demangled, "undefined");
function.setReturnType(defaultReturnType);
} }
private LambdaName getLambdaName(String name) { private LambdaName getLambdaName(String name) {
Matcher matcher = LAMBDA_PATTERN.matcher(name); if (!name.startsWith("{")) {
// the text must start with the lambda syntax; ignore lambdas that are internal to
// the given name
return null;
}
// This replacement string will leave the initial 'lambda' text and replace all others
// with a placeholder value. This allows us to use a simple regex pattern when pulling
// the lambda apart. This is required to handle the case where a lambda expression
// contains a nested lambda expression.
LambdaReplacedString replacedString = new LambdaReplacedString(name);
String updatedName = replacedString.getModifiedText();
Matcher matcher = LAMBDA_PATTERN.matcher(updatedName);
if (!matcher.matches()) { if (!matcher.matches()) {
return null; return null;
} }
// restore the placeholder values to get back the original lambda text
String fullText = matcher.group(1); String fullText = matcher.group(1);
fullText = replacedString.restoreReplacedText(fullText);
String params = matcher.group(2); String params = matcher.group(2);
params = replacedString.restoreReplacedText(params);
String trailing = matcher.group(3); String trailing = matcher.group(3);
trailing = replacedString.restoreReplacedText(trailing);
String modifiers = matcher.group(4); String modifiers = matcher.group(4);
return new LambdaName(fullText, params, trailing, modifiers); return new LambdaName(fullText, params, trailing, modifiers);
} }
@ -513,7 +551,21 @@ public class GnuDemanglerParser {
DemangledObject parent = parseFunctionOrVariable(parentText); DemangledObject parent = parseFunctionOrVariable(parentText);
String name = itemText.substring(pos + 2); String name = itemText.substring(pos + 2);
DemangledObject item = parseFunctionOrVariable(name); DemangledObject item = parseFunctionOrVariable(name);
item.setNamespace(parent);
//
// Convert the parent type to a suitable namespace. The parent type may have spaces
// in its name, which is not allowed in an applied namespace.
//
// We may eventually want to move this logic into the DemangledObject's
// createNamespace() method. This would also apply to the convertToNamespaces()
// method in this class.
//
String namespaceName = parent.getNamespaceName();
String escapedName = removeBadSpaces(namespaceName);
DemangledType type = new DemangledType(mangledSource, demangledSource, escapedName);
type.setNamespace(parent.getNamespace());
item.setNamespace(type);
return item; return item;
} }
@ -530,6 +582,19 @@ public class GnuDemanglerParser {
return condensedString.getCondensedText(); return condensedString.getCondensedText();
} }
private String removeTrailingDereferenceCharacters(String text) {
int i = text.length() - 1;
for (; i >= 0; i--) {
char c = text.charAt(i);
if (c == '*' || c == '&') {
continue;
}
break;
}
return text.substring(0, i + 1);
}
/** /**
* This method separates the parameters as strings. * This method separates the parameters as strings.
* This is more complicated then one might initially think. * This is more complicated then one might initially think.
@ -670,6 +735,12 @@ public class GnuDemanglerParser {
return dt; return dt;
} }
// this handles the case where the demangled template has an empty argument
if ("".equals(parameter.trim())) {
return new DemangledDataType(mangledSource, demangledSource, "missing_argument");
}
return parseDataType(parameter); return parseDataType(parameter);
} }
@ -682,10 +753,16 @@ public class GnuDemanglerParser {
DemangledDataType dt = createTypeInNamespace(fullDatatype); DemangledDataType dt = createTypeInNamespace(fullDatatype);
String datatype = dt.getDemangledName(); String datatype = dt.getDemangledName();
if ("*".equals(datatype)) { if (isMemberPointerOrReference(fullDatatype, datatype)) {
return createMemberPointer(fullDatatype); return createMemberPointer(fullDatatype);
} }
// note: we should only encounter literals as template arguments. Function parameters
// and return types should never be literals.
if (isLiteral(fullDatatype)) {
return createLiteral(fullDatatype);
}
boolean finishedName = false; boolean finishedName = false;
for (int i = 0; i < datatype.length(); ++i) { for (int i = 0; i < datatype.length(); ++i) {
char ch = datatype.charAt(i); char ch = datatype.charAt(i);
@ -734,23 +811,37 @@ public class GnuDemanglerParser {
LambdaName lambdaName = getLambdaName(datatype); LambdaName lambdaName = getLambdaName(datatype);
// check for array case //
Matcher arrayMatcher = ARRAY_POINTER_REFERENCE_PATTERN.matcher(datatype); // Check for array case
if (arrayMatcher.matches()) { //
Demangled namespace = dt.getNamespace(); // remove the templates to allow us to use a simpler regex when checking for arrays
String name = arrayMatcher.group(1);// group 0 is the entire string DemangledDataType newDt = tryToParseArrayPointerOrReference(dt, datatype);
dt = parseArrayPointerOrReference(datatype, name, arrayMatcher); if (newDt != null) {
dt.setNamespace(namespace); dt = newDt;
i = arrayMatcher.end(); i = datatype.length();
} }
// lambda case, maybe an array
else if (lambdaName != null) { else if (lambdaName != null) {
DemangledDataType lambdaArrayDt =
tryToParseLambdaArrayPointerOrReference(lambdaName, dt, datatype);
if (lambdaArrayDt != null) {
dt = lambdaArrayDt;
i = datatype.length();
}
else {
// try a non-array lambda
String fullText = lambdaName.getFullText(); String fullText = lambdaName.getFullText();
dt.setName(fullText); dt.setName(fullText);
int offset = fullText.indexOf('('); int offset = fullText.indexOf('(');
// to to the end of the lambda, which is its length, minus our position
// inside the lambda
int remaining = fullText.length() - offset; int remaining = fullText.length() - offset;
i = i + remaining; // end of lambda's closing '}' i = i + remaining; // end of lambda's closing '}'
i = i - 1; // back up one space to catch optional templates on next loop pass i = i - 1; // back up one space to catch optional templates on next loop pass
} }
}
// function pointer case
else { else {
// e.g., unsigned long (*)(long const &) // e.g., unsigned long (*)(long const &)
boolean hasPointerParens = hasConsecutiveSetsOfParens(datatype.substring(i)); boolean hasPointerParens = hasConsecutiveSetsOfParens(datatype.substring(i));
@ -856,6 +947,81 @@ public class GnuDemanglerParser {
return dt; return dt;
} }
private DemangledDataType createLiteral(String datatype) {
// literal cases handled: -1, -1l, -1ul
char lastChar = datatype.charAt(datatype.length() - 1);
if (lastChar == 'l') {
return new DemangledDataType(mangledSource, demangledSource, "long");
}
return new DemangledDataType(mangledSource, demangledSource, "int");
}
private boolean isLiteral(String fullDatatype) {
Matcher m = LITERAL_NUMBER_PATTERN.matcher(fullDatatype);
return m.matches();
}
private DemangledDataType tryToParseLambdaArrayPointerOrReference(LambdaName lambdaName,
DemangledDataType dt, String datatype) {
// remove the lambda text to allow us to use a simpler regex when checking for arrays
String fullText = lambdaName.getFullText();
ReplacedString lambdaString = new CustomReplacedString(datatype, fullText);
String noLambdaString = lambdaString.getModifiedText();
Matcher matcher = ARRAY_POINTER_REFERENCE_PATTERN.matcher(noLambdaString);
if (!matcher.find()) {
return null;
}
int start = matcher.start(0);
String leading = noLambdaString.substring(0, start);
leading = removeTrailingDereferenceCharacters(leading);
Demangled namespace = dt.getNamespace();
String name = leading;
DemangledDataType newDt = parseArrayPointerOrReference(datatype, name, lambdaString,
matcher);
newDt.setNamespace(namespace);
return newDt;
}
private DemangledDataType tryToParseArrayPointerOrReference(DemangledDataType dt,
String datatype) {
ReplacedString templatedString = new TemplatedString(datatype);
String untemplatedDatatype = templatedString.getModifiedText();
Matcher matcher = ARRAY_POINTER_REFERENCE_PATTERN.matcher(untemplatedDatatype);
if (!matcher.find()) {
return null;
}
int start = matcher.start(0);
String leading = untemplatedDatatype.substring(0, start);
leading = removeTrailingDereferenceCharacters(leading);
Demangled namespace = dt.getNamespace();
String name = leading;
DemangledDataType newDt = parseArrayPointerOrReference(datatype, name, templatedString,
matcher);
newDt.setNamespace(namespace);
return newDt;
}
private boolean isMemberPointerOrReference(String fullDataType, String datatype) {
String test = datatype;
test = test.replaceAll("const|\\*|&|\\s", "");
if (!test.isEmpty()) {
return false;
}
return fullDataType.endsWith(Namespace.DELIMITER + datatype);
}
private boolean hasConsecutiveSetsOfParens(String text) { private boolean hasConsecutiveSetsOfParens(String text) {
int end = findBalancedEnd(text, 0, '(', ')'); int end = findBalancedEnd(text, 0, '(', ')');
if (end < -1) { if (end < -1) {
@ -869,9 +1035,12 @@ public class GnuDemanglerParser {
private DemangledDataType createMemberPointer(String datatype) { private DemangledDataType createMemberPointer(String datatype) {
// this is temp code we expect to update as more samples arrive // this is temp code we expect to update as more samples arrive
// example: NS1::Type1 NS1::ParenType::* //
int trimLength = 3; // '::*' // Examples:
String typeWithoutPointer = datatype.substring(0, datatype.length() - trimLength); // Type NS1::Type1 NS1::ParenType::*
// Type NS1::Type1 NS1::ParenType::* const&
int namespaceEnd = datatype.lastIndexOf(Namespace.DELIMITER);
String typeWithoutPointer = datatype.substring(0, namespaceEnd);
int space = typeWithoutPointer.indexOf(' '); int space = typeWithoutPointer.indexOf(' ');
DemangledDataType dt; DemangledDataType dt;
if (space != -1) { if (space != -1) {
@ -906,6 +1075,7 @@ public class GnuDemanglerParser {
Character.isDigit(ch) || Character.isDigit(ch) ||
ch == ':' || ch == ':' ||
ch == '_' || ch == '_' ||
ch == '{' ||
ch == '$'; ch == '$';
//@formatter:on //@formatter:on
} }
@ -1114,12 +1284,16 @@ public class GnuDemanglerParser {
} }
private DemangledDataType parseArrayPointerOrReference(String datatype, String name, private DemangledDataType parseArrayPointerOrReference(String datatype, String name,
Matcher matcher) { ReplacedString replacedString, Matcher matcher) {
// int (*)[8] // int (*)[8]
// char (&)[7] // char (&)[7]
// Foo<Bar> const* const (&) [3]
DemangledDataType dt = new DemangledDataType(mangledSource, demangledSource, name); String realName = replacedString.restoreReplacedText(name);
String type = matcher.group(2);
DemangledDataType dt = new DemangledDataType(mangledSource, demangledSource, realName);
String type = matcher.group(1);
if (type.equals("*")) { if (type.equals("*")) {
dt.incrementPointerLevels(); dt.incrementPointerLevels();
} }
@ -1130,9 +1304,29 @@ public class GnuDemanglerParser {
throw new DemanglerParseException("Unexpected charater inside of parens: " + type); throw new DemanglerParseException("Unexpected charater inside of parens: " + type);
} }
String arraySubscripts = matcher.group(3); //
int n = StringUtilities.countOccurrences(arraySubscripts, '['); // Grab the middle text, for example, inside:
dt.setArray(n); //
// Foo<Bar> const* const (&) [3]
//
// we would like to grab 'const* const(&)' and similar text such as 'const* const*'
//
String safeDatatype = replacedString.getModifiedText();
int midTextStart = safeDatatype.indexOf(name) + name.length();
int midTextEnd = matcher.start(1) - 1; // -1 for opening '('
String midText = safeDatatype.substring(midTextStart, midTextEnd);
if (midText.contains(CONST)) {
dt.setConst();
}
int pointers = StringUtils.countMatches(midText, '*');
for (int i = 0; i < pointers; i++) {
dt.incrementPointerLevels();
}
String arraySubscripts = matcher.group(2);
int arrays = StringUtilities.countOccurrences(arraySubscripts, '[');
dt.setArray(arrays);
return dt; return dt;
} }
@ -1141,14 +1335,14 @@ public class GnuDemanglerParser {
//unsigned long (*)(long const &) //unsigned long (*)(long const &)
int parenStart = functionString.indexOf('('); int parenStart = functionString.indexOf('(');
int parenEnd = functionString.indexOf(')'); int parenEnd = findBalancedEnd(functionString, parenStart, '(', ')');
String returnType = functionString.substring(0, parenStart).trim(); String returnType = functionString.substring(0, parenStart).trim();
int paramStart = functionString.indexOf('(', parenEnd + 1); int paramStart = functionString.indexOf('(', parenEnd + 1);
int paramEnd = functionString.lastIndexOf(')'); int paramEnd = functionString.lastIndexOf(')');
String parameters = functionString.substring(paramStart + 1, paramEnd); String parameters = functionString.substring(paramStart + 1, paramEnd);
return createFunctionPointer(parameters, returnType); DemangledFunctionPointer dfp = createFunctionPointer(parameters, returnType);
return dfp;
} }
private DemangledFunctionPointer parseFunction(String functionString, int offset) { private DemangledFunctionPointer parseFunction(String functionString, int offset) {
@ -1156,7 +1350,6 @@ public class GnuDemanglerParser {
int parenStart = functionString.indexOf('(', offset); int parenStart = functionString.indexOf('(', offset);
int parenEnd = findBalancedEnd(functionString, parenStart, '(', ')'); int parenEnd = findBalancedEnd(functionString, parenStart, '(', ')');
//int parenEnd = functionString.indexOf(')', parenStart + 1);
String returnType = functionString.substring(0, parenStart).trim(); String returnType = functionString.substring(0, parenStart).trim();
@ -1164,7 +1357,9 @@ public class GnuDemanglerParser {
int paramEnd = parenEnd; int paramEnd = parenEnd;
String parameters = functionString.substring(paramStart + 1, paramEnd); String parameters = functionString.substring(paramStart + 1, paramEnd);
DemangledFunctionPointer dfp = createFunctionPointer(parameters, returnType); DemangledFunctionPointer dfp = createFunctionPointer(parameters, returnType);
dfp.setDisplayFunctionPointerParens(false);
// disable the function pointer display so this type reads like a function
dfp.setDisplayDefaultFunctionPointerSyntax(false);
return dfp; return dfp;
} }
@ -1324,7 +1519,6 @@ public class GnuDemanglerParser {
DemangledObject doBuild(Demangled demangledObject) { DemangledObject doBuild(Demangled demangledObject) {
DemangledFunction function = (DemangledFunction) demangledObject; DemangledFunction function = (DemangledFunction) demangledObject;
function.setSignature(type);
function.setCallingConvention(CompilerSpec.CALLING_CONVENTION_thiscall); function.setCallingConvention(CompilerSpec.CALLING_CONVENTION_thiscall);
DemangledThunk thunk = new DemangledThunk(mangledSource, demangledSource, function); DemangledThunk thunk = new DemangledThunk(mangledSource, demangledSource, function);
@ -1545,9 +1739,14 @@ public class GnuDemanglerParser {
// Ghidra does not allow spaces in the name or extra parens. So, make a name that is // Ghidra does not allow spaces in the name or extra parens. So, make a name that is
// as clear as possible in describing the construct. // as clear as possible in describing the construct.
// //
if (shortReturnTypeName.contains("(")) {
// assume function pointer
shortReturnTypeName = "function.pointer";
}
method.setName("operator.cast.to." + shortReturnTypeName); method.setName("operator.cast.to." + shortReturnTypeName);
method.setSignature(fullName + " " + fullReturnType); method.setBackupPlateComment(fullName + " " + fullReturnType + "()");
method.setOverloadedOperator(true); method.setOverloadedOperator(true);
return method; return method;
@ -1604,10 +1803,9 @@ public class GnuDemanglerParser {
if (arrayBrackets != null) { if (arrayBrackets != null) {
name += "[]"; name += "[]";
} }
function.setName("operator." + name); function.setName("operator." + name);
function.setBackupPlateComment(operatorText + " " + operatorName);
function.setSignature(operatorText + " " + operatorName);
return function; return function;
} }
} }
@ -1628,6 +1826,7 @@ public class GnuDemanglerParser {
paramEnd = -1; paramEnd = -1;
return; return;
} }
paramStart = findParameterStart(text, paramEnd); paramStart = findParameterStart(text, paramEnd);
int templateEnd = findTemplateEnd(text, 0); int templateEnd = findTemplateEnd(text, 0);
int templateStart = -1; int templateStart = -1;
@ -1727,6 +1926,7 @@ public class GnuDemanglerParser {
private String returnType; private String returnType;
private String name; private String name;
private String rawParameterPrefix;
private List<DemangledDataType> parameters; private List<DemangledDataType> parameters;
@ -1746,9 +1946,9 @@ public class GnuDemanglerParser {
// 'prefix' is the text before the parameters // 'prefix' is the text before the parameters
int prefixEndPos = paramStart; int prefixEndPos = paramStart;
String rawPrefix = signatureString.substring(0, prefixEndPos).trim(); rawParameterPrefix = signatureString.substring(0, prefixEndPos).trim();
CondensedString prefixString = new CondensedString(rawPrefix); CondensedString prefixString = new CondensedString(rawParameterPrefix);
String prefix = prefixString.getCondensedText(); String prefix = prefixString.getCondensedText();
int nameStart = Math.max(0, prefix.lastIndexOf(' ')); int nameStart = Math.max(0, prefix.lastIndexOf(' '));
name = prefix.substring(nameStart, prefix.length()).trim(); name = prefix.substring(nameStart, prefix.length()).trim();
@ -1767,6 +1967,11 @@ public class GnuDemanglerParser {
return name; return name;
} }
// this is the original demangled text up to, but excluding, the parameters
String getRawParameterPrefix() {
return rawParameterPrefix;
}
boolean isValidFunction() { boolean isValidFunction() {
return isFunction; return isFunction;
} }
@ -1880,4 +2085,149 @@ public class GnuDemanglerParser {
} }
} }
} }
/**
* A class that allows us to pass around string content that has had some of its text
* replaced with temporary values. Clients can also use this class to get back the original
* text.
*/
private abstract class ReplacedString {
static final String PLACEHOLDER = "REPLACEDSTRINGTEMPNAMEPLACEHOLDERVALUE";
@SuppressWarnings("unused") // used by toString()
private String sourceText;
ReplacedString(String sourceText) {
this.sourceText = sourceText;
}
abstract String restoreReplacedText(String modifiedText);
abstract String getModifiedText();
@Override
public String toString() {
return Json.toString(this);
}
}
/**
* A string that clients can use to replace specific text patterns
*/
private class CustomReplacedString extends ReplacedString {
private String placeholderText = getClass().getSimpleName().toUpperCase() + PLACEHOLDER;
private String replacedText;
private String modifiedText;
CustomReplacedString(String input, String textToReplace) {
super(input);
this.replacedText = textToReplace;
this.modifiedText = input.replace(textToReplace, placeholderText);
}
@Override
String restoreReplacedText(String mutatedText) {
return mutatedText.replace(placeholderText, replacedText);
}
@Override
String getModifiedText() {
return modifiedText;
}
}
/**
* A simple class to replace templates with a temporary placeholder value
*/
private class TemplatedString extends ReplacedString {
private String placeholderText = getClass().getSimpleName().toUpperCase() + PLACEHOLDER;
private String replacedText;
private String modifiedText;
TemplatedString(String input) {
super(input);
replaceTemplates(input);
}
private void replaceTemplates(String string) {
StringBuilder buffy = new StringBuilder();
StringBuilder templateBuffer = new StringBuilder();
int depth = 0;
for (int i = 0; i < string.length(); i++) {
char c = string.charAt(i);
if (c == '<') {
if (depth == 0) {
buffy.append(placeholderText);
}
templateBuffer.append(c);
depth++;
continue;
}
else if (c == '>') {
templateBuffer.append(c);
depth--;
continue;
}
if (depth == 0) {
buffy.append(c);
}
else {
templateBuffer.append(c);
}
}
modifiedText = buffy.toString();
replacedText = templateBuffer.toString();
}
@Override
String restoreReplacedText(String s) {
return s.replace(placeholderText, replacedText);
}
@Override
String getModifiedText() {
return modifiedText;
}
}
/**
* A simple class to replace the text 'lambda' with a temporary placeholder value
*/
private class LambdaReplacedString extends ReplacedString {
private String placeholderText = getClass().getSimpleName().toUpperCase() + PLACEHOLDER;
private String modifiedText;
LambdaReplacedString(String input) {
super(input);
StringBuilder buffer = new StringBuilder();
Pattern p = Pattern.compile(LAMBDA);
Matcher matcher = p.matcher(input);
matcher.find(); // keep the first match
while (matcher.find()) {
matcher.appendReplacement(buffer, placeholderText);
}
matcher.appendTail(buffer);
modifiedText = buffer.toString();
}
@Override
String restoreReplacedText(String s) {
return s.replaceAll(placeholderText, LAMBDA);
}
@Override
String getModifiedText() {
return modifiedText;
}
}
} }

View file

@ -42,6 +42,12 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
// bob(int const[8] (*) [12]) // bob(int const[8] (*) [12])
//
// Note: it is not clear whether the this demangled string is valid modern demangler output.
// We are creating a constant array pointer from this construct, but is this what
// we should be creating?
//
String demangled = "bob(int const[8] (*) [12])"; String demangled = "bob(int const[8] (*) [12])";
DemangledObject object = parser.parse("fake", demangled); DemangledObject object = parser.parse("fake", demangled);
assertType(object, DemangledFunction.class); assertType(object, DemangledFunction.class);
@ -52,7 +58,27 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
assertEquals(1, parameters.size()); assertEquals(1, parameters.size());
DemangledDataType p1 = parameters.get(0); DemangledDataType p1 = parameters.get(0);
assertEquals("bob(int const[8] (*) [12])", p1.getOriginalDemangled()); assertEquals("bob(int const[8] (*) [12])", p1.getOriginalDemangled());
assertEquals("undefined bob(int *[])", object.getSignature(false)); assertEquals("undefined bob(int const *[])", object.getSignature(false));
}
@Test
public void testParse_ArrayPointerReferencePattern_ConstPointerToArrayReference()
throws Exception {
// _S_ptr(entt::sparse_set<EntityId> const * &[])
String mangled =
"_ZNSt14__array_traitsIPKN4entt10sparse_setI8EntityIdEELm3EE6_S_ptrERA3_KS5_";
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
assertNotNull(object);
assertType(object, DemangledFunction.class);
String signature = object.getSignature(false);
assertEquals(
"undefined std::__array_traits<entt::sparse_set<EntityId>const*,3ul>::_S_ptr(entt::sparse_set<EntityId> const * &[])",
signature);
} }
@Test @Test
@ -122,14 +148,14 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
assertEquals( assertEquals(
"undefined XpsMap<long,CORBA_TypeCode*>::XpsMap(" + "undefined XpsMap<long,CORBA_TypeCode*>::XpsMap(" +
"unsigned long ()(long const &),unsigned long,unsigned long,float)", "unsigned long (*)(long const &),unsigned long,unsigned long,float)",
object.getSignature(false)); object.getSignature(false));
DemangledFunction method = (DemangledFunction) object; DemangledFunction method = (DemangledFunction) object;
List<DemangledDataType> parameters = method.getParameters(); List<DemangledDataType> parameters = method.getParameters();
assertEquals(4, parameters.size()); assertEquals(4, parameters.size());
assertEquals("unsigned long ()(long const &)", parameters.get(0).getSignature()); assertEquals("unsigned long (*)(long const &)", parameters.get(0).getSignature());
assertEquals("unsigned long", parameters.get(1).getSignature()); assertEquals("unsigned long", parameters.get(1).getSignature());
assertEquals("unsigned long", parameters.get(2).getSignature()); assertEquals("unsigned long", parameters.get(2).getSignature());
assertEquals("float", parameters.get(3).getSignature()); assertEquals("float", parameters.get(3).getSignature());
@ -182,7 +208,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
"__gnu_cxx::__normal_iterator<std::pair<unsigned long,PcodeOp *> *,std::vector<std::pair<unsigned long,PcodeOp *>,std::allocator<std::pair<unsigned long,PcodeOp *>>>>", "__gnu_cxx::__normal_iterator<std::pair<unsigned long,PcodeOp *> *,std::vector<std::pair<unsigned long,PcodeOp *>,std::allocator<std::pair<unsigned long,PcodeOp *>>>>",
parameters.get(1).toString()); parameters.get(1).toString());
assertEquals( assertEquals(
"bool ()(std::pair<unsigned long,PcodeOp *> const &,std::pair<unsigned long,PcodeOp *> const &)", "bool (*)(std::pair<unsigned long,PcodeOp *> const &,std::pair<unsigned long,PcodeOp *> const &)",
parameters.get(2).toString()); parameters.get(2).toString());
assertType(parameters.get(2), DemangledFunctionPointer.class); assertType(parameters.get(2), DemangledFunctionPointer.class);
@ -334,6 +360,64 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
object.getSignature(false)); object.getSignature(false));
} }
@Test
public void testParse_DefaultArg() throws Exception {
//
// The demangled string contains this string: {default arg#1}
//
String mangled =
"_ZZN12PackManifest18CapabilityRegistry18registerCapabilityEN3gsl17basic_string_spanIKcLln1EEEbSt8functionIFbRS_R10PackReportbEEEd_NKUlS6_S8_bE_clES6_S8_b";
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
assertNotNull(object);
assertType(object, DemangledFunction.class);
String signature = object.getSignature(false);
assertEquals(
"PackManifest::CapabilityRegistry::registerCapability(gsl::basic_string_span<char_const,-1l>,bool,std::function<bool(PackManifest&,PackReport&,bool)>)::{default arg#1}::{lambda(PackManifest&,PackReport&,bool)#1}::operator()(PackManifest &,PackReport &,bool)",
signature);
}
@Test
public void testParse_UnnamedType() throws Exception {
//
// The demangled string contains this string: {unnamed_type#1}
//
String mangled =
"_ZN14GoalDefinitionUt_aSERKS0_";
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
assertNotNull(object);
assertType(object, DemangledFunction.class);
String signature = object.getSignature(false);
assertEquals(
"undefined GoalDefinition::{unnamed_type#1}::operator=({unnamed const &)",
signature);
}
@Test
public void testParse_DecltypeAuto() throws Exception {
String mangled =
"_Z9enum_castIN17FurnaceBlockActorUt_EEDcT_";
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
assertNotNull(object);
assertType(object, DemangledFunction.class);
String signature = object.getSignature(false);
assertEquals("decltype (auto)", signature);
}
@Test @Test
public void testMethod() throws Exception { public void testMethod() throws Exception {
String mangled = "_ZN3Foo7getBoolEf"; String mangled = "_ZN3Foo7getBoolEf";
@ -416,9 +500,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
DemangledObject object = parser.parse(mangled, demangled); DemangledObject object = parser.parse(mangled, demangled);
assertType(object, DemangledVariable.class); assertType(object, DemangledVariable.class);
assertName(object, "dotdot", "KSimpleFileFilter", "passesFilter(KFileItem const *)"); assertName(object, "dotdot", "KSimpleFileFilter", "passesFilter(KFileItem_const*)");
assertEquals("KSimpleFileFilter::passesFilter(KFileItem const *)::dotdot", assertEquals("KSimpleFileFilter::passesFilter(KFileItem_const*)::dotdot",
object.getSignature(false)); object.getSignature(false));
} }
@ -682,8 +766,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
assertType(object, DemangledFunction.class); assertType(object, DemangledFunction.class);
assertName(object, "graphNew", "Layout"); assertName(object, "graphNew", "Layout");
// note: the two pointers were condensed to one (I think this is correct, but not sure) assertEquals("undefined Layout::graphNew(_GRAPH * *[],char *)", object.getSignature(false));
assertEquals("undefined Layout::graphNew(_GRAPH *[],char *)", object.getSignature(false));
} }
@Test @Test
@ -913,7 +996,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
assertEquals("operator<<", name); assertEquals("operator<<", name);
assertName(object, "operator<<", "std", "basic_ostream<char,std::char_traits<char>>"); assertName(object, "operator<<", "std", "basic_ostream<char,std::char_traits<char>>");
assertEquals("undefined std::basic_ostream<char,std::char_traits<char>>" + "::operator<<(" + assertEquals("undefined std::basic_ostream<char,std::char_traits<char>>" + "::operator<<(" +
"std::basic_ostream<char,std::char_traits<char>> & ()(std::basic_ostream<char,std::char_traits<char>> &))", "std::basic_ostream<char,std::char_traits<char>> & (*)(std::basic_ostream<char,std::char_traits<char>> &))",
object.getSignature()); object.getSignature());
} }
@ -979,6 +1062,128 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
assertEquals("Magick::Image &", parameters.get(0).getSignature()); assertEquals("Magick::Image &", parameters.get(0).getSignature());
} }
@Test
public void testPointerToArray_WithLambda() throws Exception {
String mangled =
"_ZNSt14__array_traitsIN12LayerDetails15RandomProviderTIZNKS0_9LayerBase10initRandomEllEUlRljE_EELm4EE6_S_refERA4_KS5_m";
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
assertNotNull(object);
assertType(object, DemangledFunction.class);
String signature = object.getSignature(false);
assertEquals(
"undefined std::__array_traits<LayerDetails::RandomProviderT<LayerDetails::LayerBase::initRandom(long,long)const::{lambda(long&,unsigned_int)#1}>,4ul>::_S_ref(LayerDetails::LayerBase::initRandom(long,long) const::{lambda(long&, unsigned int)#1} const &[],unsigned long)",
signature);
}
@Test
public void testOperator_WithTemplatesMissingATemplateArgument() throws Exception {
/*
Note: the empty template type: '<, std...'
<, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > std::_Bind<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > (EduAppConfigs::*(EduAppConfigs const*))() const>::operator()<, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >()
*/
String mangled =
"_ZNSt5_BindIFM13EduAppConfigsKFNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEvEPKS0_EEclIJES6_EET0_DpOT_";
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
assertNotNull(object);
assertType(object, DemangledFunction.class);
String signature = object.getSignature(false);
assertEquals(
"std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>> std::_Bind<std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>(EduAppConfigs::*(EduAppConfigs_const*))()const>::operator()<missing_argument,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>(void)",
signature);
}
@Test
public void testParamegterWithTemplateValue_DataTypeLiteral_int() throws Exception {
String mangled = "_Z13reverse_rangeIjLin1EE5RangeIiXT0_EET_";
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
assertNotNull(object);
assertType(object, DemangledFunction.class);
String signature = object.getSignature(false);
assertEquals("Range<int,int> reverse_range<unsigned_int,-1>(unsigned int)", signature);
}
@Test
public void testParamegterWithTemplateValue_DataTypeLiteral_long() throws Exception {
String mangled =
"_ZN3gsl9to_stringIKcLln1EEENSt7__cxx1112basic_stringINSt12remove_constIT_E4typeESt11char_traitsIS7_ESaIS7_EEENS_17basic_string_spanIS5_XT0_EEE";
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
assertNotNull(object);
assertType(object, DemangledFunction.class);
String signature = object.getSignature(false);
assertEquals(
"std::__cxx11::basic_string<std::remove_const<char_const>::type,std::char_traits<std::remove_const<char_const>::type>,std::allocator<std::remove_const<char_const>::type>> gsl::to_string<char_const,-1l>(gsl::basic_string_span<char const,long>)",
signature);
}
@Test
public void testLambdaWithLambdaParameters() throws Exception {
/*
lambda contents - lambdas in templates and as a parameter
bool (***
const* std::
__addressof<
Bedrock::
Threading::
TLSDetail::
DefaultConstructor<bool (**)(AssertHandlerContext const&), void>::
create()::
{lambda(bool (*** const)(AssertHandlerContext const&))#1}
>
(
Bedrock::
Threading::
TLSDetail::
DefaultConstructor<bool (**)(AssertHandlerContext const&), void>::
create()::
{lambda(bool (*** const&)(AssertHandlerContext const&))#1}
)
)(AssertHandlerContext const&)
*/
String mangled =
"_ZSt11__addressofIKZN7Bedrock9Threading9TLSDetail18DefaultConstructorIPPFbRK20AssertHandlerContextEvE6createEvEUlPS9_E_EPT_RSE_";
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
assertNotNull(object);
assertType(object, DemangledFunction.class);
String signature = object.getSignature(false);
assertEquals(
"undefined Bedrock::Threading::TLSDetail::DefaultConstructor<bool(**)(AssertHandlerContext_const&),void>::create()::{lambda(bool(***const*std::__addressof<Bedrock::Threading::TLSDetail::DefaultConstructor<bool(**)(AssertHandlerContext_const&),void>::create()::{lambda(bool(***const)(AssertHandlerContext_const&))#1}>(Bedrock::Threading::TLSDetail::DefaultConstructor<bool(**)(AssertHandlerContext_const&),void>::create()::{lambda(bool(***const&)(AssertHandlerContext_const&))#1}))(AssertHandlerContext_const&))#1}",
signature);
}
@Test @Test
public void testOperatorCastTo() throws Exception { public void testOperatorCastTo() throws Exception {
// //
@ -998,6 +1203,53 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
assertEquals("bool std::integral_constant::operator.cast.to.bool(void)", signature); assertEquals("bool std::integral_constant::operator.cast.to.bool(void)", signature);
} }
@Test
public void testOperatorCastTo_FunctionPointer() throws Exception {
String mangled =
"_ZZNK4entt14basic_registryI8EntityIdE6assureI32FilteredTransformationAttributesI26PreHillsEdgeTransformationEEERKNS2_12pool_handlerIT_EEvENKUlRNS_10sparse_setIS1_EERS2_S1_E_cvPFvSE_SF_S1_EEv";
String demangled = process.demangle(mangled);
/*
Full demangled:
Operator Text
entt::
basic_registry<EntityId>::
assure<FilteredTransformationAttributes<PreHillsEdgeTransformation> >() const::
{lambda(entt::sparse_set<EntityId>&, entt::basic_registry<EntityId>&, EntityId)#1}::
operator void (*)(entt::sparse_set<EntityId>&, entt::basic_registry<EntityId>&, EntityId)() const
Operartor Without Namespace
operator void (*)(entt::sparse_set<EntityId>&, entt::basic_registry<EntityId>&, EntityId)()
Simplified Cast Operator Construct
operator void (*)(A,B,C)()
*/
DemangledObject object = parser.parse(mangled, demangled);
assertNotNull(object);
assertType(object, DemangledFunction.class);
//@formatter:off
String expected =
"void (* " +
"entt::" +
"basic_registry::" +
"assure() const::" +
"{lambda(entt::sparse_set&,entt::basic_registry&,EntityId)#1}::" +
"operator.cast.to.function.pointer(void)" +
")(entt::sparse_set<EntityId> &,entt::basic_registry<EntityId> &,EntityId)";
//@formatter:on
String signature = object.getSignature(false);
assertEquals(expected, signature);
}
@Test @Test
public void testConversionOperator() throws Exception { public void testConversionOperator() throws Exception {
@ -1226,7 +1478,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
assertName(object, "FTransferGvmlData", "Dr", "ClipboardHelper"); assertName(object, "FTransferGvmlData", "Dr", "ClipboardHelper");
assertEquals( assertEquals(
"undefined Dr::ClipboardHelper::FTransferGvmlData(Art::Transaction &,Ofc::TReferringPtr<Dr::DrawingE2o> const &,bool,Ofc::TCntPtr<IDataObject>,Dr::IClientDataCreator &,Ofc::TVector<Ofc::TWeakPtr<Dr::DrawingElement>,0u,4294967295u> &,Art::Rect64 &)", "undefined Dr::ClipboardHelper::FTransferGvmlData(Art::Transaction &,Ofc::TReferringPtr<Dr::DrawingE2o> const &,bool,Ofc::TCntPtr<IDataObject>,Dr::IClientDataCreator &,Ofc::TVector<Ofc::TWeakPtr<Dr::DrawingElement>,int,int> &,Art::Rect64 &)",
object.getSignature(false)); object.getSignature(false));
} }
@ -1549,7 +1801,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String signature = object.getSignature(false); String signature = object.getSignature(false);
assertEquals( assertEquals(
"WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1} brigand::for_each_args<WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1},brigand::type_<std::__1::integral_constant<long,0l>>,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std::__1<long,1l>>,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std::__1<long,2l>>>(WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1},brigand::type_<std::__1::integral_constant<long,0l>> &&,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std::__1<long,1l>> &&,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std::__1<long,2l>> &&)", "WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1} brigand::for_each_args<WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1},brigand::type_<std::__1::integral_constant<long,0l>>,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std::__1<long,1l>>,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std::__1<long,2l>>>(WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1},brigand::type_<std::__1::integral_constant<long,long>> &&,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std::__1<long,long>> &&,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std::__1<long,long>> &&)",
signature); signature);
} }
@ -1587,7 +1839,46 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String signature = object.getSignature(false); String signature = object.getSignature(false);
assertEquals( assertEquals(
"undefined WebCore::FontSelectionAlgorithm::filterCapability(bool *,WebCore::FontSelectionAlgorithm::DistanceResult ()(WebCore::FontSelectionCapabilities) const,WebCore::FontSelectionCapabilities::FontSelectionRange *)", "undefined WebCore::FontSelectionAlgorithm::filterCapability(bool *,WebCore::FontSelectionAlgorithm::DistanceResult (*)(WebCore::FontSelectionCapabilities) const,WebCore::FontSelectionCapabilities::FontSelectionRange *)",
signature);
}
@Test
public void testFunctionParameterWithMemberPointer_ToFloat() throws Exception {
//
// Test to ensure proper handling of 'float AvoidBlockGoal::Definition::* const&'
// which is a const reference to a floating point member of the class
// AvoidBlockGoal::Definition
//
/*
Demangled:
auto && JsonUtil::
addMember<std::shared_ptr<JsonUtil::JsonSchemaObjectNode<JsonUtil::EmptyClass,AvoidBlockGoal::Definition>>,AvoidBlockGoal::Definition,float>
(
std::shared_ptr<JsonUtil::JsonSchemaObjectNode<JsonUtil::EmptyClass,AvoidBlockGoal::Definition>>,
float AvoidBlockGoal::Definition::*,
char const *,
float AvoidBlockGoal::Definition::* const&
)
*/
String mangled =
"_ZN8JsonUtil9addMemberISt10shared_ptrINS_20JsonSchemaObjectNodeINS_10EmptyClassEN14AvoidBlockGoal10DefinitionEEEES5_fEEODaT_MT0_T1_PKcRKSC_";
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
assertNotNull(object);
assertType(object, DemangledFunction.class);
String signature = object.getSignature(false);
assertEquals(
"auto && JsonUtil::addMember<std::shared_ptr<JsonUtil::JsonSchemaObjectNode<JsonUtil::EmptyClass,AvoidBlockGoal::Definition>>,AvoidBlockGoal::Definition,float>(std::shared_ptr<JsonUtil::JsonSchemaObjectNode<JsonUtil::EmptyClass,AvoidBlockGoal::Definition>>,AvoidBlockGoal::Definition::float *,char const *,AvoidBlockGoal::Definition::float *)",
signature); signature);
} }
@ -1618,7 +1909,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String signature = object.getSignature(false); String signature = object.getSignature(false);
assertEquals( assertEquals(
"undefined WebCore::TextCodecICU::registerCodecs(void ()(char const *,WTF::Function<std::__1::unique_ptr<WebCore::TextCodec,std::__1::default_delete<WebCore::TextCodec>> ()> &&))", "undefined WebCore::TextCodecICU::registerCodecs(void (*)(char const *,WTF::Function<std::__1::unique_ptr<WebCore::TextCodec,std::__1::default_delete<WebCore::TextCodec>> ()> &&))",
signature); signature);
} }
@ -1676,6 +1967,23 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
} }
@Test
public void testOperator_ArrayReference() throws Exception {
String mangled =
"_ZN12LayerDetails15RandomProviderTIZNKS_9LayerBase10initRandomEllEUlRljE_EclIiLm2EEET_RAT0__KS6_";
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
assertNotNull(object);
assertType(object, DemangledFunction.class);
String signature = object.getSignature(false);
assertEquals(
"int LayerDetails::RandomProviderT<LayerDetails::LayerBase::initRandom(long,long)const::{lambda(long&,unsigned_int)#1}>::operator()<int,2ul>(LayerDetails::RandomProviderT<LayerDetails::LayerBase::initRandom(long,long)const::{lambda(long&,unsigned_int)#1}>::operator() const &[])",
signature);
}
@Test @Test
public void testLambdaWithTemplates() throws Exception { public void testLambdaWithTemplates() throws Exception {

View file

@ -476,6 +476,15 @@ public class MDMangBaseTest extends AbstractGenericTest {
demangleAndTest(); demangleAndTest();
} }
@Test
public void testFunctionPointer_NamedFunctionPointerWithAnonymousFunctionPointerParameter()
throws Exception {
mangled = "?fun@@3P6KXP6KXH@Z@ZA";
msTruth = "void (* fun)(void (*)(int))";
mdTruth = msTruth;
demangleAndTest();
}
@Test @Test
public void testFunctionPointer_EMod_invalid() throws Exception { public void testFunctionPointer_EMod_invalid() throws Exception {
mangled = "?fn@@3PE6AHH@ZA"; mangled = "?fn@@3PE6AHH@ZA";

View file

@ -2312,7 +2312,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
SourceType originalSource = namespaceSymbol.getSource(); SourceType originalSource = namespaceSymbol.getSource();
// no duplicate check, since this class name will be set to that of the existing namespace // no duplicate check, since this class name will be set to that of the existing namespace
String tempName = name + System.nanoTime(); String tempName = "_temp_" + System.nanoTime();
SymbolDB classSymbol = SymbolDB classSymbol =
doCreateSpecialSymbol(Address.NO_ADDRESS, tempName, namespace.getParentNamespace(), doCreateSpecialSymbol(Address.NO_ADDRESS, tempName, namespace.getParentNamespace(),
SymbolType.CLASS, -1, -1, null, originalSource, false /*check for duplicate */); SymbolType.CLASS, -1, -1, null, originalSource, false /*check for duplicate */);
@ -2334,7 +2334,8 @@ public class SymbolManager implements SymbolTable, ManagerDB {
return classNamespace; return classNamespace;
} }
catch (DuplicateNameException | InvalidInputException | CircularDependencyException e) { catch (DuplicateNameException | InvalidInputException | CircularDependencyException e) {
throw new AssertException("Unexpected exception creating class from namespace", e); throw new AssertException("Unexpected exception creating class from namespace: " +
e.getMessage(), e);
} }
finally { finally {
lock.release(); lock.release();