diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorModel.java index a9c2884052..4940221a1c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorModel.java @@ -642,8 +642,8 @@ public class FunctionEditorModel { try { if (autoParamCount < oldAutoCount) { if (oldParams.get(autoParamCount) - .getStorage() - .getAutoParameterType() != storage.getAutoParameterType()) { + .getStorage() + .getAutoParameterType() != storage.getAutoParameterType()) { adjustSelectionForRowRemoved(i); } } @@ -1047,6 +1047,24 @@ public class FunctionEditorModel { return true; } + private FunctionSignature getFunctionSignature() { + FunctionDefinitionDataType funDt = new FunctionDefinitionDataType(name); + funDt.setReturnType(returnInfo.getFormalDataType()); + List params = new ArrayList<>(); + + for (ParamInfo paramInfo : parameters) { + if (paramInfo.isAutoParameter()) { + continue; + } + String paramName = paramInfo.getName(); + DataType paramDt = paramInfo.getFormalDataType(); + params.add(new ParameterDefinitionImpl(paramName, paramDt, null)); + } + funDt.setArguments(params.toArray(new ParameterDefinition[params.size()])); + funDt.setVarArgs(hasVarArgs); + return funDt; + } + Program getProgram() { return program; } @@ -1170,7 +1188,7 @@ public class FunctionEditorModel { public void parseSignatureFieldText() throws ParseException, CancelledException { FunctionSignatureParser parser = new FunctionSignatureParser(program.getDataTypeManager(), dataTypeManagerService); - FunctionDefinitionDataType f = parser.parse(function.getSignature(), signatureFieldText); + FunctionDefinitionDataType f = parser.parse(getFunctionSignature(), signatureFieldText); setFunctionData(f); isInParsingMode = false; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/parser/FunctionSignatureParser.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/parser/FunctionSignatureParser.java index 603eac7cd5..cac7e71780 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/parser/FunctionSignatureParser.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/parser/FunctionSignatureParser.java @@ -16,7 +16,6 @@ package ghidra.app.util.parser; import java.util.*; -import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.help.UnsupportedOperationException; @@ -98,7 +97,6 @@ public class FunctionSignatureParser { */ public FunctionDefinitionDataType parse(FunctionSignature originalSignature, String signatureText) throws ParseException, CancelledException { - dtMap.clear(); nameMap.clear(); if (dtmService != null) { @@ -236,18 +234,18 @@ public class FunctionSignatureParser { } private String replaceDataTypeIfNeeded(String text, DataType dataType, String replacementName) { - String displayName = dataType.getDisplayName(); - if (canParse(displayName)) { + String datatypeName = dataType.getName(); + if (canParseType(datatypeName)) { return text; } dtMap.put(replacementName, dataType); - return substitute(text, displayName, replacementName); + return substitute(text, datatypeName, replacementName); } private String replaceNameIfNeeded(String text, String name, String replacementName) { - if (canParse(name)) { + if (canParseName(name)) { return text; } nameMap.put(replacementName, name); @@ -272,36 +270,11 @@ public class FunctionSignatureParser { return dt; } - // The following regex pattern attempts to isolate the parameter name from - // the beginning of a parameter specification. Since the name is optional, - // additional steps must be taken in code to ensure that the trailing word of - // a multi-word type-specified is not treated as a name (e.g., unsigned long). - // - // The regex pattern attempts to isolate the following fields: - // - // [|]* [param-name] - // group-1 group-3 group-4 - // - // Note: group-2 is an inner group to group-3 is not useful - // - private static final Pattern parameterNameCapturePattern = - Pattern.compile("(.+?)((\\[\\d*\\]|\\*\\d*)\\s*)*([^\\s\\[\\*]+)"); - private DataType resolveDataType(String dataTypeName) throws CancelledException { if (dtMap.containsKey(dataTypeName)) { return dtMap.get(dataTypeName); } - Matcher m = parameterNameCapturePattern.matcher(dataTypeName); - if (m.matches()) { - boolean hasPointerOrArraySpec = m.group(3) != null; - boolean hasName = (m.group(4) != null) && (m.group(4).length() != 0); - if (hasPointerOrArraySpec && hasName) { - // name after array/pointer spec - dataTypeName is not a valid datatype - return null; - } - } - DataType dataType = null; try { dataType = dataTypeParser.parse(dataTypeName); @@ -330,7 +303,7 @@ public class FunctionSignatureParser { if (nameMap.containsKey(name)) { return nameMap.get(name); } - if (!canParse(name)) { + if (!canParseName(name)) { throw new ParseException("Can't parse name: " + name); } return name; @@ -340,10 +313,14 @@ public class FunctionSignatureParser { return text.replaceFirst(Pattern.quote(searchString), replacementString); } - private boolean canParse(String text) { + private boolean canParseName(String text) { return !StringUtils.containsAny(text, "()*[], "); } + private boolean canParseType(String text) { + return !StringUtils.containsAny(text, "()<>,"); + } + /** * Provides a simple caching datatype manager service wrapper.
* Implementation intended for use with {@link FunctionSignatureParser} diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/parser/FunctionSignatureParserTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/parser/FunctionSignatureParserTest.java index 719a79b6b3..efe852b848 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/parser/FunctionSignatureParserTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/parser/FunctionSignatureParserTest.java @@ -194,6 +194,75 @@ public class FunctionSignatureParserTest extends AbstractGhidraHeadedIntegration assertEquals("char Bob(longlong a, uint b)", dt.getRepresentation(null, null, 0)); } + @Test + public void testReturnPointerToNoPointer() throws Exception { + FunctionSignature f = fun("char *", "Bob"); + FunctionDefinitionDataType dt = parser.parse(f, "char Bob()"); + assertEquals("char Bob(void)", dt.getRepresentation(null, null, 0)); + } + + @Test + public void testReturnPointerToSizedPointer() throws Exception { + FunctionSignature f = fun("char *", "Bob"); + FunctionDefinitionDataType dt = parser.parse(f, "char *32 Bob()"); + assertEquals("char * Bob(void)", dt.getRepresentation(null, null, 0)); + assertEquals("char *", dt.getReturnType().getDisplayName()); + } + + @Test + public void testReturnPointerToPointerPointer() throws Exception { + FunctionSignature f = fun("char *", "Bob"); + FunctionDefinitionDataType dt = parser.parse(f, "char * * Bob()"); + assertEquals("char * * Bob(void)", dt.getRepresentation(null, null, 0)); + } + + @Test + public void testChangeReturnTypeToIncludeSizedPointer() throws Exception { + FunctionSignature f = fun("char", "Bob"); + FunctionDefinitionDataType dt = parser.parse(f, "char *32 Bob(void)"); + assertEquals("char * Bob(void)", dt.getRepresentation(null, null, 0)); + assertEquals("char *32", dt.getReturnType().getName()); + } + + @Test + public void testParseFunctionNameWithSizedPointerReturnType() throws Exception { + FunctionSignature f = fun("char *32", "Bob"); + FunctionDefinitionDataType dt = parser.parse(f, "char *32 Joe()"); + assertEquals("char * Joe(void)", dt.getRepresentation(null, null, 0)); + } + + @Test + public void testParamPointerToSizedPointer() throws Exception { + FunctionSignature f = fun("int", "Bob", "char *", "p1"); + FunctionDefinitionDataType dt = parser.parse(f, "int Bob(char *32 p1)"); + assertEquals("int Bob(char * p1)", dt.getRepresentation(null, null, 0)); + ParameterDefinition[] arguments = dt.getArguments(); + assertEquals("char *32", arguments[0].getDataType().getName()); + } + + @Test + public void testParamPointerToPointerPointer() throws Exception { + FunctionSignature f = fun("int", "Bob", "char *", "p1"); + FunctionDefinitionDataType dt = parser.parse(f, "int Bob(char * * p1)"); + assertEquals("int Bob(char * * p1)", dt.getRepresentation(null, null, 0)); + } + + @Test + public void testChangeParamTypeToIncludeSizedPointer() throws Exception { + FunctionSignature f = fun("int", "Bob", "char", "p1"); + FunctionDefinitionDataType dt = parser.parse(f, "int Bob(char *32 p1)"); + assertEquals("int Bob(char * p1)", dt.getRepresentation(null, null, 0)); + ParameterDefinition[] arguments = dt.getArguments(); + assertEquals("char *32", arguments[0].getDataType().getName()); + } + + @Test + public void testParseParamNameWithSizedPointerDataType() throws Exception { + FunctionSignature f = fun("int", "Bob", "char *32", "p1"); + FunctionDefinitionDataType dt = parser.parse(f, "int Bob(char *32 p2)"); + assertEquals("int Bob(char * p2)", dt.getRepresentation(null, null, 0)); + } + @Test public void testSpacesNotAllowedInTypedFunctionName() { FunctionSignature f = fun("int", "Bob", "int", "a"); @@ -419,6 +488,15 @@ public class FunctionSignatureParserTest extends AbstractGhidraHeadedIntegration if (name.equals("int")) { return new IntegerDataType(); } + if (name.equals("char *")) { + return new PointerDataType(new CharDataType()); + } + if (name.equals("char *32")) { + return new Pointer32DataType(new CharDataType()); + } + if (name.equals("char * *32")) { + return new Pointer32DataType(new PointerDataType(new CharDataType())); + } return new StructureDataType(name, 2); }