GP-94 - Gnu Demangler - review fixes

Closes #2214
This commit is contained in:
dragonmacher 2021-02-12 16:05:26 -05:00
parent 443e398bb4
commit 8f1dc3f476
14 changed files with 386 additions and 319 deletions

View file

@ -386,10 +386,7 @@
<U><B>Use Deprecated Demangler</B></U> - <U><B>Use Deprecated Demangler</B></U> -
By default, GCC symbols will be demangled using the most up-to-date demangler By default, GCC symbols will be demangled using the most up-to-date demangler
that Ghidra contains (<B>version 2.33.1</B> as of this writing). Turning this that Ghidra contains (<B>version 2.33.1</B> as of this writing). Turning this
option on will also invoke the now deprecated previous version of the demangler option on will invoke the now deprecated version of the demangler (<B>version 2.24</B>).
(<B>version 2.24</B>) if the preferred demangler cannot demangle a given symbol.
This option only has an effect when the demangler format is available in both
the deprecated and modern demanglers.
</P> </P>
<P> <P>
Support for older demangling styles was removed in <CODE>c++filt (v2.32)</CODE>. Support for older demangling styles was removed in <CODE>c++filt (v2.32)</CODE>.
@ -402,10 +399,15 @@
<P> <P>
The available programs are: The available programs are:
<UL> <UL>
<LI><CODE>demangler_gnu_v2_33_1</CODE></LI> <LI><CODE CLASS="path">
<LI><CODE>demangler_gnu_v2_24</CODE></LI> &lt;GHIDRA_INSTALL_DIR&gt;/GPL/DemanglerGnu/os/&lt;OS&gt;/
</CODE><CODE>demangler_gnu_v2_33_1</CODE></LI>
<LI><CODE CLASS="path">
&lt;GHIDRA_INSTALL_DIR&gt;/GPL/DemanglerGnu/os/&lt;OS&gt;/
</CODE><CODE>demangler_gnu_v2_24</CODE></LI>
</UL> </UL>
</P> </P>
<P style="background-color: #FFF0E0;"> <P style="background-color: #FFF0E0;">
<IMG SRC="../../shared/warning.png" />When using an external GNU demangler, <IMG SRC="../../shared/warning.png" />When using an external GNU demangler,
please understand the risks associated with using that version of the please understand the risks associated with using that version of the

View file

@ -16,7 +16,7 @@
package ghidra.app.util.demangler; package ghidra.app.util.demangler;
/** /**
* A simple class to contain the various settings for demangling. * A simple class to contain the various settings for demangling
*/ */
public class DemanglerOptions { public class DemanglerOptions {
@ -35,18 +35,18 @@ public class DemanglerOptions {
} }
/** /**
* Checks if the apply signature option is currently set. * Checks if the apply signature option is currently set
* *
* @return true if set to apply function signatures that are demangled. * @return true if set to apply function signatures that are demangled
*/ */
public boolean applySignature() { public boolean applySignature() {
return applySignature; return applySignature;
} }
/** /**
* Set the option to apply function signatures that are demangled. * Set the option to apply function signatures that are demangled
* *
* @param applySignature true to apply function signatures that are demangled. * @param applySignature true to apply function signatures that are demangled
*/ */
public void setApplySignature(boolean applySignature) { public void setApplySignature(boolean applySignature) {
this.applySignature = applySignature; this.applySignature = applySignature;
@ -54,9 +54,9 @@ public class DemanglerOptions {
/** /**
* Checks if the option to perform disassembly for known data structures (like functions) when * Checks if the option to perform disassembly for known data structures (like functions) when
* demangling is set. * demangling is set
* *
* @return true if the option is set. * @return true if the option is set
*/ */
public boolean doDisassembly() { public boolean doDisassembly() {
return doDisassembly; return doDisassembly;
@ -64,18 +64,18 @@ public class DemanglerOptions {
/** /**
* Sets the option to perform disassembly for known data structures (like functions) when * Sets the option to perform disassembly for known data structures (like functions) when
* demangling. * demangling
* *
* @param doDisassembly true to perform disassembly when demangling. * @param doDisassembly true to perform disassembly when demangling
*/ */
public void setDoDisassembly(boolean doDisassembly) { public void setDoDisassembly(boolean doDisassembly) {
this.doDisassembly = doDisassembly; this.doDisassembly = doDisassembly;
} }
/** /**
* Checks if the option to only demangle known mangled patterns is set. * Checks if the option to only demangle known mangled patterns is set
* *
* @return true if only known mangled patterns will be demangled. * @return true if only known mangled patterns will be demangled
*/ */
public boolean demangleOnlyKnownPatterns() { public boolean demangleOnlyKnownPatterns() {
return demangleOnlyKnownPatterns; return demangleOnlyKnownPatterns;
@ -83,10 +83,15 @@ public class DemanglerOptions {
/** /**
* Sets the option to only demangle known mangled patterns. Setting this to false causes * Sets the option to only demangle known mangled patterns. Setting this to false causes
* all symbols to be demangled, which may result in some symbols getting demangled that were not * most symbols to be demangled, which may result in some symbols getting demangled that were
* actually mangled symbols. * not actually mangled symbols.
* *
* @param demangleOnlyKnownPatterns true to only demangle known mangled patterns. * <P>Generally, a demangler will report an error if a symbol fails to demangle. Hence,
* clients can use this flag to prevent such errors, signalling to the demangler to only
* attempt those symbols that have a known start pattern. If the known start pattern list
* becomes comprehensive, then this flag can go away.
*
* @param demangleOnlyKnownPatterns true to only demangle known mangled patterns
*/ */
public void setDemangleOnlyKnownPatterns(boolean demangleOnlyKnownPatterns) { public void setDemangleOnlyKnownPatterns(boolean demangleOnlyKnownPatterns) {
this.demangleOnlyKnownPatterns = demangleOnlyKnownPatterns; this.demangleOnlyKnownPatterns = demangleOnlyKnownPatterns;

View file

@ -21,8 +21,7 @@
//@category Examples.Demangler //@category Examples.Demangler
import ghidra.app.script.GhidraScript; import ghidra.app.script.GhidraScript;
import ghidra.app.util.demangler.DemangledObject; import ghidra.app.util.demangler.DemangledObject;
import ghidra.app.util.demangler.gnu.GnuDemangler; import ghidra.app.util.demangler.gnu.*;
import ghidra.app.util.demangler.gnu.GnuDemanglerOptions;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
public class DemangleElfWithOptionScript extends GhidraScript { public class DemangleElfWithOptionScript extends GhidraScript {
@ -33,8 +32,8 @@ public class DemangleElfWithOptionScript extends GhidraScript {
GnuDemangler demangler = new GnuDemangler(); GnuDemangler demangler = new GnuDemangler();
if (!demangler.canDemangle(currentProgram)) { if (!demangler.canDemangle(currentProgram)) {
String executableFormat = currentProgram.getExecutableFormat(); String executableFormat = currentProgram.getExecutableFormat();
println("Cannot use the elf demangling options for executable format: " + println(
executableFormat); "Cannot use the elf demangling options for executable format: " + executableFormat);
return; return;
} }
@ -49,14 +48,12 @@ public class DemangleElfWithOptionScript extends GhidraScript {
String mangled = symbol.getName(); String mangled = symbol.getName();
GnuDemanglerOptions options = new GnuDemanglerOptions(); GnuDemanglerOptions options = new GnuDemanglerOptions(GnuDemanglerFormat.AUTO, false);
options.setDoDisassembly(false); options.setDoDisassembly(false);
options.setDemanglerApplicationArguments("-s auto");
/* /*
// for older formats use the deprecated demangler // for older formats use the deprecated demangler
options.setDemanglerName(GnuDemanglerOptions.GNU_DEMANGLER_V2_24); options = options.withDemanglerFormat(GnuDemanglerFormat.ARM, true);
options.setDemanglerApplicationArguments("-s arm");
*/ */
DemangledObject demangledObject = demangler.demangle(mangled, options); DemangledObject demangledObject = demangler.demangle(mangled, options);

View file

@ -19,6 +19,7 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Arrays; import java.util.Arrays;
import docking.options.editor.BooleanEditor;
import ghidra.app.util.demangler.*; import ghidra.app.util.demangler.*;
import ghidra.app.util.demangler.gnu.*; import ghidra.app.util.demangler.gnu.*;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
@ -26,8 +27,6 @@ import ghidra.framework.options.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import docking.options.editor.BooleanEditor;
/** /**
* A version of the demangler analyzer to handle GNU GCC symbols * A version of the demangler analyzer to handle GNU GCC symbols
*/ */
@ -91,8 +90,8 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
options.registerOption(OPTION_NAME_USE_DEPRECATED_DEMANGLER, OptionType.BOOLEAN_TYPE, options.registerOption(OPTION_NAME_USE_DEPRECATED_DEMANGLER, OptionType.BOOLEAN_TYPE,
useDeprecatedDemangler, help, OPTION_DESCRIPTION_DEPRECATED_DEMANGLER, editor); useDeprecatedDemangler, help, OPTION_DESCRIPTION_DEPRECATED_DEMANGLER, editor);
options.registerOption(OPTION_NAME_DEMANGLER_FORMAT, OptionType.ENUM_TYPE, options.registerOption(OPTION_NAME_DEMANGLER_FORMAT, OptionType.ENUM_TYPE, demanglerFormat,
demanglerFormat, help, OPTION_DESCRIPTION_DEMANGLER_FORMAT, formatEditor); help, OPTION_DESCRIPTION_DEMANGLER_FORMAT, formatEditor);
} }
@Override @Override
@ -118,7 +117,7 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
@Override @Override
protected DemangledObject doDemangle(String mangled, DemanglerOptions demanglerOtions, protected DemangledObject doDemangle(String mangled, DemanglerOptions demanglerOtions,
MessageLog log) throws DemangledException { MessageLog log) throws DemangledException {
return demangler.demangle(mangled, (GnuDemanglerOptions) demanglerOtions); return demangler.demangle(mangled, demanglerOtions);
} }
private static class FormatEditor extends EnumEditor implements PropertyChangeListener { private static class FormatEditor extends EnumEditor implements PropertyChangeListener {
@ -164,7 +163,8 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
if (format.isAvailable(isDeprecatedDemangler())) { if (format.isAvailable(isDeprecatedDemangler())) {
setValue(format); setValue(format);
selector.setFormat(format); selector.setFormat(format);
} else { }
else {
setValue(GnuDemanglerFormat.AUTO); setValue(GnuDemanglerFormat.AUTO);
} }
} }
@ -178,18 +178,16 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
} }
} }
@SuppressWarnings("serial")
private static class FormatSelector extends PropertySelector { private static class FormatSelector extends PropertySelector {
public FormatSelector(FormatEditor fe) { public FormatSelector(FormatEditor fe) {
super(fe); super(fe);
} }
@SuppressWarnings("unchecked")
void reset(String[] tags) { void reset(String[] tags) {
removeAllItems(); removeAllItems();
for (int i = 0; i < tags.length; i++) { for (String tag : tags) {
addItem(tags[i]); addItem(tag);
} }
} }

View file

@ -106,15 +106,13 @@ public class GnuDemangler implements Demangler {
} }
boolean onlyKnownPatterns = options.demangleOnlyKnownPatterns(); boolean onlyKnownPatterns = options.demangleOnlyKnownPatterns();
DemangledObject demangledObject = DemangledObject demangledObject = parse(mangled, process, demangled, onlyKnownPatterns);
parse(mangled, process, demangled, onlyKnownPatterns);
if (demangledObject == null) { if (demangledObject == null) {
return demangledObject; return demangledObject;
} }
if (globalPrefix != null) { if (globalPrefix != null) {
DemangledFunction dfunc = DemangledFunction dfunc = new DemangledFunction(originalMangled, demangled,
new DemangledFunction(originalMangled, demangled,
globalPrefix + demangledObject.getName()); globalPrefix + demangledObject.getName());
dfunc.setNamespace(demangledObject.getNamespace()); dfunc.setNamespace(demangledObject.getNamespace());
demangledObject = dfunc; demangledObject = dfunc;
@ -163,6 +161,19 @@ public class GnuDemangler implements Demangler {
applicationOptions); applicationOptions);
} }
/**
* Determines if the given mangled string should not be demangled. There are a couple
* patterns that will always be skipped.
* If {@link GnuDemanglerOptions#demangleOnlyKnownPatterns()} is true, then only mangled
* symbols matching a list of known start patters will not be skipped.
*
* <P>This demangler class will default to demangling most patterns, since we do not yet
* have a comprehensive list of known start patterns.
*
* @param mangled the mangled string
* @param options the options
* @return true if the string should not be demangled
*/
private boolean skip(String mangled, GnuDemanglerOptions options) { private boolean skip(String mangled, GnuDemanglerOptions options) {
// Ignore versioned symbols which are generally duplicated at the same address // Ignore versioned symbols which are generally duplicated at the same address
@ -179,13 +190,8 @@ public class GnuDemangler implements Demangler {
return false; // let it go through return false; // let it go through
} }
// TODO provide some checks specific to the other formats // This is the current list of known demangler start patterns. Add to this list if we
GnuDemanglerFormat format = options.getDemanglerFormat(); // find any other known GNU start patterns.
if (format == GnuDemanglerFormat.AUTO) {
return false;
}
if (format == GnuDemanglerFormat.GNUV3) {
// add to this list if we find any other known GNU start patterns
if (mangled.startsWith("_Z")) { if (mangled.startsWith("_Z")) {
return false; return false;
} }
@ -201,14 +207,12 @@ public class GnuDemangler implements Demangler {
if (isGnu2Or3Pattern(mangled)) { if (isGnu2Or3Pattern(mangled)) {
return false; return false;
} }
}
return true; return true;
} }
private DemangledObject parse(String mangled, GnuDemanglerNativeProcess process, private DemangledObject parse(String mangled, GnuDemanglerNativeProcess process,
String demangled, String demangled, boolean demangleOnlyKnownPatterns) {
boolean demangleOnlyKnownPatterns) {
if (demangleOnlyKnownPatterns && !isKnownMangledString(mangled, demangled)) { if (demangleOnlyKnownPatterns && !isKnownMangledString(mangled, demangled)) {
return null; return null;

View file

@ -22,36 +22,35 @@ public enum GnuDemanglerFormat {
// OLD: none,auto,gnu,lucid,arm,hp,edg,gnu-v3,java,gnat // OLD: none,auto,gnu,lucid,arm,hp,edg,gnu-v3,java,gnat
// NEW: none,auto,gnu-v3,java,gnat,dlang,rust // NEW: none,auto,gnu-v3,java,gnat,dlang,rust
/** Automatic mangling format detection */ /** Automatic mangling format detection */
AUTO("", 0), AUTO("", Version.ALL),
/** GNUv2 mangling format */ /** GNUv2 mangling format */
GNU("gnu", -1), GNU("gnu", Version.DEPRECATED),
/** lucid mangling format */ /** lucid mangling format */
LUCID("lucid", -1), LUCID("lucid", Version.DEPRECATED),
/** arm mangling format */ /** arm mangling format */
ARM("arm", -1), ARM("arm", Version.DEPRECATED),
/** hp mangling format */ /** hp mangling format */
HP("hp", -1), HP("hp", Version.DEPRECATED),
/** mangling format used by the Edison Design Group (EDG) compiler */ /** mangling format used by the Edison Design Group (EDG) compiler */
EDG("edg", -1), EDG("edg", Version.DEPRECATED),
/** GNUv3 mangling format */ /** GNUv3 mangling format */
GNUV3("gnu-v3", 0), GNUV3("gnu-v3", Version.ALL),
/** Java mangling format */ /** Java mangling format */
JAVA("java", 0), JAVA("java", Version.ALL),
/** GNAT Ada compiler mangling format */ /** GNAT Ada compiler mangling format */
GNAT("gnat", 0), GNAT("gnat", Version.ALL),
/** D mangling format */ /** D mangling format */
DLANG("dlang", 1), DLANG("dlang", Version.MODERN),
/** Rust mangling format */ /** Rust mangling format */
RUST("rust", 1); RUST("rust", Version.MODERN);
/** the format option string */ /** the format option string used by the native demangler */
private final String format; private final String format;
/** private sentinal. deprecated = -1, both = 0, new = 1 */ private final Version version;
private final byte version;
private GnuDemanglerFormat(String format, int version) { private GnuDemanglerFormat(String format, Version version) {
this.format = format; this.format = format;
this.version = (byte) version; this.version = version;
} }
/** /**
@ -59,20 +58,20 @@ public enum GnuDemanglerFormat {
* @return true if this format is available in the deprecated gnu demangler * @return true if this format is available in the deprecated gnu demangler
*/ */
public boolean isDeprecatedFormat() { public boolean isDeprecatedFormat() {
return version <= 0; return version == Version.DEPRECATED || version == Version.ALL;
} }
/** /**
* Checks if this format is available in a modern version of the gnu demangler * Checks if this format is available in a modern version of the gnu demangler
* @return true if this format is available in a modern version of the gnu demangler. * @return true if this format is available in a modern version of the gnu demangler
*/ */
public boolean isModernFormat() { public boolean isModernFormat() {
return version >= 0; return version == Version.MODERN || version == Version.ALL;
} }
/** /**
* Checks if this format is available for the specified demangler * Checks if this format is available for the specified demangler
* @param isDeprecated true for the deprecated demangler, false for the modern demangler. * @param isDeprecated true for the deprecated demangler, false for the modern demangler
* @return true if the format is available * @return true if the format is available
*/ */
public boolean isAvailable(boolean isDeprecated) { public boolean isAvailable(boolean isDeprecated) {
@ -86,4 +85,8 @@ public enum GnuDemanglerFormat {
public String getFormat() { public String getFormat() {
return format; return format;
} }
private enum Version {
DEPRECATED, MODERN, ALL
}
} }

View file

@ -46,17 +46,32 @@ public class GnuDemanglerOptions extends DemanglerOptions {
private final GnuDemanglerFormat format; private final GnuDemanglerFormat format;
private final boolean isDeprecated; private final boolean isDeprecated;
/**
* Default constructor to use the modern demangler with auto-detect for the format. This
* constructor will limit demangling to only known symbols.
*/
public GnuDemanglerOptions() { public GnuDemanglerOptions() {
// use default values
this(GnuDemanglerFormat.AUTO); this(GnuDemanglerFormat.AUTO);
} }
/**
* Constructor to specify a particular format
*
* @param format signals to use the given format
*/
public GnuDemanglerOptions(GnuDemanglerFormat format) { public GnuDemanglerOptions(GnuDemanglerFormat format) {
this.format = format; this(format, !format.isModernFormat());
// default to the "new" demangler if the format is available in both
this.isDeprecated = !format.isModernFormat();
} }
/**
* Constructor to specify the format to use and whether to prefer the deprecated format when
* both deprecated and modern are available
*
* @param format the format
* @param isDeprecated true if the format is not available in the modern demangler
* @throws IllegalArgumentException if the given format is not available in the deprecated
* demangler
*/
public GnuDemanglerOptions(GnuDemanglerFormat format, boolean isDeprecated) { public GnuDemanglerOptions(GnuDemanglerFormat format, boolean isDeprecated) {
this.format = format; this.format = format;
this.isDeprecated = isDeprecated; this.isDeprecated = isDeprecated;
@ -66,6 +81,10 @@ public class GnuDemanglerOptions extends DemanglerOptions {
} }
} }
/**
* Copy constructor to create a version of this class from a more generic set of options
* @param copy the options to copy
*/
public GnuDemanglerOptions(DemanglerOptions copy) { public GnuDemanglerOptions(DemanglerOptions copy) {
super(copy); super(copy);
@ -73,7 +92,8 @@ public class GnuDemanglerOptions extends DemanglerOptions {
GnuDemanglerOptions gCopy = (GnuDemanglerOptions) copy; GnuDemanglerOptions gCopy = (GnuDemanglerOptions) copy;
format = gCopy.format; format = gCopy.format;
isDeprecated = gCopy.isDeprecated; isDeprecated = gCopy.isDeprecated;
} else { }
else {
format = GnuDemanglerFormat.AUTO; format = GnuDemanglerFormat.AUTO;
isDeprecated = false; isDeprecated = false;
} }
@ -97,23 +117,24 @@ public class GnuDemanglerOptions extends DemanglerOptions {
/** /**
* A convenience method to copy the state of this options object, changing the * A convenience method to copy the state of this options object, changing the
* demangler executable name and demangler format to the specified values. * demangler executable name and demangler format to the specified values
* @param format the demangling format to use *
* @param isDeprecated true to use the deprecated gnu demangler, else false * @param demanglerFormat the demangling format to use
* @param useDeprecated true to use the deprecated gnu demangler, else false
* @return the new options * @return the new options
* @throws IllegalArgumentException if the current format is not available in the * @throws IllegalArgumentException if the current format is not available in the
* selected demangler. * selected demangler.
*/ */
public GnuDemanglerOptions withDemanglerFormat(GnuDemanglerFormat format, boolean isDeprecated) public GnuDemanglerOptions withDemanglerFormat(GnuDemanglerFormat demanglerFormat,
throws IllegalArgumentException { boolean useDeprecated) throws IllegalArgumentException {
if (this.format == format && this.isDeprecated == isDeprecated) { if (this.format == demanglerFormat && this.isDeprecated == useDeprecated) {
return this; return this;
} }
if (format.isAvailable(isDeprecated)) { if (demanglerFormat.isAvailable(useDeprecated)) {
return new GnuDemanglerOptions(this, format, isDeprecated); return new GnuDemanglerOptions(this, demanglerFormat, useDeprecated);
} }
throw new IllegalArgumentException( throw new IllegalArgumentException(
format.name() + " is not available in the "+getDemanglerName()); demanglerFormat.name() + " is not available in the " + getDemanglerName());
} }
/** /**

View file

@ -1353,7 +1353,7 @@ public class GnuDemanglerParser {
DemangledObject doBuild(Demangled namespace) { DemangledObject doBuild(Demangled namespace) {
DemangledString demangledString = new DemangledString(mangledSource, demangledSource, DemangledString demangledString = new DemangledString(mangledSource, demangledSource,
"typeinfo-name", type, -1/*unknown length*/, false); "typeinfo-name", type, -1/*unknown length*/, false);
demangledString.setSpecialPrefix("typeinfo name for "); demangledString.setSpecialPrefix(TYPEINFO_NAME_FOR);
String namespaceString = removeBadSpaces(type); String namespaceString = removeBadSpaces(type);
setNamespace(demangledString, namespaceString); setNamespace(demangledString, namespaceString);
return demangledString; return demangledString;

View file

@ -17,12 +17,16 @@ package ghidra.app.plugin.core.analysis;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.Arrays;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import docking.options.editor.BooleanEditor;
import ghidra.app.cmd.label.AddLabelCmd; import ghidra.app.cmd.label.AddLabelCmd;
import ghidra.app.util.demangler.gnu.GnuDemanglerFormat; import ghidra.app.util.demangler.gnu.GnuDemanglerFormat;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.EnumEditor;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB; import ghidra.program.database.ProgramDB;
@ -104,6 +108,24 @@ public class GnuDemanglerAnalyzerTest extends AbstractGhidraHeadlessIntegrationT
assertDemangled(addr, "__dt"); assertDemangled(addr, "__dt");
} }
@Test
public void testMangledString_WithArguments_Valid() {
//
// The below demangles to std::io::Read::read_to_end
//
String mangled = "_ZN3std2io4Read11read_to_end17hb85a0f6802e14499E";
Address addr = addr("0x110");
createSymbol(addr, mangled);
setFormat(GnuDemanglerFormat.RUST);
analyze();
assertDemangled(addr, "read_to_end");
}
@Test @Test
public void testMangledString_WithArguments_ValidButWrongFormat() { public void testMangledString_WithArguments_ValidButWrongFormat() {
@ -123,26 +145,13 @@ public class GnuDemanglerAnalyzerTest extends AbstractGhidraHeadlessIntegrationT
} }
@Test @Test
public void testUseDeprecatedOptionRemoval_WithDeprecatedFormat() { public void testUseDeprecatedOptionUpdatesAvailableFormats() {
setFormat(GnuDemanglerFormat.GNU);
Options options = program.getOptions("Analyzers");
assertFalse(options.contains(GnuDemanglerAnalyzer.OPTION_NAME_USE_DEPRECATED_DEMANGLER));
}
@Test setOption_UseDeprecatedDemangler(false);
public void testUseDeprecatedOptionRemoval_WithRecentFormat() { assertFormatAvailable(GnuDemanglerFormat.RUST, true);
setFormat(GnuDemanglerFormat.RUST);
Options options = program.getOptions("Analyzers");
assertFalse(options.contains(GnuDemanglerAnalyzer.OPTION_NAME_USE_DEPRECATED_DEMANGLER));
}
@Test setOption_UseDeprecatedDemangler(true);
public void testUseDeprecatedOptionAddition() { assertFormatAvailable(GnuDemanglerFormat.RUST, false);
// remove it first
testUseDeprecatedOptionRemoval_WithDeprecatedFormat();
setFormat(GnuDemanglerFormat.AUTO);
Options options = program.getOptions("Analyzers");
assertTrue(options.contains(GnuDemanglerAnalyzer.OPTION_NAME_USE_DEPRECATED_DEMANGLER));
} }
// things missed: // things missed:
@ -181,6 +190,32 @@ public class GnuDemanglerAnalyzerTest extends AbstractGhidraHeadlessIntegrationT
fail("Unable to find demangled symbol '" + name + "'"); fail("Unable to find demangled symbol '" + name + "'");
} }
private void assertFormatAvailable(GnuDemanglerFormat format, boolean isAvailable) {
Options options = program.getOptions("Analyzers");
Options analyzerOptions = options.getOptions(analyzer.getName());
EnumEditor enumEditor =
(EnumEditor) runSwing(() -> analyzerOptions.getPropertyEditor("Demangler Format"));
assertNotNull(enumEditor);
Enum<?>[] values = enumEditor.getEnums();
for (Enum<?> enum1 : values) {
if (format.equals(enum1)) {
if (isAvailable) {
return;
}
fail("Found bad enum in list of choices: " + format + ".\nFound: " +
Arrays.toString(values));
}
}
if (isAvailable) {
fail("Did not find enum in list of choices: " + format + ".\nInstead found: " +
Arrays.toString(values));
}
}
private void setOption(String optionName, boolean doUse) { private void setOption(String optionName, boolean doUse) {
String fullOptionName = analyzer.getName() + Options.DELIMITER_STRING + optionName; String fullOptionName = analyzer.getName() + Options.DELIMITER_STRING + optionName;
@ -199,6 +234,18 @@ public class GnuDemanglerAnalyzerTest extends AbstractGhidraHeadlessIntegrationT
fail("Could not find option '" + optionName + "'"); fail("Could not find option '" + optionName + "'");
} }
private void setOption_UseDeprecatedDemangler(boolean use) {
Options options = program.getOptions("Analyzers");
Options analyzerOptions = options.getOptions(analyzer.getName());
BooleanEditor enumEditor = (BooleanEditor) runSwing(
() -> analyzerOptions.getPropertyEditor("Use Deprecated Demangler"));
assertNotNull(enumEditor);
runSwing(() -> enumEditor.setValue(use));
}
private void setFormat(GnuDemanglerFormat format) { private void setFormat(GnuDemanglerFormat format) {
String optionName = GnuDemanglerAnalyzer.OPTION_NAME_DEMANGLER_FORMAT; String optionName = GnuDemanglerAnalyzer.OPTION_NAME_DEMANGLER_FORMAT;

View file

@ -32,8 +32,8 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
process = GnuDemanglerNativeProcess.getDemanglerNativeProcess( process = GnuDemanglerNativeProcess
GnuDemanglerOptions.GNU_DEMANGLER_V2_33_1); .getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_33_1);
parser = new GnuDemanglerParser(); parser = new GnuDemanglerParser();
} }
@ -111,8 +111,8 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
public void testFunctionPointers() throws Exception { public void testFunctionPointers() throws Exception {
String mangled = "__t6XpsMap2ZlZP14CORBA_TypeCodePFRCl_UlUlUlf"; String mangled = "__t6XpsMap2ZlZP14CORBA_TypeCodePFRCl_UlUlUlf";
process = GnuDemanglerNativeProcess.getDemanglerNativeProcess( process = GnuDemanglerNativeProcess
GnuDemanglerOptions.GNU_DEMANGLER_V2_24); .getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
String demangled = process.demangle(mangled); String demangled = process.demangle(mangled);
@ -564,8 +564,8 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String mangled = "CalcPortExposedRect__13LScrollerViewCFR4Rectb"; String mangled = "CalcPortExposedRect__13LScrollerViewCFR4Rectb";
// use an older demangler; the current demangler cannot handle this string // use an older demangler; the current demangler cannot handle this string
process = GnuDemanglerNativeProcess.getDemanglerNativeProcess( process = GnuDemanglerNativeProcess
GnuDemanglerOptions.GNU_DEMANGLER_V2_24); .getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
String demangled = process.demangle(mangled); String demangled = process.demangle(mangled);
@ -589,8 +589,8 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String mangled = "__dt__Q26MsoDAL9VertFrameFv"; String mangled = "__dt__Q26MsoDAL9VertFrameFv";
// use an older demangler; the current demangler cannot handle this string // use an older demangler; the current demangler cannot handle this string
process = GnuDemanglerNativeProcess.getDemanglerNativeProcess( process = GnuDemanglerNativeProcess
GnuDemanglerOptions.GNU_DEMANGLER_V2_24); .getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
String demangled = process.demangle(mangled); String demangled = process.demangle(mangled);
@ -630,8 +630,8 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String mangled = "GetColWidths__13CDataRendererCFRA7_s"; String mangled = "GetColWidths__13CDataRendererCFRA7_s";
// use an older demangler; the current demangler cannot handle this string // use an older demangler; the current demangler cannot handle this string
process = GnuDemanglerNativeProcess.getDemanglerNativeProcess( process = GnuDemanglerNativeProcess
GnuDemanglerOptions.GNU_DEMANGLER_V2_24); .getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
String demangled = process.demangle(mangled); String demangled = process.demangle(mangled);
@ -655,8 +655,8 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String mangled = "GetColWidths__13CDataRendererCFPA7_s"; String mangled = "GetColWidths__13CDataRendererCFPA7_s";
// use an older demangler; the current demangler cannot handle this string // use an older demangler; the current demangler cannot handle this string
process = GnuDemanglerNativeProcess.getDemanglerNativeProcess( process = GnuDemanglerNativeProcess
GnuDemanglerOptions.GNU_DEMANGLER_V2_24); .getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
String demangled = process.demangle(mangled); String demangled = process.demangle(mangled);
@ -714,8 +714,8 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String mangled = "_gmStage2__FP12SECTION_INFOPiPA12_iiPCs"; String mangled = "_gmStage2__FP12SECTION_INFOPiPA12_iiPCs";
// use an older demangler; the current demangler cannot handle this string // use an older demangler; the current demangler cannot handle this string
process = GnuDemanglerNativeProcess.getDemanglerNativeProcess( process = GnuDemanglerNativeProcess
GnuDemanglerOptions.GNU_DEMANGLER_V2_24); .getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
String demangled = process.demangle(mangled); String demangled = process.demangle(mangled);
@ -749,8 +749,8 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String mangled = "__ct__Q24CStr6BufferFR4CStrUl"; String mangled = "__ct__Q24CStr6BufferFR4CStrUl";
// use an older demangler; the current demangler cannot handle this string // use an older demangler; the current demangler cannot handle this string
process = GnuDemanglerNativeProcess.getDemanglerNativeProcess( process = GnuDemanglerNativeProcess
GnuDemanglerOptions.GNU_DEMANGLER_V2_24); .getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
String demangled = process.demangle(mangled); String demangled = process.demangle(mangled);
@ -887,16 +887,14 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
@Test @Test
public void testOverloadedShiftOperatorTemplated_LeftShift() { public void testOverloadedShiftOperatorTemplated_LeftShift() {
String raw = String raw = "std::basic_ostream<char, std::char_traits<char> >& " +
"std::basic_ostream<char, std::char_traits<char> >& " +
"std::operator<< <std::char_traits<char> >" + "std::operator<< <std::char_traits<char> >" +
"(std::basic_ostream<char, std::char_traits<char> >&, char const*)"; "(std::basic_ostream<char, std::char_traits<char> >&, char const*)";
String formatted = "std::basic_ostream<char,std::char_traits<char>> & " + String formatted = "std::basic_ostream<char,std::char_traits<char>> & " +
"std::operator<<<std::char_traits<char>>" + "std::operator<<<std::char_traits<char>>" +
"(std::basic_ostream<char,std::char_traits<char>> &,char const *)"; "(std::basic_ostream<char,std::char_traits<char>> &,char const *)";
DemangledObject object = parser.parse( DemangledObject object =
"_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc", parser.parse("_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc", raw);
raw);
String name = object.getName(); String name = object.getName();
assertEquals("operator<<", name); assertEquals("operator<<", name);
assertEquals(formatted, object.getSignature()); assertEquals(formatted, object.getSignature());
@ -1475,13 +1473,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
// Incorrect Native Output: uv(double, *__restrict) // Incorrect Native Output: uv(double, *__restrict)
// //
String mangled = "uv__dup"; String mangled = "uv__dup";
GnuDemangler demangler = new GnuDemangler(); GnuDemangler demangler = new GnuDemangler();
try { DemangledObject demangled = demangler.demangle(mangled);
demangler.demangle(mangled); assertNull(demangled);
} catch (DemangledException e) {
assertTrue(e.isInvalidMangledName());
}
} }
@Test @Test
@ -1551,8 +1545,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String name = String name =
"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>>>"; "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>>>";
assertName(object, name, assertName(object, name, "brigand");
"brigand");
String signature = object.getSignature(false); String signature = object.getSignature(false);
assertEquals( assertEquals(
@ -1673,8 +1666,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
assertNotNull(object); assertNotNull(object);
assertType(object, DemangledFunction.class); assertType(object, DemangledFunction.class);
String name = String name = "operator=";
"operator=";
assertName(object, name, "WTF", "Function<void()>"); assertName(object, name, "WTF", "Function<void()>");
String signature = object.getSignature(false); String signature = object.getSignature(false);

View file

@ -57,7 +57,8 @@ public class GnuDemanglerTest extends AbstractGenericTest {
// this throws an exception with the bug in place // this throws an exception with the bug in place
try { try {
demangler.demangle(mangled); demangler.demangle(mangled);
} catch (DemangledException e) { }
catch (DemangledException e) {
assertTrue(e.isInvalidMangledName()); assertTrue(e.isInvalidMangledName());
} }
} }
@ -184,6 +185,22 @@ public class GnuDemanglerTest extends AbstractGenericTest {
assertNull(result); assertNull(result);
} }
@Test
public void testDemangler_Format_EDG_DemangleOnlyKnownPatterns_False()
throws DemangledException {
String mangled = "_$_10MyFunction";
GnuDemangler demangler = new GnuDemangler();
demangler.canDemangle(program);// this perform initialization
GnuDemanglerOptions options = new GnuDemanglerOptions(GnuDemanglerFormat.AUTO, true);
options.setDemangleOnlyKnownPatterns(false);
DemangledObject result = demangler.demangle(mangled, options);
assertNotNull(result);
assertEquals("undefined MyFunction::~MyFunction(void)", result.getSignature(false));
}
@Test @Test
public void testDemangler_Format_CodeWarrior_MacOS8or9() throws DemangledException { public void testDemangler_Format_CodeWarrior_MacOS8or9() throws DemangledException {
// NOTE: mangled CodeWarrior format symbols with templates will fail // NOTE: mangled CodeWarrior format symbols with templates will fail
@ -196,6 +213,7 @@ public class GnuDemanglerTest extends AbstractGenericTest {
demangler.canDemangle(program);// this perform initialization demangler.canDemangle(program);// this perform initialization
GnuDemanglerOptions options = new GnuDemanglerOptions(GnuDemanglerFormat.AUTO, true); GnuDemanglerOptions options = new GnuDemanglerOptions(GnuDemanglerFormat.AUTO, true);
options.setDemangleOnlyKnownPatterns(false);
DemangledObject result = demangler.demangle(mangled, options); DemangledObject result = demangler.demangle(mangled, options);
assertNotNull(result); assertNotNull(result);
assertEquals("undefined TTextPanel::scroll(unsigned char,short,int)", assertEquals("undefined TTextPanel::scroll(unsigned char,short,int)",

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,38 +15,26 @@
*/ */
package ghidra.framework.options; package ghidra.framework.options;
import ghidra.util.Msg;
import java.beans.PropertyEditorSupport; import java.beans.PropertyEditorSupport;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.HashSet; import java.util.HashSet;
import ghidra.util.Msg;
public class EnumEditor extends PropertyEditorSupport { public class EnumEditor extends PropertyEditorSupport {
private Enum<?> value; private Enum<?> value;
/**
*
* @see java.beans.PropertyEditor#setValue(java.lang.Object)
*/
@Override @Override
public void setValue(Object o) { public void setValue(Object o) {
value = (Enum<?>) o; value = (Enum<?>) o;
} }
/**
*
* @see java.beans.PropertyEditor#getValue()
*/
@Override @Override
public Object getValue() { public Object getValue() {
return value; return value;
} }
/**
*
* @see java.beans.PropertyEditor#getTags()
*/
@Override @Override
public String[] getTags() { public String[] getTags() {
@ -85,28 +72,20 @@ public class EnumEditor extends PropertyEditorSupport {
return new Enum<?>[] { value }; return new Enum<?>[] { value };
} }
/**
*
* @see java.beans.PropertyEditor#getAsText()
*/
@Override @Override
public String getAsText() { public String getAsText() {
return value.toString(); return value.toString();
} }
/**
*
* @see java.beans.PropertyEditor#setAsText(java.lang.String)
*/
@Override @Override
public void setAsText(String s) { public void setAsText(String s) {
try { try {
Method m = value.getClass().getMethod("values"); Method m = value.getClass().getMethod("values");
Enum<?>[] enums = (Enum<?>[]) m.invoke(null); Enum<?>[] enums = (Enum<?>[]) m.invoke(null);
for (int i = 0; i < enums.length; i++) { for (Enum<?> enum1 : enums) {
if (s.equals(enums[i].toString())) { if (s.equals(enum1.toString())) {
value = enums[i]; value = enum1;
break; break;
} }
} }

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,19 +16,19 @@
package ghidra.framework.options; package ghidra.framework.options;
// Support for PropertyEditors that use tags. // Support for PropertyEditors that use tags.
import java.awt.event.ItemEvent;
import java.awt.event.*; import java.awt.event.ItemListener;
import java.beans.*; import java.beans.*;
import javax.swing.*; import javax.swing.JComboBox;
/** /**
* An implementation of a PropertyComponent that is represented as a * An implementation of a PropertyComponent that is represented as a
* combo box. * combo box.
*/ */
public class PropertySelector extends JComboBox implements ItemListener { public class PropertySelector extends JComboBox<String> implements ItemListener {
private PropertyEditor editor; private PropertyEditor propertyEditor;
private boolean notifyEditorOfChanges = true; private boolean notifyEditorOfChanges = true;
/** /**
@ -38,22 +37,23 @@ public class PropertySelector extends JComboBox implements ItemListener {
* changes in the combo box * changes in the combo box
*/ */
public PropertySelector(PropertyEditor pe) { public PropertySelector(PropertyEditor pe) {
editor = pe; propertyEditor = pe;
String tags[] = editor.getTags(); String tags[] = propertyEditor.getTags();
for (int i = 0; i < tags.length; i++) { for (String tag : tags) {
addItem(tags[i]); addItem(tag);
} }
setSelectedIndex(0); setSelectedIndex(0);
// This is a no-op if the getAsText is not a tag that we set from getTags() above // This is a no-op if the getAsText is not a tag that we set from getTags() above
setSelectedItem(editor.getAsText()); setSelectedItem(propertyEditor.getAsText());
addItemListener(this); addItemListener(this);
invalidate(); invalidate();
editor.addPropertyChangeListener(new PropertyChangeListener() { propertyEditor.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
String value = editor.getAsText(); String value = propertyEditor.getAsText();
if (!value.equals(getSelectedItem())) { if (!value.equals(getSelectedItem())) {
notifyEditorOfChanges = false; notifyEditorOfChanges = false;
try { try {
@ -67,14 +67,15 @@ public class PropertySelector extends JComboBox implements ItemListener {
}); });
} }
/* @Override
* (non-Javadoc)
* @see java.awt.event.ItemListener#itemStateChanged(java.awt.event.ItemEvent)
*/
public void itemStateChanged(ItemEvent evt) { public void itemStateChanged(ItemEvent evt) {
if (notifyEditorOfChanges) { if (!notifyEditorOfChanges) {
return;
}
String s = (String) getSelectedItem(); String s = (String) getSelectedItem();
editor.setAsText(s); if (s != null) {
propertyEditor.setAsText(s);
} }
} }
} }

View file

@ -61,7 +61,7 @@ public class GnuDemanglerIntegrationTest extends AbstractGhidraHeadlessIntegrati
GnuDemanglerOptions options = new GnuDemanglerOptions(); GnuDemanglerOptions options = new GnuDemanglerOptions();
options.setDemangleOnlyKnownPatterns(false); options.setDemangleOnlyKnownPatterns(false);
options = options.withDeprecatedDemangler(); options = options.withDemanglerFormat(GnuDemanglerFormat.AUTO, true);
DemangledObject result = demangler.demangle(mangled, options); DemangledObject result = demangler.demangle(mangled, options);
assertNotNull(result); assertNotNull(result);
assertEquals("undefined MyNamespace::MyFunction($ParamNamespace::paramName *)", assertEquals("undefined MyNamespace::MyFunction($ParamNamespace::paramName *)",
@ -86,7 +86,7 @@ public class GnuDemanglerIntegrationTest extends AbstractGhidraHeadlessIntegrati
GnuDemanglerOptions options = new GnuDemanglerOptions(); GnuDemanglerOptions options = new GnuDemanglerOptions();
options.setDemangleOnlyKnownPatterns(false); options.setDemangleOnlyKnownPatterns(false);
options = options.withDeprecatedDemangler(); options = options.withDemanglerFormat(GnuDemanglerFormat.AUTO, true);
DemangledObject result = demangler.demangle(mangled, options); DemangledObject result = demangler.demangle(mangled, options);
assertNotNull(result); assertNotNull(result);
assertEquals("undefined SoloGimbalEKF::{unnamed_type#1}::SoloGimbalEKF(void)", assertEquals("undefined SoloGimbalEKF::{unnamed_type#1}::SoloGimbalEKF(void)",
@ -116,7 +116,7 @@ public class GnuDemanglerIntegrationTest extends AbstractGhidraHeadlessIntegrati
GnuDemanglerOptions options = new GnuDemanglerOptions(); GnuDemanglerOptions options = new GnuDemanglerOptions();
options.setDemangleOnlyKnownPatterns(false); options.setDemangleOnlyKnownPatterns(false);
options = options.withDeprecatedDemangler(); options = options.withDemanglerFormat(GnuDemanglerFormat.AUTO, true);
DemangledObject result = demangler.demangle(mangled, options); DemangledObject result = demangler.demangle(mangled, options);
assertNotNull(result); assertNotNull(result);
assertEquals( assertEquals(