From ff5d7a12d61247711bf8f7c8143daecf0471c098 Mon Sep 17 00:00:00 2001 From: ghidragon <106987263+ghidragon@users.noreply.github.com> Date: Fri, 1 May 2026 14:49:47 -0400 Subject: [PATCH] GP-6779 Refactoring DataTypeManagers/Archives --- .../main/java/ghidra/dbg/isf/IsfServer.java | 7 +- .../ghidra/trace/database/DBTraceManager.java | 8 +- .../database/data/DBTraceDataTypeManager.java | 46 +- .../guest/DBTracePlatformManager.java | 6 +- .../database/program/DBTraceProgramView.java | 11 +- .../DBTraceProgramViewFunctionManager.java | 8 +- .../main/java/ghidra/trace/model/Trace.java | 5 +- .../data/DBTraceDataTypeManagerTest.java | 19 +- .../Base/ghidra_scripts/CompareGDTs.java | 79 +- .../CreateDefaultGDTArchivesScript.java | 2396 +++++++++-------- .../CreateExampleGDTArchiveScript.java | 808 +++--- .../CreateUEFIGDTArchivesScript.java | 77 +- .../FixupCompositeDataTypesScript.java | 27 +- .../SynchronizeGDTCategoryPaths.java | 57 +- .../data_type_manager_description.htm | 31 +- .../foundation/FoundationInitializer.java | 5 +- .../merge/DataTypeArchiveMergeManager.java | 39 +- .../DataTypeArchiveMergeManagerPlugin.java | 8 +- .../java/ghidra/app/merge/MergeManager.java | 27 +- .../merge/ProgramMultiUserMergeManager.java | 40 +- .../merge/datatypes/DataTypeMergeManager.java | 78 +- .../analysis/ApplyDataArchiveAnalyzer.java | 12 +- .../DefaultDataTypeManagerService.java | 4 +- .../CompositeEditorDtArchiveDB.java | 109 + .../compositeeditor/CompositeEditorModel.java | 14 +- .../CompositeViewerDataTypeManager.java | 121 +- .../app/plugin/core/cparser/CParserTask.java | 68 +- .../app/plugin/core/cparser/ParseDialog.java | 34 +- .../core/datamgr/ActiveDataTypeStores.java | 572 ++++ .../{archive => }/ArchiveFileChooser.java | 24 +- .../plugin/core/datamgr/ArchiveManager.java | 826 ++++++ .../core/datamgr/ArchiveManagerListener.java | 73 + .../datamgr/ArchiveUndoCheckoutTask.java} | 123 +- .../datamgr/CreateProjectArchiveDialog.java | 87 + .../core/datamgr/DataTypeArchiveSaveTask.java | 105 + .../{archive => }/DataTypeIndexer.java | 2 +- .../core/datamgr/DataTypeManagerPlugin.java | 636 +++-- .../core/datamgr/DataTypePropertyManager.java | 22 +- .../core/datamgr/DataTypeSynchronizer.java | 29 +- .../core/datamgr/DataTypesActionContext.java | 25 +- .../core/datamgr/DataTypesProvider.java | 208 +- .../core/datamgr/OpenDomainFileTask.java | 239 -- .../core/datamgr/OpenFileArchiveTask.java | 117 + .../core/datamgr/OpenProjectArchiveTask.java | 162 ++ .../datamgr/ProjectArchiveSaveAsDialog.java | 137 + ...tractUndoRedoArchiveTransactionAction.java | 43 +- .../CaptureFunctionDataTypesAction.java | 12 +- .../ClearArchiveArchitectureAction.java | 66 +- .../datamgr/actions/CloseArchiveAction.java | 81 +- ...java => CloseArchiveForEditingAction.java} | 78 +- .../core/datamgr/actions/CopyAction.java | 2 +- .../datamgr/actions/CreateArchiveAction.java | 12 +- .../datamgr/actions/CreateCategoryAction.java | 13 +- .../datamgr/actions/CreatePointerAction.java | 2 +- .../actions/CreateProjectArchiveAction.java | 17 +- .../datamgr/actions/CreateTypeDefAction.java | 8 +- .../core/datamgr/actions/DeleteAction.java | 4 +- .../datamgr/actions/DeleteArchiveAction.java | 15 +- .../actions/DerivativeDataTypeInfo.java | 18 +- .../actions/EditArchivePathAction.java | 14 +- .../datamgr/actions/OpenArchiveAction.java | 45 +- ....java => OpenArchiveForEditingAction.java} | 46 +- .../actions/OpenProjectArchiveAction.java | 39 +- .../core/datamgr/actions/PasteAction.java | 4 +- .../actions/RecentlyOpenedArchiveAction.java | 45 +- .../actions/RedoArchiveTransactionAction.java | 27 +- ...RemoveInvalidArchiveFromProgramAction.java | 19 +- .../core/datamgr/actions/RenameAction.java | 10 +- .../actions/ReplaceDataTypeAction.java | 20 +- .../datamgr/actions/SaveArchiveAction.java | 63 +- ...AsAction.java => SaveArchiveAsAction.java} | 52 +- .../actions/SetArchiveArchitectureAction.java | 111 +- .../actions/UndoArchiveTransactionAction.java | 27 +- .../UpdateSourceArchiveNamesAction.java | 96 - .../associate/AssociateDataTypeAction.java | 66 +- .../actions/associate/CommitAction.java | 13 +- .../associate/CommitSingleDataTypeAction.java | 18 +- .../actions/associate/DisassociateAction.java | 20 +- .../associate/DisassociateDataTypeAction.java | 10 +- .../actions/associate/RevertAction.java | 16 +- .../associate/RevertDataTypeAction.java | 16 +- .../datamgr/actions/associate/SyncAction.java | 18 +- .../actions/associate/SyncRefreshAction.java | 15 +- .../actions/associate/UpdateAction.java | 11 +- .../associate/UpdateSingleDataTypeAction.java | 17 +- .../plugin/core/datamgr/archive/Archive.java | 84 - .../archive/ArchiveManagerListener.java | 44 - .../core/datamgr/archive/ArchiveUtils.java | 215 -- .../archive/BasicDataTypeArchiveService.java | 215 ++ .../core/datamgr/archive/BuiltInArchive.java | 89 - .../archive/DataTypeManagerHandler.java | 1810 ------------- .../DefaultDataTypeArchiveService.java | 236 -- .../core/datamgr/archive/FileArchive.java | 330 --- ...inFileArchive.java => InvalidArchive.java} | 19 +- .../datamgr/archive/InvalidFileArchive.java | 110 - .../core/datamgr/archive/ProgramArchive.java | 135 - .../core/datamgr/archive/ProjectArchive.java | 231 -- .../datamgr/editor/DataTypeEditorManager.java | 19 +- .../core/datamgr/tree/ArchiveComparator.java} | 27 +- .../plugin/core/datamgr/tree/ArchiveNode.java | 528 +--- .../core/datamgr/tree/ArchiveRootNode.java | 197 +- .../datamgr/tree/ArchiveRootNodeListener.java | 4 +- .../core/datamgr/tree/BuiltInArchiveNode.java | 16 +- .../core/datamgr/tree/CategoryNode.java | 6 +- .../datamgr/tree/DataTypeArchiveGTree.java | 51 +- .../tree/DataTypeDragNDropHandler.java | 20 +- .../core/datamgr/tree/DataTypeNode.java | 4 +- .../core/datamgr/tree/DataTypeStoreNode.java | 447 +++ .../core/datamgr/tree/DataTypeTreeNode.java | 6 +- .../core/datamgr/tree/FileArchiveNode.java | 36 +- .../core/datamgr/tree/InvalidArchiveNode.java | 41 +- .../core/datamgr/tree/ProgramArchiveNode.java | 72 +- .../core/datamgr/tree/ProjectArchiveNode.java | 97 +- ...FileArchiveNode.java => VersionState.java} | 130 +- .../core/datamgr/tree/VersionedNode.java | 45 + .../datamgr/util/DataTypeArchiveUtility.java | 12 +- .../datamgr/util/DataTypeChooserDialog.java | 13 +- .../util/DataTypeTreeCopyMoveTask.java | 39 +- .../datamgr/util/DataTypeTreeDeleteTask.java | 22 +- .../core/datamgr/util/DataTypeUtils.java | 42 +- .../datapreview/DataTypePreviewPlugin.java | 7 +- .../core/stackeditor/StackEditorModel.java | 2 +- .../app/services/DataTypeArchiveService.java | 193 +- .../java/ghidra/app/services/Recover.java} | 16 +- .../java/ghidra/app/services/Upgrade.java | 26 + .../golang/structmapping/DataTypeMapper.java | 30 +- .../app/util/cparser/C/CParserUtils.java | 197 +- .../ghidra/app/util/exporter/GdtExporter.java | 4 +- .../ghidra/app/util/opinion/GdtLoader.java | 46 +- .../main/OpenVersionedFileDialog.java | 5 + ...draDataTypeArchiveMergeManagerFactory.java | 35 +- ...raProgramMultiUserMergeManagerFactory.java | 25 +- .../program/flatapi/FlatProgramAPI.java | 32 + .../ghidra/program/util/DataTypeCleaner.java | 7 +- .../src/main/java/ghidra/test/TestEnv.java | 8 +- .../support/ProcessorEmulatorTestAdapter.java | 22 +- .../javacc/ghidra/app/util/cparser/C/C.jj | 23 +- .../app/merge/DummyArchiveMergeManager.java | 24 +- .../core/compositeeditor/CommonTestData.java | 234 +- .../StructureEditorArchiveTest.java | 2 +- .../ParseDialogParsingAndPromptsTest.java | 25 +- .../core/data/ApplyDataTypeToBrowserTest.java | 7 +- .../datamgr/AbstractCreateArchiveTest.java | 21 +- .../datamgr/ArchiveRemappedHeadedTest.java | 101 +- .../datamgr/ArchiveRemappedHeadlessTest.java | 18 +- .../core/datamgr/CreateArchive1Test.java | 35 +- .../datamgr/CreateEnumFromSelectionTest.java | 8 +- .../datamgr/CreateLabelsFromEnumsTest.java | 8 +- .../core/datamgr/DataTypeArchiveIDTest.java | 72 +- .../datamgr/DataTypeCopyMoveDragTest.java | 8 +- .../datamgr/DataTypeManagerPluginTest.java | 28 +- .../core/datamgr/DataTypeTestUtils.java | 94 +- .../core/datamgr/FavoritesAndMiscTest.java | 10 +- .../FileArchiveEnumEditorUndoRedoTest.java | 21 +- .../ProjectArchiveEnumEditorUndoRedoTest.java | 35 +- .../dwarf/DWARFConflictHandlerTest.java | 8 +- .../datatype/DataTypeSelectionDialogTest.java | 48 +- .../DataTypeSelectionTextFieldTest.java | 19 +- .../database/code/CodeUnitIteratorTest.java | 13 +- .../database/data/ConflictHandlerTest.java | 2 +- .../database/data/DataManagerTest.java | 18 +- .../TestDoubleDataTypeManagerService.java | 37 +- .../ghidra/app/util/cparser/CParserTest.java | 185 +- .../app/util/cparser/PreProcessorTest.java | 117 +- .../database/MergeTestFacilitator.java | 6 +- .../model/data/AbstractCompositeTest.java | 7 +- .../model/data/StructureDataTypeTest.java | 3 +- .../program/model/data/UnionDataTypeTest.java | 3 +- .../pdbapplicator/CppCompositeTypeTest.java | 9 +- .../datamgr/RustDataTypeArchiveIDTest.java | 21 +- .../EmuLinuxAmd64SyscallUseropLibrary.java | 16 +- .../EmuLinuxX86SyscallUseropLibrary.java | 16 +- .../ghidra/pcode/struct/StructuredSleigh.java | 5 +- .../feature/vt/api/util/EmptyVTSession.java | 5 + .../framework/data/DomainFileProxy.java | 25 +- .../framework/data/DomainObjectAdapter.java | 6 +- .../framework/data/DomainObjectDbModule.java | 73 + .../ghidra/framework/data/GhidraFileData.java | 131 +- .../framework/data/TransientDataManager.java | 16 +- .../DomainFileProviderContextAction.java | 6 +- .../main/datatree/VersionControlDialog.java | 6 +- .../ghidra/framework/model/DomainObject.java | 6 + .../framework/model/RuntimeIOException.java | 13 +- .../framework/project/DefaultProject.java | 5 +- .../program/database/DataTypeArchiveDB.java | 575 ---- .../database/ProgramAddressFactory.java | 4 +- .../database/ProgramContentHandler.java | 5 +- .../ghidra/program/database/ProgramDB.java | 18 +- .../{ManagerDB.java => ProgramDBModule.java} | 43 +- .../ProgramMultiUserMergeManagerFactory.java | 19 +- .../database/ProgramOverlayAddressSpace.java | 7 +- .../database/ProjectDataTypeManager.java | 322 --- .../database/bookmark/BookmarkDBManager.java | 6 +- .../program/database/code/CodeManager.java | 6 +- .../data/ArchiveDataTypeManagerDB.java | 428 +++ .../database/data/BitFieldDBDataType.java | 2 +- .../data/BuiltInDataTypeManagerDB.java | 136 + .../program/database/data/CategoryDB.java | 1 - .../DataTypeArchiveMergeManagerFactory.java | 23 +- .../data/DataTypeArchiveTransformer.java | 165 +- .../data/DataTypeArchiveTransformerPanel.java | 7 +- .../database/data/DataTypeIDConverter.java | 31 +- .../database/data/DataTypeManagerDB.java | 322 +-- .../database/data/FileDataTypeManagerDB.java | 187 ++ .../data/ProgramBasedDataTypeManagerDB.java | 11 +- .../database/data/ProgramDataTypeManager.java | 44 +- .../data/ProjectDataTypeManagerDB.java | 107 + .../database/data/SourceArchiveDB.java | 3 +- .../data/TransientDataTypeManager.java | 141 + .../program/database/data/TypedefDB.java | 2 +- .../dtarchive/BuiltInDtArchiveDB.java | 173 ++ .../DataTypeArchiveContentHandler.java | 61 +- .../database/dtarchive/DataTypeArchiveDB.java | 1024 +++++++ .../DataTypeArchiveDBChangeSet.java | 6 +- .../dtarchive/DataTypeArchiveFactory.java | 311 +++ .../DataTypeArchiveLinkContentHandler.java | 6 +- .../dtarchive/DataTypeStoreDBModule.java} | 15 +- .../database/dtarchive/DefaultConsumer.java | 29 + .../database/dtarchive/FileDtArchiveDB.java | 429 +++ .../dtarchive/ProjectDtArchiveDB.java | 302 +++ .../dtarchive/TransientDtArchiveDB.java | 80 + .../database/external/ExternalManagerDB.java | 8 +- .../database/function/FunctionManagerDB.java | 4 +- .../program/database/mem/MemoryMapDB.java | 6 +- .../program/database/module/TreeManager.java | 8 +- .../properties/DBPropertyMapManager.java | 8 +- .../references/ReferenceDBManager.java | 6 +- .../register/OldProgramContextDB.java | 8 +- .../register/ProgramRegisterContextDB.java | 8 +- .../database/reloc/RelocationManager.java | 8 +- .../sourcemap/SourceFileManagerDB.java | 8 +- .../database/symbol/EquateManager.java | 6 +- .../database/symbol/NamespaceManager.java | 8 +- .../symbol/SymbolDatabaseAdapterV0.java | 2 +- .../database/symbol/SymbolManager.java | 6 +- .../model/data/ArchiveDataTypeManager.java | 130 + .../program/model/data/ArchiveType.java | 21 +- .../model/data/BuiltInDataTypeManager.java | 202 +- .../model/data/DataTypeArchiveIdDumper.java | 21 +- .../model/data/DataTypeConflictHandler.java | 22 +- .../program/model/data/DataTypeManager.java | 44 +- .../data/DataTypeManagerDomainObject.java | 22 - .../model/data/FileDataTypeManager.java | 395 +-- .../data/ProgramBasedDataTypeManager.java | 10 +- .../ProjectArchiveBasedDataTypeManager.java | 24 - .../model/data/StandAloneDataTypeManager.java | 1095 +------- .../model/dtarchive/ArchiveWarning.java | 88 + .../model/dtarchive/DataTypeArchive.java | 176 ++ .../model/dtarchive/DataTypeStore.java | 54 + .../model/dtarchive/FileDataTypeArchive.java | 90 + .../dtarchive/PersistentDataTypeArchive.java | 46 + .../dtarchive/ProjectDataTypeArchive.java | 32 + .../dtarchive/TransientDataTypeArchive.java} | 24 +- .../model/lang/ProgramArchitecture.java | 32 +- .../model/listing/DataTypeArchive.java | 68 - .../model/listing/FunctionManager.java | 4 +- .../ghidra/program/model/listing/Program.java | 53 +- .../program/util/DtArchiveChangeRecord.java | 52 + .../DtArchiveEvent.java} | 21 +- .../database/data/DataTypeUtilitiesTest.java | 5 +- .../ghidra/program/model/data/EnumTest.java | 18 +- .../model/data/FileDataTypeManagerTest.java | 101 +- .../data/FunctionDefinitionDataTypeTest.java | 15 +- .../database/data/BitFieldDBDataTypeTest.java | 22 +- .../database/data/DataTypeDBReplaceTest.java | 23 +- .../database/data/StructureDBTest.java | 12 +- .../program/database/data/TypedefDBTest.java | 28 +- .../program/database/data/UnionDBTest.java | 43 +- .../database/data/merge/EnumMergerTest.java | 10 +- .../data/merge/StructureMergerTest.java | 6 +- .../database/data/merge/UnionMergerTest.java | 6 +- .../program/database/mem/MemBlockDBTest.java | 6 +- .../program/model/StubFunctionManager.java | 8 +- .../ghidra/program/model/StubProgram.java | 35 + .../model/data/TestDoubleDataTypeManager.java | 20 +- .../model/data/TestDummyDataTypeManager.java | 21 +- .../CreateAVR8GDTArchiveScript.java | 25 +- .../CreateJNIGDTArchivesScript.java | 160 +- .../javaclass/test/DescriptorDecoderTest.java | 36 +- .../core/analysis/eBPFHelperDataTypes.java | 10 +- .../DataTypeManagerPluginScreenShots.java | 48 +- .../screenshot/RepositoryScreenShots.java | 4 +- .../datatree/ProjectLinkFileStatusTest.java | 10 +- .../model/lang/AbstractProtoModelTest.java | 28 +- 284 files changed, 13132 insertions(+), 11825 deletions(-) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorDtArchiveDB.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ActiveDataTypeStores.java rename Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/{archive => }/ArchiveFileChooser.java (87%) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ArchiveManager.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ArchiveManagerListener.java rename Ghidra/Features/Base/src/main/java/ghidra/{framework/main/datatree/VersionControlDataTypeArchiveUndoCheckoutAction.java => app/plugin/core/datamgr/ArchiveUndoCheckoutTask.java} (66%) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/CreateProjectArchiveDialog.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveSaveTask.java rename Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/{archive => }/DataTypeIndexer.java (99%) delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenDomainFileTask.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenFileArchiveTask.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenProjectArchiveTask.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ProjectArchiveSaveAsDialog.java rename Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/{UnlockArchiveAction.java => CloseArchiveForEditingAction.java} (66%) rename Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/{LockArchiveAction.java => OpenArchiveForEditingAction.java} (77%) rename Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/{SaveAsAction.java => SaveArchiveAsAction.java} (59%) delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UpdateSourceArchiveNamesAction.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/Archive.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ArchiveManagerListener.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ArchiveUtils.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/BasicDataTypeArchiveService.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/BuiltInArchive.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DefaultDataTypeArchiveService.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/FileArchive.java rename Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/{DomainFileArchive.java => InvalidArchive.java} (62%) delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/InvalidFileArchive.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProgramArchive.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProjectArchive.java rename Ghidra/Features/Base/src/main/java/ghidra/{framework/main/datatree/ArchiveProvider.java => app/plugin/core/datamgr/tree/ArchiveComparator.java} (55%) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeStoreNode.java rename Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/{DomainFileArchiveNode.java => VersionState.java} (58%) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/VersionedNode.java rename Ghidra/{Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileArchiveBasedDataTypeManager.java => Features/Base/src/main/java/ghidra/app/services/Recover.java} (55%) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/services/Upgrade.java create mode 100644 Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectDbModule.java delete mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java rename Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/{ManagerDB.java => ProgramDBModule.java} (53%) delete mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProjectDataTypeManager.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArchiveDataTypeManagerDB.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltInDataTypeManagerDB.java rename Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/{ => data}/DataTypeArchiveMergeManagerFactory.java (62%) create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FileDataTypeManagerDB.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManagerDB.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TransientDataTypeManager.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/BuiltInDtArchiveDB.java rename Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/{ => dtarchive}/DataTypeArchiveContentHandler.java (83%) create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeArchiveDB.java rename Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/{ => dtarchive}/DataTypeArchiveDBChangeSet.java (99%) create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeArchiveFactory.java rename Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/{ => dtarchive}/DataTypeArchiveLinkContentHandler.java (91%) rename Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/{model/data/FileBasedDataTypeManager.java => database/dtarchive/DataTypeStoreDBModule.java} (68%) create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DefaultConsumer.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/FileDtArchiveDB.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/ProjectDtArchiveDB.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/TransientDtArchiveDB.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArchiveDataTypeManager.java delete mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerDomainObject.java delete mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ProjectArchiveBasedDataTypeManager.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/ArchiveWarning.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/DataTypeArchive.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/DataTypeStore.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/FileDataTypeArchive.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/PersistentDataTypeArchive.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/ProjectDataTypeArchive.java rename Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/{app/merge/DataTypeManagerOwner.java => program/model/dtarchive/TransientDataTypeArchive.java} (59%) delete mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataTypeArchive.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/DtArchiveChangeRecord.java rename Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/{model/data/DomainFileBasedDataTypeManager.java => util/DtArchiveEvent.java} (59%) diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/dbg/isf/IsfServer.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/dbg/isf/IsfServer.java index aeba8d0941..86b4b0f6bb 100644 --- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/dbg/isf/IsfServer.java +++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/dbg/isf/IsfServer.java @@ -32,8 +32,9 @@ import ghidra.framework.model.DomainFile; import ghidra.framework.model.ProjectData; import ghidra.framework.store.db.PackedDatabase; import ghidra.program.database.ProgramDB; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.program.model.listing.Program; import ghidra.util.Msg; import ghidra.util.task.TaskMonitor; @@ -132,7 +133,9 @@ public class IsfServer extends Thread { private DataTypeManager openAsDataTypeArchive(String ns) throws Exception { File gdt = new File(ns); - return FileDataTypeManager.openFileArchive(gdt, false); + FileDataTypeArchive archive = + DataTypeArchiveFactory.openReadOnly(gdt, this, TaskMonitor.DUMMY); + return archive != null ? archive.getDataTypeManager() : null; } private DataTypeManager openAsProgramDatabase(String ns) throws Exception { diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceManager.java index b2e149559f..e4a4b93e39 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceManager.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,14 +16,14 @@ package ghidra.trace.database; import db.util.ErrorHandler; -import ghidra.program.database.ManagerDB; +import ghidra.program.database.ProgramDBModule; public interface DBTraceManager extends ErrorHandler { /** * Invalidate this manager's caches * * @param all probably nothing. Check out implementations of - * {@link ManagerDB#invalidateCache(boolean)}. + * {@link ProgramDBModule#invalidateCache(boolean)}. */ void invalidateCache(boolean all); } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataTypeManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataTypeManager.java index d59a9fe049..69b1d8408c 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataTypeManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataTypeManager.java @@ -18,7 +18,6 @@ package ghidra.trace.database.data; import java.io.IOException; import java.util.Map; import java.util.Set; -import java.util.concurrent.locks.ReadWriteLock; import db.DBHandle; import db.Transaction; @@ -27,13 +26,14 @@ import ghidra.framework.model.DomainFile; import ghidra.program.database.data.ProgramBasedDataTypeManagerDB; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.DataTypeStore; import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTraceManager; import ghidra.trace.database.guest.DBTraceGuestPlatform; import ghidra.trace.database.guest.DBTracePlatformManager.DBTraceHostPlatform; import ghidra.trace.database.guest.InternalTracePlatform; import ghidra.trace.model.data.TraceBasedDataTypeManager; -import ghidra.util.InvalidNameException; +import ghidra.util.Lock; import ghidra.util.UniversalID; import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; @@ -42,17 +42,6 @@ import ghidra.util.task.TaskMonitor; public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB implements TraceBasedDataTypeManager, DBTraceManager { - /** - * NOTE: This "read-write" lock is actually just a compatibility wrapper around the - * {@link ghidra.util.Lock} for the entire trace database. There was a time when I dreamed of - * using an actual read-write lock (though it's not known if that'd actually achieve any - * appreciable speed up); however, inheriting the existing DataTypeManager implementation - * required its lock to be used throughout the database. Rather than convert all my code (and - * lose the distinction of where I need write vs. read locks), I just wrapped the API. So no, - * this code does not refer to the wrapper, but it does still use the lock. I keep a reference - * to it here in case I ever need it. - */ - protected final ReadWriteLock lock; protected final DBTrace trace; protected final InternalTracePlatform platform; @@ -64,11 +53,10 @@ public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB }; } - public DBTraceDataTypeManager(DBHandle dbh, OpenMode openMode, ReadWriteLock lock, + public DBTraceDataTypeManager(DBHandle dbh, OpenMode openMode, Lock lock, TaskMonitor monitor, DBTrace trace, InternalTracePlatform platform) throws CancelledException, VersionException, IOException { - super(dbh, null, openMode, computePrefix(platform), trace, trace.getLock(), monitor); - this.lock = lock; // TODO: nothing uses this local lock - not sure what its purpose is + super(dbh, null, openMode, computePrefix(platform), trace, lock, monitor); this.trace = trace; this.platform = platform; @@ -79,6 +67,11 @@ public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB } } + @Override + public DataTypeStore getDataStore() { + return trace.getProgramView(); + } + @Override protected void dataSettingChanged(Address address) { // ignored - instance settings are not current supported (no AddressMap provided) @@ -99,16 +92,6 @@ public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB return trace.getName(); } - @Override - public void setName(String name) throws InvalidNameException { - if (name == null || name.length() == 0) { - throw new InvalidNameException("Name must be at least one character long: " + name); - } - - trace.setName(name); - categoryRenamed(CategoryPath.ROOT, getCategory(CategoryPath.ROOT)); - } - @Override public InternalTracePlatform getPlatform() { return platform; @@ -239,23 +222,12 @@ public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB return trace; } - @Override - public DomainFile getDomainFile() { - return trace.getDomainFile(); - } - @Override protected String getDomainFileID() { DomainFile domainFile = trace.getDomainFile(); // Can be null if never saved return domainFile == null ? null : domainFile.getFileID(); } - @Override - public String getPath() { - DomainFile domainFile = trace.getDomainFile(); // Can be null if never saved - return domainFile == null ? null : domainFile.getPathname(); - } - @Override public ArchiveType getType() { /** diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java index 15afbf3003..eb313a20f7 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/guest/DBTracePlatformManager.java @@ -17,7 +17,6 @@ package ghidra.trace.database.guest; import java.io.IOException; import java.util.*; -import java.util.concurrent.locks.ReadWriteLock; import db.DBHandle; import ghidra.framework.data.OpenMode; @@ -33,6 +32,7 @@ import ghidra.trace.model.data.TraceBasedDataTypeManager; import ghidra.trace.model.guest.*; import ghidra.trace.util.TraceChangeRecord; import ghidra.trace.util.TraceEvents; +import ghidra.util.Lock; import ghidra.util.LockHold; import ghidra.util.database.DBCachedObjectStore; import ghidra.util.database.DBCachedObjectStoreFactory; @@ -49,7 +49,7 @@ import ghidra.util.task.TaskMonitor; */ public class DBTracePlatformManager implements DBTraceManager, TracePlatformManager { protected final DBHandle dbh; - protected final ReadWriteLock lock; + protected final Lock lock; protected final Language baseLanguage; protected final CompilerSpec baseCompilerSpec; protected final DBTrace trace; @@ -157,7 +157,7 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana protected final InternalTracePlatform hostPlatform = new DBTraceHostPlatform(); - public DBTracePlatformManager(DBHandle dbh, OpenMode openMode, ReadWriteLock lock, + public DBTracePlatformManager(DBHandle dbh, OpenMode openMode, Lock lock, TaskMonitor monitor, CompilerSpec baseCompilerSpec, DBTrace trace) throws VersionException, IOException, CancelledException { this.dbh = dbh; diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java index 721a5808e8..7afb967734 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java @@ -56,9 +56,8 @@ import ghidra.trace.model.bookmark.TraceBookmark; import ghidra.trace.model.bookmark.TraceBookmarkType; import ghidra.trace.model.data.TraceBasedDataTypeManager; import ghidra.trace.model.listing.*; -import ghidra.trace.model.memory.TraceMemoryRegion; -import ghidra.trace.model.memory.TraceMemoryState; import ghidra.trace.model.memory.TraceMemoryOperations.StatePredicate; +import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.symbol.*; import ghidra.trace.util.TraceEvents; @@ -599,8 +598,7 @@ public class DBTraceProgramView implements TraceProgramView { } protected static class OverlappingAddressRangeKeyIteratorMerger extends - PairingIteratorMerger, Entry, - Entry> { + PairingIteratorMerger, Entry, Entry> { protected static Iterable, Entry>> iter( Iterable> left, Iterable> right) { @@ -1565,4 +1563,9 @@ public class DBTraceProgramView implements TraceProgramView { } return queues; } + + @Override + public ArchiveType getArchiveType() { + return ArchiveType.PROGRAM; + } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewFunctionManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewFunctionManager.java index 96a007dc64..2ebf2d1886 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewFunctionManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewFunctionManager.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -169,12 +169,12 @@ public class DBTraceProgramViewFunctionManager implements FunctionManager { } @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { throw new UnsupportedOperationException(); } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { throw new UnsupportedOperationException(); } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java index 1e8c51a7b7..ab2e8131e5 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java @@ -20,9 +20,9 @@ import java.util.Collection; import javax.swing.Icon; import generic.theme.GIcon; +import ghidra.framework.model.DomainObject; import ghidra.program.model.address.AddressFactory; import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.DataTypeManagerDomainObject; import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.Language; import ghidra.program.model.listing.Program; @@ -60,7 +60,7 @@ import ghidra.util.LockHold; * specific snapshot and/or manipulate the trace as if it were a program, use * {@link #getProgramView()}. */ -public interface Trace extends DataTypeManagerDomainObject { +public interface Trace extends DomainObject { Icon TRACE_ICON = new GIcon("icon.content.handler.trace"); public interface TraceProgramViewListener { @@ -92,7 +92,6 @@ public interface Trace extends DataTypeManagerDomainObject { * For traces, this gets the "base" or "host" {@link DataTypeManager}. For platform-specific * managers, see {@link TracePlatform#getDataTypeManager()}. */ - @Override default TraceBasedDataTypeManager getDataTypeManager() { return getBaseDataTypeManager(); } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/data/DBTraceDataTypeManagerTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/data/DBTraceDataTypeManagerTest.java index fae5d7d1a8..9e6399122c 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/data/DBTraceDataTypeManagerTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/data/DBTraceDataTypeManagerTest.java @@ -25,7 +25,9 @@ import java.nio.file.Path; import org.junit.*; import db.Transaction; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.program.model.lang.Language; import ghidra.program.model.lang.LanguageID; import ghidra.program.util.DefaultLanguageService; @@ -77,22 +79,17 @@ public class DBTraceDataTypeManagerTest extends AbstractGhidraHeadlessIntegratio assertEquals("Testing", dtm.getName()); } - @Test - public void testSetName() throws InvalidNameException { - try (Transaction tx = trace.openTransaction("Testing")) { - dtm.setName("Another name"); - } - assertEquals("Another name", trace.getName()); - } - @Test public void testAddSourceArchive() throws IOException { StructureDataType mine = getTestDataType(); DataTypePath minePath = mine.getDataTypePath(); Path tmpDir = Files.createTempDirectory("test"); File archiveFile = tmpDir.resolve("test.gdt").toFile(); - FileDataTypeManager dtm2 = FileDataTypeManager.createFileArchive(archiveFile); - try (Transaction tx = dtm2.openTransaction("Testing")) { + + FileDataTypeArchive archive = DataTypeArchiveFactory.createFileArchive(archiveFile, this); + DataTypeManager dtm2 = archive.getDataTypeManager(); + + try (Transaction tx = archive.openTransaction("Testing")) { dtm2.addDataType(mine, DataTypeConflictHandler.DEFAULT_HANDLER); } DataType got = dtm2.getDataType(minePath); @@ -100,7 +97,7 @@ public class DBTraceDataTypeManagerTest extends AbstractGhidraHeadlessIntegratio try (Transaction tx = trace.openTransaction("Testing")) { dtm.addDataType(got, DataTypeConflictHandler.DEFAULT_HANDLER); } - dtm2.delete(); + archive.delete(); // TODO: Listen for sourceArchiveAdded event diff --git a/Ghidra/Features/Base/ghidra_scripts/CompareGDTs.java b/Ghidra/Features/Base/ghidra_scripts/CompareGDTs.java index d2dac13c92..98e756d34d 100644 --- a/Ghidra/Features/Base/ghidra_scripts/CompareGDTs.java +++ b/Ghidra/Features/Base/ghidra_scripts/CompareGDTs.java @@ -27,7 +27,8 @@ import java.util.*; import ghidra.app.script.GhidraScript; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; -import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; +import ghidra.program.model.dtarchive.ArchiveWarning; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.util.UniversalID; public class CompareGDTs extends GhidraScript { @@ -35,8 +36,10 @@ public class CompareGDTs extends GhidraScript { private File firstFile; private File secondFile; private File outputFile; - private FileDataTypeManager firstArchive; - private FileDataTypeManager secondArchive; + private FileDataTypeArchive archive1; + private FileDataTypeArchive archive2; + private DataTypeManager dtm1; + private DataTypeManager dtm2; private PrintWriter printWriter; boolean matchByName; boolean checkPointers; @@ -57,23 +60,24 @@ public class CompareGDTs extends GhidraScript { } } - firstArchive = openDataTypeArchive(firstFile, false); - if (firstArchive.getWarning() != ArchiveWarning.NONE) { + archive1 = openFileDataTypeArchive(firstFile, false); + if (archive1.getWarning() != ArchiveWarning.NONE) { popup( "An architecture language error occured while opening archive (see log for details)\n" + firstFile.getPath()); return; } - secondArchive = openDataTypeArchive(secondFile, false); - if (secondArchive.getWarning() != ArchiveWarning.NONE) { + archive2 = openFileDataTypeArchive(secondFile, false); + if (archive2.getWarning() != ArchiveWarning.NONE) { popup( "An architecture language error occured while opening archive (see log for details)\n" + secondFile.getPath()); - firstArchive.close(); + archive1.release(this); return; } - + dtm1 = archive1.getDataTypeManager(); + dtm2 = archive2.getDataTypeManager(); matchByName = askYesNo("Match Data Types By Path Name?", "Do you want to match data types by their path names (rather than by Universal ID)?"); checkPointers = askYesNo("Check Pointers?", "Do you want to check Pointers?"); @@ -85,8 +89,8 @@ public class CompareGDTs extends GhidraScript { } finally { printWriter.close(); - firstArchive.close(); - secondArchive.close(); + archive1.release(this); + archive2.release(this); } } @@ -100,30 +104,30 @@ public class CompareGDTs extends GhidraScript { secondFile.getAbsolutePath() + "."); output("\nThe following data types are only in " + firstFile.getAbsolutePath() + "."); - long onlyInFirst = outputEachDataTypeOnlyInFirst(firstArchive, secondArchive); + long onlyInFirst = outputEachDataTypeOnlyInFirst(dtm1, dtm2); output(onlyInFirst + " data types that were only in first archive."); output("\nThe following data types are only in " + secondFile.getAbsolutePath() + "."); - long onlyInSecond = outputEachDataTypeOnlyInFirst(secondArchive, firstArchive); + long onlyInSecond = outputEachDataTypeOnlyInFirst(dtm2, dtm1); output(onlyInSecond + " data types that were only in second archive."); output("\nThe following are different kinds of data types."); - long differentKinds = outputWhereTypesDiffer(firstArchive, secondArchive); + long differentKinds = outputWhereTypesDiffer(dtm1, dtm2); output(differentKinds + " data types had different types."); output("\nThe following data types are defined differently."); - long differentDefinitions = outputWhereDefinitionsDiffer(firstArchive, secondArchive); + long differentDefinitions = outputWhereDefinitionsDiffer(dtm1, dtm2); output(differentDefinitions + " data types had different definitions."); output("\nThe following data types are different sizes"); - long differentSizes = outputWhereSizesDiffer(firstArchive, secondArchive); + long differentSizes = outputWhereSizesDiffer(dtm1, dtm2); output(differentSizes + " data types had different sizes."); output("\n"); } - private long outputEachDataTypeOnlyInFirst(FileDataTypeManager dtmArchive1, - FileDataTypeManager dtmArchive2) { + private long outputEachDataTypeOnlyInFirst(DataTypeManager dtmArchive1, + DataTypeManager dtmArchive2) { long missingCount = 0; Iterator allDataTypes = dtmArchive1.getAllDataTypes(); @@ -136,7 +140,7 @@ public class CompareGDTs extends GhidraScript { return missingCount; } - private boolean outputIfMissingDataType(DataType dataType, FileDataTypeManager dtmArchive) { + private boolean outputIfMissingDataType(DataType dataType, DataTypeManager dtmArchive) { if (!checkPointers && dataType instanceof Pointer) { return false; @@ -155,7 +159,7 @@ public class CompareGDTs extends GhidraScript { return false; } - private DataType getMatchingDataType(DataType dataType, FileDataTypeManager dtmArchive) { + private DataType getMatchingDataType(DataType dataType, DataTypeManager dtmArchive) { if (!matchByName) { UniversalID universalID = dataType.getUniversalID(); @@ -172,17 +176,20 @@ public class CompareGDTs extends GhidraScript { // find by name List list = new ArrayList(); - dtmArchive.findDataTypes(dataType.getName(), list ); + dtmArchive.findDataTypes(dataType.getName(), list); for (DataType dtc : list) { - if (dataType.getCategoryPath().getPath().toLowerCase().equals(dtc.getCategoryPath().getPath().toLowerCase())) { + if (dataType.getCategoryPath() + .getPath() + .toLowerCase() + .equals(dtc.getCategoryPath().getPath().toLowerCase())) { return dtc; } } return null; } - private long outputWhereTypesDiffer(FileDataTypeManager dtmArchive1, - FileDataTypeManager dtmArchive2) { + private long outputWhereTypesDiffer(DataTypeManager dtmArchive1, + DataTypeManager dtmArchive2) { long differCount = 0; Iterator allDataTypes = dtmArchive1.getAllDataTypes(); @@ -195,8 +202,8 @@ public class CompareGDTs extends GhidraScript { return differCount; } - private long outputWhereSizesDiffer(FileDataTypeManager dtmArchive1, - FileDataTypeManager dtmArchive2) { + private long outputWhereSizesDiffer(DataTypeManager dtmArchive1, + DataTypeManager dtmArchive2) { long differCount = 0; Iterator allDataTypes = dtmArchive1.getAllDataTypes(); @@ -209,7 +216,7 @@ public class CompareGDTs extends GhidraScript { return differCount; } - private boolean outputIfDifferentTypes(DataType dataType, FileDataTypeManager dtmArchive) { + private boolean outputIfDifferentTypes(DataType dataType, DataTypeManager dtmArchive) { if (!checkPointers && dataType instanceof Pointer) { return false; @@ -233,8 +240,8 @@ public class CompareGDTs extends GhidraScript { return false; } - private long outputWhereDefinitionsDiffer(FileDataTypeManager dtmArchive1, - FileDataTypeManager dtmArchive2) { + private long outputWhereDefinitionsDiffer(DataTypeManager dtmArchive1, + DataTypeManager dtmArchive2) { long differCount = 0; Iterator allDataTypes = dtmArchive1.getAllDataTypes(); @@ -248,7 +255,7 @@ public class CompareGDTs extends GhidraScript { } private boolean outputIfDifferentDefinitions(DataType dataType, - FileDataTypeManager dtmArchive) { + DataTypeManager dtmArchive) { if (!checkPointers && dataType instanceof Pointer) { return false; @@ -263,7 +270,7 @@ public class CompareGDTs extends GhidraScript { Class dtClass = dataType.getClass(); Class sameNamedDtClass = matchingDataType.getClass(); if (dtClass == sameNamedDtClass) { - if (dataType instanceof Enum && (((Enum) dataType).getCount()==1)) { + if (dataType instanceof Enum && (((Enum) dataType).getCount() == 1)) { // don't check single entry enums. Size will vary, and they are extracted defines return checkEnum((Enum) dataType, (Enum) matchingDataType); } @@ -283,18 +290,18 @@ public class CompareGDTs extends GhidraScript { return true; } // Check that the name is the same - if (! e1.getNames()[0].equals(e2.getNames()[0])) { + if (!e1.getNames()[0].equals(e2.getNames()[0])) { return true; } // Check the value is the same if (e1.getValues()[0] != e2.getValues()[0]) { return true; - } - + } + return false; } - private boolean outputIfDifferentSizes(DataType dataType, FileDataTypeManager dtmArchive) { + private boolean outputIfDifferentSizes(DataType dataType, DataTypeManager dtmArchive) { if (!checkPointers && dataType instanceof Pointer) { return false; @@ -309,7 +316,7 @@ public class CompareGDTs extends GhidraScript { Class dtClass = dataType.getClass(); Class sameNamedDtClass = matchingDataType.getClass(); if (dtClass == sameNamedDtClass) { - if (dataType instanceof Enum && (((Enum) dataType).getCount()==1)) { + if (dataType instanceof Enum && (((Enum) dataType).getCount() == 1)) { // don't check single entry enums. Size will vary, and they are extracted defines return checkEnum((Enum) dataType, (Enum) matchingDataType); } diff --git a/Ghidra/Features/Base/ghidra_scripts/CreateDefaultGDTArchivesScript.java b/Ghidra/Features/Base/ghidra_scripts/CreateDefaultGDTArchivesScript.java index 18adfc1438..47e0f7c707 100644 --- a/Ghidra/Features/Base/ghidra_scripts/CreateDefaultGDTArchivesScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/CreateDefaultGDTArchivesScript.java @@ -32,13 +32,15 @@ import java.io.IOException; import ghidra.app.script.GhidraScript; import ghidra.app.util.cparser.C.CParserUtils; import ghidra.app.util.cparser.C.ParseException; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.dtarchive.FileDataTypeArchive; +import ghidra.util.task.TaskMonitor; public class CreateDefaultGDTArchivesScript extends GhidraScript { private File outputDirectory; - + private static String headerFilePath = "/data/HeaderFiles"; @Override @@ -47,34 +49,37 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { parseGDT_CLIB32(); parseGDT_CLIB64(); - + parseGDT_VS12_32(); parseGDT_VS12_64(); - + parseGDT_WinVS22(); parseGDT_WinVS22_WDK(); } - - private void parseHeaderFilesToGDT(File outputDir, String gdtName, String languageID, String compiler, - String[] filenames, String includePaths[], String[] args) + + private void parseHeaderFilesToGDT(File outputDir, String gdtName, String languageID, + String compiler, String[] filenames, String includePaths[], String[] args) throws ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { DataTypeManager openTypes[] = null; - - parseHeaderFilesToGDT(openTypes, outputDir, gdtName, languageID, compiler, filenames, includePaths, args); + + parseHeaderFilesToGDT(openTypes, outputDir, gdtName, languageID, compiler, filenames, + includePaths, args); } - private void parseHeaderFilesToGDT(DataTypeManager openTypes[], File outputDir, String gdtName, String languageID, String compiler, - String[] filenames, String[] includePaths, String[] args) + private void parseHeaderFilesToGDT(DataTypeManager openTypes[], File outputDir, String gdtName, + String languageID, String compiler, String[] filenames, String[] includePaths, + String[] args) throws ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { - + String dataTypeFile = outputDir + File.separator + gdtName + ".gdt"; File f = getArchiveFile(dataTypeFile); - FileDataTypeManager dtMgr = CParserUtils.parseHeaderFiles(openTypes, filenames, - includePaths, args, f.getAbsolutePath(), languageID, compiler, monitor); - - dtMgr.close(); + FileDataTypeArchive archive = CParserUtils.parseHeaderFiles(filenames, + includePaths, args, f.getAbsolutePath(), languageID, compiler, openTypes, this, + monitor); + + archive.release(this); } /** @@ -96,230 +101,9 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { } return f; } - - public void parseGDT_VS12_32() throws Exception { - - String filenames[] = { - "sdkddkver.h", - "sal.h", - "assert.h", - "conio.h", - "crtdefs.h", - "crtdbg.h", - "crtwrn.h", - "ctype.h", - "basetsd.h", - "WinDef.h", - "WinNT.h", - "delayimp.h", - "direct.h", - "dos.h", - "errno.h", - "excpt.h", - "fcntl.h", - "float.h", - "fpieee.h", - "io.h", - "iso646.h", - "limits.h", - "locale.h", - "malloc.h", - "math.h", - "mbctype.h", - "mbstring.h", - "memory.h", - "minmax.h", - "new.h", - "omp.h", - "pgobootrun.h", - "process.h", - "rtcapi.h", - "search.h", - "setjmp.h", - "setjmpex.h", - "share.h", - "signal.h", - "srv.h", - "stdarg.h", - "stddef.h", - "stdexcpt.h", - "stdio.h", - "stdlib.h", - "string.h", - "tchar.h", - "time.h", - "use_ansi.h", - "vadefs.h", - "varargs.h", - "wchar.h", - "wctype.h", - "xlocinfo.h", - "xmath.h", - "ymath.h", - "yvals.h", - "CommDlg.h", - "WinUser.h", - "WinNls.h", - "internal.h", - "strsafe.h", - "align.h", - "awint.h", - "crtversion.h", - "cruntime.h", - "ctime.h", - "cvt.h", - "dbgint.h", - "ehdata.h", - "emmintrin.h", - "errmsg.h", - "fenv.h", - "file2.h", - "fltintrn.h", - "immintrin.h", - "internal_securecrt.h", - "inttypes.h", - "isa_availability.h", - "mbdata.h", - "msdos.h", - "mtdll.h", - "nlsdownlevel.h", - "nlsint.h", - "nmmintrin.h", - "oscalls.h", - "pmmintrin.h", - "rtcsup.h", - "rterr.h", - "sect_attribs.h", - "setlocal.h", - "smmintrin.h", - "stdbool.h", - "stdint.h", - "syserr.h", - "targetver.h", - "tmmintrin.h", - "winheap.h", - "wmmintrin.h", - "wrapwin.h", - "xkeycheck.h", - "xmmintrin.h", - "xmtx.h", - "xtgmath.h", - "xxcctype.h", - "xxdftype.h", - "xxfftype.h", - "xxlftype.h", - "xxwctype.h", - "xxxprec.h", - "shlobj.h", - "evntprov.h", - "uiautomation.h", - "aclapi.h", - "appcompatapi.h", - "capi.h", - "clusapi.h", - "cryptuiapi.h", - "cscapi.h", - "devpropdef.h", - "dhcpsapi.h", - "dwmapi.h", - "ehstorapi.h", - "functiondiscoveryapi.h", - "ipexport.h", - "icmpapi.h", - "iepmapi.h", - "imapi.h", - "ksopmapi.h", - "locationapi.h", - "lpmapi.h", - "mapi.h", - "mbnapi.h", - "mfapi.h", - "mgmtapi.h", - "mmdeviceapi.h", - "mprapi.h", - "msctfmonitorapi.h", - "ndfapi.h", - "winsock2.h", - "ws2tcpip.h", - "iphlpapi.h", - "netioapi.h", - "npapi.h", - "nspapi.h", - "ntdsapi.h", - "ntmsapi.h", - "ntsecapi.h", - "patchapi.h", - "portabledeviceapi.h", - "portabledeviceconnectapi.h", - "propapi.h", - "psapi.h", - "rdpencomapi.h", - "resapi.h", - "sapi.h", - "searchapi.h", - "sensapi.h", - "sensorsapi.h", - "setupapi.h", - "shellapi.h", - "shlwapi.h", - "srrestoreptapi.h", - "svrapi.h", - "t2embapi.h", - "tapi.h", - "uiautomationcoreapi.h", - "wcnapi.h", - "wdsclientapi.h", - "werapi.h", - "windowssideshowapi.h", - "wlanapi.h", - "wpapi.h", - "wpcapi.h", - "wscapi.h", - "wsdapi.h", - "rpcproxy.h", - }; - - String includePaths[] = { - headerFilePath+"/VC/VS12/src", - headerFilePath+"/VC/VS12/include", - headerFilePath+"/VC/SDK/v7.1A/Include", - }; - - String args[] = { - "-D_M_IX86=300", - "-D_MSC_VER=1200", - "-D_INTEGRAL_MAX_BITS=32", - "-DNTDDI_VERSION=0x09000000", - "-DWINVER=0x0900", - "-D_X86_", - "-D_WIN32", - "-DCRTDLL", - "-D_USE_ATTRIBUTES_FOR_SAL", - "-D_CRTBLD", - "-D_OPENMP_NOFORCE_MANIFEST", - "-DLPSKBINFO=LPARAM", - "-DCONST=const", - "-D_CRT_SECURE_NO_WARNINGS", - "-D_CRT_NONSTDC_NO_DEPRECATE", - "-D_CRT_NONSTDC_NO_WARNINGS", - "-D_CRT_OBSOLETE_NO_DEPRECATE", - "-D_ALLOW_KEYWORD_MACROS", - "-D_ASSERT_OK", - "-DSTRSAFE_NO_DEPRECATE", - "-D__possibly_notnullterminated", - "-Dtype_info=\"void *\"", - "-D_ThrowInfo=ThrowInfo", - "-v0", - "-D__inner_checkReturn=", - }; - - parseHeaderFilesToGDT(outputDirectory, "windows_vs12_32_new", "x86:LE:32:default", "windows", filenames, includePaths, args); - } - - - public void parseGDT_VS12_64() throws Exception { - + public void parseGDT_VS12_32() throws Exception { + String filenames[] = { "sdkddkver.h", "sal.h", @@ -459,10 +243,11 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { "mmdeviceapi.h", "mprapi.h", "msctfmonitorapi.h", - "ndfapi.h", + "ndfapi.h", "winsock2.h", "ws2tcpip.h", - "iphlpapi.h", + "iphlpapi.h", + "netioapi.h", "npapi.h", "nspapi.h", "ntdsapi.h", @@ -493,995 +278,1220 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { "windowssideshowapi.h", "wlanapi.h", "wpapi.h", - "wpcapi.h", + "wpcapi.h", "wscapi.h", "wsdapi.h", "rpcproxy.h", }; - + String includePaths[] = { - headerFilePath+"/VC/VS12/src", - headerFilePath+"/VC/VS12/include", - headerFilePath+"/VC/SDK/v7.1A/Include", + headerFilePath + "/VC/VS12/src", + headerFilePath + "/VC/VS12/include", + headerFilePath + "/VC/SDK/v7.1A/Include", }; - + String args[] = { - "-D_MSC_VER=1200", - "-D_INTEGRAL_MAX_BITS=64", - "-D_WIN32_WINNT=0x601", - "-DNTDDI_VERSION=0x06010000", - "-DWINVER=0x0900", - "-D_AMD64_", - "-D_M_AMD64", - "-D_M_X64", - "-D_WIN64", - "-D_WIN32", - "-D_USE_ATTRIBUTES_FOR_SAL", - "-D_CRTBLD", - "-D_OPENMP_NOFORCE_MANIFEST", - "-DLPSKBINFO=LPARAM", - "-DCONST=const", - "-D_CRT_SECURE_NO_WARNINGS", - "-D_CRT_NONSTDC_NO_DEPRECATE", - "-D_CRT_NONSTDC_NO_WARNINGS", - "-D_CRT_OBSOLETE_NO_DEPRECATE", - "-D_ALLOW_KEYWORD_MACROS", - "-D_ASSERT_OK", - "-DSTRSAFE_NO_DEPRECATE", - "-D__possibly_notnullterminated", - "-Dtype_info=\"void *\"", - "-D_ThrowInfo=ThrowInfo", - "-D__unaligned=", - "-v0", - "-D__inner_checkReturn=", + "-D_M_IX86=300", + "-D_MSC_VER=1200", + "-D_INTEGRAL_MAX_BITS=32", + "-DNTDDI_VERSION=0x09000000", + "-DWINVER=0x0900", + "-D_X86_", + "-D_WIN32", + "-DCRTDLL", + "-D_USE_ATTRIBUTES_FOR_SAL", + "-D_CRTBLD", + "-D_OPENMP_NOFORCE_MANIFEST", + "-DLPSKBINFO=LPARAM", + "-DCONST=const", + "-D_CRT_SECURE_NO_WARNINGS", + "-D_CRT_NONSTDC_NO_DEPRECATE", + "-D_CRT_NONSTDC_NO_WARNINGS", + "-D_CRT_OBSOLETE_NO_DEPRECATE", + "-D_ALLOW_KEYWORD_MACROS", + "-D_ASSERT_OK", + "-DSTRSAFE_NO_DEPRECATE", + "-D__possibly_notnullterminated", + "-Dtype_info=\"void *\"", + "-D_ThrowInfo=ThrowInfo", + "-v0", + "-D__inner_checkReturn=", }; - - parseHeaderFilesToGDT(outputDirectory, "windows_vs12_64_new", "x86:LE:64:default", "windows", filenames, includePaths, args); + + parseHeaderFilesToGDT(outputDirectory, "windows_vs12_32_new", "x86:LE:32:default", + "windows", filenames, includePaths, args); } - - + + public void parseGDT_VS12_64() throws Exception { + + String filenames[] = { + "sdkddkver.h", + "sal.h", + "assert.h", + "conio.h", + "crtdefs.h", + "crtdbg.h", + "crtwrn.h", + "ctype.h", + "basetsd.h", + "WinDef.h", + "WinNT.h", + "delayimp.h", + "direct.h", + "dos.h", + "errno.h", + "excpt.h", + "fcntl.h", + "float.h", + "fpieee.h", + "io.h", + "iso646.h", + "limits.h", + "locale.h", + "malloc.h", + "math.h", + "mbctype.h", + "mbstring.h", + "memory.h", + "minmax.h", + "new.h", + "omp.h", + "pgobootrun.h", + "process.h", + "rtcapi.h", + "search.h", + "setjmp.h", + "setjmpex.h", + "share.h", + "signal.h", + "srv.h", + "stdarg.h", + "stddef.h", + "stdexcpt.h", + "stdio.h", + "stdlib.h", + "string.h", + "tchar.h", + "time.h", + "use_ansi.h", + "vadefs.h", + "varargs.h", + "wchar.h", + "wctype.h", + "xlocinfo.h", + "xmath.h", + "ymath.h", + "yvals.h", + "CommDlg.h", + "WinUser.h", + "WinNls.h", + "internal.h", + "strsafe.h", + "align.h", + "awint.h", + "crtversion.h", + "cruntime.h", + "ctime.h", + "cvt.h", + "dbgint.h", + "ehdata.h", + "emmintrin.h", + "errmsg.h", + "fenv.h", + "file2.h", + "fltintrn.h", + "immintrin.h", + "internal_securecrt.h", + "inttypes.h", + "isa_availability.h", + "mbdata.h", + "msdos.h", + "mtdll.h", + "nlsdownlevel.h", + "nlsint.h", + "nmmintrin.h", + "oscalls.h", + "pmmintrin.h", + "rtcsup.h", + "rterr.h", + "sect_attribs.h", + "setlocal.h", + "smmintrin.h", + "stdbool.h", + "stdint.h", + "syserr.h", + "targetver.h", + "tmmintrin.h", + "winheap.h", + "wmmintrin.h", + "wrapwin.h", + "xkeycheck.h", + "xmmintrin.h", + "xmtx.h", + "xtgmath.h", + "xxcctype.h", + "xxdftype.h", + "xxfftype.h", + "xxlftype.h", + "xxwctype.h", + "xxxprec.h", + "shlobj.h", + "evntprov.h", + "uiautomation.h", + "aclapi.h", + "appcompatapi.h", + "capi.h", + "clusapi.h", + "cryptuiapi.h", + "cscapi.h", + "devpropdef.h", + "dhcpsapi.h", + "dwmapi.h", + "ehstorapi.h", + "functiondiscoveryapi.h", + "ipexport.h", + "icmpapi.h", + "iepmapi.h", + "imapi.h", + "ksopmapi.h", + "locationapi.h", + "lpmapi.h", + "mapi.h", + "mbnapi.h", + "mfapi.h", + "mgmtapi.h", + "mmdeviceapi.h", + "mprapi.h", + "msctfmonitorapi.h", + "ndfapi.h", + "winsock2.h", + "ws2tcpip.h", + "iphlpapi.h", + "npapi.h", + "nspapi.h", + "ntdsapi.h", + "ntmsapi.h", + "ntsecapi.h", + "patchapi.h", + "portabledeviceapi.h", + "portabledeviceconnectapi.h", + "propapi.h", + "psapi.h", + "rdpencomapi.h", + "resapi.h", + "sapi.h", + "searchapi.h", + "sensapi.h", + "sensorsapi.h", + "setupapi.h", + "shellapi.h", + "shlwapi.h", + "srrestoreptapi.h", + "svrapi.h", + "t2embapi.h", + "tapi.h", + "uiautomationcoreapi.h", + "wcnapi.h", + "wdsclientapi.h", + "werapi.h", + "windowssideshowapi.h", + "wlanapi.h", + "wpapi.h", + "wpcapi.h", + "wscapi.h", + "wsdapi.h", + "rpcproxy.h", + }; + + String includePaths[] = { + headerFilePath + "/VC/VS12/src", + headerFilePath + "/VC/VS12/include", + headerFilePath + "/VC/SDK/v7.1A/Include", + }; + + String args[] = { + "-D_MSC_VER=1200", + "-D_INTEGRAL_MAX_BITS=64", + "-D_WIN32_WINNT=0x601", + "-DNTDDI_VERSION=0x06010000", + "-DWINVER=0x0900", + "-D_AMD64_", + "-D_M_AMD64", + "-D_M_X64", + "-D_WIN64", + "-D_WIN32", + "-D_USE_ATTRIBUTES_FOR_SAL", + "-D_CRTBLD", + "-D_OPENMP_NOFORCE_MANIFEST", + "-DLPSKBINFO=LPARAM", + "-DCONST=const", + "-D_CRT_SECURE_NO_WARNINGS", + "-D_CRT_NONSTDC_NO_DEPRECATE", + "-D_CRT_NONSTDC_NO_WARNINGS", + "-D_CRT_OBSOLETE_NO_DEPRECATE", + "-D_ALLOW_KEYWORD_MACROS", + "-D_ASSERT_OK", + "-DSTRSAFE_NO_DEPRECATE", + "-D__possibly_notnullterminated", + "-Dtype_info=\"void *\"", + "-D_ThrowInfo=ThrowInfo", + "-D__unaligned=", + "-v0", + "-D__inner_checkReturn=", + }; + + parseHeaderFilesToGDT(outputDirectory, "windows_vs12_64_new", "x86:LE:64:default", + "windows", filenames, includePaths, args); + } + public void parseGDT_CLIB64() throws Exception { - + String filenames[] = { - "sys/types.h", - "types.h", - "stddef.h", - "stddef.h", - "openssl/opensslconf.h", - "openssl/bn.h", - "openssl/ssl.h", - "openssl/asn1_mac.h", - "openssl/asn1t.h", - "openssl/blowfish.h", - "openssl/camellia.h", - "openssl/cast.h", - "openssl/cmac.h", - "openssl/cms.h", - "openssl/conf_api.h", - "openssl/des.h", - "openssl/dso.h", - "openssl/engine.h", - "openssl/fips_rand.h", - "openssl/idea.h", - "openssl/krb5_asn.h", - "openssl/md2.h", - "openssl/md4.h", - "openssl/md5.h", - "openssl/ocsp.h", - "openssl/pkcs12.h", - "openssl/rc2.h", - "openssl/rc4.h", - "openssl/ripemd.h", - "openssl/seed.h", - "openssl/ssl3.h", - "openssl/txt_db.h", - "openssl/whrlpool.h", - "aio.h", - "arpa/inet.h", - "cpio.h", - "dirent.h", - "fcntl.h", - "fmtmsg.h", - "fnmatch.h", - "ftw.h", - "glob.h", - "grp.h", - "iconv.h", - "langinfo.h", - "libgen.h", - "monetary.h", - "mqueue.h", - "ndbm.h", - "net/if.h", - "netdb.h", - "netinet/in.h", - "netinet/tcp.h", - "nl_types.h", - "poll.h", - "pthread.h", - "pwd.h", - "regex.h", - "sched.h", - "search.h", - "semaphore.h", - "spawn.h", - "strings.h", - "stropts.h", - "sys/ipc.h", - "sys/mman.h", - "sys/msg.h", - "sys/resource.h", - "sys/select.h", - "sys/sem.h", - "sys/shm.h", - "sys/socket.h", - "sys/stat.h", - "sys/statvfs.h", - "sys/time.h", - "sys/times.h", - "sys/types.h", - "sys/uio.h", - "sys/un.h", - "sys/utsname.h", - "sys/wait.h", - "syslog.h", - "tar.h", - "termios.h", - "trace.h", - "ulimit.h", - "unistd.h", - "utime.h", - "utmpx.h", - "wordexp.h", - "assert.h", - "complex.h", - "ctype.h", - "fenv.h", - "float.h", - "inttypes.h", - "iso646.h", - "limits.h", - "locale.h", - "math.h", - "setjmp.h", - "signal.h", - "stdarg.h", - "stdbool.h", - "stddef.h", - "stdint.h", - "stdio.h", - "stdlib.h", - "string.h", - "time.h", - "wchar.h", - "wctype.h", - "sys/acct.h", - "sys/debugreg.h", - "sys/epoll.h", - "sys/eventfd.h", - "sys/fcntl.h", - "sys/fsuid.h", - "sys/gmon.h", - "sys/gmon_out.h", - "sys/inotify.h", - "sys/io.h", - "sys/kd.h", - "sys/kdaemon.h", - "sys/klog.h", - "sys/mount.h", - "sys/mtio.h", - "sys/pci.h", - "sys/perm.h", - "sys/personality.h", - "sys/prctl.h", - "sys/profil.h", - "sys/ptrace.h", - "sys/quota.h", - "sys/raw.h", - "sys/reboot.h", - "sys/reg.h", - "sys/sem.h", - "sys/sendfile.h", - "sys/signal.h", - "sys/signalfd.h", - "sys/socketvar.h", - "sys/soundcard.h", - "sys/statvfs.h", - "sys/swap.h", - "sys/sysctl.h", - "sys/sysinfo.h", - "sys/termios.h", - "sys/timerfd.h", - "sys/ttychars.h", - "sys/ultrasound.h", - "sys/unistd.h", - "sys/ustat.h", - "sys/vfs.h", - "sys/vlimit.h", - "sys/vt.h", - "sys/vtimes.h", - "sys/xattr.h", - "errno.h", - "mathcalls.h", - "net/ethernet.h", - "net/if_arp.h", - "net/if_ppp.h", - "net/if_slip.h", - "net/ppp_defs.h", - "net/if.h", - "net/if_packet.h", - "net/if_shaper.h", - "net/ppp-comp.h", - "net/route.h", - "netinet/ether.h", - "netinet/if_fddi.h", - "netinet/in.h ", - "netinet/ip6.h", - "netinet/udp.h", - "netinet/icmp6.h", - "netinet/if_tr.h", - "netinet/in_systm.h", - "netinet/ip_icmp.h", - "netinet/if_ether.h", - "netinet/igmp.h", - "netinet/ip.h", - "netinet/tcp.h", - "rpc/types.h", - "rpc/auth.h", - "rpc/des_crypt.h", - "rpc/pmap_prot.h", - "rpc/rpc_msg.h", - "rpc/xdr.h", - "rpc/auth_des.h", - "rpc/key_prot.h", - "rpc/pmap_rmt.h", - "rpc/svc.h", - "rpc/auth_unix.h", - "rpc/netdb.h", - "rpc/rpc.h", - "rpc/svc_auth.h", - "rpc/clnt.h", - "rpc/pmap_clnt.h", - "rpc/rpc_des.h", - "rpcsvc/bootparam.h", - "rpcsvc/nis_callback.h", - "rpcsvc/yp_prot.h", - "rpcsvc/rstat.h", - "rpcsvc/rusers.h", - "rpcsvc/spray.h", - "rpcsvc/ypupd.h", - "rpcsvc/mount.h", - "rpcsvc/nis.h", - "protocols/routed.h", - "protocols/rwhod.h", - "protocols/talkd.h", - "protocols/timed.h", - "arpa/ftp.h", - "arpa/inet.h", - "arpa/nameser.h", - "arpa/nameser_compat.h", - "arpa/telnet.h", - "arpa/tftp.h", + "sys/types.h", + "types.h", + "stddef.h", + "stddef.h", + "openssl/opensslconf.h", + "openssl/bn.h", + "openssl/ssl.h", + "openssl/asn1_mac.h", + "openssl/asn1t.h", + "openssl/blowfish.h", + "openssl/camellia.h", + "openssl/cast.h", + "openssl/cmac.h", + "openssl/cms.h", + "openssl/conf_api.h", + "openssl/des.h", + "openssl/dso.h", + "openssl/engine.h", + "openssl/fips_rand.h", + "openssl/idea.h", + "openssl/krb5_asn.h", + "openssl/md2.h", + "openssl/md4.h", + "openssl/md5.h", + "openssl/ocsp.h", + "openssl/pkcs12.h", + "openssl/rc2.h", + "openssl/rc4.h", + "openssl/ripemd.h", + "openssl/seed.h", + "openssl/ssl3.h", + "openssl/txt_db.h", + "openssl/whrlpool.h", + "aio.h", + "arpa/inet.h", + "cpio.h", + "dirent.h", + "fcntl.h", + "fmtmsg.h", + "fnmatch.h", + "ftw.h", + "glob.h", + "grp.h", + "iconv.h", + "langinfo.h", + "libgen.h", + "monetary.h", + "mqueue.h", + "ndbm.h", + "net/if.h", + "netdb.h", + "netinet/in.h", + "netinet/tcp.h", + "nl_types.h", + "poll.h", + "pthread.h", + "pwd.h", + "regex.h", + "sched.h", + "search.h", + "semaphore.h", + "spawn.h", + "strings.h", + "stropts.h", + "sys/ipc.h", + "sys/mman.h", + "sys/msg.h", + "sys/resource.h", + "sys/select.h", + "sys/sem.h", + "sys/shm.h", + "sys/socket.h", + "sys/stat.h", + "sys/statvfs.h", + "sys/time.h", + "sys/times.h", + "sys/types.h", + "sys/uio.h", + "sys/un.h", + "sys/utsname.h", + "sys/wait.h", + "syslog.h", + "tar.h", + "termios.h", + "trace.h", + "ulimit.h", + "unistd.h", + "utime.h", + "utmpx.h", + "wordexp.h", + "assert.h", + "complex.h", + "ctype.h", + "fenv.h", + "float.h", + "inttypes.h", + "iso646.h", + "limits.h", + "locale.h", + "math.h", + "setjmp.h", + "signal.h", + "stdarg.h", + "stdbool.h", + "stddef.h", + "stdint.h", + "stdio.h", + "stdlib.h", + "string.h", + "time.h", + "wchar.h", + "wctype.h", + "sys/acct.h", + "sys/debugreg.h", + "sys/epoll.h", + "sys/eventfd.h", + "sys/fcntl.h", + "sys/fsuid.h", + "sys/gmon.h", + "sys/gmon_out.h", + "sys/inotify.h", + "sys/io.h", + "sys/kd.h", + "sys/kdaemon.h", + "sys/klog.h", + "sys/mount.h", + "sys/mtio.h", + "sys/pci.h", + "sys/perm.h", + "sys/personality.h", + "sys/prctl.h", + "sys/profil.h", + "sys/ptrace.h", + "sys/quota.h", + "sys/raw.h", + "sys/reboot.h", + "sys/reg.h", + "sys/sem.h", + "sys/sendfile.h", + "sys/signal.h", + "sys/signalfd.h", + "sys/socketvar.h", + "sys/soundcard.h", + "sys/statvfs.h", + "sys/swap.h", + "sys/sysctl.h", + "sys/sysinfo.h", + "sys/termios.h", + "sys/timerfd.h", + "sys/ttychars.h", + "sys/ultrasound.h", + "sys/unistd.h", + "sys/ustat.h", + "sys/vfs.h", + "sys/vlimit.h", + "sys/vt.h", + "sys/vtimes.h", + "sys/xattr.h", + "errno.h", + "mathcalls.h", + "net/ethernet.h", + "net/if_arp.h", + "net/if_ppp.h", + "net/if_slip.h", + "net/ppp_defs.h", + "net/if.h", + "net/if_packet.h", + "net/if_shaper.h", + "net/ppp-comp.h", + "net/route.h", + "netinet/ether.h", + "netinet/if_fddi.h", + "netinet/in.h ", + "netinet/ip6.h", + "netinet/udp.h", + "netinet/icmp6.h", + "netinet/if_tr.h", + "netinet/in_systm.h", + "netinet/ip_icmp.h", + "netinet/if_ether.h", + "netinet/igmp.h", + "netinet/ip.h", + "netinet/tcp.h", + "rpc/types.h", + "rpc/auth.h", + "rpc/des_crypt.h", + "rpc/pmap_prot.h", + "rpc/rpc_msg.h", + "rpc/xdr.h", + "rpc/auth_des.h", + "rpc/key_prot.h", + "rpc/pmap_rmt.h", + "rpc/svc.h", + "rpc/auth_unix.h", + "rpc/netdb.h", + "rpc/rpc.h", + "rpc/svc_auth.h", + "rpc/clnt.h", + "rpc/pmap_clnt.h", + "rpc/rpc_des.h", + "rpcsvc/bootparam.h", + "rpcsvc/nis_callback.h", + "rpcsvc/yp_prot.h", + "rpcsvc/rstat.h", + "rpcsvc/rusers.h", + "rpcsvc/spray.h", + "rpcsvc/ypupd.h", + "rpcsvc/mount.h", + "rpcsvc/nis.h", + "protocols/routed.h", + "protocols/rwhod.h", + "protocols/talkd.h", + "protocols/timed.h", + "arpa/ftp.h", + "arpa/inet.h", + "arpa/nameser.h", + "arpa/nameser_compat.h", + "arpa/telnet.h", + "arpa/tftp.h", }; - + String includePaths[] = { - headerFilePath+"/linux/include", - headerFilePath+"/linux/include/sys", - headerFilePath+"/linux/gcc/include", - headerFilePath+"/linux/include/openssl", - headerFilePath+"/linux/x86_64-redhat-linux5E/include", - headerFilePath+"/linux/x86_64-redhat-linux5E/include/sys", + headerFilePath + "/linux/include", + headerFilePath + "/linux/include/sys", + headerFilePath + "/linux/gcc/include", + headerFilePath + "/linux/include/openssl", + headerFilePath + "/linux/x86_64-redhat-linux5E/include", + headerFilePath + "/linux/x86_64-redhat-linux5E/include/sys", }; - + String args[] = { - "-D_X86_", - "-D__x86_64__", - "-D__STDC__", - "-D_GNU_SOURCE", - "-D__WORDSIZE=64", - "-D__builtin_va_list=void *", - "-D__DO_NOT_DEFINE_COMPILE", - "-D_Complex", - "-D__NO_STRING_INLINES", - "-D__signed__", - "-D__extension__=", - "-D__GLIBC_HAVE_LONG_LONG=1", - "-D__need_sigset_t", - "-Daligned_u64=uint64_t", + "-D_X86_", + "-D__x86_64__", + "-D__STDC__", + "-D_GNU_SOURCE", + "-D__WORDSIZE=64", + "-D__builtin_va_list=void *", + "-D__DO_NOT_DEFINE_COMPILE", + "-D_Complex", + "-D__NO_STRING_INLINES", + "-D__signed__", + "-D__extension__=", + "-D__GLIBC_HAVE_LONG_LONG=1", + "-D__need_sigset_t", + "-Daligned_u64=uint64_t", }; - - parseHeaderFilesToGDT(outputDirectory, "generic_clib_64_new", "x86:LE:64:default", "gcc", filenames, includePaths, args); + + parseHeaderFilesToGDT(outputDirectory, "generic_clib_64_new", "x86:LE:64:default", "gcc", + filenames, includePaths, args); } - - + public void parseGDT_CLIB32() throws Exception { - - String filenames[] = { - "sys/types.h", - "types.h", - "stddef.h", - "stddef.h", - "openssl/opensslconf.h", - "openssl/des.h", - "openssl/bn.h", - "openssl/ssl.h", - "openssl/asn1_mac.h", - "openssl/asn1t.h", - "openssl/blowfish.h", - "openssl/camellia.h", - "openssl/cast.h", - "openssl/cmac.h", - "openssl/cms.h", - "openssl/conf_api.h", - "openssl/dso.h", - "openssl/engine.h", - "openssl/fips_rand.h", - "openssl/idea.h", - "openssl/krb5_asn.h", - "openssl/md2.h", - "openssl/md4.h", - "openssl/md5.h", - "openssl/ocsp.h", - "openssl/pkcs12.h", - "openssl/rc2.h", - "openssl/rc4.h", - "openssl/ripemd.h", - "openssl/seed.h", - "openssl/ssl3.h", - "openssl/txt_db.h", - "openssl/whrlpool.h", - "aio.h", - "arpa/inet.h", - "cpio.h", - "dirent.h", - "fcntl.h", - "fmtmsg.h", - "fnmatch.h", - "ftw.h", - "glob.h", - "grp.h", - "iconv.h", - "langinfo.h", - "libgen.h", - "monetary.h", - "mqueue.h", - "ndbm.h", - "net/if.h", - "netdb.h", - "netinet/in.h", - "netinet/tcp.h", - "nl_types.h", - "poll.h", - "pthread.h", - "pwd.h", - "regex.h", - "sched.h", - "search.h", - "semaphore.h", - "spawn.h", - "strings.h", - "stropts.h", - "sys/ipc.h", - "sys/mman.h", - "sys/msg.h", - "sys/resource.h", - "sys/select.h", - "sys/sem.h", - "sys/shm.h", - "sys/socket.h", - "sys/stat.h", - "sys/statvfs.h", - "sys/time.h", - "sys/times.h", - "sys/types.h", - "sys/uio.h", - "sys/un.h", - "sys/utsname.h", - "sys/wait.h", - "syslog.h", - "tar.h", - "termios.h", - "trace.h", - "ulimit.h", - "unistd.h", - "utime.h", - "utmpx.h", - "wordexp.h", - "assert.h", - "complex.h", - "ctype.h", - "fenv.h", - "float.h", - "inttypes.h", - "iso646.h", - "limits.h", - "locale.h", - "math.h", - "setjmp.h", - "signal.h", - "stdarg.h", - "stdbool.h", - "stddef.h", - "stdint.h", - "stdio.h", - "stdlib.h", - "string.h", - "time.h", - "wchar.h", - "wctype.h", - "sys/acct.h", - "sys/debugreg.h", - "sys/epoll.h", - "sys/eventfd.h", - "sys/fcntl.h", - "sys/fsuid.h", - "sys/gmon.h", - "sys/gmon_out.h", - "sys/inotify.h", - "sys/io.h", - "sys/kd.h", - "sys/kdaemon.h", - "sys/klog.h", - "sys/mount.h", - "sys/mtio.h", - "sys/pci.h", - "sys/perm.h", - "sys/personality.h", - "sys/prctl.h", - "sys/profil.h", - "sys/ptrace.h", - "sys/quota.h", - "sys/raw.h", - "sys/reboot.h", - "sys/reg.h", - "sys/sem.h", - "sys/sendfile.h", - "sys/signal.h", - "sys/signalfd.h", - "sys/socketvar.h", - "sys/soundcard.h", - "sys/statvfs.h", - "sys/swap.h", - "sys/sysctl.h", - "sys/sysinfo.h", - "sys/termios.h", - "sys/timerfd.h", - "sys/ttychars.h", - "sys/ultrasound.h", - "sys/unistd.h", - "sys/ustat.h", - "sys/vfs.h", - "sys/vlimit.h", - "sys/vt.h", - "sys/vtimes.h", - "sys/xattr.h", - "errno.h", - "mathcalls.h", - "net/ethernet.h", - "net/if_arp.h", - "net/if_ppp.h", - "net/if_slip.h", - "net/ppp_defs.h", - "net/if.h", - "net/if_packet.h", - "net/if_shaper.h", - "net/ppp-comp.h", - "net/route.h", - "netinet/ether.h", - "netinet/if_fddi.h", - "netinet/in.h ", - "netinet/ip6.h", - "netinet/udp.h", - "netinet/icmp6.h", - "netinet/if_tr.h", - "netinet/in_systm.h", - "netinet/ip_icmp.h", - "netinet/if_ether.h", - "netinet/igmp.h", - "netinet/ip.h", - "netinet/tcp.h", - "rpc/types.h", - "rpc/auth.h", - "rpc/des_crypt.h", - "rpc/pmap_prot.h", - "rpc/rpc_msg.h", - "rpc/xdr.h", - "rpc/auth_des.h", - "rpc/key_prot.h", - "rpc/pmap_rmt.h", - "rpc/svc.h", - "rpc/auth_unix.h", - "rpc/netdb.h", - "rpc/rpc.h", - "rpc/svc_auth.h", - "rpc/clnt.h", - "rpc/pmap_clnt.h", - "rpc/rpc_des.h", - "rpcsvc/bootparam.h", - "rpcsvc/nis_callback.h", - "rpcsvc/yp_prot.h", - "rpcsvc/rstat.h", - "rpcsvc/rusers.h", - "rpcsvc/spray.h", - "rpcsvc/ypupd.h", - "rpcsvc/mount.h", - "rpcsvc/nis.h", - "protocols/routed.h", - "protocols/rwhod.h", - "protocols/talkd.h", - "protocols/timed.h", - "arpa/ftp.h", - "arpa/inet.h", - "arpa/nameser.h", - "arpa/nameser_compat.h", - "arpa/telnet.h", - "arpa/tftp.h", - }; - - String includePaths[] = { - headerFilePath+"/linux/include", - headerFilePath+"/linux/include/sys", - headerFilePath+"/linux/gcc/include", - headerFilePath+"/linux/include/openssl", - headerFilePath+"/linux/x86_64-redhat-linux5E/include", - headerFilePath+"/linux/x86_64-redhat-linux5E/include/sys", - }; - - String args[] = { - "-D_X86_", - "-D__i386__", - "-D__STDC__", - "-D_GNU_SOURCE", - "-D__WORDSIZE=32", - "-D__builtin_va_list=void *", - "-D__DO_NOT_DEFINE_COMPILE", - "-D_Complex", - "-D__NO_STRING_INLINES", - "-D__NO_LONG_DOUBLE_MATH", - "-D__signed__", - "-D__extension__=", - "-D__GLIBC_HAVE_LONG_LONG=1", - "-Daligned_u64=uint64_t", - }; - - parseHeaderFilesToGDT(outputDirectory, "generic_clib_new", "x86:LE:32:default", "gcc", filenames, includePaths, args); - } - - public void parseGDT_WinVS22() throws Exception { - String filenames[] = { - "# Core necessary files", - "winapifamily.h", - "winpackagefamily.h", - "sdkddkver.h", - "sal.h", - "no_sal2.h", - "corecrt.h", - "wtypes.h", - "winnt.h", - "winternl.h", - "#ntdef.h", - - "# Common headers ", - "dos.h", - "errno.h", - "malloc.h", - "signal.h", - "stdalign.h", - "stddef.h", - "stdio.h", - "stdlib.h", - "assert.h", - "crtdbg.h", - "ctype.h", - "conio.h", - "direct.h", - "fcntl.h", - "float.h", - "fpieee.h", - "inttypes.h", - "io.h", - "locale.h", - "complex.h", - "math.h", - "mbctype.h", - "mbstring.h", - "memory.h", - "minmax.h", - "new.h", - "process.h", - "search.h", - "share.h", - "winbase.h", - "winuser.h", - "Windows.h", - - "# Security and identity (https://docs.microsoft.com/en-us/windows/win32/api/_security/)", - "accctrl.h", - "aclapi.h", - "aclui.h", - "adtgen.h", - "authz.h", - "azroles.h", - "bcrypt.h", - "casetup.h", - "ccgplugins.h", - "celib.h", - "ntlsa.h", - "sspi.h", - "ntsecapi.h", - "ntsecpkg.h", - "schannel.h", - "certadm.h", - "certbcli.h", - "certcli.h", - "certenroll.h", - "certexit.h", - "certif.h", - "certmod.h", - "certpol.h", - "CertPolEng.h", - "certsrv.h", - "certview.h", - "credssp.h", - "cryptdlg.h", - "cryptuiapi.h", - "cryptxml.h", - "diagnosticdataquery.h", - "diagnosticdataquerytypes.h", - "dpapi.h", - "dssec.h", - "iads.h", - "IdentityCommon.h", - "IdentityProvider.h", - "identitystore.h", - "keycredmgr.h", - "lmaccess.h", - "lsalookup.h", - "mmcobj.h", - "mscat.h", - "mssip.h", - "namedpipeapi.h", - "ncrypt.h", - "ncryptprotect.h", - "npapi.h", - "processthreadsapi.h", - "sas.h", - "scesvc.h", - "sddl.h", - "securityappcontainer.h", - "securitybaseapi.h", - "slpublic.h", - "subauth.h", - "tokenbinding.h", - "tpmvscmgr.h", - "wincred.h", - "wincrypt.h", - "winnetwk.h", - "winreg.h", - "winsafer.h", - "winscard.h", - "winsvc.h", - "wintrust.h", - "winwlx.h", - "xenroll.h", - - "# Windows sockets", - "af_irda.h", - "in6addr.h", - "mstcpip.h", - "winsock2.h", - "nsemail.h", - "nspapi.h", - "socketapi.h", - "# Nothing includes this; is it necessary?", - "#sporder.h", - "transportsettingcommon.h", - "ws2atm.h", - "ws2spi.h", - "MSWSock.h", - "ws2tcpip.h", - "wsipv6ok.h", - "WSNwLink.h", - "wsrm.h", - "mswsockdef.h", - - "# Remote Procedure Call (RPC)", - "midles.h", - "midlbase.h", - "rpc.h", - "rpcndr.h", - "rpcasync.h", - "rpcdcep.h", - "rpcnsi.h", - "rpcproxy.h", - "rpcssl.h", - - "# COM", - "accctrl.h", - "callobj.h", - "combaseapi.h", - "comcat.h", - "ctxtcall.h", - "dmerror.h", - "docobj.h", - "eventsys.h", - "initguid.h", - "guiddef.h", - "iaccess.h", - "hstring.h", - "IMessageDispatcher.h", - "MessageDispatcherApi.h", - "objbase.h", - "objidlbase.h", - "objidl.h", - "ocidl.h", - "ole.h", - "ole2.h", - "oledlg.h", - "oleidl.h", - "roapi.h", - "rpcdce.h", - "servprov.h", - "shobjidl.h", - "txlogpub.h", - "unknwnbase.h", - "unknwn.h", - "urlmon.h", - "vbinterf.h", - "winddi.h", - "winerror.h", - "wtypesbase.h", - - "# COM+", - "comadmin.h", - "mtxdm.h", - - "# More", - "inspectable.h", - - "# Windows Internet", - "proofofpossessioncookieinfo.h", - "wininet.h", - "winineti.h", - - "# Windows HTTP Services", - "winhttp.h", - - "# Compression", - "compressapi.h", - - "# TraceLogging", - "#traceloggingactivity.h", - "#traceloggingprovider.h", - - "# Windows Error Reporting", - "errorrep.h", - "werapi.h", - - "# Windows and MEssages", - "olectl.h", - "windef.h", - "windowsx.h", - - "# Shell", - "appmgmt.h", - "appnotify.h", - "cpl.h", - "credentialprovider.h", - "dimm.h", - "imagetranscode.h", - "inputpanelconfiguration.h", - "intsafe.h", - "intshcut.h", - "mobsync.h", - "objectarray.h", - "pathcch.h", - "profinfo.h", - "propkeydef.h", - "scrnsave.h", - "shappmgr.h", - "shdeprecated.h", - "shidfact.h", - "shimgdata.h", - "shlwapi.h", - "shtypes.h", - "storageprovider.h", - "syncmgr.h", - "thumbcache.h", - "thumbnailstreamcache.h", - "tlogstg.h", - "UserEnv.h", - - "# Windows Controls", - "commctrl.h", - "commoncontrols.h", - "dpa_dsa.h", - "prsht.h", - "richedit.h", - "richole.h", - "shlobj_core.h", - "shlobj.h", - "#textserv.h", // C++ - "tom.h", - "uxtheme.h", - - "# Menus and other resources", - "resourceindexer.h", - "strsafe.h", - "verrsrc.h", - "winver.h", - - "# Windows Accessibility Features", - "oleacc.h", - "uiautomationcore.h", - "uiautomationclient.h", - "uiautomationcoreapi.h", - - "# Internationalization", - "datetimeapi.h", - "elscore.h", - "gb18030.h", - "imepad.h", - "imm.h", - "immdev.h", - "msime.h", - "msimeapi.h", - "muiload.h", - "spellcheck.h", - "spellcheckprovider.h", - "stringapiset.h", - "usp10.h", - "winnls.h", - - "# HTTP Server API", - "#http.h", // included by something else - - "# IP Helper", - "#icmpapi.h", // Something wrong with IP_ADDR - "ifdef.h", - "inaddr.h", - "ip2string.h", - "ipexport.h", - "iphlpapi.h", - "iprtrmib.h", - "iptypes.h", - "nldef.h", - "tcpestats.h", - - "# Network Management", - "atacct.h", - "lmalert.h", - "lmapibuf.h", - "lmat.h", - "lmaudit.h", - "lmconfig.h", - "lmerrlog.h", - "lmjoin.h", - "lmmsg.h", - "lmremutl.h", - "lmserver.h", - "lmsvc.h", - "lmuse.h", - "lmwksta.h" + "sys/types.h", + "types.h", + "stddef.h", + "stddef.h", + "openssl/opensslconf.h", + "openssl/des.h", + "openssl/bn.h", + "openssl/ssl.h", + "openssl/asn1_mac.h", + "openssl/asn1t.h", + "openssl/blowfish.h", + "openssl/camellia.h", + "openssl/cast.h", + "openssl/cmac.h", + "openssl/cms.h", + "openssl/conf_api.h", + "openssl/dso.h", + "openssl/engine.h", + "openssl/fips_rand.h", + "openssl/idea.h", + "openssl/krb5_asn.h", + "openssl/md2.h", + "openssl/md4.h", + "openssl/md5.h", + "openssl/ocsp.h", + "openssl/pkcs12.h", + "openssl/rc2.h", + "openssl/rc4.h", + "openssl/ripemd.h", + "openssl/seed.h", + "openssl/ssl3.h", + "openssl/txt_db.h", + "openssl/whrlpool.h", + "aio.h", + "arpa/inet.h", + "cpio.h", + "dirent.h", + "fcntl.h", + "fmtmsg.h", + "fnmatch.h", + "ftw.h", + "glob.h", + "grp.h", + "iconv.h", + "langinfo.h", + "libgen.h", + "monetary.h", + "mqueue.h", + "ndbm.h", + "net/if.h", + "netdb.h", + "netinet/in.h", + "netinet/tcp.h", + "nl_types.h", + "poll.h", + "pthread.h", + "pwd.h", + "regex.h", + "sched.h", + "search.h", + "semaphore.h", + "spawn.h", + "strings.h", + "stropts.h", + "sys/ipc.h", + "sys/mman.h", + "sys/msg.h", + "sys/resource.h", + "sys/select.h", + "sys/sem.h", + "sys/shm.h", + "sys/socket.h", + "sys/stat.h", + "sys/statvfs.h", + "sys/time.h", + "sys/times.h", + "sys/types.h", + "sys/uio.h", + "sys/un.h", + "sys/utsname.h", + "sys/wait.h", + "syslog.h", + "tar.h", + "termios.h", + "trace.h", + "ulimit.h", + "unistd.h", + "utime.h", + "utmpx.h", + "wordexp.h", + "assert.h", + "complex.h", + "ctype.h", + "fenv.h", + "float.h", + "inttypes.h", + "iso646.h", + "limits.h", + "locale.h", + "math.h", + "setjmp.h", + "signal.h", + "stdarg.h", + "stdbool.h", + "stddef.h", + "stdint.h", + "stdio.h", + "stdlib.h", + "string.h", + "time.h", + "wchar.h", + "wctype.h", + "sys/acct.h", + "sys/debugreg.h", + "sys/epoll.h", + "sys/eventfd.h", + "sys/fcntl.h", + "sys/fsuid.h", + "sys/gmon.h", + "sys/gmon_out.h", + "sys/inotify.h", + "sys/io.h", + "sys/kd.h", + "sys/kdaemon.h", + "sys/klog.h", + "sys/mount.h", + "sys/mtio.h", + "sys/pci.h", + "sys/perm.h", + "sys/personality.h", + "sys/prctl.h", + "sys/profil.h", + "sys/ptrace.h", + "sys/quota.h", + "sys/raw.h", + "sys/reboot.h", + "sys/reg.h", + "sys/sem.h", + "sys/sendfile.h", + "sys/signal.h", + "sys/signalfd.h", + "sys/socketvar.h", + "sys/soundcard.h", + "sys/statvfs.h", + "sys/swap.h", + "sys/sysctl.h", + "sys/sysinfo.h", + "sys/termios.h", + "sys/timerfd.h", + "sys/ttychars.h", + "sys/ultrasound.h", + "sys/unistd.h", + "sys/ustat.h", + "sys/vfs.h", + "sys/vlimit.h", + "sys/vt.h", + "sys/vtimes.h", + "sys/xattr.h", + "errno.h", + "mathcalls.h", + "net/ethernet.h", + "net/if_arp.h", + "net/if_ppp.h", + "net/if_slip.h", + "net/ppp_defs.h", + "net/if.h", + "net/if_packet.h", + "net/if_shaper.h", + "net/ppp-comp.h", + "net/route.h", + "netinet/ether.h", + "netinet/if_fddi.h", + "netinet/in.h ", + "netinet/ip6.h", + "netinet/udp.h", + "netinet/icmp6.h", + "netinet/if_tr.h", + "netinet/in_systm.h", + "netinet/ip_icmp.h", + "netinet/if_ether.h", + "netinet/igmp.h", + "netinet/ip.h", + "netinet/tcp.h", + "rpc/types.h", + "rpc/auth.h", + "rpc/des_crypt.h", + "rpc/pmap_prot.h", + "rpc/rpc_msg.h", + "rpc/xdr.h", + "rpc/auth_des.h", + "rpc/key_prot.h", + "rpc/pmap_rmt.h", + "rpc/svc.h", + "rpc/auth_unix.h", + "rpc/netdb.h", + "rpc/rpc.h", + "rpc/svc_auth.h", + "rpc/clnt.h", + "rpc/pmap_clnt.h", + "rpc/rpc_des.h", + "rpcsvc/bootparam.h", + "rpcsvc/nis_callback.h", + "rpcsvc/yp_prot.h", + "rpcsvc/rstat.h", + "rpcsvc/rusers.h", + "rpcsvc/spray.h", + "rpcsvc/ypupd.h", + "rpcsvc/mount.h", + "rpcsvc/nis.h", + "protocols/routed.h", + "protocols/rwhod.h", + "protocols/talkd.h", + "protocols/timed.h", + "arpa/ftp.h", + "arpa/inet.h", + "arpa/nameser.h", + "arpa/nameser_compat.h", + "arpa/telnet.h", + "arpa/tftp.h", }; String includePaths[] = { - headerFilePath+"/VC/VS22/Community/VC/Tools/MSVC/14.29.30133/include", - headerFilePath+"/VC/SDK/10/Include/10.0.22000.0/shared", - headerFilePath+"/VC/SDK/10/Include/10.0.22000.0/um", - headerFilePath+"/VC/SDK/10/Include/10.0.22000.0/km", - headerFilePath+"/VC/VS22/10.0.19041.0/shared", - headerFilePath+"/VC/VS22/10.0.19041.0/um", - headerFilePath+"/VC/VS22/10.0.19041.0/ucrt", - headerFilePath+"/VC/VS22/10.0.19041.0/winrt", + headerFilePath + "/linux/include", + headerFilePath + "/linux/include/sys", + headerFilePath + "/linux/gcc/include", + headerFilePath + "/linux/include/openssl", + headerFilePath + "/linux/x86_64-redhat-linux5E/include", + headerFilePath + "/linux/x86_64-redhat-linux5E/include/sys", }; - + String args[] = { - "-D_MSC_VER=1924", - "-D_INTEGRAL_MAX_BITS=64", - "-DWINVER=0x0a00", - "-D_WIN32_WINNT=0x0a00", - "-D_AMD64_", - "-D_M_AMD64", - "-D_M_X64", - "-D_WIN64", - "-D_WIN32", - "-D_USE_ATTRIBUTES_FOR_SAL", - "-D_CRTBLD", - "-D_OPENMP_NOFORCE_MANIFEST", - "-DLPSKBINFO=LPARAM", - "-DCONST=const", - "-D_CRT_SECURE_NO_WARNINGS", - "-D_CRT_NONSTDC_NO_DEPRECATE", - "-D_CRT_NONSTDC_NO_WARNINGS", - "-D_CRT_OBSOLETE_NO_DEPRECATE", - "-D_ALLOW_KEYWORD_MACROS", - "-D_ASSERT_OK", - "-DSTRSAFE_NO_DEPRECATE", - "-D__possibly_notnullterminated", - "-Dtype_info=\"void *\"", - "-D_ThrowInfo=ThrowInfo", - "-D__unaligned=", - "-v0", - "-D__inner_checkReturn=", - "-DDECLSPEC_DEPRECATED_DDK", - "-DWINAPI_PARTITION_APP=1", - "-DWINAPI_PARTITION_SYSTEM=1", - "-DWINAPI_PARTITION_GAMES=1", - "-DSECURITY_WIN32", - "-DSIZE_T=size_t", + "-D_X86_", + "-D__i386__", + "-D__STDC__", + "-D_GNU_SOURCE", + "-D__WORDSIZE=32", + "-D__builtin_va_list=void *", + "-D__DO_NOT_DEFINE_COMPILE", + "-D_Complex", + "-D__NO_STRING_INLINES", + "-D__NO_LONG_DOUBLE_MATH", + "-D__signed__", + "-D__extension__=", + "-D__GLIBC_HAVE_LONG_LONG=1", + "-Daligned_u64=uint64_t", }; - - parseHeaderFilesToGDT(outputDirectory, "windows_vs22_64_new", "x86:LE:64:default", "windows", filenames, includePaths, args); + + parseHeaderFilesToGDT(outputDirectory, "generic_clib_new", "x86:LE:32:default", "gcc", + filenames, includePaths, args); } + public void parseGDT_WinVS22() throws Exception { + + String filenames[] = { + "# Core necessary files", + "winapifamily.h", + "winpackagefamily.h", + "sdkddkver.h", + "sal.h", + "no_sal2.h", + "corecrt.h", + "wtypes.h", + "winnt.h", + "winternl.h", + "#ntdef.h", + + "# Common headers ", + "dos.h", + "errno.h", + "malloc.h", + "signal.h", + "stdalign.h", + "stddef.h", + "stdio.h", + "stdlib.h", + "assert.h", + "crtdbg.h", + "ctype.h", + "conio.h", + "direct.h", + "fcntl.h", + "float.h", + "fpieee.h", + "inttypes.h", + "io.h", + "locale.h", + "complex.h", + "math.h", + "mbctype.h", + "mbstring.h", + "memory.h", + "minmax.h", + "new.h", + "process.h", + "search.h", + "share.h", + "winbase.h", + "winuser.h", + "Windows.h", + + "# Security and identity (https://docs.microsoft.com/en-us/windows/win32/api/_security/)", + "accctrl.h", + "aclapi.h", + "aclui.h", + "adtgen.h", + "authz.h", + "azroles.h", + "bcrypt.h", + "casetup.h", + "ccgplugins.h", + "celib.h", + "ntlsa.h", + "sspi.h", + "ntsecapi.h", + "ntsecpkg.h", + "schannel.h", + "certadm.h", + "certbcli.h", + "certcli.h", + "certenroll.h", + "certexit.h", + "certif.h", + "certmod.h", + "certpol.h", + "CertPolEng.h", + "certsrv.h", + "certview.h", + "credssp.h", + "cryptdlg.h", + "cryptuiapi.h", + "cryptxml.h", + "diagnosticdataquery.h", + "diagnosticdataquerytypes.h", + "dpapi.h", + "dssec.h", + "iads.h", + "IdentityCommon.h", + "IdentityProvider.h", + "identitystore.h", + "keycredmgr.h", + "lmaccess.h", + "lsalookup.h", + "mmcobj.h", + "mscat.h", + "mssip.h", + "namedpipeapi.h", + "ncrypt.h", + "ncryptprotect.h", + "npapi.h", + "processthreadsapi.h", + "sas.h", + "scesvc.h", + "sddl.h", + "securityappcontainer.h", + "securitybaseapi.h", + "slpublic.h", + "subauth.h", + "tokenbinding.h", + "tpmvscmgr.h", + "wincred.h", + "wincrypt.h", + "winnetwk.h", + "winreg.h", + "winsafer.h", + "winscard.h", + "winsvc.h", + "wintrust.h", + "winwlx.h", + "xenroll.h", + + "# Windows sockets", + "af_irda.h", + "in6addr.h", + "mstcpip.h", + "winsock2.h", + "nsemail.h", + "nspapi.h", + "socketapi.h", + "# Nothing includes this; is it necessary?", + "#sporder.h", + "transportsettingcommon.h", + "ws2atm.h", + "ws2spi.h", + "MSWSock.h", + "ws2tcpip.h", + "wsipv6ok.h", + "WSNwLink.h", + "wsrm.h", + "mswsockdef.h", + + "# Remote Procedure Call (RPC)", + "midles.h", + "midlbase.h", + "rpc.h", + "rpcndr.h", + "rpcasync.h", + "rpcdcep.h", + "rpcnsi.h", + "rpcproxy.h", + "rpcssl.h", + + "# COM", + "accctrl.h", + "callobj.h", + "combaseapi.h", + "comcat.h", + "ctxtcall.h", + "dmerror.h", + "docobj.h", + "eventsys.h", + "initguid.h", + "guiddef.h", + "iaccess.h", + "hstring.h", + "IMessageDispatcher.h", + "MessageDispatcherApi.h", + "objbase.h", + "objidlbase.h", + "objidl.h", + "ocidl.h", + "ole.h", + "ole2.h", + "oledlg.h", + "oleidl.h", + "roapi.h", + "rpcdce.h", + "servprov.h", + "shobjidl.h", + "txlogpub.h", + "unknwnbase.h", + "unknwn.h", + "urlmon.h", + "vbinterf.h", + "winddi.h", + "winerror.h", + "wtypesbase.h", + + "# COM+", + "comadmin.h", + "mtxdm.h", + + "# More", + "inspectable.h", + + "# Windows Internet", + "proofofpossessioncookieinfo.h", + "wininet.h", + "winineti.h", + + "# Windows HTTP Services", + "winhttp.h", + + "# Compression", + "compressapi.h", + + "# TraceLogging", + "#traceloggingactivity.h", + "#traceloggingprovider.h", + + "# Windows Error Reporting", + "errorrep.h", + "werapi.h", + + "# Windows and MEssages", + "olectl.h", + "windef.h", + "windowsx.h", + + "# Shell", + "appmgmt.h", + "appnotify.h", + "cpl.h", + "credentialprovider.h", + "dimm.h", + "imagetranscode.h", + "inputpanelconfiguration.h", + "intsafe.h", + "intshcut.h", + "mobsync.h", + "objectarray.h", + "pathcch.h", + "profinfo.h", + "propkeydef.h", + "scrnsave.h", + "shappmgr.h", + "shdeprecated.h", + "shidfact.h", + "shimgdata.h", + "shlwapi.h", + "shtypes.h", + "storageprovider.h", + "syncmgr.h", + "thumbcache.h", + "thumbnailstreamcache.h", + "tlogstg.h", + "UserEnv.h", + + "# Windows Controls", + "commctrl.h", + "commoncontrols.h", + "dpa_dsa.h", + "prsht.h", + "richedit.h", + "richole.h", + "shlobj_core.h", + "shlobj.h", + "#textserv.h", // C++ + "tom.h", + "uxtheme.h", + + "# Menus and other resources", + "resourceindexer.h", + "strsafe.h", + "verrsrc.h", + "winver.h", + + "# Windows Accessibility Features", + "oleacc.h", + "uiautomationcore.h", + "uiautomationclient.h", + "uiautomationcoreapi.h", + + "# Internationalization", + "datetimeapi.h", + "elscore.h", + "gb18030.h", + "imepad.h", + "imm.h", + "immdev.h", + "msime.h", + "msimeapi.h", + "muiload.h", + "spellcheck.h", + "spellcheckprovider.h", + "stringapiset.h", + "usp10.h", + "winnls.h", + + "# HTTP Server API", + "#http.h", // included by something else + + "# IP Helper", + "#icmpapi.h", // Something wrong with IP_ADDR + "ifdef.h", + "inaddr.h", + "ip2string.h", + "ipexport.h", + "iphlpapi.h", + "iprtrmib.h", + "iptypes.h", + "nldef.h", + "tcpestats.h", + + "# Network Management", + "atacct.h", + "lmalert.h", + "lmapibuf.h", + "lmat.h", + "lmaudit.h", + "lmconfig.h", + "lmerrlog.h", + "lmjoin.h", + "lmmsg.h", + "lmremutl.h", + "lmserver.h", + "lmsvc.h", + "lmuse.h", + "lmwksta.h" + }; + + String includePaths[] = { + headerFilePath + "/VC/VS22/Community/VC/Tools/MSVC/14.29.30133/include", + headerFilePath + "/VC/SDK/10/Include/10.0.22000.0/shared", + headerFilePath + "/VC/SDK/10/Include/10.0.22000.0/um", + headerFilePath + "/VC/SDK/10/Include/10.0.22000.0/km", + headerFilePath + "/VC/VS22/10.0.19041.0/shared", + headerFilePath + "/VC/VS22/10.0.19041.0/um", + headerFilePath + "/VC/VS22/10.0.19041.0/ucrt", + headerFilePath + "/VC/VS22/10.0.19041.0/winrt", + }; + + String args[] = { + "-D_MSC_VER=1924", + "-D_INTEGRAL_MAX_BITS=64", + "-DWINVER=0x0a00", + "-D_WIN32_WINNT=0x0a00", + "-D_AMD64_", + "-D_M_AMD64", + "-D_M_X64", + "-D_WIN64", + "-D_WIN32", + "-D_USE_ATTRIBUTES_FOR_SAL", + "-D_CRTBLD", + "-D_OPENMP_NOFORCE_MANIFEST", + "-DLPSKBINFO=LPARAM", + "-DCONST=const", + "-D_CRT_SECURE_NO_WARNINGS", + "-D_CRT_NONSTDC_NO_DEPRECATE", + "-D_CRT_NONSTDC_NO_WARNINGS", + "-D_CRT_OBSOLETE_NO_DEPRECATE", + "-D_ALLOW_KEYWORD_MACROS", + "-D_ASSERT_OK", + "-DSTRSAFE_NO_DEPRECATE", + "-D__possibly_notnullterminated", + "-Dtype_info=\"void *\"", + "-D_ThrowInfo=ThrowInfo", + "-D__unaligned=", + "-v0", + "-D__inner_checkReturn=", + "-DDECLSPEC_DEPRECATED_DDK", + "-DWINAPI_PARTITION_APP=1", + "-DWINAPI_PARTITION_SYSTEM=1", + "-DWINAPI_PARTITION_GAMES=1", + "-DSECURITY_WIN32", + "-DSIZE_T=size_t", + }; + + parseHeaderFilesToGDT(outputDirectory, "windows_vs22_64_new", "x86:LE:64:default", + "windows", filenames, includePaths, args); + } public void parseGDT_WinVS22_WDK() throws Exception { - - String filenames[] = { - "# Core necessary files", - "# Windows Drivers WDK", - "initguid.h", - "ntddk.h", - "ntintsafe.h", - "wdf.h", + String filenames[] = { + "# Core necessary files", + + "# Windows Drivers WDK", + "initguid.h", + "ntddk.h", + "ntintsafe.h", + "wdf.h", }; String includePaths[] = { - headerFilePath+"/VC/VS22/Community/VC/Tools/MSVC/14.29.30133/include", - headerFilePath+"/VC/SDK/10/Include/10.0.22000.0/shared", - headerFilePath+"/VC/SDK/10/Include/10.0.22000.0/um", - headerFilePath+"/VC/SDK/10/Include/10.0.22000.0/km", - headerFilePath+"/VC/SDK/10/Include/wdf/umdf/2.33", - headerFilePath+"/VC/VS22/10.0.19041.0/shared", - headerFilePath+"/VC/VS22/10.0.19041.0/um", - headerFilePath+"/VC/VS22/10.0.19041.0/ucrt", - headerFilePath+"/VC/VS22/10.0.19041.0/winrt", + headerFilePath + "/VC/VS22/Community/VC/Tools/MSVC/14.29.30133/include", + headerFilePath + "/VC/SDK/10/Include/10.0.22000.0/shared", + headerFilePath + "/VC/SDK/10/Include/10.0.22000.0/um", + headerFilePath + "/VC/SDK/10/Include/10.0.22000.0/km", + headerFilePath + "/VC/SDK/10/Include/wdf/umdf/2.33", + headerFilePath + "/VC/VS22/10.0.19041.0/shared", + headerFilePath + "/VC/VS22/10.0.19041.0/um", + headerFilePath + "/VC/VS22/10.0.19041.0/ucrt", + headerFilePath + "/VC/VS22/10.0.19041.0/winrt", }; - + String args[] = { - "-D_MSC_VER=1924", - "-D_INTEGRAL_MAX_BITS=64", - "-DWINVER=0x0a00", - "-D_WIN32_WINNT=0x0a00", - "-D_AMD64_", - "-D_M_AMD64", - "-D_M_X64", - "-D_WIN64", - "-D_WIN32", - "-D_USE_ATTRIBUTES_FOR_SAL", - "-D_CRTBLD", - "-D_OPENMP_NOFORCE_MANIFEST", - "-DLPSKBINFO=LPARAM", - "-DCONST=const", - "-D_CRT_SECURE_NO_WARNINGS", - "-D_CRT_NONSTDC_NO_DEPRECATE", - "-D_CRT_NONSTDC_NO_WARNINGS", - "-D_CRT_OBSOLETE_NO_DEPRECATE", - "-D_ALLOW_KEYWORD_MACROS", - "-D_ASSERT_OK", - "-DSTRSAFE_NO_DEPRECATE", - "-D__possibly_notnullterminated", - "-Dtype_info=\"void *\"", - "-D_ThrowInfo=ThrowInfo", - "-D__unaligned=", - "-v0", - "-D__inner_checkReturn=", - "-DDECLSPEC_DEPRECATED_DDK", - "-DWINAPI_PARTITION_APP=1", - "-DWINAPI_PARTITION_SYSTEM=1", - "-DWINAPI_PARTITION_GAMES=1", - "-DSECURITY_WIN32", - "-DSIZE_T=size_t", + "-D_MSC_VER=1924", + "-D_INTEGRAL_MAX_BITS=64", + "-DWINVER=0x0a00", + "-D_WIN32_WINNT=0x0a00", + "-D_AMD64_", + "-D_M_AMD64", + "-D_M_X64", + "-D_WIN64", + "-D_WIN32", + "-D_USE_ATTRIBUTES_FOR_SAL", + "-D_CRTBLD", + "-D_OPENMP_NOFORCE_MANIFEST", + "-DLPSKBINFO=LPARAM", + "-DCONST=const", + "-D_CRT_SECURE_NO_WARNINGS", + "-D_CRT_NONSTDC_NO_DEPRECATE", + "-D_CRT_NONSTDC_NO_WARNINGS", + "-D_CRT_OBSOLETE_NO_DEPRECATE", + "-D_ALLOW_KEYWORD_MACROS", + "-D_ASSERT_OK", + "-DSTRSAFE_NO_DEPRECATE", + "-D__possibly_notnullterminated", + "-Dtype_info=\"void *\"", + "-D_ThrowInfo=ThrowInfo", + "-D__unaligned=", + "-v0", + "-D__inner_checkReturn=", + "-DDECLSPEC_DEPRECATED_DDK", + "-DWINAPI_PARTITION_APP=1", + "-DWINAPI_PARTITION_SYSTEM=1", + "-DWINAPI_PARTITION_GAMES=1", + "-DSECURITY_WIN32", + "-DSIZE_T=size_t", }; - - File file = new File(outputDirectory+"/windows_vs22_64_new.gdt"); - DataTypeManager vsDTMgr = FileDataTypeManager.openFileArchive(file, false); + + File file = new File(outputDirectory + "/windows_vs22_64_new.gdt"); + + FileDataTypeArchive archive = + DataTypeArchiveFactory.openReadOnly(file, this, TaskMonitor.DUMMY); + + DataTypeManager vsDTMgr = archive.getDataTypeManager(); DataTypeManager openTypes[] = { vsDTMgr }; - - parseHeaderFilesToGDT(openTypes, outputDirectory, "windows_vs22_wdk_64_new", "x86:LE:64:default", "windows", filenames, includePaths, args); + + parseHeaderFilesToGDT(openTypes, outputDirectory, "windows_vs22_wdk_64_new", + "x86:LE:64:default", "windows", filenames, includePaths, args); + archive.release(this); } } diff --git a/Ghidra/Features/Base/ghidra_scripts/CreateExampleGDTArchiveScript.java b/Ghidra/Features/Base/ghidra_scripts/CreateExampleGDTArchiveScript.java index 09ad80ee16..1ea5ea1050 100644 --- a/Ghidra/Features/Base/ghidra_scripts/CreateExampleGDTArchiveScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/CreateExampleGDTArchiveScript.java @@ -33,44 +33,47 @@ import ghidra.app.script.GhidraScript; import ghidra.app.util.cparser.C.CParserUtils; import ghidra.app.util.cparser.C.ParseException; import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.dtarchive.FileDataTypeArchive; public class CreateExampleGDTArchiveScript extends GhidraScript { private File outputDirectory; - + // location of header files base directory private static String headerFilePath = "/data/HeaderFiles"; @Override protected void run() throws Exception { outputDirectory = askDirectory("Select Directory for GDT files", "Select GDT Output Dir"); - + parseGDT_DirectX(); - + parseGDT_WinVS22(); } - - private void parseHeaderFilesToGDT(File outputDir, String gdtName, String languageID, String compiler, - String[] filenames, String includePaths[], String[] args) + + private void parseHeaderFilesToGDT(File outputDir, String gdtName, String languageID, + String compiler, String[] filenames, String includePaths[], String[] args) throws ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { DataTypeManager openTypes[] = null; - - parseHeaderFilesToGDT(openTypes, outputDir, gdtName, languageID, compiler, filenames, includePaths, args); + + parseHeaderFilesToGDT(openTypes, outputDir, gdtName, languageID, compiler, filenames, + includePaths, args); } - private void parseHeaderFilesToGDT(DataTypeManager openTypes[], File outputDir, String gdtName, String languageID, String compiler, - String[] filenames, String[] includePaths, String[] args) + private void parseHeaderFilesToGDT(DataTypeManager openTypes[], File outputDir, String gdtName, + String languageID, String compiler, String[] filenames, String[] includePaths, + String[] args) throws ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { - - String dataTypeFile = outputDir + File.separator + gdtName + ".gdt"; - - File f = getArchiveFile(dataTypeFile); - - FileDataTypeManager dtMgr = CParserUtils.parseHeaderFiles(openTypes, filenames, - includePaths, args, f.getAbsolutePath(), languageID, compiler, monitor); - dtMgr.close(); + String dataTypeFile = outputDir + File.separator + gdtName + ".gdt"; + + File f = getArchiveFile(dataTypeFile); + + FileDataTypeArchive archive = CParserUtils.parseHeaderFiles(filenames, + includePaths, args, f.getAbsolutePath(), languageID, compiler, openTypes, this, + monitor); + + archive.release(this); } /** @@ -92,404 +95,405 @@ public class CreateExampleGDTArchiveScript extends GhidraScript { } return f; } - - + public void parseGDT_DirectX() throws Exception { - + String filenames[] = { - "SDKDDKVer.h", - "Windows.h", - "winnt.h", - - "windowsx.h", - "ddraw.h", - "d3d.h", - "dinput.h", - "mmreg.h", - "dsound.h", + "SDKDDKVer.h", + "Windows.h", + "winnt.h", + + "windowsx.h", + "ddraw.h", + "d3d.h", + "dinput.h", + "mmreg.h", + "dsound.h", }; - + String includeFiles[] = { - headerFilePath+"/VC/VS22/Community/VC/Tools/MSVC/14.29.30133/include", - headerFilePath+"/VC/VS22/10.0.19041.0/shared", - headerFilePath+"/VC/VS22/10.0.19041.0/ucrt", - headerFilePath+"/VC/VS22/10.0.19041.0/um", + headerFilePath + "/VC/VS22/Community/VC/Tools/MSVC/14.29.30133/include", + headerFilePath + "/VC/VS22/10.0.19041.0/shared", + headerFilePath + "/VC/VS22/10.0.19041.0/ucrt", + headerFilePath + "/VC/VS22/10.0.19041.0/um", }; - + String args[] = { - "-D_AMD64_", - "-D_M_AMD64", - "-D_M_X64", - "-D_WIN64", - "-D_WIN32", - "-v0", + "-D_AMD64_", + "-D_M_AMD64", + "-D_M_X64", + "-D_WIN64", + "-D_WIN32", + "-v0", }; - - parseHeaderFilesToGDT(outputDirectory, "directX64", "x86:LE:64:default", "windows", filenames, includeFiles, args); + + parseHeaderFilesToGDT(outputDirectory, "directX64", "x86:LE:64:default", "windows", + filenames, includeFiles, args); } - + public void parseGDT_WinVS22() throws Exception { String filenames[] = { - "# Core necessary files", - "winapifamily.h", - "winpackagefamily.h", - "sdkddkver.h", - "sal.h", - "no_sal2.h", - "corecrt.h", - "wtypes.h", - "winnt.h", - "winternl.h", - "#ntdef.h", - - "# Common headers ", - "dos.h", - "errno.h", - "malloc.h", - "signal.h", - "stdalign.h", - "stddef.h", - "stdio.h", - "stdlib.h", - "assert.h", - "crtdbg.h", - "ctype.h", - "conio.h", - "direct.h", - "fcntl.h", - "float.h", - "fpieee.h", - "inttypes.h", - "io.h", - "locale.h", - "complex.h", - "math.h", - "mbctype.h", - "mbstring.h", - "memory.h", - "minmax.h", - "new.h", - "process.h", - "search.h", - "share.h", - "winbase.h", - "winuser.h", - "Windows.h", - - "# Security and identity (https://docs.microsoft.com/en-us/windows/win32/api/_security/)", - "accctrl.h", - "aclapi.h", - "aclui.h", - "adtgen.h", - "authz.h", - "azroles.h", - "bcrypt.h", - "casetup.h", - "ccgplugins.h", - "celib.h", - "ntlsa.h", - "sspi.h", - "ntsecapi.h", - "ntsecpkg.h", - "schannel.h", - "certadm.h", - "certbcli.h", - "certcli.h", - "certenroll.h", - "certexit.h", - "certif.h", - "certmod.h", - "certpol.h", - "certpoleng.h", - "certsrv.h", - "certview.h", - "credssp.h", - "cryptdlg.h", - "cryptuiapi.h", - "cryptxml.h", - "diagnosticdataquery.h", - "diagnosticdataquerytypes.h", - "dpapi.h", - "dssec.h", - "iads.h", - "identitycommon.h", - "identityprovider.h", - "identitystore.h", - "keycredmgr.h", - "lmaccess.h", - "lsalookup.h", - "mmcobj.h", - "mscat.h", - "mssip.h", - "namedpipeapi.h", - "ncrypt.h", - "ncryptprotect.h", - "npapi.h", - "processthreadsapi.h", - "sas.h", - "scesvc.h", - "sddl.h", - "securityappcontainer.h", - "securitybaseapi.h", - "slpublic.h", - "subauth.h", - "tokenbinding.h", - "tpmvscmgr.h", - "wincred.h", - "wincrypt.h", - "winnetwk.h", - "winreg.h", - "winsafer.h", - "winscard.h", - "winsvc.h", - "wintrust.h", - "winwlx.h", - "xenroll.h", - - "# Windows sockets", - "af_irda.h", - "in6addr.h", - "mstcpip.h", - "winsock2.h", - "nsemail.h", - "nspapi.h", - "socketapi.h", - "# Nothing includes this; is it necessary?", - "#sporder.h", - "transportsettingcommon.h", - "ws2atm.h", - "ws2spi.h", - "mswsock.h", - "ws2tcpip.h", - "wsipv6ok.h", - "wsnwlink.h", - "wsrm.h", - "mswsockdef.h", - - "# Remote Procedure Call (RPC)", - "midles.h", - "midlbase.h", - "rpc.h", - "rpcndr.h", - "rpcasync.h", - "rpcdcep.h", - "rpcnsi.h", - "rpcproxy.h", - "rpcssl.h", - - "# COM", - "accctrl.h", - "callobj.h", - "combaseapi.h", - "comcat.h", - "ctxtcall.h", - "dmerror.h", - "docobj.h", - "eventsys.h", - "guiddef.h", - "iaccess.h", - "hstring.h", - "imessagedispatcher.h", - "messagedispatherapi.h", - "objbase.h", - "objidlbase.h", - "objidl.h", - "ocidl.h", - "ole.h", - "ole2.h", - "oledlg.h", - "oleidl.h", - "roapi.h", - "rpcdce.h", - "servprov.h", - "shobjidl.h", - "txlogpub.h", - "unknwnbase.h", - "unknwn.h", - "urlmon.h", - "vbinterf.h", - "winddi.h", - "winerror.h", - "wtypesbase.h", - - "# COM+", - "comadmin.h", - "mtxdm.h", - - "# More", - "inspectable.h", - - "# Windows Internet", - "proofofpossessioncookieinfo.h", - "wininet.h", - "winineti.h", - - "# Windows HTTP Services", - "winhttp.h", - - "# Compression", - "compressapi.h", - - "# TraceLogging", - "#traceloggingactivity.h", - "#traceloggingprovider.h", - - "# Windows Error Reporting", - "errorrep.h", - "werapi.h", - - "# Windows and MEssages", - "olectl.h", - "windef.h", - "windowsx.h", - - "# Shell", - "appmgmt.h", - "appnotify.h", - "cpl.h", - "credentialprovider.h", - "dimm.h", - "imagetranscode.h", - "inputpanelconfiguration.h", - "intsafe.h", - "intshcut.h", - "mobsync.h", - "objectarray.h", - "pathcch.h", - "profinfo.h", - "propkeydef.h", - "scrnsave.h", - "shappmgr.h", - "shdeprecated.h", - "shidfact.h", - "shimgdata.h", - "shlwapi.h", - "shtypes.h", - "storageprovider.h", - "syncmgr.h", - "thumbcache.h", - "thumbnailstreamcache.h", - "tlogstg.h", - "userenv.h", - - "# Windows Controls", - "commctrl.h", - "commoncontrols.h", - "dpa_dsa.h", - "prsht.h", - "richedit.h", - "richole.h", - "shlobj_core.h", - "shlobj.h", - "#textserv.h", // C++ - "tom.h", - "uxtheme.h", - - "# Menus and other resources", - "resourceindexer.h", - "strsafe.h", - "verrsrc.h", - "winver.h", - - "# Windows Accessibility Features", - "oleacc.h", - "uiautomationcore.h", - "uiautomationclient.h", - "uiautomationcoreapi.h", - - "# Internationalization", - "datetimeapi.h", - "elscore.h", - "gb18030.h", - "imepad.h", - "imm.h", - "immdev.h", - "msime.h", - "msimeapi.h", - "muiload.h", - "spellcheck.h", - "spellcheckprovider.h", - "stringapiset.h", - "usp10.h", - "winnls.h", - - "# HTTP Server API", - "#http.h", // included by something else - - "# IP Helper", - "ifdef.h", - "inaddr.h", - "ip2string.h", - "ipexport.h", - "iphlpapi.h", - "icmpapi.h", // Must be included after iphlpapi.h - "iprtrmib.h", - "iptypes.h", - "netioapi.h", - "nldef.h", - "tcpestats.h", - "ws2ipdef.h", - - "# Network Management", - "atacct.h", - "lmalert.h", - "lmapibuf.h", - "lmat.h", - "lmaudit.h", - "lmconfig.h", - "lmerrlog.h", - "lmjoin.h", - "lmmsg.h", - "lmremutl.h", - "lmserver.h", - "lmsvc.h", - "lmuse.h", - "lmwksta.h" + "# Core necessary files", + "winapifamily.h", + "winpackagefamily.h", + "sdkddkver.h", + "sal.h", + "no_sal2.h", + "corecrt.h", + "wtypes.h", + "winnt.h", + "winternl.h", + "#ntdef.h", + + "# Common headers ", + "dos.h", + "errno.h", + "malloc.h", + "signal.h", + "stdalign.h", + "stddef.h", + "stdio.h", + "stdlib.h", + "assert.h", + "crtdbg.h", + "ctype.h", + "conio.h", + "direct.h", + "fcntl.h", + "float.h", + "fpieee.h", + "inttypes.h", + "io.h", + "locale.h", + "complex.h", + "math.h", + "mbctype.h", + "mbstring.h", + "memory.h", + "minmax.h", + "new.h", + "process.h", + "search.h", + "share.h", + "winbase.h", + "winuser.h", + "Windows.h", + + "# Security and identity (https://docs.microsoft.com/en-us/windows/win32/api/_security/)", + "accctrl.h", + "aclapi.h", + "aclui.h", + "adtgen.h", + "authz.h", + "azroles.h", + "bcrypt.h", + "casetup.h", + "ccgplugins.h", + "celib.h", + "ntlsa.h", + "sspi.h", + "ntsecapi.h", + "ntsecpkg.h", + "schannel.h", + "certadm.h", + "certbcli.h", + "certcli.h", + "certenroll.h", + "certexit.h", + "certif.h", + "certmod.h", + "certpol.h", + "certpoleng.h", + "certsrv.h", + "certview.h", + "credssp.h", + "cryptdlg.h", + "cryptuiapi.h", + "cryptxml.h", + "diagnosticdataquery.h", + "diagnosticdataquerytypes.h", + "dpapi.h", + "dssec.h", + "iads.h", + "identitycommon.h", + "identityprovider.h", + "identitystore.h", + "keycredmgr.h", + "lmaccess.h", + "lsalookup.h", + "mmcobj.h", + "mscat.h", + "mssip.h", + "namedpipeapi.h", + "ncrypt.h", + "ncryptprotect.h", + "npapi.h", + "processthreadsapi.h", + "sas.h", + "scesvc.h", + "sddl.h", + "securityappcontainer.h", + "securitybaseapi.h", + "slpublic.h", + "subauth.h", + "tokenbinding.h", + "tpmvscmgr.h", + "wincred.h", + "wincrypt.h", + "winnetwk.h", + "winreg.h", + "winsafer.h", + "winscard.h", + "winsvc.h", + "wintrust.h", + "winwlx.h", + "xenroll.h", + + "# Windows sockets", + "af_irda.h", + "in6addr.h", + "mstcpip.h", + "winsock2.h", + "nsemail.h", + "nspapi.h", + "socketapi.h", + "# Nothing includes this; is it necessary?", + "#sporder.h", + "transportsettingcommon.h", + "ws2atm.h", + "ws2spi.h", + "mswsock.h", + "ws2tcpip.h", + "wsipv6ok.h", + "wsnwlink.h", + "wsrm.h", + "mswsockdef.h", + + "# Remote Procedure Call (RPC)", + "midles.h", + "midlbase.h", + "rpc.h", + "rpcndr.h", + "rpcasync.h", + "rpcdcep.h", + "rpcnsi.h", + "rpcproxy.h", + "rpcssl.h", + + "# COM", + "accctrl.h", + "callobj.h", + "combaseapi.h", + "comcat.h", + "ctxtcall.h", + "dmerror.h", + "docobj.h", + "eventsys.h", + "guiddef.h", + "iaccess.h", + "hstring.h", + "imessagedispatcher.h", + "messagedispatherapi.h", + "objbase.h", + "objidlbase.h", + "objidl.h", + "ocidl.h", + "ole.h", + "ole2.h", + "oledlg.h", + "oleidl.h", + "roapi.h", + "rpcdce.h", + "servprov.h", + "shobjidl.h", + "txlogpub.h", + "unknwnbase.h", + "unknwn.h", + "urlmon.h", + "vbinterf.h", + "winddi.h", + "winerror.h", + "wtypesbase.h", + + "# COM+", + "comadmin.h", + "mtxdm.h", + + "# More", + "inspectable.h", + + "# Windows Internet", + "proofofpossessioncookieinfo.h", + "wininet.h", + "winineti.h", + + "# Windows HTTP Services", + "winhttp.h", + + "# Compression", + "compressapi.h", + + "# TraceLogging", + "#traceloggingactivity.h", + "#traceloggingprovider.h", + + "# Windows Error Reporting", + "errorrep.h", + "werapi.h", + + "# Windows and MEssages", + "olectl.h", + "windef.h", + "windowsx.h", + + "# Shell", + "appmgmt.h", + "appnotify.h", + "cpl.h", + "credentialprovider.h", + "dimm.h", + "imagetranscode.h", + "inputpanelconfiguration.h", + "intsafe.h", + "intshcut.h", + "mobsync.h", + "objectarray.h", + "pathcch.h", + "profinfo.h", + "propkeydef.h", + "scrnsave.h", + "shappmgr.h", + "shdeprecated.h", + "shidfact.h", + "shimgdata.h", + "shlwapi.h", + "shtypes.h", + "storageprovider.h", + "syncmgr.h", + "thumbcache.h", + "thumbnailstreamcache.h", + "tlogstg.h", + "userenv.h", + + "# Windows Controls", + "commctrl.h", + "commoncontrols.h", + "dpa_dsa.h", + "prsht.h", + "richedit.h", + "richole.h", + "shlobj_core.h", + "shlobj.h", + "#textserv.h", // C++ + "tom.h", + "uxtheme.h", + + "# Menus and other resources", + "resourceindexer.h", + "strsafe.h", + "verrsrc.h", + "winver.h", + + "# Windows Accessibility Features", + "oleacc.h", + "uiautomationcore.h", + "uiautomationclient.h", + "uiautomationcoreapi.h", + + "# Internationalization", + "datetimeapi.h", + "elscore.h", + "gb18030.h", + "imepad.h", + "imm.h", + "immdev.h", + "msime.h", + "msimeapi.h", + "muiload.h", + "spellcheck.h", + "spellcheckprovider.h", + "stringapiset.h", + "usp10.h", + "winnls.h", + + "# HTTP Server API", + "#http.h", // included by something else + + "# IP Helper", + "ifdef.h", + "inaddr.h", + "ip2string.h", + "ipexport.h", + "iphlpapi.h", + "icmpapi.h", // Must be included after iphlpapi.h + "iprtrmib.h", + "iptypes.h", + "netioapi.h", + "nldef.h", + "tcpestats.h", + "ws2ipdef.h", + + "# Network Management", + "atacct.h", + "lmalert.h", + "lmapibuf.h", + "lmat.h", + "lmaudit.h", + "lmconfig.h", + "lmerrlog.h", + "lmjoin.h", + "lmmsg.h", + "lmremutl.h", + "lmserver.h", + "lmsvc.h", + "lmuse.h", + "lmwksta.h" }; - + String includeFiles[] = { - headerFilePath+"/VC/VS22/Community/VC/Tools/MSVC/14.29.30133/include", - headerFilePath+"/VC/VS22/10.0.19041.0/shared", - headerFilePath+"/VC/VS22/10.0.19041.0/ucrt", - headerFilePath+"/VC/VS22/10.0.19041.0/um", - headerFilePath+"/VC/VS22/10.0.19041.0/winrt", + headerFilePath + "/VC/VS22/Community/VC/Tools/MSVC/14.29.30133/include", + headerFilePath + "/VC/VS22/10.0.19041.0/shared", + headerFilePath + "/VC/VS22/10.0.19041.0/ucrt", + headerFilePath + "/VC/VS22/10.0.19041.0/um", + headerFilePath + "/VC/VS22/10.0.19041.0/winrt", }; - + String args[] = { - "-D_MSC_VER=1924", - "-D_INTEGRAL_MAX_BITS=64", - "-DWINVER=0x0a00", - "-D_WIN32_WINNT=0x0a00", - "-D_AMD64_", - "-D_M_AMD64", - "-D_M_X64", - "-D_WIN64", - "-D_WIN32", - "-D_USE_ATTRIBUTES_FOR_SAL", - "-D_CRTBLD", - "-D_OPENMP_NOFORCE_MANIFEST", - "-DLPSKBINFO=LPARAM", - "-DCONST=const", - "-D_CRT_SECURE_NO_WARNINGS", - "-D_CRT_NONSTDC_NO_DEPRECATE", - "-D_CRT_NONSTDC_NO_WARNINGS", - "-D_CRT_OBSOLETE_NO_DEPRECATE", - "-D_ALLOW_KEYWORD_MACROS", - "-D_ASSERT_OK", - "-DSTRSAFE_NO_DEPRECATE", - "-D__possibly_notnullterminated", - "-Dtype_info=\"void *\"", - "-D_ThrowInfo=ThrowInfo", - "-D__unaligned=", - "-v0", - "-D__inner_checkReturn=", - "-DWINAPI_PARTITION_APP=1", - "-DWINAPI_PARTITION_SYSTEM=1", - "-DWINAPI_PARTITION_GAMES=1", - "-DSECURITY_WIN32", + "-D_MSC_VER=1924", + "-D_INTEGRAL_MAX_BITS=64", + "-DWINVER=0x0a00", + "-D_WIN32_WINNT=0x0a00", + "-D_AMD64_", + "-D_M_AMD64", + "-D_M_X64", + "-D_WIN64", + "-D_WIN32", + "-D_USE_ATTRIBUTES_FOR_SAL", + "-D_CRTBLD", + "-D_OPENMP_NOFORCE_MANIFEST", + "-DLPSKBINFO=LPARAM", + "-DCONST=const", + "-D_CRT_SECURE_NO_WARNINGS", + "-D_CRT_NONSTDC_NO_DEPRECATE", + "-D_CRT_NONSTDC_NO_WARNINGS", + "-D_CRT_OBSOLETE_NO_DEPRECATE", + "-D_ALLOW_KEYWORD_MACROS", + "-D_ASSERT_OK", + "-DSTRSAFE_NO_DEPRECATE", + "-D__possibly_notnullterminated", + "-Dtype_info=\"void *\"", + "-D_ThrowInfo=ThrowInfo", + "-D__unaligned=", + "-v0", + "-D__inner_checkReturn=", + "-DWINAPI_PARTITION_APP=1", + "-DWINAPI_PARTITION_SYSTEM=1", + "-DWINAPI_PARTITION_GAMES=1", + "-DSECURITY_WIN32", }; - - parseHeaderFilesToGDT(outputDirectory, "windows_vs22_64_new", "x86:LE:64:default", "windows", filenames, includeFiles, args); + + parseHeaderFilesToGDT(outputDirectory, "windows_vs22_64_new", "x86:LE:64:default", + "windows", filenames, includeFiles, args); } } diff --git a/Ghidra/Features/Base/ghidra_scripts/CreateUEFIGDTArchivesScript.java b/Ghidra/Features/Base/ghidra_scripts/CreateUEFIGDTArchivesScript.java index 07db2763f0..e895d9232e 100644 --- a/Ghidra/Features/Base/ghidra_scripts/CreateUEFIGDTArchivesScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/CreateUEFIGDTArchivesScript.java @@ -35,12 +35,14 @@ import ghidra.app.script.GhidraScript; import ghidra.app.util.cparser.C.CParserUtils; import ghidra.app.util.cparser.C.ParseException; import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.dtarchive.FileDataTypeArchive; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; public class CreateUEFIGDTArchivesScript extends GhidraScript { private File outputDirectory; - + private static String headerFilePath = "/data/HeaderFiles/git/edk2"; @Override @@ -49,41 +51,49 @@ public class CreateUEFIGDTArchivesScript extends GhidraScript { parseUEFIHeaders("X64", "x86:LE:64:default", "windows"); parseUEFIHeaders("Ia32", "x86:LE:32:default", "windows"); - + parseUEFIHeaders("AArch64", "AARCH64:LE:64:v8A", "windows"); parseUEFIHeaders("Arm", "ARM:LE:32:v8", "default"); - + parseUEFIHeaders("RiscV64", "RISCV:LE:64:RV64G", "gcc"); parseUEFIHeaders("LoongArch64", "Loongarch:LE:64:lp64d", "default"); } - - private void parseHeaderFilesToGDT(File outputDir, String gdtName, String languageID, String compiler, - String[] filenames, String includePaths[], String[] args) + + private void parseHeaderFilesToGDT(File outputDir, String gdtName, String languageID, + String compiler, String[] filenames, String includePaths[], String[] args) throws ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { DataTypeManager openTypes[] = null; - - parseHeaderFilesToGDT(openTypes, outputDir, gdtName, languageID, compiler, filenames, includePaths, args); + + parseHeaderFilesToGDT(openTypes, outputDir, gdtName, languageID, compiler, filenames, + includePaths, args); } - private void parseHeaderFilesToGDT(DataTypeManager openTypes[], File outputDir, String gdtName, String languageID, String compiler, - String[] filenames, String[] includePaths, String[] args) + private void parseHeaderFilesToGDT(DataTypeManager openTypes[], File outputDir, String gdtName, + String languageID, String compiler, String[] filenames, String[] includePaths, + String[] args) throws ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { - - String dataTypeFile = outputDir + File.separator + gdtName + ".gdt"; - - File f = getArchiveFile(dataTypeFile); - - FileDataTypeManager dtMgr = CParserUtils.parseHeaderFiles(openTypes, filenames, - includePaths, args, f.getAbsolutePath(), languageID, compiler, monitor); - dtMgr.save(); - dtMgr.close(); + String dataTypeFile = outputDir + File.separator + gdtName + ".gdt"; + + File f = getArchiveFile(dataTypeFile); + + FileDataTypeArchive archive = CParserUtils.parseHeaderFiles(filenames, + includePaths, args, f.getAbsolutePath(), languageID, compiler, openTypes, this, + monitor); + + try { + archive.save(null, TaskMonitor.DUMMY); + } + catch (CancelledException e) { + // can't happen since a DUMMY monitor was used + } + archive.release(this); } /** * Turn string into a file, delete old archive if it exists * - * @param dataTypeFile + * @param dataTypeFile the file name of the stored archive * * @return file */ @@ -99,9 +109,9 @@ public class CreateUEFIGDTArchivesScript extends GhidraScript { } return f; } - + public void parseUEFIHeaders(String name, String languageID, String compiler) throws Exception { - + String filenames[] = { "ProcessorBind.h", "Uefi/UefiBaseType.h", @@ -116,22 +126,23 @@ public class CreateUEFIGDTArchivesScript extends GhidraScript { "Library/StandaloneMmDriverEntryPoint.h", "Library/UefiApplicationEntryPoint.h", "Library/UefiDriverEntryPoint.h", - headerFilePath+"/MdePkg/Include/Pi/", - headerFilePath+"/MdePkg/Include/Ppi/", - headerFilePath+"/MdePkg/Include/Protocol/", - headerFilePath+"/MdePkg/Include/IndustryStandard/", + headerFilePath + "/MdePkg/Include/Pi/", + headerFilePath + "/MdePkg/Include/Ppi/", + headerFilePath + "/MdePkg/Include/Protocol/", + headerFilePath + "/MdePkg/Include/IndustryStandard/", }; - + String includePaths[] = { - headerFilePath+"/MdePkg/Include/"+name, - headerFilePath+"/MdePkg/Include", + headerFilePath + "/MdePkg/Include/" + name, + headerFilePath + "/MdePkg/Include", }; - + String args[] = { "-DSTATIC_ASSERT=static_assert" }; - - parseHeaderFilesToGDT(outputDirectory, "uefi_"+name, languageID, compiler, filenames, includePaths, args); + + parseHeaderFilesToGDT(outputDirectory, "uefi_" + name, languageID, compiler, filenames, + includePaths, args); } } diff --git a/Ghidra/Features/Base/ghidra_scripts/FixupCompositeDataTypesScript.java b/Ghidra/Features/Base/ghidra_scripts/FixupCompositeDataTypesScript.java index a69867fcc7..9168ea6d43 100644 --- a/Ghidra/Features/Base/ghidra_scripts/FixupCompositeDataTypesScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/FixupCompositeDataTypesScript.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -29,10 +29,11 @@ import ghidra.app.script.GhidraScript; import ghidra.app.services.DataTypeManagerService; import ghidra.framework.model.DomainFile; import ghidra.framework.plugintool.PluginTool; -import ghidra.program.database.ProjectDataTypeManager; -import ghidra.program.database.data.*; +import ghidra.program.database.data.DataTypeManagerDB; +import ghidra.program.database.data.ProgramDataTypeManager; import ghidra.program.model.data.BuiltInDataTypeManager; import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; import ghidra.program.model.listing.Program; public class FixupCompositeDataTypesScript extends GhidraScript { @@ -50,33 +51,30 @@ public class FixupCompositeDataTypesScript extends GhidraScript { popup("This script requires the DataTypeManagerService"); return; } - + ArrayList dtms = new ArrayList<>(); for (DataTypeManager dtm : service.getDataTypeManagers()) { if (dtm instanceof BuiltInDataTypeManager) { continue; } - if (dtm instanceof ProgramDataTypeManager) { - dtms.add(0, new DTMWrapper((ProgramDataTypeManager) dtm)); - } - else if (dtm instanceof DataTypeManagerDB) { - dtms.add(new DTMWrapper((DataTypeManagerDB) dtm)); + if (dtm instanceof DataTypeManagerDB dtmDB) { + dtms.add(new DTMWrapper(dtmDB)); } } DataTypeManagerDB dtm = askChoice("Fixup All Composites", "Select Data Type Manager: ", dtms, dtms.get(0)).dtm; - if (dtm instanceof ProgramDataTypeManager) { - Program program = ((ProgramDataTypeManager) dtm).getProgram(); + if (dtm instanceof ProgramDataTypeManager programDtm) { + Program program = programDtm.getProgram(); if (!program.hasExclusiveAccess()) { popup("Shared program must have an exclusive checkout."); return; } } - else if (dtm instanceof ProjectDataTypeManager) { - DomainFile df = ((ProjectDataTypeManager) dtm).getDomainFile(); + else if (dtm.getDataStore() instanceof ProjectDataTypeArchive projectArchive) { + DomainFile df = projectArchive.getDomainFile(); if (df.isVersioned() && !df.isCheckedOutExclusive()) { popup("Shared project archive must have an exclusive checkout."); return; @@ -87,7 +85,6 @@ public class FixupCompositeDataTypesScript extends GhidraScript { popup("Selected archive must be open for update."); return; } - dtm.fixupComposites(monitor); } diff --git a/Ghidra/Features/Base/ghidra_scripts/SynchronizeGDTCategoryPaths.java b/Ghidra/Features/Base/ghidra_scripts/SynchronizeGDTCategoryPaths.java index f1cd181d43..ba47a9e9e5 100644 --- a/Ghidra/Features/Base/ghidra_scripts/SynchronizeGDTCategoryPaths.java +++ b/Ghidra/Features/Base/ghidra_scripts/SynchronizeGDTCategoryPaths.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,48 +22,49 @@ import java.io.File; import ghidra.app.script.GhidraScript; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.data.Category; -import ghidra.program.model.data.FileDataTypeManager; -import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; +import ghidra.program.model.dtarchive.ArchiveWarning; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.util.InvalidNameException; import ghidra.util.exception.DuplicateNameException; +import ghidra.util.task.TaskMonitor; public class SynchronizeGDTCategoryPaths extends GhidraScript { @Override protected void run() throws Exception { - File firstFile = askFile("Select First GDT File", "Select 1st"); - try (FileDataTypeManager firstArchive = - FileDataTypeManager.openFileArchive(firstFile, false)) { - if (hasWarning(firstArchive, firstFile)) { + FileDataTypeArchive archive1 = null; + FileDataTypeArchive archive2 = null; + try { + File file1 = askFile("Select First GDT File", "Select 1st"); + archive1 = DataTypeArchiveFactory.openReadOnly(file1, this, TaskMonitor.DUMMY); + if (hasWarning(archive1, file1)) { return; } + File file2 = askFile("Select Second GDT File", "Select 2nd"); + archive2 = DataTypeArchiveFactory.openReadOnly(file2, this, TaskMonitor.DUMMY); + if (hasWarning(archive2, file2)) { + return; + } + Category firstCategory = archive1.getDataTypeManager().getRootCategory(); + Category secondCategory = archive2.getDataTypeManager().getRootCategory(); - File secondFile = askFile("Select Second GDT File", "Select 2nd"); - try (FileDataTypeManager secondArchive = - FileDataTypeManager.openFileArchive(secondFile, true)) { - if (hasWarning(secondArchive, secondFile)) { - return; - } - - int transactionID = - secondArchive.startTransaction("Synchronize Category Path Names"); - try { - Category firstCategory = firstArchive.getRootCategory(); - Category secondCategory = secondArchive.getRootCategory(); - synchronizeCategory(firstCategory, secondCategory); - } - finally { - secondArchive.endTransaction(transactionID, true); - secondArchive.save(); - secondArchive.close(); - } + archive2.withTransaction("Synchronized Category Path Names", + () -> synchronizeCategory(firstCategory, secondCategory)); + } + finally { + if (archive1 != null) { + archive1.release(this); + } + if (archive2 != null) { + archive2.release(this); } } } - private boolean hasWarning(FileDataTypeManager archive, File file) { + private boolean hasWarning(FileDataTypeArchive archive, File file) { ArchiveWarning warning = archive.getWarning(); if (warning == ArchiveWarning.NONE) { return false; diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm index 4286217eaf..cfc8cd30e1 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm @@ -94,7 +94,7 @@ Data Type Manager window. When a data type archive is open, it is displayed as a node in the Data Type Manager tree. Archives can be opened by the user or automatically when a program is opened which - references that archive. Data type archives can be open for + references that archive. Data type archives can be open for modification or as read-only. Within the Data Type Manager window there are actions for opening, closing, renaming, and making archives modifiable.

@@ -243,17 +243,6 @@ the program.

-

Update Source Archive Names

- -
-

When new releases of Ghidra are installed, old datatype archives may be dropped. In some - cases these old archives may be replaced with a differently named archive. When this - happens, this popup action can be used on a client archive to update the old source archive - references. This popup action is only available when such an archive name change has - occurred. Any programs or other client archives which reference the old archive will have - this popup action available to update the source archive name reference.

-
-

Committing Changes To Source Archive

@@ -349,7 +338,7 @@ files are organized into directories in a filesystem. Archives are useful for sharing data types with other users, or making your data types available for use in other projects. Normally, file archives are opened in a read-only mode, but can optionally be opened for editing. Project archives are normally opened for + "#Open_Archive_For_Editing">opened for editing. Project archives are normally opened for editing, since they support sharing and version control and therefore allow more than one user to modify them at a time. Only one user at a time can have a file archive opened for editing.

@@ -467,7 +456,7 @@ Archive action. The archive will be removed from the tree.

-

Opening a File Data Type Archive for Editing

+

Opening a File Data Type Archive for Editing

When an archive is first opened, it is not editable. In order to make any changes to @@ -479,13 +468,13 @@

-

Closing a File Data Type +

Closing a File Data Type Archive for Editing

-

When an archive that is open for editing no longer +

When an archive that is open for editing no longer needs to be edited, then it should be put back to a read-only mode so that other users can then modify it. Select the data type archive to close for editing, right-click on it and select the Close for Editing action. If the archive has unsaved @@ -502,7 +491,6 @@ changes will be saved and the name will be updated to not show a '*'.

-

Undo Unsaved Archive Change

@@ -523,7 +510,7 @@ stack of unsaved changes. The next change which may be reverted is described by the archive's Undo Change popup menu item. If this action is used and a change is reverted it may be re-applied by using the Redo Change action. When the data type archive is - saved or closed for editing the undo/redo stack is + saved or closed for editing the undo/redo stack is cleared.

@@ -538,7 +525,7 @@ The next reverted change which may be re-applied is described by the archive's Redo Change popup menu item. If this action is used and a change is re-applied it may again be reverted by using the Undo Change action. When the data type archive is - saved or closed for editing the undo/redo stack is + saved or closed for editing the undo/redo stack is cleared.

@@ -550,7 +537,7 @@

Deleting an archive will not only remove the archive from the tree, but will permanently remove it from the filesystem. To delete an archive, right-click on it and select the Delete Archive action. An archive file must be open for editing - before this action will appear (see Opening a Data Type + before this action will appear (see Opening a Data Type Archive for Editing).

@@ -615,7 +602,7 @@

The source archive must be editable in order to commit. File archives must be open for editing and project + "data_type_manager_description.htm#Open_Archive_For_Editing">open for editing and project archives that are under version control must be checked-out.

Any data type dependencies for a comitted diff --git a/Ghidra/Features/Base/src/main/java/foundation/FoundationInitializer.java b/Ghidra/Features/Base/src/main/java/foundation/FoundationInitializer.java index 2634a143fd..87fb473ce8 100644 --- a/Ghidra/Features/Base/src/main/java/foundation/FoundationInitializer.java +++ b/Ghidra/Features/Base/src/main/java/foundation/FoundationInitializer.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,6 +22,7 @@ import ghidra.framework.PluggableServiceRegistry; import ghidra.framework.data.ToolStateFactory; import ghidra.framework.main.datatree.GhidraDataFlavorHandlerService; import ghidra.program.database.*; +import ghidra.program.database.data.DataTypeArchiveMergeManagerFactory; public class FoundationInitializer implements ModuleInitializer { @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/DataTypeArchiveMergeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/DataTypeArchiveMergeManager.java index d751da4055..59598b330b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/DataTypeArchiveMergeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/DataTypeArchiveMergeManager.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,21 +16,20 @@ package ghidra.app.merge; import ghidra.app.merge.datatypes.DataTypeMergeManager; -import ghidra.framework.model.DomainObject; import ghidra.framework.plugintool.ModalPluginTool; -import ghidra.program.model.data.DataTypeManagerDomainObject; -import ghidra.program.model.listing.DataTypeArchive; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; import ghidra.program.model.listing.DataTypeArchiveChangeSet; /** * Top level object that manages each step of the merge/resolve conflicts * process. */ -public class DataTypeArchiveMergeManager extends MergeManager { +public class DataTypeArchiveMergeManager + extends MergeManager { - public DataTypeArchiveMergeManager(DataTypeManagerDomainObject resultDtArchive, - DataTypeManagerDomainObject myDtArchive, DataTypeManagerDomainObject originalDtArchive, - DataTypeManagerDomainObject latestDtArchive, DataTypeArchiveChangeSet latestChangeSet, + public DataTypeArchiveMergeManager(ProjectDataTypeArchive resultDtArchive, + ProjectDataTypeArchive myDtArchive, ProjectDataTypeArchive originalDtArchive, + ProjectDataTypeArchive latestDtArchive, DataTypeArchiveChangeSet latestChangeSet, DataTypeArchiveChangeSet myChangeSet) { super(resultDtArchive, myDtArchive, originalDtArchive, latestDtArchive, latestChangeSet, myChangeSet); @@ -43,11 +42,8 @@ public class DataTypeArchiveMergeManager extends MergeManager { mergeResolvers = new MergeResolver[1]; mergeResolvers[idx++] = - new DataTypeMergeManager(this, (DataTypeManagerDomainObject) resultDomainObject, - (DataTypeManagerDomainObject) myDomainObject, - (DataTypeManagerDomainObject) originalDomainObject, - (DataTypeManagerDomainObject) latestDomainObject, - (DataTypeArchiveChangeSet) latestChangeSet, (DataTypeArchiveChangeSet) myChangeSet); + new DataTypeMergeManager(this, resultDomainObject, myDomainObject, originalDomainObject, + latestDomainObject, latestChangeSet, myChangeSet); } /** @@ -58,16 +54,16 @@ public class DataTypeArchiveMergeManager extends MergeManager { * @return the indicated program version or null if a valid version isn't specified. * @see MergeConstants */ - public DataTypeArchive getDataTypeArchive(int version) { + public ProjectDataTypeArchive getDataTypeArchive(int version) { switch (version) { case MergeConstants.LATEST: - return (DataTypeArchive) latestDomainObject; + return latestDomainObject; case MergeConstants.MY: - return (DataTypeArchive) myDomainObject; + return myDomainObject; case MergeConstants.ORIGINAL: - return (DataTypeArchive) originalDomainObject; + return originalDomainObject; case MergeConstants.RESULT: - return (DataTypeArchive) resultDomainObject; + return resultDomainObject; default: return null; } @@ -75,9 +71,10 @@ public class DataTypeArchiveMergeManager extends MergeManager { @Override protected MergeManagerPlugin createMergeManagerPlugin(ModalPluginTool mergePluginTool, - MergeManager multiUserMergeManager, DomainObject modifiableDomainObject) { + MergeManager multiUserMergeManager, + ProjectDataTypeArchive modifiableDomainObject) { return new DataTypeArchiveMergeManagerPlugin(mergeTool, DataTypeArchiveMergeManager.this, - (DataTypeArchive) resultDomainObject); + resultDomainObject); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/DataTypeArchiveMergeManagerPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/DataTypeArchiveMergeManagerPlugin.java index 99d2698dc4..a96f9712dc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/DataTypeArchiveMergeManagerPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/DataTypeArchiveMergeManagerPlugin.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,7 +21,7 @@ import ghidra.app.CorePluginPackage; import ghidra.app.plugin.PluginCategoryNames; import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.util.PluginStatus; -import ghidra.program.model.listing.DataTypeArchive; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; /** * Plugin that provides a merge component provider for data type archives. @@ -45,7 +45,7 @@ public class DataTypeArchiveMergeManagerPlugin extends MergeManagerPlugin { */ public DataTypeArchiveMergeManagerPlugin(PluginTool tool, DataTypeArchiveMergeManager mergeManager, - DataTypeArchive dataTypeArchive) { + ProjectDataTypeArchive dataTypeArchive) { super(tool, mergeManager, dataTypeArchive); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/MergeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/MergeManager.java index 97a57b7e52..198935542a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/MergeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/MergeManager.java @@ -38,17 +38,20 @@ import help.HelpService; /** * Top level object that manages each step of the merge/resolve conflicts * process. + * @param domain object implementation class + * @param domain object change set implementation class */ -public abstract class MergeManager implements DomainObjectMergeManager { +public abstract class MergeManager + implements DomainObjectMergeManager { protected MergeResolver[] mergeResolvers; - protected DomainObject resultDomainObject; // where changes will be merged to - protected DomainObject myDomainObject; // source of changes to be applied - protected DomainObject originalDomainObject; // original version that was checked out - protected DomainObject latestDomainObject; // latest version of the program - protected DomainObjectChangeSet latestChangeSet; - protected DomainObjectChangeSet myChangeSet; + protected T resultDomainObject; // where changes will be merged to + protected T myDomainObject; // source of changes to be applied + protected T originalDomainObject; // original version that was checked out + protected T latestDomainObject; // latest version of the program + protected C latestChangeSet; + protected C myChangeSet; protected MergeManagerPlugin mergePlugin; // protected ListingMergePanelPlugin listingPlugin; @@ -69,9 +72,9 @@ public abstract class MergeManager implements DomainObjectMergeManager { // protected boolean isShowingListingMergePanel = false; - public MergeManager(DomainObject resultDomainObject, DomainObject myDomainObject, - DomainObject originalDomainObject, DomainObject latestDomainObject, - DomainObjectChangeSet latestChangeSet, DomainObjectChangeSet myChangeSet) { + public MergeManager(T resultDomainObject, T myDomainObject, + T originalDomainObject, T latestDomainObject, + C latestChangeSet, C myChangeSet) { this.resultDomainObject = resultDomainObject; this.myDomainObject = myDomainObject; this.originalDomainObject = originalDomainObject; @@ -95,7 +98,7 @@ public abstract class MergeManager implements DomainObjectMergeManager { * @return the indicated program version or null if a valid version isn't specified. * @see MergeConstants */ - public DomainObject getDomainObject(int version) { + public T getDomainObject(int version) { switch (version) { case MergeConstants.LATEST: return latestDomainObject; @@ -208,7 +211,7 @@ public abstract class MergeManager implements DomainObjectMergeManager { } protected abstract MergeManagerPlugin createMergeManagerPlugin(ModalPluginTool mergePluginTool, - MergeManager multiUserMergeManager, DomainObject modifiableDomainObject); + MergeManager multiUserMergeManager, T modifiableDomainObject); protected abstract void initializeMerge(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/ProgramMultiUserMergeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/ProgramMultiUserMergeManager.java index b228515145..7183d84d2a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/ProgramMultiUserMergeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/ProgramMultiUserMergeManager.java @@ -33,7 +33,6 @@ import ghidra.app.nav.*; import ghidra.app.plugin.core.navigation.GoToAddressLabelPlugin; import ghidra.app.util.ListingHighlightProvider; import ghidra.app.util.viewer.util.FieldNavigator; -import ghidra.framework.model.DomainObject; import ghidra.framework.plugintool.ModalPluginTool; import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.util.PluginException; @@ -50,7 +49,7 @@ import help.HelpService; /** * Top level object that manages each step of the merge/resolve conflicts process. */ -public class ProgramMultiUserMergeManager extends MergeManager { +public class ProgramMultiUserMergeManager extends MergeManager { private ListingMergePanelPlugin listingPlugin; private GoToAddressLabelPlugin goToPlugin; @@ -73,10 +72,10 @@ public class ProgramMultiUserMergeManager extends MergeManager { @Override protected void createMergeResolvers() { - Program resultProgram = (Program) resultDomainObject; - Program myProgram = (Program) myDomainObject; - Program originalProgram = (Program) originalDomainObject; - Program latestProgram = (Program) latestDomainObject; + Program resultProgram = resultDomainObject; + Program myProgram = myDomainObject; + Program originalProgram = originalDomainObject; + Program latestProgram = latestDomainObject; // create the merge resolvers int idx = 0; mergeResolvers = new MergeResolver[8]; @@ -85,28 +84,28 @@ public class ProgramMultiUserMergeManager extends MergeManager { mergeResolvers[idx++] = new ProgramTreeMergeManager(this, resultProgram, myProgram, originalProgram, - latestProgram, (ProgramChangeSet) latestChangeSet, (ProgramChangeSet) myChangeSet); + latestProgram, latestChangeSet, myChangeSet); mergeResolvers[idx++] = new DataTypeMergeManager(this, resultProgram, myProgram, originalProgram, latestProgram, - (ProgramChangeSet) latestChangeSet, (ProgramChangeSet) myChangeSet); + latestChangeSet, myChangeSet); mergeResolvers[idx++] = new ProgramContextMergeManager(this, resultProgram, originalProgram, latestProgram, - myProgram, (ProgramChangeSet) latestChangeSet, (ProgramChangeSet) myChangeSet); + myProgram, latestChangeSet, myChangeSet); mergeResolvers[idx++] = new FunctionTagMerger(this, resultProgram, originalProgram, latestProgram, myProgram, - (ProgramChangeSet) latestChangeSet, (ProgramChangeSet) myChangeSet); + latestChangeSet, myChangeSet); ListingMergeManager listingMergeManager = new ListingMergeManager(this, resultProgram, originalProgram, latestProgram, myProgram, - (ProgramChangeSet) latestChangeSet, (ProgramChangeSet) myChangeSet); + latestChangeSet, myChangeSet); mergeResolvers[idx++] = listingMergeManager; mergeResolvers[idx++] = new ExternalProgramMerger(this, resultProgram, originalProgram, latestProgram, - myProgram, (ProgramChangeSet) latestChangeSet, (ProgramChangeSet) myChangeSet); + myProgram, latestChangeSet, myChangeSet); mergeResolvers[idx++] = new PropertyListMergeManager(this, resultProgram, myProgram, originalProgram, latestProgram); @@ -122,13 +121,13 @@ public class ProgramMultiUserMergeManager extends MergeManager { public Program getProgram(int version) { switch (version) { case MergeConstants.LATEST: - return (Program) resultDomainObject; + return resultDomainObject; case MergeConstants.MY: - return (Program) myDomainObject; + return myDomainObject; case MergeConstants.ORIGINAL: - return (Program) originalDomainObject; + return originalDomainObject; case MergeConstants.RESULT: - return (Program) latestDomainObject; + return latestDomainObject; default: return null; } @@ -136,15 +135,16 @@ public class ProgramMultiUserMergeManager extends MergeManager { @Override protected MergeManagerPlugin createMergeManagerPlugin(ModalPluginTool mergePluginTool, - MergeManager multiUserMergeManager, DomainObject modifiableDomainObject) { + MergeManager multiUserMergeManager, + Program modifiableDomainObject) { return new ProgramMergeManagerPlugin(mergeTool, ProgramMultiUserMergeManager.this, - (Program) resultDomainObject); + resultDomainObject); } @Override protected void initializeMerge() { - mergePanel = new ListingMergePanel(mergeTool, (Program) originalDomainObject, - (Program) resultDomainObject, (Program) myDomainObject, (Program) latestDomainObject, + mergePanel = new ListingMergePanel(mergeTool, originalDomainObject, + resultDomainObject, myDomainObject, latestDomainObject, showListingPanels); mergePanel.removeDomainObjectListener(); navigatable = new MergeNavigatable(mergePanel); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java index 05d6ca7113..4c217f7f8f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java @@ -26,11 +26,14 @@ import org.apache.commons.lang3.Strings; import ghidra.app.merge.*; import ghidra.app.util.HelpTopics; import ghidra.framework.data.DomainObjectMergeManager; +import ghidra.framework.model.DomainObject; import ghidra.program.database.data.DataTypeManagerDB; import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; import ghidra.program.model.listing.DataTypeChangeSet; +import ghidra.program.model.listing.Program; import ghidra.util.*; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; @@ -54,7 +57,7 @@ public class DataTypeMergeManager implements MergeResolver { static final int OPTION_ORIGINAL = 2; // Original private DomainObjectMergeManager mergeManager; - private DataTypeManagerDomainObject[] domainObjects = new DataTypeManagerDomainObject[4]; + private DomainObject[] domainObjects = new DomainObject[4]; private DataTypeManager[] dtms = new DataTypeManager[4]; private TaskMonitor currentMonitor; private int originalConflictOption; @@ -93,30 +96,55 @@ public class DataTypeMergeManager implements MergeResolver { /** * Manager for merging the data types using the four programs. * @param mergeManager overall merge manager for domain object - * @param resultDomainObject the program to be updated with the result of the merge. + * @param resultArchive the program to be updated with the result of the merge. * This is the program that will actually get checked in. - * @param myDomainObject the program requesting to be checked in. - * @param originalDomainObject the program that was checked out. - * @param latestDomainObject the latest checked-in version of the program. + * @param myArchive the program requesting to be checked in. + * @param originalArchive the program that was checked out. + * @param latestArchive the latest checked-in version of the program. * @param latestChanges the address set of changes between original and latest versioned program. * @param myChanges the address set of changes between original and my modified program. */ public DataTypeMergeManager(DomainObjectMergeManager mergeManager, - DataTypeManagerDomainObject resultDomainObject, - DataTypeManagerDomainObject myDomainObject, - DataTypeManagerDomainObject originalDomainObject, - DataTypeManagerDomainObject latestDomainObject, DataTypeChangeSet latestChanges, + ProjectDataTypeArchive resultArchive, + ProjectDataTypeArchive myArchive, + ProjectDataTypeArchive originalArchive, + ProjectDataTypeArchive latestArchive, + DataTypeChangeSet latestChanges, DataTypeChangeSet myChanges) { - this.mergeManager = mergeManager; - domainObjects[RESULT] = resultDomainObject; - domainObjects[ORIGINAL] = originalDomainObject; - domainObjects[LATEST] = latestDomainObject; - domainObjects[MY] = myDomainObject; - dtms[RESULT] = resultDomainObject.getDataTypeManager(); - dtms[ORIGINAL] = originalDomainObject.getDataTypeManager(); - dtms[LATEST] = latestDomainObject.getDataTypeManager(); - dtms[MY] = myDomainObject.getDataTypeManager(); + this.mergeManager = mergeManager; + domainObjects[RESULT] = resultArchive; + domainObjects[ORIGINAL] = originalArchive; + domainObjects[LATEST] = latestArchive; + domainObjects[MY] = myArchive; + dtms[RESULT] = resultArchive.getDataTypeManager(); + dtms[ORIGINAL] = originalArchive.getDataTypeManager(); + dtms[LATEST] = latestArchive.getDataTypeManager(); + dtms[MY] = myArchive.getDataTypeManager(); + init(latestChanges, myChanges); + } + + public DataTypeMergeManager(DomainObjectMergeManager mergeManager, + Program resultProgram, + Program myProgram, + Program originalProgram, + Program latestProgram, + DataTypeChangeSet latestChanges, + DataTypeChangeSet myChanges) { + + this.mergeManager = mergeManager; + domainObjects[RESULT] = resultProgram; + domainObjects[ORIGINAL] = originalProgram; + domainObjects[LATEST] = latestProgram; + domainObjects[MY] = myProgram; + dtms[RESULT] = resultProgram.getDataTypeManager(); + dtms[ORIGINAL] = originalProgram.getDataTypeManager(); + dtms[LATEST] = latestProgram.getDataTypeManager(); + dtms[MY] = myProgram.getDataTypeManager(); + init(latestChanges, myChanges); + } + + private void init(DataTypeChangeSet latestChanges, DataTypeChangeSet myChanges) { totalConflictCount = 0; setupSourceArchiveChanges(latestChanges, myChanges); setupDataTypeChanges(latestChanges, myChanges); @@ -1315,7 +1343,7 @@ public class DataTypeMergeManager implements MergeResolver { try { resultComp = destStruct.addBitField(resultCompDt, bfDt.getDeclaredBitSize(), - sourceComp.getFieldName(), comment); + sourceComp.getFieldName(), comment); } catch (InvalidDataTypeException e) { displayError(destStruct, e); @@ -1338,14 +1366,14 @@ public class DataTypeMergeManager implements MergeResolver { // If I have compDt, it should now be from result DTM. resultComp = destStruct.add(resultCompDt, length, sourceComp.getFieldName(), - comment); + comment); } catch (IllegalArgumentException e) { comment = buildDataTypeFailureComment(sourceCompDt, e.getMessage(), comment); resultComp = destStruct.add(BadDataType.dataType, sourceComp.getLength(), - sourceComp.getFieldName(), comment); + sourceComp.getFieldName(), comment); if (e.getCause() instanceof DataTypeDependencyException) { badIdDtMsgs.put(dtId, e.getMessage()); } @@ -1364,8 +1392,8 @@ public class DataTypeMergeManager implements MergeResolver { try { resultComp = destStruct .insertBitFieldAt(sourceComp.getOffset(), sourceComp.getLength(), - bfDt.getBitOffset(), resultCompDt, bfDt.getDeclaredBitSize(), - sourceComp.getFieldName(), comment); + bfDt.getBitOffset(), resultCompDt, bfDt.getDeclaredBitSize(), + sourceComp.getFieldName(), comment); } catch (InvalidDataTypeException e) { displayError(destStruct, e); @@ -1376,7 +1404,7 @@ public class DataTypeMergeManager implements MergeResolver { try { resultComp = destStruct.addBitField(primitiveBaseDt, bfDt.getDeclaredBitSize(), - sourceComp.getFieldName(), comment); + sourceComp.getFieldName(), comment); } catch (InvalidDataTypeException exc) { throw new RuntimeException(exc); // unexpected @@ -1429,7 +1457,7 @@ public class DataTypeMergeManager implements MergeResolver { comment = buildDataTypeFailureComment(sourceCompDt, badMsg, comment); resultComp = destStruct.insertAtOffset(sourceComp.getOffset(), BadDataType.dataType, - sourceComp.getLength(), sourceComp.getFieldName(), comment); + sourceComp.getLength(), sourceComp.getFieldName(), comment); } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ApplyDataArchiveAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ApplyDataArchiveAnalyzer.java index a4f5b4b601..35b60feb79 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ApplyDataArchiveAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ApplyDataArchiveAnalyzer.java @@ -33,9 +33,9 @@ import ghidra.framework.model.*; import ghidra.framework.options.OptionType; import ghidra.framework.options.Options; import ghidra.program.model.address.AddressSetView; -import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.FileDataTypeManager; -import ghidra.program.model.listing.DataTypeArchive; +import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.FileDataTypeArchive; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; import ghidra.program.model.listing.Program; import ghidra.program.model.symbol.SourceType; import ghidra.util.Msg; @@ -132,11 +132,11 @@ public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer { options.registerOption(OPTION_NAME_GDT_FILEPATH, OptionType.FILE_TYPE, null, null, OPTION_DESCRIPTION_GDT_FILEPATH, - () -> new FileChooserEditor(FileDataTypeManager.GDT_FILEFILTER)); + () -> new FileChooserEditor(FileDataTypeArchive.GDT_FILEFILTER)); options.registerOption(OPTION_NAME_PROJECT_PATH, OptionType.STRING_TYPE, null, null, OPTION_DESCRIPTION_PROJECT_PATH, () -> new ProjectPathChooserEditor("Choose Data Type Archive", - new DefaultDomainFileFilter(DataTypeArchive.class, false))); + new DefaultDomainFileFilter(ProjectDataTypeArchive.class, false))); } @Override @@ -293,7 +293,7 @@ public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer { log.appendMsg("Missing project archive: %s".formatted(filename)); return List.of(); } - if (!DataTypeArchive.class.isAssignableFrom(gdtDomainFile.getDomainObjectClass())) { + if (!ProjectDataTypeArchive.class.isAssignableFrom(gdtDomainFile.getDomainObjectClass())) { log.appendMsg("Bad project file type: %s".formatted(filename)); return List.of(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DefaultDataTypeManagerService.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DefaultDataTypeManagerService.java index e2cff4fa1f..9ec6982d2d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DefaultDataTypeManagerService.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DefaultDataTypeManagerService.java @@ -21,14 +21,14 @@ import java.util.Set; import javax.swing.tree.TreePath; import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive; -import ghidra.app.plugin.core.datamgr.archive.DefaultDataTypeArchiveService; +import ghidra.app.plugin.core.datamgr.archive.BasicDataTypeArchiveService; import ghidra.app.services.DataTypeManagerService; import ghidra.program.model.data.*; import ghidra.util.HelpLocation; import ghidra.util.task.TaskMonitor; // FIXME!! TESTING -public class DefaultDataTypeManagerService extends DefaultDataTypeArchiveService +public class DefaultDataTypeManagerService extends BasicDataTypeArchiveService implements DataTypeManagerService { // TODO: This implementation needs to be consolidated with the tool-based service in diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorDtArchiveDB.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorDtArchiveDB.java new file mode 100644 index 0000000000..c0035a9f4d --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorDtArchiveDB.java @@ -0,0 +1,109 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.compositeeditor; + +import java.io.IOException; + +import db.DBHandle; +import ghidra.framework.data.OpenMode; +import ghidra.program.database.dtarchive.DataTypeArchiveDB; +import ghidra.program.model.data.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; +import utility.function.Callback; + +/** + * DataTypeAchive used to edit composites (unions or structures) + * + * @param The specific type of composite + */ +public class CompositeEditorDtArchiveDB extends DataTypeArchiveDB { + private int transactionId; + + /** + * Special constructor for stack editor. We create a transaction that is always open for the + * life of this archive so that undo/redo is not available. + * @param originalDTM the data type manager this archive is shadowing. + * @throws IOException if an {@link IOException} occurs initializing the database. + */ + CompositeEditorDtArchiveDB(DataTypeManager originalDTM) throws IOException { + this(originalDTM, null, null, null); + + // This prevents undo/redo by always have a transaction open during its full lifecycle. + transactionId = startTransaction("Composite Edit"); + } + + public CompositeEditorDtArchiveDB(T originalComposite, Callback changeCallback, + Callback restoredCallback) throws IOException { + this(originalComposite.getDataTypeManager(), originalComposite, changeCallback, + restoredCallback); + } + + protected CompositeEditorDtArchiveDB(DataTypeManager originalDTM, T originalComposite, + Callback changeCallback, + Callback restoredCallback) throws IOException { + super(new DBHandle(), originalDTM.getName(), new Object()); + CompositeViewerDataTypeManager dtm = getDataTypeManager(); + withTransaction("Setup for Edit", () -> { + dtm.intialize(originalDTM, originalComposite, changeCallback, restoredCallback); + }); + clearUndo(); + } + + @SuppressWarnings("unchecked") + @Override + public CompositeViewerDataTypeManager getDataTypeManager() { + return (CompositeViewerDataTypeManager) super.getDataTypeManager(); + } + + @Override + protected CompositeViewerDataTypeManager createDataTypeManager(DBHandle handle, + OpenMode openMode, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { + + return new CompositeViewerDataTypeManager<>(handle, openMode, this, lock, monitor); + } + + @Override + public boolean isTemporary() { + return true; + } + + @Override + public String getDescription() { + return "Temporary Data Type Archive For Composite Editing"; + } + + @Override + protected boolean isArchitectureChangeAllowed() { + return false; + } + + @Override + protected void close() { + if (transactionId != 0) { + endTransaction(transactionId, changed); + } + super.close(); + } + + @Override + public ArchiveType getArchiveType() { + return ArchiveType.TEMPORARY; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java index d2c47818b7..d397b15691 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java @@ -100,9 +100,10 @@ abstract public class CompositeEditorModel extends Composit originalDataTypePath = originalComposite.getDataTypePath(); currentName = originalComposite.getName(); - // Use temporary standalone view datatype manager - viewDTM = new CompositeViewerDataTypeManager<>(viewDTM.getName(), - viewDTM.getResolvedViewComposite(), this::componentEdited, this::restoreEditor); + // Use temporary view datatype manager + T composite = viewDTM.getResolvedViewComposite(); + viewDTM = CompositeViewerDataTypeManager.createUndoableInstance(composite, this::componentEdited, + this::restoreEditor); viewComposite = viewDTM.getResolvedViewComposite(); @@ -225,10 +226,9 @@ abstract public class CompositeEditorModel extends Composit viewDTM = null; } - // Use temporary standalone view datatype manager - viewDTM = - new CompositeViewerDataTypeManager<>(originalComposite.getDataTypeManager().getName(), - originalComposite, this::componentEdited, this::restoreEditor); + // Use temporary stand-alone view datatype archive + viewDTM = CompositeViewerDataTypeManager.createUndoableInstance(originalComposite, + this::componentEdited, this::restoreEditor); viewComposite = viewDTM.getResolvedViewComposite(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java index a8575c90ea..d3c187f6d9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java @@ -21,13 +21,17 @@ import java.util.TreeSet; import javax.help.UnsupportedOperationException; +import db.DBHandle; import db.util.ErrorHandler; +import ghidra.framework.data.OpenMode; +import ghidra.framework.model.RuntimeIOException; import ghidra.program.database.DbObject; +import ghidra.program.database.data.ArchiveDataTypeManagerDB; import ghidra.program.model.data.*; import ghidra.program.model.lang.ProgramArchitecture; +import ghidra.util.Lock; import ghidra.util.Swing; -import ghidra.util.exception.AssertException; -import ghidra.util.exception.CancelledException; +import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; import utility.function.Callback; @@ -38,25 +42,24 @@ import utility.function.Callback; * the editor. * @param Specific {@link Composite} type being managed */ -public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager +public class CompositeViewerDataTypeManager extends ArchiveDataTypeManagerDB implements ErrorHandler { /** * The data type manager for original composite data type being edited. * This is where the edited datatype will be written back to. */ - private final DataTypeManager originalDTM; - private final T originalComposite; // may be null if not resolved into this DTM - private final T viewComposite; // may be null if not resolved into this DTM + private DataTypeManager originalDTM; + private T originalComposite; // may be null if not resolved into this DTM + private T viewComposite; // may be null if not resolved into this DTM // Database-backed datatype ID map, view to/from original DTM // This is needed to account for datatype use and ID alterations across undo/redo - private final IDMapDB dataTypeIDMap; + private IDMapDB dataTypeIDMap; // Editor transaction use only - undo/redo not supported if restoreCallback is null private Callback restoredCallback; private Callback changeCallback; - private int transactionId = 0; private boolean dataTypeChanged; // Modification count used to signal optional clearing of undo/redo stack at the end of a @@ -66,49 +69,51 @@ public class CompositeViewerDataTypeManager extends StandAl // datatype IDs to be checked as orphaned. // NOTE: Orphan removal can only be done when this DTM actively manages the viewComposite private TreeSet orphanIds = new TreeSet<>(); + private int transactionCount; - /** - * Creates a data type manager that the composite editor will use internally for managing - * dependencies without resolving the actual composite being edited. A single transaction - * will be started with this instantiation and held open until this instance is closed. - * Undo/redo and datatype pruning is not be supported. - * @param rootName the root name for this data type manager (usually the program name). - * @param originalDTM the original data type manager. - */ - public CompositeViewerDataTypeManager(String rootName, DataTypeManager originalDTM) { - this(rootName, originalDTM, null, null, null); - transactionId = startTransaction("Composite Edit"); + public static CompositeViewerDataTypeManager createNonUndoableInstance( + DataTypeManager originalDTM) { + try { + CompositeEditorDtArchiveDB archive = + new CompositeEditorDtArchiveDB(originalDTM); + + return archive.getDataTypeManager(); + } + catch (IOException e) { + throw new RuntimeIOException(e); + } } - /** - * Creates a data type manager that the structure editor will use internally for managing a - * structure being edited and its dependencies. - * @param rootName the root name for this data type manager (usually the program name). - * @param originalComposite the original composite data type that is being edited. - * @param changeCallback Callback will be invoked when any change is made to the view composite. - * @param restoredCallback Callback will be invoked following any undo/redo. - */ - public CompositeViewerDataTypeManager(String rootName, T originalComposite, - Callback changeCallback, Callback restoredCallback) { - this(rootName, originalComposite.getDataTypeManager(), originalComposite, changeCallback, - restoredCallback); + public static CompositeViewerDataTypeManager createUndoableInstance( + T originalComposite, Callback changeCallback, + Callback restoredCallback) { + try { + CompositeEditorDtArchiveDB archive = + new CompositeEditorDtArchiveDB(originalComposite, changeCallback, + restoredCallback); + return archive.getDataTypeManager(); + } + catch (IOException e) { + throw new RuntimeIOException(e); + } } - /** - * Constructor - * @param rootName the root name for this data type manager (usually the program name). - * @param originalDTM the original datatype manager - * @param originalComposite the original composite data type that is being edited. (may be null) - * @param changeCallback Callback will be invoked when any change is made to the view composite. - * @param restoredCallback Callback will be invoked following any undo/redo. - */ - private CompositeViewerDataTypeManager(String rootName, DataTypeManager originalDTM, - T originalComposite, Callback changeCallback, Callback restoredCallback) { - super(rootName, originalDTM.getDataOrganization()); - this.originalDTM = originalDTM; - this.originalComposite = originalComposite; - this.changeCallback = changeCallback; - this.restoredCallback = restoredCallback; + CompositeViewerDataTypeManager(DBHandle handle, + OpenMode openMode, ErrorHandler errHandler, Lock lock, TaskMonitor monitor) + throws CancelledException, VersionException, IOException { + super(handle, openMode, errHandler, lock, monitor); + } + + @Override + public CompositeEditorDtArchiveDB getDataStore() { + return (CompositeEditorDtArchiveDB) super.getDataStore(); + } + + void intialize(DataTypeManager dtm, T composite, Callback changeCb, Callback restoredCb) { + this.originalDTM = dtm; + this.originalComposite = composite; + this.changeCallback = changeCb; + this.restoredCallback = restoredCb; int txId = startTransaction("Setup for Edit"); try { @@ -120,6 +125,7 @@ public class CompositeViewerDataTypeManager extends StandAl endTransaction(txId, true); } clearUndo(); + } @SuppressWarnings("unchecked") @@ -150,10 +156,8 @@ public class CompositeViewerDataTypeManager extends StandAl return dbHandle.getModCount(); } - @Override - protected synchronized void clearUndo() { - // Exposes method for test use - super.clearUndo(); + synchronized void clearUndo() { + archive.clearUndo(); } @Override @@ -197,10 +201,7 @@ public class CompositeViewerDataTypeManager extends StandAl @Override public synchronized void close() { - if (transactionId != 0) { - super.endTransaction(transactionId, true); - } - super.close(); + getDataStore().close(); } /** @@ -360,10 +361,16 @@ public class CompositeViewerDataTypeManager extends StandAl } } + @Override + public synchronized int startTransaction(String description) { + transactionCount++; + return super.startTransaction(description); + } + @Override public synchronized boolean endTransaction(int transactionID, boolean commit) { - if (viewComposite != null && getTransactionCount() == 1) { + if (viewComposite != null && transactionCount == 1) { // Perform orphan removal only at the end of the outer-most transaction synchronized (orphanIds) { checkOrphansForRemoval(false); @@ -371,6 +378,7 @@ public class CompositeViewerDataTypeManager extends StandAl } boolean committed = super.endTransaction(transactionID, commit); + transactionCount--; if (!isTransactionActive() && flattenModCount != -1) { if (flattenModCount != dbHandle.getModCount()) { @@ -382,9 +390,6 @@ public class CompositeViewerDataTypeManager extends StandAl if (committed && dataTypeChanged && changeCallback != null) { Swing.runLater(() -> changeCallback.call()); - } - - if (getTransactionCount() == 0) { dataTypeChanged = false; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java index 50fda6c116..5fd61cfdde 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java @@ -17,15 +17,18 @@ package ghidra.app.plugin.core.cparser; import java.io.File; import java.io.IOException; +import java.util.Objects; import javax.help.UnsupportedOperationException; import javax.swing.SwingUtilities; import docking.widgets.dialogs.MultiLineMessageDialog; import ghidra.app.util.cparser.C.CParserUtils.CParseResults; -import ghidra.program.database.data.ProgramDataTypeManager; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.dtarchive.DataTypeStore; +import ghidra.program.model.dtarchive.FileDataTypeArchive; +import ghidra.program.model.listing.Program; import ghidra.util.Msg; import ghidra.util.exception.DuplicateFileException; import ghidra.util.task.Task; @@ -39,16 +42,16 @@ import ghidra.util.task.TaskMonitor; class CParserTask extends Task { private CParserPlugin plugin; - + private String[] filenames; private String[] includePaths; - + private String options; - + // Language and Compiler Spec IDs valid only for new dataFileName use private String languageId; private String compilerSpecId; - + // Either dataTypeManager or dataFileName must be set, but not both private final DataTypeManager dataTypeManager; // specified for an existing DataTypeManager private final File dataFile; // specified for a new file @@ -61,6 +64,7 @@ class CParserTask extends Task { */ CParserTask(CParserPlugin plugin, String dataFileName) { super("Parsing C Files", true, false, false); + Objects.requireNonNull(dataFileName, "Target datatype archive filename required"); dataTypeManager = null; this.plugin = plugin; this.dataFile = new File(dataFileName); @@ -77,6 +81,7 @@ class CParserTask extends Task { */ public CParserTask(CParserPlugin plugin, DataTypeManager dataTypeManager) { super("Parsing C Files", true, false, false); + Objects.requireNonNull(dataTypeManager, "Target datatype manager required"); dataFile = null; this.plugin = plugin; this.dataTypeManager = dataTypeManager; @@ -101,7 +106,7 @@ class CParserTask extends Task { this.languageId = languageId; return this; } - + /** * Set the compiler spec ID to be used. This ID must be defined for the specified language. * @@ -121,17 +126,17 @@ class CParserTask extends Task { this.compilerSpecId = compilerSpecId; return this; } - + public CParserTask setIncludePaths(String includePaths[]) { this.includePaths = includePaths.clone(); return this; } - + public CParserTask setFileNames(String names[]) { this.filenames = names.clone(); return this; } - + public CParserTask setOptions(String options) { this.options = options; return this; @@ -158,16 +163,16 @@ class CParserTask extends Task { return msg; } - private String getParseDestination(DataTypeManager dtMgr) { + private String getParseDestination(DataTypeStore archive) { String parseDest = ""; - if (dtMgr instanceof ProgramDataTypeManager) { - parseDest = "Program " + dtMgr.getName(); + if (archive instanceof Program) { + parseDest = "Program " + archive.getName(); } - else if (dtMgr instanceof FileDataTypeManager fileDtm) { - parseDest = "Archive File: " + fileDtm.getFilename(); + else if (archive instanceof FileDataTypeArchive fileArchive) { + parseDest = "Archive File: " + fileArchive.getPath(); } else { - parseDest = dtMgr.getName(); + parseDest = archive.getName(); } return parseDest; } @@ -175,7 +180,7 @@ class CParserTask extends Task { @Override public void run(TaskMonitor monitor) { - FileDataTypeManager fileDtMgr = null; + FileDataTypeArchive dataFileArchive = null; if (dataFile != null) { try { if ((languageId != null) != (compilerSpecId != null)) { @@ -184,8 +189,8 @@ class CParserTask extends Task { compilerSpecId); return; } - fileDtMgr = - FileDataTypeManager.createFileArchive(dataFile, languageId, compilerSpecId); + dataFileArchive = DataTypeArchiveFactory.createFileArchive(dataFile, languageId, + compilerSpecId, this); } catch (IOException e) { Msg.showError(this, plugin.getDialog().getComponent(), "Archive Failure", @@ -194,10 +199,12 @@ class CParserTask extends Task { } } - DataTypeManager dtMgr = fileDtMgr != null ? fileDtMgr : dataTypeManager; + DataTypeManager dtMgr = + dataFileArchive != null ? dataFileArchive.getDataTypeManager() : dataTypeManager; int initialDtCount = dtMgr.getDataTypeCount(true); + String archiveDetination = getParseDestination(dtMgr.getDataStore()); try { CParseResults results = plugin.parse(filenames, includePaths, options, dtMgr, monitor); @@ -205,10 +212,10 @@ class CParserTask extends Task { return; // cancelled } - if (fileDtMgr != null && dtMgr.getDataTypeCount(true) != 0) { + if (dataFileArchive != null && dtMgr.getDataTypeCount(true) != 0) { // If archive created - save to file try { - fileDtMgr.save(); + dataFileArchive.save(null, monitor); } catch (DuplicateFileException e) { Msg.showError(this, plugin.getDialog().getComponent(), @@ -228,7 +235,7 @@ class CParserTask extends Task { if (!results.successful()) { MultiLineMessageDialog.showModalMessageDialog( plugin.getDialog().getComponent(), "C-Parse Failed", - "Failed to parse header file(s) to " + getParseDestination(dtMgr), + "Failed to parse header file(s) to " + archiveDetination, plugin.getFormattedParseMessage(msg), MultiLineMessageDialog.INFORMATION_MESSAGE); } @@ -236,7 +243,7 @@ class CParserTask extends Task { MultiLineMessageDialog.showModalMessageDialog( plugin.getDialog().getComponent(), "C-Parse Completed", - "Successfully parsed header file(s) to " + getParseDestination(dtMgr), + "Successfully parsed header file(s) to " + archiveDetination, plugin.getFormattedParseMessage(msg), MultiLineMessageDialog.INFORMATION_MESSAGE); } @@ -247,7 +254,7 @@ class CParserTask extends Task { SwingUtilities.invokeLater(() -> { MultiLineMessageDialog.showMessageDialog(plugin.getDialog().getComponent(), "C-Parse Failed", - "Failed to parse header file(s) to " + getParseDestination(dtMgr), + "Failed to parse header file(s) to " + archiveDetination, plugin.getFormattedParseMessage(errMsg), MultiLineMessageDialog.ERROR_MESSAGE); }); @@ -257,7 +264,7 @@ class CParserTask extends Task { SwingUtilities.invokeLater(() -> { MultiLineMessageDialog.showMessageDialog(plugin.getDialog().getComponent(), "C-PreProcessor Parse Failed", - "Failed to parse header file(s) to " + getParseDestination(dtMgr), + "Failed to parse header file(s) to " + archiveDetination, plugin.getFormattedParseMessage(errMsg), MultiLineMessageDialog.ERROR_MESSAGE); }); @@ -270,15 +277,16 @@ class CParserTask extends Task { SwingUtilities.invokeLater(() -> { MultiLineMessageDialog.showMessageDialog(plugin.getDialog().getComponent(), "Error During C-Parse", - "Failed to parse header file(s) to " + getParseDestination(dtMgr), + "Failed to parse header file(s) to " + archiveDetination, plugin.getFormattedParseMessage(errMsg), MultiLineMessageDialog.ERROR_MESSAGE); }); } finally { - if (fileDtMgr != null) { - boolean deleteFile = fileDtMgr.getDataTypeCount(true) == 0; - fileDtMgr.close(); + if (dataFileArchive != null) { + boolean deleteFile = + dataFileArchive.getDataTypeManager().getDataTypeCount(true) == 0; + dataFileArchive.release(this); if (deleteFile) { dataFile.delete(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/ParseDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/ParseDialog.java index 21396ceece..5d08775154 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/ParseDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/ParseDialog.java @@ -46,7 +46,7 @@ import ghidra.framework.Application; import ghidra.framework.options.SaveState; import ghidra.framework.preferences.Preferences; import ghidra.framework.store.db.PackedDatabase; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.program.model.lang.CompilerSpecID; import ghidra.program.model.lang.LanguageID; import ghidra.util.HelpLocation; @@ -150,7 +150,7 @@ class ParseDialog extends ReusableDialogComponentProvider { void writeState(SaveState saveState) { // Get the current state if the dialog has been displayed if (!initialBuild) { - ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + ComboBoxItem item = comboBox.getSelectedItem(); currentProfileName = item.file.getName(); userDefined = item.isUserDefined; @@ -172,7 +172,7 @@ class ParseDialog extends ReusableDialogComponentProvider { if (initialBuild) { return; } - ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + ComboBoxItem item = comboBox.getSelectedItem(); if (item.isChanged) { processItemChanged(item); } @@ -255,7 +255,7 @@ class ParseDialog extends ReusableDialogComponentProvider { }); tableListener = e -> { - ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + ComboBoxItem item = comboBox.getSelectedItem(); item.isChanged = !initialBuild; notifyContextChanged(); }; @@ -270,7 +270,7 @@ class ParseDialog extends ReusableDialogComponentProvider { new ExtensionFileFilter(new String[] { "h" }, "C Header Files")); parsePathTableListener = e -> { - ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + ComboBoxItem item = comboBox.getSelectedItem(); item.isChanged = !initialBuild; notifyContextChanged(); pathPanel.getTable().repaint(); @@ -485,7 +485,7 @@ class ParseDialog extends ReusableDialogComponentProvider { } private void itemChanged() { - ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + ComboBoxItem item = comboBox.getSelectedItem(); if (item == null) { return; } @@ -497,12 +497,12 @@ class ParseDialog extends ReusableDialogComponentProvider { saveAction = new DockingAction("Save Profile", plugin.getName()) { @Override public void actionPerformed(ActionContext context) { - save((ComboBoxItem) comboBox.getSelectedItem()); + save(comboBox.getSelectedItem()); } @Override public boolean isEnabledForContext(ActionContext context) { - ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + ComboBoxItem item = comboBox.getSelectedItem(); return item.isChanged && item.isUserDefined; } }; @@ -516,7 +516,7 @@ class ParseDialog extends ReusableDialogComponentProvider { saveAsAction = new DockingAction("Save Profile As", plugin.getName()) { @Override public void actionPerformed(ActionContext context) { - saveAs((ComboBoxItem) comboBox.getSelectedItem()); + saveAs(comboBox.getSelectedItem()); } @Override @@ -577,7 +577,7 @@ class ParseDialog extends ReusableDialogComponentProvider { @Override public boolean isEnabledForContext(ActionContext context) { - ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + ComboBoxItem item = comboBox.getSelectedItem(); return item.isUserDefined; } }; @@ -590,7 +590,7 @@ class ParseDialog extends ReusableDialogComponentProvider { } private void refresh() { - ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + ComboBoxItem item = comboBox.getSelectedItem(); if (item.isChanged) { processItemChanged(item); } @@ -610,7 +610,7 @@ class ParseDialog extends ReusableDialogComponentProvider { private void clear() { pathPanel.clear(); parseOptionsField.setText(""); - ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + ComboBoxItem item = comboBox.getSelectedItem(); item.isChanged = true; } @@ -681,7 +681,7 @@ class ParseDialog extends ReusableDialogComponentProvider { } tableModel.removeTableModelListener(tableListener); parsePathTableModel.removeTableModelListener(parsePathTableListener); - ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + ComboBoxItem item = comboBox.getSelectedItem(); item.isChanged = false; StringBuffer sb = new StringBuffer(); @@ -817,7 +817,7 @@ class ParseDialog extends ReusableDialogComponentProvider { } private void delete() { - ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + ComboBoxItem item = comboBox.getSelectedItem(); if (item.isUserDefined) { if (OptionDialog.showOptionDialog(getComponent(), "Delete Profile?", "Are you sure you want to delete profile " + item.getName(), "Delete", @@ -944,8 +944,8 @@ class ParseDialog extends ReusableDialogComponentProvider { } String name = file.getName(); - if (!file.getName().endsWith(FileDataTypeManager.SUFFIX)) { - file = new File(file.getParentFile(), name + FileDataTypeManager.SUFFIX); + if (!file.getName().endsWith(FileDataTypeArchive.SUFFIX)) { + file = new File(file.getParentFile(), name + FileDataTypeArchive.SUFFIX); } if (!file.exists()) { @@ -1086,7 +1086,7 @@ class ParseDialog extends ReusableDialogComponentProvider { } ComboBoxItem getCurrentItem() { - ComboBoxItem item = (ComboBoxItem) comboBox.getSelectedItem(); + ComboBoxItem item = comboBox.getSelectedItem(); return item; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ActiveDataTypeStores.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ActiveDataTypeStores.java new file mode 100644 index 0000000000..77d5dc2a7f --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ActiveDataTypeStores.java @@ -0,0 +1,572 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.datamgr; + +import java.util.*; + +import generic.jar.ResourceFile; +import ghidra.app.plugin.core.datamgr.archive.InvalidArchive; +import ghidra.framework.data.DomainFileProxy; +import ghidra.framework.model.DomainFile; +import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.*; +import ghidra.program.model.listing.Program; +import ghidra.util.*; + +/** + * Class to track and manage all the open DataTypeStores ( BuiltInDataTypeManager, the active + * program, FileDataTypeArchives, and Project DataTypeArchives). This class adds + * DataTypeManagerListers to each corresponding DataTypeManager and consolidates those events into + * ArchiveManagerListener events that are used by GUI elements such as the DataTypes tree to keep + * it in synch with the state of the archives. + */ +class ActiveDataTypeStores { + + private Program program; + private DataTypeManager builtInDataTypesManager; + private List openArchives = new ArrayList<>(); + private Map invalidArchives = new HashMap<>(); + + private DataTypeIndexer dataTypeIndexer; + private DataTypeManagerListenerDelegate listenerDelegate; + private List dataTypeManagerListeners = new ArrayList<>(); + private List archiveManagerlisteners = new ArrayList<>(); + + // maintain a set of archives that should automatically be opened the next time the tool is run + private Set rememberedArchives = new HashSet<>(); + + // Updated anytime any datatype or category changes in any open archive, including the program + // archive. Currently used by data type tree nodes to know if their tooltip cache is stale. + private volatile long modCount = 0; + + ActiveDataTypeStores() { + listenerDelegate = new DataTypeManagerListenerDelegate(); + builtInDataTypesManager = BuiltInDataTypeManager.getDataTypeManager(); + builtInDataTypesManager.addDataTypeManagerListener(listenerDelegate); + dataTypeIndexer = new DataTypeIndexer(); + dataTypeIndexer.addDataTypeManager(builtInDataTypesManager); + } + + void dispose() { + removeAllArchives(); + dataTypeIndexer.removeDataTypeManager(builtInDataTypesManager); + builtInDataTypesManager.removeDataTypeManagerListener(listenerDelegate); + + } + + void setProgram(Program newProgram) { + if (this.program != null) { + this.program.getDataTypeManager().removeDataTypeManagerListener(listenerDelegate); + dataTypeIndexer.removeDataTypeManager(this.program.getDataTypeManager()); + notifyProgramClosed(); + this.program = null; + } + if (newProgram != null) { + this.program = newProgram; + newProgram.getDataTypeManager().addDataTypeManagerListener(listenerDelegate); + dataTypeIndexer.addDataTypeManager(newProgram.getDataTypeManager()); + notifyProgramOpened(); + } + } + + void addArchive(PersistentDataTypeArchive archive, boolean rememberInTool) { + archive.addConsumer(this); + openArchives.add(archive); + syncArchiveNames(archive); + archive.getDataTypeManager().addDataTypeManagerListener(listenerDelegate); + dataTypeIndexer.addDataTypeManager(archive.getDataTypeManager()); + if (rememberInTool) { + rememberedArchives.add(archive); + } + notifyArchiveOpened(archive); + } + + /** + * Removes all open archives from the service + */ + void removeAllArchives() { + // this list will get modified as we close archives, so work from a copy + List copy = new ArrayList<>(openArchives); + for (PersistentDataTypeArchive archive : copy) { + removeArchive(archive); + } + } + + /** + * Closes the given archive. + * @param archive the archive to close in the tool + */ + void removeArchive(PersistentDataTypeArchive archive) { + + if (!openArchives.remove(archive)) { + return; + } + + archive.getDataTypeManager().removeDataTypeManagerListener(listenerDelegate); + dataTypeIndexer.removeDataTypeManager(archive.getDataTypeManager()); + + rememberedArchives.remove(archive); + archive.release(this); + notifyArchiveClosed(archive); + } + + /** + * Adds an ArchiveManagerListener + * @param listener the listener + */ + void addArchiveManagerListener(ArchiveManagerListener listener) { + archiveManagerlisteners.add(listener); + } + + boolean contains(FileDataTypeArchive archive) { + return openArchives.contains(archive); + } + + boolean isRemembered(PersistentDataTypeArchive archive) { + return rememberedArchives.contains(archive); + } + + ProjectDataTypeArchive getArchiveForDomainFile(DomainFile domainFile, int version) { + for (PersistentDataTypeArchive archive : openArchives) { + if (archive instanceof ProjectDataTypeArchive projectArchive) { + DomainFile openedDf = projectArchive.getDomainFile(); + int openedVersion = openedDf.getVersion(); + if (openedDf instanceof DomainFileProxy proxy) { + openedDf = proxy.getOriginalDomainFile(); + } + if (openedVersion == version && domainFile.equals(openedDf)) { + return projectArchive; + } + } + } + return null; + } + + FileDataTypeArchive getArchiveForFile(ResourceFile file) { + for (PersistentDataTypeArchive archive : openArchives) { + if (archive instanceof FileDataTypeArchive fileArchive) { + if (file.equals(fileArchive.getFile())) { + return fileArchive; + } + } + } + return null; + } + + void archiveNameChanged(String fileId, String newName) { + if (program != null) { + updateSourceArchiveName(program.getDataTypeManager(), fileId, newName); + } + for (PersistentDataTypeArchive archive : openArchives) { + if (!archive.isChangeable()) { + continue; + } + DataTypeManager dataTypeManager = archive.getDataTypeManager(); + updateSourceArchiveName(dataTypeManager, fileId, newName); + } + } + + /** + * Removes an ArchiveManagerListene3r + * @param listener the listener to remove + */ + void removeArchiveManagerListener(ArchiveManagerListener listener) { + archiveManagerlisteners.remove(listener); + } + + /** + * {@return a list of all open DataTypeManagers in the tool} + */ + DataTypeManager[] getDataTypeManagers() { + List managers = new ArrayList<>(); + managers.add(builtInDataTypesManager); + if (program != null) { + managers.add(program.getDataTypeManager()); + } + for (PersistentDataTypeArchive archive : openArchives) { + managers.add(archive.getDataTypeManager()); + } + return managers.toArray(new DataTypeManager[managers.size()]); + } + + /** + * {@return a list of all open file or project archives} + */ + List getArchives() { + return new ArrayList<>(openArchives); + } + + /** + * Returns all favorite DataTypes in all archives. + * @return all favorite DataTypes in all archives. + */ + List getFavoriteDataTypes() { + List list = new ArrayList<>(); + list.addAll(builtInDataTypesManager.getFavorites()); + if (program != null) { + list.addAll(program.getDataTypeManager().getFavorites()); + } + for (PersistentDataTypeArchive archive : openArchives) { + DataTypeManager dataTypeManager = archive.getDataTypeManager(); + list.addAll(dataTypeManager.getFavorites()); + } + + return list; + } + + Set getRememberedArchives() { + return rememberedArchives; + } + + /** + * {@return a list of all open file archives in the tool} + */ + List getOpenFileArchives() { + List archiveList = new ArrayList<>(); + for (PersistentDataTypeArchive archive : openArchives) { + if (archive instanceof FileDataTypeArchive fileArchive) { + archiveList.add(fileArchive); + } + } + return archiveList; + } + + /** + * {@return a list of all open project archives in the tool} + */ + List getOpenProjectArchives() { + List archiveList = new ArrayList<>(); + for (PersistentDataTypeArchive archive : openArchives) { + if (archive instanceof ProjectDataTypeArchive projectArchive) { + archiveList.add(projectArchive); + } + } + return archiveList; + } + + List getSortedDataTypeList() { + return dataTypeIndexer.getSortedDataTypeList(); + } + + List getSortedCategoryPathList() { + return dataTypeIndexer.getSortedCategoryPathList(); + } + + /** + * Adds a listener for when DataTypes are added/removed/changed. + * @param listener the listener to be notified + */ + void addDataTypeManagerChangeListener(DataTypeManagerChangeListener listener) { + dataTypeManagerListeners.add(listener); + } + + void removeDataTypeManagerChangeListener(DataTypeManagerChangeListener listener) { + dataTypeManagerListeners.remove(listener); + } + + /** + * {@return the DatatypeManager associated with the given SourceArchive} + * @param source the SourceArchive to find its associated DataTypeManager + */ + DataTypeManager getDataTypeManager(SourceArchive source) { + return getDataTypeManager(source.getSourceArchiveID()); + } + + DataTypeManager getDataTypeManager(UniversalID id) { + for (PersistentDataTypeArchive archive : openArchives) { + DataTypeManager dataTypeManager = archive.getDataTypeManager(); + UniversalID universalID = dataTypeManager.getUniversalID(); + if ((universalID != null) && universalID.equals(id)) { + return dataTypeManager; + } + } + return null; + } + + /** + * Returns the current modification count which is incremented anytime any archive or any + * category or datatype it contains is changed in any way. This includes the datatypes in + * the current program. + * @return the current modification count. + */ + long getModificationCount() { + return modCount; + } + + boolean isAlreadyOpen(SourceArchive source) { + if (getDataTypeManager(source) != null) { + return true; + } + return invalidArchives.containsKey(source.getSourceArchiveID()); + } + + void createInvalidArchive(SourceArchive sourceArchive) { + String name = sourceArchive.getName(); + UniversalID id = sourceArchive.getSourceArchiveID(); + ArchiveType type = sourceArchive.getArchiveType(); + String domainFileId = sourceArchive.getDomainFileID(); + + InvalidArchive invalidArchive = new InvalidArchive(name, id, type, domainFileId); + invalidArchives.put(id, invalidArchive); + notifyInvalidArchiveAdded(invalidArchive); + + } + + /** + * Removes the given InvalidArchive from the tool + * @param archive the invalid archive to remove from the tool + */ + void removeInvalidArchive(InvalidArchive archive) { + invalidArchives.remove(archive.id()); + notifyInvalidArchiveRemoved(archive); + } + + Program getProgram() { + return program; + } + + List getInvalidArchives() { + return new ArrayList<>(invalidArchives.values()); + } + + void notifyStateChanged(DataTypeStore dataTypeStore) { + Swing.runNow(() -> { + for (ArchiveManagerListener listener : archiveManagerlisteners) { + listener.stateChanged(dataTypeStore); + } + }); + } + + private void notifyProgramClosed() { + SystemUtilities.runSwingNow(() -> { + for (ArchiveManagerListener listener : archiveManagerlisteners) { + listener.programClosed(program); + } + }); + } + + private void notifyArchiveOpened(PersistentDataTypeArchive archive) { + SystemUtilities.runSwingNow(() -> { + for (ArchiveManagerListener listener : archiveManagerlisteners) { + listener.archiveOpened(archive); + } + }); + } + + private void notifyInvalidArchiveAdded(InvalidArchive archive) { + SystemUtilities.runSwingNow(() -> { + for (ArchiveManagerListener listener : archiveManagerlisteners) { + listener.invalidArchiveAdded(archive); + } + }); + } + + private void notifyInvalidArchiveRemoved(InvalidArchive archive) { + SystemUtilities.runSwingNow(() -> { + for (ArchiveManagerListener listener : archiveManagerlisteners) { + listener.invalidArchiveRemoved(archive); + } + }); + } + + private void notifyArchiveClosed(PersistentDataTypeArchive archive) { + SystemUtilities.runSwingNow(() -> { + for (ArchiveManagerListener listener : archiveManagerlisteners) { + listener.archiveClosed(archive); + } + }); + } + + private void notifyProgramOpened() { + SystemUtilities.runSwingNow(() -> { + for (ArchiveManagerListener listener : archiveManagerlisteners) { + listener.programOpened(program); + } + }); + } + + /** + * Update the given archive and any other open archives to have the up-to-date archive names, + * as names may have changed while archives were not open. + * @param archive the archive + */ + private void syncArchiveNames(PersistentDataTypeArchive archive) { + try { + DataTypeManager dtm = archive.getDataTypeManager(); + if (program != null) { + synchronizeSourceArchiveNames(dtm, program.getDataTypeManager()); + } + for (PersistentDataTypeArchive openArchive : openArchives) { + synchronizeSourceArchiveNames(dtm, openArchive.getDataTypeManager()); + } + } + catch (Exception e) { + // can't update now (we are probably immutable), no problem + } + } + + private void synchronizeSourceArchiveNames(DataTypeManager dtm1, DataTypeManager dtm2) { + updateSourceArchiveName(dtm1, dtm2); + updateSourceArchiveName(dtm2, dtm1); + } + + private void updateSourceArchiveName(DataTypeManager destination, DataTypeManager source) { + if (!destination.getDataStore().isChangeable()) { + return; + } + destination.withTransaction("Udaate Data Tpye Source Archive Name", () -> { + UniversalID id = source.getUniversalID(); + String name = source.getName(); + destination.updateSourceArchiveName(id, name); + }); + + } + + private void updateSourceArchiveName(DataTypeManager dataTypeManager, String fileID, + String name) { + dataTypeManager.withTransaction("Update Data Type Source Archive Name", + () -> dataTypeManager.updateSourceArchiveName(fileID, name)); + } + + class DataTypeManagerListenerDelegate implements DataTypeManagerChangeListener { + + @Override + public void categoryAdded(DataTypeManager dtm, CategoryPath path) { + modCount++; + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.categoryAdded(dtm, path); + } + } + + @Override + public void categoryMoved(DataTypeManager dtm, CategoryPath oldPath, CategoryPath newPath) { + modCount++; + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.categoryMoved(dtm, oldPath, newPath); + } + } + + @Override + public void categoryRemoved(DataTypeManager dtm, CategoryPath path) { + modCount++; + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.categoryRemoved(dtm, path); + } + } + + @Override + public void categoryRenamed(DataTypeManager dtm, CategoryPath oldPath, + CategoryPath newPath) { + modCount++; + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.categoryRenamed(dtm, oldPath, newPath); + } + } + + @Override + public void dataTypeAdded(DataTypeManager dtm, DataTypePath path) { + modCount++; + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.dataTypeAdded(dtm, path); + } + } + + @Override + public void dataTypeChanged(DataTypeManager dtm, DataTypePath path) { + modCount++; + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.dataTypeChanged(dtm, path); + } + } + + @Override + public void dataTypeMoved(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) { + modCount++; + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.dataTypeMoved(dtm, oldPath, newPath); + } + } + + @Override + public void dataTypeRemoved(DataTypeManager dtm, DataTypePath path) { + modCount++; + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.dataTypeRemoved(dtm, path); + } + } + + @Override + public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath, + DataTypePath newPath) { + modCount++; + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.dataTypeRenamed(dtm, oldPath, newPath); + } + } + + @Override + public void dataTypeReplaced(DataTypeManager dtm, DataTypePath oldPath, + DataTypePath newPath, DataType newDataType) { + modCount++; + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.dataTypeReplaced(dtm, oldPath, newPath, newDataType); + } + } + + @Override + public void favoritesChanged(DataTypeManager dtm, DataTypePath path, boolean isFavorite) { + modCount++; + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.favoritesChanged(dtm, path, isFavorite); + } + } + + @Override + public void sourceArchiveAdded(DataTypeManager dataTypeManager, + SourceArchive dataTypeSource) { + modCount++; + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.sourceArchiveAdded(dataTypeManager, dataTypeSource); + } + } + + @Override + public void sourceArchiveChanged(DataTypeManager dataTypeManager, + SourceArchive dataTypeSource) { + modCount++; + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.sourceArchiveChanged(dataTypeManager, dataTypeSource); + } + } + + @Override + public void programArchitectureChanged(DataTypeManager dataTypeManager) { + modCount++; + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.programArchitectureChanged(dataTypeManager); + } + } + + @Override + public void restored(DataTypeManager dataTypeManager) { + modCount++; + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.restored(dataTypeManager); + } + } + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ArchiveFileChooser.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ArchiveFileChooser.java similarity index 87% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ArchiveFileChooser.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ArchiveFileChooser.java index f9d5b01f27..ef40efb1fc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ArchiveFileChooser.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ArchiveFileChooser.java @@ -1,37 +1,35 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.datamgr.archive; - -import ghidra.framework.GenericRunInfo; -import ghidra.framework.preferences.Preferences; -import ghidra.program.model.data.FileDataTypeManager; -import ghidra.util.filechooser.ExtensionFileFilter; +package ghidra.app.plugin.core.datamgr; import java.awt.Component; import java.io.File; import docking.widgets.filechooser.GhidraFileChooser; +import ghidra.framework.GenericRunInfo; +import ghidra.framework.preferences.Preferences; +import ghidra.program.model.dtarchive.FileDataTypeArchive; +import ghidra.util.filechooser.ExtensionFileFilter; public class ArchiveFileChooser extends GhidraFileChooser { public ArchiveFileChooser(Component component) { super(component); - setFileFilter(new ExtensionFileFilter(new String[] { FileDataTypeManager.EXTENSION }, + setFileFilter(new ExtensionFileFilter(new String[] { FileDataTypeArchive.EXTENSION }, "Ghidra Data Type Files")); setApproveButtonText("Save As"); setApproveButtonToolTipText("Save As"); @@ -52,7 +50,7 @@ public class ArchiveFileChooser extends GhidraFileChooser { } setCurrentDirectory(projectDirectory); - String suggestedName = suggestedFileName + FileDataTypeManager.SUFFIX; + String suggestedName = suggestedFileName + FileDataTypeArchive.SUFFIX; setSelectedFile(new File(projectDirectory, suggestedName)); File file = getSelectedFile(); @@ -68,10 +66,10 @@ public class ArchiveFileChooser extends GhidraFileChooser { private File fixFilenameSuffix(File file) { String filename = file.getName(); - if (filename.endsWith(FileDataTypeManager.SUFFIX)) { + if (filename.endsWith(FileDataTypeArchive.SUFFIX)) { return file; } - filename += FileDataTypeManager.SUFFIX; + filename += FileDataTypeArchive.SUFFIX; return new File(file.getParentFile(), filename); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ArchiveManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ArchiveManager.java new file mode 100644 index 0000000000..b552e697fb --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ArchiveManager.java @@ -0,0 +1,826 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.datamgr; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +import docking.widgets.pathmanager.PathManager; +import docking.widgets.tree.GTree; +import docking.widgets.tree.GTreeState; +import generic.jar.ResourceFile; +import generic.util.Path; +import ghidra.app.plugin.core.datamgr.archive.DuplicateIdException; +import ghidra.app.plugin.core.datamgr.archive.InvalidArchive; +import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility; +import ghidra.app.services.Recover; +import ghidra.app.services.Upgrade; +import ghidra.framework.main.AppInfo; +import ghidra.framework.model.*; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.database.dtarchive.DataTypeArchiveContentHandler; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; +import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.*; +import ghidra.program.model.listing.Program; +import ghidra.util.Msg; +import ghidra.util.UniversalID; +import ghidra.util.exception.*; +import ghidra.util.task.TaskLauncher; +import ghidra.util.task.TaskMonitor; + +/** + * Helper class to create, open, and close DataTypeArchives. Internally, it uses an + * {@link ActiveDataTypeStores} to track all open archives in the tool, maintain a + * complete index of all datatypes open, and remember which archives should be automatically + * opened when the tool is re-launched. + */ +public class ArchiveManager { + private static final String[] UNALLOWED_ARCHIVE_PATH_FRAGMENTS = new String[] { + "/Ghidra/Extensions/", "/Ghidra/docs/", "/Ghidra/Features/", "/Ghidra/Test/" }; + + public static final String OLD_DATA_TYPE_ARCHIVE_PATH_KEY = "DATA_TYPE_ARCHIVE_PATH"; + public static final String DATA_TYPE_ARCHIVE_PATH_KEY = "DATATYPE_ARCHIVE_PATHS"; + public static final String DISABLED_DATA_TYPE_ARCHIVE_PATH_KEY = + "DISABLED_DATA_TYPE_ARCHIVE_PATH"; + + private ActiveDataTypeStores openStores; + private DataTypeManagerPlugin plugin; + private MyFolderListener folderListener; + + public ArchiveManager(DataTypeManagerPlugin plugin) { + this.plugin = plugin; + openStores = new ActiveDataTypeStores(); + initializeFavorites(); + + folderListener = new MyFolderListener(); + PluginTool tool = plugin.getTool(); + Project project = tool.getProject(); + project.getProjectData().addDomainFolderChangeListener(folderListener); + } + + /** + * Close and reopen the given archive, preserving the tree selection + * @param archive the archive to reopen + * @return the reopened archive instance. The old instance is now invalid + */ + public PersistentDataTypeArchive reopenArchive(PersistentDataTypeArchive archive) { + if (archive instanceof FileDataTypeArchive fileArchive) { + boolean changeable = fileArchive.isChangeable(); + return reopenFileArchive(fileArchive, changeable); + } + else if (archive instanceof ProjectDataTypeArchive projectArchive) { + return doReopenProjectArchive(projectArchive); + } + return null; + } + + /** + * Close and reopen the given FileDataTypeArchive in either update or read-only mode. If going + * from read-only to open for update fails, it will attempt to keep it open read-only (It closed + * the read-only version before attempt to open it for update.) + * @param archive the archive to reopen + * @param forUpdate whether or not the archive is modifiable + * @return the new archive instance that was reopened + */ + public FileDataTypeArchive reopenFileArchive(FileDataTypeArchive archive, boolean forUpdate) { + GTreeState treeState = getTreeState(); + ResourceFile file = archive.getFile(); + boolean rememberInTool = false; + if (openStores.contains(archive)) { + rememberInTool = openStores.isRemembered(archive); + openStores.removeArchive(archive); + } + FileDataTypeArchive reopenedArchive = null; + reopenedArchive = openFileArchiveInTask(file, forUpdate, Upgrade.ASK, rememberInTool); + if (reopenedArchive == null && forUpdate) { + // Open for update failed - try re-open as read-only + reopenedArchive = openFileArchiveInTask(file, false, Upgrade.NO, rememberInTool); + } + restoreTreeState(treeState); + return reopenedArchive; + } + + /** + * Called when the tool is closed + */ + public void dispose() { + PluginTool tool = plugin.getTool(); + Project project = tool.getProject(); + ProjectData projectData = project.getProjectData(); + projectData.removeDomainFolderChangeListener(folderListener); + openStores.dispose(); + } + + /** + * {@return a list of all archives that have been marked that they should be re-opened when + * the tool is re-launched} + */ + public Set getRememberedArchives() { + return openStores.getRememberedArchives(); + } + + /** + * Creates a new ProjectDataTypeArchive. The user will be prompted for the folder and name + * of the new archive. + * @return the newly created ProjectDaaTypeArchive + */ + public ProjectDataTypeArchive createProjectArchive() { + CreateProjectArchiveDialog dialog = new CreateProjectArchiveDialog(this); + + plugin.getTool().showDialog(dialog); + ProjectDataTypeArchive archive = dialog.getArchive(); + if (archive != null) { + openStores.addArchive(archive, true); + archive.release(this); + } + return archive; + } + + /** + * Opens the give DomainFile in the tool as a ProjectDataTypeArchive. This method should be + * used by clients that are already in the background and have a TaskMonitor that can be + * used to cancel the operation. + * @param file the DomainFile to open + * @param version the version (if the file is versioned, otherwise use DomainFile.DEFAULT_VERSION) + * @param upgradeStrategy determine what happens if the archive needs to be upgraded before + * opening. The options are to do the upgrade, don't do the upgrade, or ask which pops up a + * dialog asking if it is ok to upgrade. + * @param recoverStrategy determine what happens if the archive has crash recovery data. The + * options are to recover the crash data changes, not recover the changes, or to ask which + * popups a dialog asking if the crash recovery data should be used. + * @param rememberInTool specifies if this archive should be remembered to be opened the next + * time the tool is launched. + * @param monitor The task monitor that can be used to cancel the operation + * @return new newly opened ProjectDataTypeArchive + * @throws VersionException if the archive is not the current version, and either it can't be + * upgraded or it was told not to upgrade. + * @throws CancelledException if the operation was cancelled + * @throws IOException if an general I/O exception occurs while reading the data from the + * database file. + * @throws DuplicateIdException if the archive has the same id as an already open archive (that + * is not from the same DomainFile - this is an unexpected situation) + */ + public ProjectDataTypeArchive openProjectArchive(DomainFile file, int version, + Upgrade upgradeStrategy, Recover recoverStrategy, boolean rememberInTool, + TaskMonitor monitor) + throws VersionException, CancelledException, IOException, DuplicateIdException { + + ProjectDataTypeArchive archive = openStores.getArchiveForDomainFile(file, version); + if (archive != null) { + return archive; + } + // The task has the open logic, so create a task and call its openArchive method directly. + OpenProjectArchiveTask task = new OpenProjectArchiveTask(file, version, + upgradeStrategy, recoverStrategy, this); + archive = task.openArchive(monitor); + checkForDuplicateArchiveID(archive); + openStores.addArchive(archive, rememberInTool); + archive.release(this); + return archive; + } + + /** + * Opens the give DomainFile in the tool as a ProjectDataTypeArchive in a background thread. + * This method should be used by clients that are working in the swing thread. + * @param file the DomainFile to open + * @param version the version (if the file is versioned, otherwise use DomainFile.DEFAULT_VERSION) + * @param upgradeStrategy determine what happens if the archive needs to be upgraded before + * opening. The options are to do the upgrade, don't do the upgrade, or ask which pops up a + * dialog asking if it is ok to upgrade. + * @param recoverStrategy determine what happens if the archive has crash recovery data. The + * options are to recover the crash data changes, not recover the changes, or to ask which + * popups a dialog asking if the crash recovery data should be used. + * @param rememberInTool specifies if this archive should be remembered to be opened the next + * time the tool is launched. + * @return the newly opened ProjectArchive or null if the open failed. (Exceptions are caught + * and reported in the task) + */ + public ProjectDataTypeArchive openProjectArchiveInTask(DomainFile file, int version, + Upgrade upgradeStrategy, Recover recoverStrategy, boolean rememberInTool) { + ProjectDataTypeArchive archive = openStores.getArchiveForDomainFile(file, version); + if (archive != null) { + return archive; + } + OpenProjectArchiveTask task = + new OpenProjectArchiveTask(file, version, upgradeStrategy, recoverStrategy, this); + TaskLauncher.launch(task); + archive = task.getArchive(); + if (archive == null) { + return null; + } + try { + checkForDuplicateArchiveID(archive); + } + catch (DuplicateIdException e) { + Msg.showError(this, null, "Error Opening Project Archive", e.getMessage()); + return null; + } + openStores.addArchive(archive, rememberInTool); + archive.release(this); + return archive; + } + + /** + * Creates a new FileDataType archive given a non-existing file + * @param file the file to use for the new archive (the file must not currently exist) + * @return the newly create FileDataTypeArchive or null if the create failed + */ + public FileDataTypeArchive createFileArchive(File file) { + try { + FileDataTypeArchive archive = DataTypeArchiveFactory.createFileArchive(file, this); + addArchivePath(new ResourceFile(file)); + openStores.addArchive(archive, true); + archive.release(this); + return archive; + } + catch (Exception e) { + Msg.showError(this, plugin.getProvider().getComponent(), "Create Archive Failed", + "Error creating archive file (" + file.getName() + "): " + e.getMessage()); + } + return null; + } + + /** + * Opens the give {@link ResourceFile} in the tool as a FileDataTypeArchive. This method should + * be used by clients that are already in the background and have a TaskMonitor that can be + * used to cancel the operation. + * @param file the {@link ResourceFile} to open + * @param openForUpdate specifies if the file should be open for update or read-only. Note that + * if openForUpdate is true and the ResourceFile does not represent a modifiable file, an + * {@link IOException} will be thrown. + * @param upgradeStrategy determine what happens if the archive needs to be upgraded before + * opening. The options are to do the upgrade, don't do the upgrade, or ask which pops up a + * dialog asking if it is ok to upgrade. + * @param rememberInTool specifies if this archive should be remembered to be opened the next + * time the tool is launched. + * @param monitor The task monitor that can be used to cancel the operation + * @return new newly opened ProjectDataTypeArchive + * @throws VersionException if the archive is not the current version, and either it can't be + * upgraded or it was told not to upgrade. + * @throws CancelledException if the operation was cancelled + * @throws IOException if an general I/O exception occurs while reading the data from the + * database file. + * @throws DuplicateIdException if the archive has the same id as an already open archive (that + * is not from the same DomainFile - this is an unexpected situation) + */ + public FileDataTypeArchive openFileArchive(ResourceFile file, boolean openForUpdate, + Upgrade upgradeStrategy, boolean rememberInTool, TaskMonitor monitor) + throws VersionException, IOException, DuplicateIdException, CancelledException { + FileDataTypeArchive archive = openStores.getArchiveForFile(file); + if (archive != null) { + if (openForUpdate && !archive.isChangeable()) { + // if the file is open read-only and we want it open for update, close the existing + openStores.removeArchive(archive); + } + else { + return archive; + } + } + OpenFileArchiveTask task = + new OpenFileArchiveTask(file, openForUpdate, upgradeStrategy, this); + + archive = task.openArchive(monitor); + if (archive != null) { + checkForDuplicateArchiveID(archive); + openStores.addArchive(archive, rememberInTool); + archive.release(this); + } + return archive; + } + + /** + * Opens the give {@link ResourceFile} in the tool as a FileDataTypeArchive in background + * thread. This method should be used by clients on the swing thread + * @param file the {@link ResourceFile} to open + * @param openForUpdate specifies if the file should be open for update or read-only. Note that + * if openForUpdate is true and the ResourceFile does not represent a modifiable file, an + * {@link IOException} will be thrown. + * @param upgradeStrategy determine what happens if the archive needs to be upgraded before + * opening. The options are to do the upgrade, don't do the upgrade, or ask which pops up a + * dialog asking if it is ok to upgrade. + * @param rememberInTool specifies if this archive should be remembered to be opened the next + * time the tool is launched. + * @return new newly opened FileDataTypeArchive or null if an error occurred. The error will + * be reported by the task + */ + public FileDataTypeArchive openFileArchiveInTask(ResourceFile file, boolean openForUpdate, + Upgrade upgradeStrategy, boolean rememberInTool) { + FileDataTypeArchive archive = openStores.getArchiveForFile(file); + if (archive != null) { + if (openForUpdate && !archive.isChangeable()) { + // if the file is open read-only and we want it open for update, close the existing + openStores.removeArchive(archive); + } + else { + return archive; + } + } + OpenFileArchiveTask task = + new OpenFileArchiveTask(file, openForUpdate, upgradeStrategy, this); + TaskLauncher.launch(task); + archive = task.getArchive(); + if (archive == null) { + return null; + } + try { + checkForDuplicateArchiveID(archive); + } + catch (DuplicateIdException e) { + Msg.showError(this, null, "Error Opening Project Archive", e.getMessage()); + return null; + } + openStores.addArchive(archive, rememberInTool); + archive.release(this); + return archive; + + } + + /** + * Updates the current program. May be null if the current program is closed or deactivated. + * @param program the currently active program or null if the program is closed or about to + * be swapped out for another + */ + public void setProgram(Program program) { + openStores.setProgram(program); + if (program != null) { + DataTypeManager programDataTypeManager = program.getDataTypeManager(); + List sources = programDataTypeManager.getSourceArchives(); + for (SourceArchive source : sources) { + if (openStores.isAlreadyOpen(source)) { + continue; + } + ArchiveType archiveType = source.getArchiveType(); + if (archiveType == ArchiveType.PROJECT) { + findAndOpenProjectArchiveForSource(source); + } + else if (archiveType == ArchiveType.FILE) { + findAndOpenFileArchiveForSource(source); + } + } + } + } + + /** + * Saves the given archive. + * @param archive the DataTypeArchive to save + */ + public void save(PersistentDataTypeArchive archive) { + if (!archive.isChanged()) { + return; + } + PluginTool tool = plugin.getTool(); + tool.prepareToSave(archive); + DataTypeArchiveSaveTask task = new DataTypeArchiveSaveTask(archive, tool); + TaskLauncher.launch(task); + openStores.notifyStateChanged(archive); + } + + /** + * Removes the SourceArchive corresponding to the invalid archive from the active program + * and removes the association from all datatypes with that SourceArchive. + * @param archive the InvalidArchive to remove the SourceArchive that it represents + */ + public void removeInvalidArchiveFromProgram(InvalidArchive archive) { + closeInvalidArchive(archive); + Program program = openStores.getProgram(); + if (program == null) { + return; + } + + DataTypeManager programDataTypeManager = program.getDataTypeManager(); + program.withTransaction("Remove Invalid Source Archive From Program", () -> { + UniversalID sourceArchiveID = archive.id(); + SourceArchive sourceArchive = programDataTypeManager.getSourceArchive(sourceArchiveID); + if (sourceArchive != null) { + programDataTypeManager.removeSourceArchive(sourceArchive); + } + }); + } + + /** + * Initiates a save as operation. The user will be prompted for additional information needed + * @param archive the archive to do a save as operation on + */ + public void saveAs(PersistentDataTypeArchive archive) { + if (!plugin.canCloseDomainObject(archive)) { + return; + } + if (archive instanceof FileDataTypeArchive fileArchive) { + saveFileArchiveAs(fileArchive); + } + else if (archive instanceof ProjectDataTypeArchive projectArchive) { + PluginTool tool = plugin.getTool(); + ProjectArchiveSaveAsDialog dialog = + new ProjectArchiveSaveAsDialog(tool, projectArchive); + tool.showDialog(dialog); + } + openStores.notifyStateChanged(archive); + } + + /** + * Closes all open DataTypeArchives. + */ + public void closeAllArchives() { + List archives = openStores.getArchives(); + for (PersistentDataTypeArchive archive : archives) { + openStores.removeArchive(archive); + } + } + +//================================================================================================== +// Pass through methods +//================================================================================================== + /** + * {@return the modification count for the last time any archive had changes} + */ + public long getModificationCount() { + return openStores.getModificationCount(); + } + + /** + * {@return the current active program. May be null} + */ + public Program getProgram() { + return openStores.getProgram(); + } + + /** + * @return all the current DataTypeManagers in the tool. This includes the built-in + * DataTypeManager, the currently active program's DataTypeManager, and all DataTypeArchive's + * DataTypeManagers} + */ + public DataTypeManager[] getDataTypeManagers() { + return openStores.getDataTypeManagers(); + } + + /** + * Closes the archive from the tool. (May not actually close the archive if some other client + * has a consumer registered on the archive.) + * @param archive the archive to close with respect to the tool + */ + public void closeArchive(PersistentDataTypeArchive archive) { + openStores.removeArchive(archive); + } + + /** + * {@return a list of all open file or project datatype archives} + */ + public List getOpenArchives() { + return openStores.getArchives(); + } + + /** + * {@return a list of all favorite datatypes from all current DataTypeManagers open in the tool} + */ + public List getFavoriteDataTypes() { + return openStores.getFavoriteDataTypes(); + } + + /** + * {@return a list of all datatypes from all current DataTypeManagers open in the tool} + */ + public List getSortedDataTypeList() { + return openStores.getSortedDataTypeList(); + } + + /** + * {@return a list of all current categories in all current DataTypeManagers open in the tool} + */ + public List getSortedCategoryPathList() { + return openStores.getSortedCategoryPathList(); + } + + /** + * Adds a DataTypeManagerChangeListener to all open DataTypeManagers open in the tool} + * @param listener the listener to be notified of datatype changes + */ + public void addDataTypeManagerChangeListener(DataTypeManagerChangeListener listener) { + openStores.addDataTypeManagerChangeListener(listener); + } + + /** + * Removes a DataTypeManagerChangeListener from all open DataTypeManagers open in the tool} + * @param listener the listener to be removed + */ + public void removeDataTypeManagerChangeListener(DataTypeManagerChangeListener listener) { + openStores.removeDataTypeManagerChangeListener(listener); + } + + /** + * {@return a list of all open FileDataTypeArhives} + */ + public List getOpenFileArchives() { + return openStores.getOpenFileArchives(); + } + + /** + * {@return a list of all open ProjetDataTypeArhives} + */ + public List getProjectArchives() { + return openStores.getOpenProjectArchives(); + } + + /** + * {@return the DatatypeManager associated with the given SourceArchive} + * @param source the SourceArchive to find its associated DataTypeManager + */ + public DataTypeManager getDataTypeManager(SourceArchive source) { + return openStores.getDataTypeManager(source); + } + + /** + * Removes the given InvalidArchive from the tool + * @param archive the invalid archive to remove from the tool + */ + public void closeInvalidArchive(InvalidArchive archive) { + openStores.removeInvalidArchive(archive); + } + + /** + * Adds an ArchiveManagerListener to all open DataTypeManagers open in the tool} + * @param listener the listener to be notified of archive changes + */ + public void addArchiveManagerListener(ArchiveManagerListener listener) { + openStores.addArchiveManagerListener(listener); + } + + /** + * removes an ArchiveManagerListener from all open DataTypeManagers open in the tool} + * @param listener the listener to be removed + */ + public void removeArchiveManagerListener(ArchiveManagerListener listener) { + openStores.removeArchiveManagerListener(listener); + } + + public List getInvalidArchives() { + return openStores.getInvalidArchives(); + } + +//================================================================================================== +// Private methods +//================================================================================================== + private void saveFileArchiveAs(FileDataTypeArchive archive) { + ArchiveFileChooser fileChooser = new ArchiveFileChooser(null); + File file = fileChooser.promptUserForFile(archive.getName()); + fileChooser.dispose(); + if (file == null) { + return; + } + if (file.exists()) { + Msg.showInfo(this, null, "Cannot Perform Save As", + "Cannot save archive to " + file.getName() + "\nbecause " + + file.getName() + " already exists."); + return; + } + TaskLauncher.launchModal("Save As", monitor -> { + try { + archive.saveAs(file, monitor); + } + catch (CancelledException e) { + // Use chose this, nothing to report + } + catch (DuplicateFileException de) { + Msg.showError(this, null, "Unable to Save File", + "Archive already exists: " + file.getName()); + } + catch (IOException ioe) { + Msg.showError(this, null, "Unable to Save File", + "Unexpected exception attempting to save archive: " + file.getName(), ioe); + } + }); + + } + + private void findAndOpenProjectArchiveForSource(SourceArchive source) { + String domainFileID = source.getDomainFileID(); + ProjectData projectData = plugin.getTool().getProject().getProjectData(); + DomainFile domainFile = projectData.getFileByID(domainFileID); + if (domainFile == null) { + openStores.createInvalidArchive(source); + } + else { + openProjectArchiveInTask(domainFile, DomainFile.DEFAULT_VERSION, Upgrade.ASK, + Recover.ASK, true); + } + } + + private void findAndOpenFileArchiveForSource(SourceArchive sourceArchive) { + String name = sourceArchive.getName(); + UniversalID id = sourceArchive.getSourceArchiveID(); + if (findAndOpenFileArchive(name, id) == null) { + openStores.createInvalidArchive(sourceArchive); + } + } + + private FileDataTypeArchive findAndOpenFileArchive(String archiveName, UniversalID archiveID) { + + if (archiveName.endsWith(FileDataTypeArchive.SUFFIX)) { + archiveName = archiveName.substring(0, + archiveName.length() - FileDataTypeArchive.SUFFIX.length()); + } + + String archiveFileName = archiveName + FileDataTypeArchive.SUFFIX; + + Path[] pathsFromPreferences = getArchivePaths(); + for (Path path : pathsFromPreferences) { + if (!path.isEnabled() || !isAllowedArchivePath(path.getPathAsString())) { + continue; + } + ResourceFile file = new ResourceFile(path.getPath(), archiveFileName); + if (file.exists()) { + FileDataTypeArchive archive = + openFileArchiveInTask(file, false, Upgrade.ASK, false); + if (matchesId(archive, archiveID)) { + return archive; + } + openStores.removeArchive(archive); + return null; + } + } + + // Look for archive provided with installation (read-only) + ResourceFile file = DataTypeArchiveUtility.findArchiveFile(archiveFileName); + if (file == null) { + Msg.showError(this, plugin.getProvider().getComponent(), "Open Archive Failed", + "Archive file not found: " + archiveFileName + FileDataTypeArchive.SUFFIX); + return null; + } + return openFileArchiveInTask(file, false, Upgrade.ASK, false); + } + + private boolean matchesId(FileDataTypeArchive archive, UniversalID archiveID) { + return archive != null && archive.getUniversalID().equals(archiveID); + } + + /** + * Determine if archive path is allowed. + * An attempt is made to disallow any path which appears to be contained + * within a Ghidra installation. + * @param path directory or file archive path + * @return true if path is allowed + */ + private boolean isAllowedArchivePath(String path) { + + if (path.startsWith(Path.GHIDRA_HOME)) { + return false; // Ghidra type info directories will always be searched + } + + for (String unallowed : UNALLOWED_ARCHIVE_PATH_FRAGMENTS) { + if (path.indexOf(unallowed) > 0) { + // Remembering paths to older Ghidra installations is bad + return false; + } + } + + return true; + } + + private void checkForDuplicateArchiveID(PersistentDataTypeArchive archive) throws DuplicateIdException { + UniversalID id = archive.getDataTypeManager().getUniversalID(); + DataTypeManager existing = openStores.getDataTypeManager(id); + if (existing != null) { + archive.release(this); + throw new DuplicateIdException(archive.getName(), existing.getName()); + } + } + + private ProjectDataTypeArchive doReopenProjectArchive(ProjectDataTypeArchive archive) { + // When re-opened the latest version will be opened + GTreeState treeState = getTreeState(); + DomainFile domainFile = archive.getDomainFile(); + ProjectLocator projectLocator = domainFile.getProjectLocator(); + String path = domainFile.getPathname(); + boolean isRemembered = openStores.isRemembered(archive); + openStores.removeArchive(archive); + ProjectData projectData = AppInfo.getActiveProject().getProjectData(projectLocator); + DomainFile file = projectData.getFile(path); + archive = openProjectArchiveInTask(file, DomainFile.DEFAULT_VERSION, Upgrade.ASK, + Recover.ASK, isRemembered); + restoreTreeState(treeState); + return archive; + } + + private GTreeState getTreeState() { + GTree tree = plugin.getProvider().getGTree(); + return tree.getTreeState(); + } + + private void restoreTreeState(GTreeState state) { + GTree tree = plugin.getProvider().getGTree(); + tree.restoreTreeState(state); + } + + private void initializeFavorites() { + BuiltInDataTypeManager dtm = BuiltInDataTypeManager.getDataTypeManager(); + dtm.setFavorite(dtm.resolve(PointerDataType.dataType, null), true); + dtm.setFavorite(dtm.resolve(CharDataType.dataType, null), true); + dtm.setFavorite(dtm.resolve(StringDataType.dataType, null), true); + dtm.setFavorite(dtm.resolve(TerminatedStringDataType.dataType, null), true); + dtm.setFavorite(dtm.resolve(TerminatedUnicodeDataType.dataType, null), true); + dtm.setFavorite(dtm.resolve(FloatDataType.dataType, null), true); + dtm.setFavorite(dtm.resolve(DoubleDataType.dataType, null), true); + dtm.setFavorite(dtm.resolve(LongDoubleDataType.dataType, null), true); + dtm.setFavorite(dtm.resolve(IntegerDataType.dataType, null), true); + dtm.setFavorite(dtm.resolve(LongDataType.dataType, null), true); + dtm.setFavorite(dtm.resolve(UnsignedIntegerDataType.dataType, null), true); + dtm.setFavorite(dtm.resolve(UnsignedLongDataType.dataType, null), true); + dtm.setFavorite(dtm.resolve(ByteDataType.dataType, null), true); + dtm.setFavorite(dtm.resolve(WordDataType.dataType, null), true); + dtm.setFavorite(dtm.resolve(DWordDataType.dataType, null), true); + dtm.setFavorite(dtm.resolve(QWordDataType.dataType, null), true); + } + + /** + * @return all archive search paths (both enabled and disabled are included) + */ + private Path[] getArchivePaths() { + return PathManager.getPathsFromPreferences(DATA_TYPE_ARCHIVE_PATH_KEY, null, + DISABLED_DATA_TYPE_ARCHIVE_PATH_KEY); + } + + private void addArchivePath(ResourceFile archiveFilePath) { + + Path newPath = new Path(archiveFilePath.getParentFile()); + if (!isAllowedArchivePath(newPath.getPathAsString())) { + return; + } + + Path[] paths = getArchivePaths(); + for (Path path : paths) { + if (path.equals(newPath)) { + if (!path.isEnabled()) { + path.setEnabled(true); + PathManager.savePathsToPreferences(DATA_TYPE_ARCHIVE_PATH_KEY, + DISABLED_DATA_TYPE_ARCHIVE_PATH_KEY, paths); + } + return; + } + } + + Path[] newPaths = new Path[paths.length + 1]; + System.arraycopy(paths, 0, newPaths, 0, paths.length); + newPaths[paths.length] = newPath; + + PathManager.savePathsToPreferences(DATA_TYPE_ARCHIVE_PATH_KEY, + DISABLED_DATA_TYPE_ARCHIVE_PATH_KEY, newPaths); + } + +//================================================================================================== +// Inner Classes +//================================================================================================== + private class MyFolderListener extends DomainFolderListenerAdapter { + + @Override + public void domainFileStatusChanged(DomainFile file, boolean fileIDset) { + if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE + .equals(file.getContentType())) { + return; + } + + ProjectDataTypeArchive existing = + openStores.getArchiveForDomainFile(file, DomainFile.DEFAULT_VERSION); + if (existing == null) { + return; + } + // we only have to worry about the projectArchive being checked out (going from + // not changeable to changeable). The other direction (check-in) is not allowed + // while the archive is open, so if it happens, we won't have the archive in our list. + if (file.isCheckedOut() && !existing.isChangeable()) { + reopenArchive(existing); + } + } + + @Override + public void domainFileRemoved(DomainFolder parentFolder, String name, String fileID) { + // ignore + } + + @Override + public void domainFileRenamed(DomainFile file, String oldName) { + if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE + .equals(file.getContentType())) { + return; + } + String newName = file.getName(); + String fileId = file.getFileID(); + openStores.archiveNameChanged(fileId, newName); + } + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ArchiveManagerListener.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ArchiveManagerListener.java new file mode 100644 index 0000000000..7d82c220f9 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ArchiveManagerListener.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.datamgr; + +import ghidra.app.plugin.core.datamgr.archive.InvalidArchive; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.program.model.dtarchive.DataTypeStore; +import ghidra.program.model.listing.Program; + +/** + * Interface for notifications when archives change in the archive manager + */ +public interface ArchiveManagerListener { + /** + * Called when a new Archive is opened. + * @param archive the new archive that was opened. + */ + public void archiveOpened(PersistentDataTypeArchive archive); + + /** + * Called when an archive is closed. + * @param archive the archive that was closed. + */ + public void archiveClosed(PersistentDataTypeArchive archive); + + /** + * Called when a new program is activated + * @param program the program that is activated + */ + public void programOpened(Program program); + + /** + * Called when the current program is deactivated + * @param program the program that is being deactivated + */ + public void programClosed(Program program); + + /** + * Called when the tool attempts to open a file or project archive that is referenced by + * a program, but can't be found. This creates a object that actions can operate on to + * either attempt to find it or use it to remove the references from a program. + * @param invalidArchive the InvalidArchive object that describes what the program was trying + * to open. + */ + public void invalidArchiveAdded(InvalidArchive invalidArchive); + + /** + * Called when an invalid archive is removed from the tool. + * @param invalidArchive the invalid archive object that was removed from the tool + */ + public void invalidArchiveRemoved(InvalidArchive invalidArchive); + + /** + * Notification that a change to the datatypeStore has occurred. Could be a datatype or + * category was added, changed, or deleted. Could also mean that the changes were saved to + * the backing store or the that a save as operation occurred. + * @param dataTypeStore The store whose state changed + */ + public void stateChanged(DataTypeStore dataTypeStore); +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/framework/main/datatree/VersionControlDataTypeArchiveUndoCheckoutAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ArchiveUndoCheckoutTask.java similarity index 66% rename from Ghidra/Features/Base/src/main/java/ghidra/framework/main/datatree/VersionControlDataTypeArchiveUndoCheckoutAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ArchiveUndoCheckoutTask.java index 950b66b92c..1e359659e4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/framework/main/datatree/VersionControlDataTypeArchiveUndoCheckoutAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ArchiveUndoCheckoutTask.java @@ -4,16 +4,16 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.framework.main.datatree; +package ghidra.app.plugin.core.datamgr; import java.io.IOException; import java.util.ArrayList; @@ -22,15 +22,19 @@ import java.util.List; import javax.swing.Icon; import docking.action.MenuData; +import docking.widgets.tree.GTreeState; import generic.theme.GIcon; -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.archive.*; import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager; +import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; +import ghidra.app.services.Recover; +import ghidra.app.services.Upgrade; import ghidra.framework.client.ClientUtil; import ghidra.framework.main.SaveDataDialog; import ghidra.framework.main.datatable.DomainFileContext; +import ghidra.framework.main.datatree.UndoActionDialog; import ghidra.framework.main.projectdata.actions.VersionControlAction; import ghidra.framework.model.DomainFile; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; import ghidra.util.exception.AssertException; import ghidra.util.exception.CancelledException; import ghidra.util.task.Task; @@ -39,24 +43,21 @@ import ghidra.util.task.TaskMonitor; /** * Action to undo checkouts for domain files in the repository. */ -public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionControlAction { +public class ArchiveUndoCheckoutTask extends VersionControlAction { private static final Icon ICON = new GIcon("icon.base.util.datatree.version.control.archive.dt.checkout.undo"); private DataTypeManagerPlugin dtmPlugin; - private ArchiveProvider archiveProvider; /** * Creates an action to undo checkouts for domain files in the repository. * @param plugin the plug-in that owns this action. * @param provider provides a list of domain files to be affected by this action. */ - public VersionControlDataTypeArchiveUndoCheckoutAction(DataTypeManagerPlugin plugin, - ArchiveProvider provider) { + public ArchiveUndoCheckoutTask(DataTypeManagerPlugin plugin) { super("UndoCheckOut", plugin.getName(), plugin.getTool()); this.dtmPlugin = plugin; - this.archiveProvider = provider; setPopupMenuData(new MenuData(new String[] { "Undo Checkout" }, ICON, GROUP)); setDescription("Undo checkout"); @@ -65,19 +66,22 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont @Override public void actionPerformed(DomainFileContext context) { - undoCheckOut(); + undoCheckOut((DataTypesActionContext) context); } @Override public boolean isEnabledForContext(DomainFileContext context) { + if (!(context instanceof DataTypesActionContext dataTypesContext)) { + return false; + } if (isFileSystemBusy()) { return false; // don't block; we should get called again later } - List domainFiles = context.getSelectedFiles(); - for (DomainFile domainFile : domainFiles) { - if (domainFile.isCheckedOut()) { - return true; // At least one checked out file selected. + List archives = dataTypesContext.getSelectedProjectArchives(); + for (ProjectDataTypeArchive archive : archives) { + if (archive.getDomainFile().isCheckedOut()) { + return true; } } return false; @@ -86,19 +90,19 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont /** * Gets the domain files from the provider and then undoes the checkout on any that are * checked out. + * @param context */ - protected void undoCheckOut() { + protected void undoCheckOut(DataTypesActionContext context) { if (!checkRepositoryConnected()) { return; } - closeEditorsForUndoCheckOut(); + List archives = context.getSelectedProjectArchives(); + closeEditorsForUndoCheckOut(archives); - List archiveList = archiveProvider.getArchives(); - List unmodifiedCheckOutsList = new ArrayList<>(); - List modifiedCheckOutsList = new ArrayList<>(); - for (Archive archive2 : archiveList) { - ProjectArchive archive = (ProjectArchive) archive2; + List unmodifiedCheckOutsList = new ArrayList<>(); + List modifiedCheckOutsList = new ArrayList<>(); + for (ProjectDataTypeArchive archive : archives) { DomainFile domainFile = archive.getDomainFile(); if (domainFile.isCheckedOut()) { if (domainFile.modifiedSinceCheckout() || domainFile.isChanged()) { @@ -119,10 +123,9 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont } } - private void closeEditorsForUndoCheckOut() { + private void closeEditorsForUndoCheckOut(List archives) { DataTypeEditorManager editorManager = dtmPlugin.getEditorManager(); - List archiveList = archiveProvider.getArchives(); - for (Archive archive : archiveList) { + for (ProjectDataTypeArchive archive : archives) { if (!editorManager.checkEditors(archive.getDataTypeManager(), true)) { continue; } @@ -140,12 +143,12 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont * @param modifiedArchivesList the list of archives that have been modified * @throws CancelledException if cancelled */ - protected void undoCheckOuts(List unmodifiedArchivesList, - List modifiedArchivesList) throws CancelledException { + protected void undoCheckOuts(List unmodifiedArchivesList, + List modifiedArchivesList) throws CancelledException { boolean saveCopy = false; - DomainFile[] selectedFiles = new DomainFile[0]; boolean undoWasCancelled = false; - List selectedArchives = modifiedArchivesList; + List selectedArchives = modifiedArchivesList; + // Now confirm the modified ones and undo checkout for the ones the user indicates. if (modifiedArchivesList.size() > 0) { UndoActionDialog dialog = new UndoActionDialog("Confirm Undo Checkout", @@ -153,14 +156,14 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont int actionID = dialog.showDialog(tool); if (actionID != UndoActionDialog.CANCEL) { saveCopy = dialog.saveCopy(); - selectedFiles = dialog.getSelectedDomainFiles(); + DomainFile[] selectedFiles = dialog.getSelectedDomainFiles(); selectedArchives = getMatchingArchives(modifiedArchivesList, selectedFiles); } else { throw new CancelledException(); } } - if ((unmodifiedArchivesList.size() > 0) || (selectedFiles.length > 0)) { + if ((unmodifiedArchivesList.size() > 0) || (selectedArchives.size() > 0)) { tool.execute(new DataTypeArchiveUndoCheckOutTask(unmodifiedArchivesList, selectedArchives, saveCopy)); } @@ -169,12 +172,12 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont } } - private List getMatchingArchives(List archivesList, - DomainFile[] selectedFiles) { - List archiveList = - new ArrayList<>(selectedFiles.length); + private List getMatchingArchives( + List archivesList, DomainFile[] selectedFiles) { + + List archiveList = new ArrayList<>(selectedFiles.length); for (DomainFile domainFile : selectedFiles) { - DomainFileArchive archive = getArchiveForDomainFile(archivesList, domainFile); + ProjectDataTypeArchive archive = getArchiveForDomainFile(archivesList, domainFile); if (archive != null) { archiveList.add(archive); } @@ -187,9 +190,9 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont return archiveList; } - private DomainFileArchive getArchiveForDomainFile(List archivesList, - DomainFile domainFile) { - for (DomainFileArchive domainFileArchive : archivesList) { + private ProjectDataTypeArchive getArchiveForDomainFile( + List archivesList, DomainFile domainFile) { + for (ProjectDataTypeArchive domainFileArchive : archivesList) { if (domainFileArchive.getDomainFile() == domainFile) { return domainFileArchive; } @@ -197,10 +200,10 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont return null; } - private List getDomainFileList(List modifiedArchivesList) { + private List getDomainFileList(List modifiedArchivesList) { List dfList = new ArrayList<>(modifiedArchivesList.size()); - for (DomainFileArchive dfArchive : modifiedArchivesList) { - dfList.add(dfArchive.getDomainFile()); + for (ProjectDataTypeArchive archive : modifiedArchivesList) { + dfList.add(archive.getDomainFile()); } return dfList; } @@ -224,8 +227,8 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont * Task for undoing check out of files that are in version control. */ private class DataTypeArchiveUndoCheckOutTask extends Task { - private List unmodifiedCheckOutsList; - private List modifiedCheckedOutFiles; + private List unmodifiedCheckOutsList; + private List modifiedCheckedOutFiles; private boolean saveCopy; /** @@ -235,8 +238,8 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont * @param saveCopy true indicates that copies of the modified files should be made * before undo of the checkout. */ - DataTypeArchiveUndoCheckOutTask(List unmodifiedCheckOutsList, - List modifiedCheckedOutFiles, boolean saveCopy) { + DataTypeArchiveUndoCheckOutTask(List unmodifiedCheckOutsList, + List modifiedCheckedOutFiles, boolean saveCopy) { super("Undo Check Out", true, true, true); this.unmodifiedCheckOutsList = unmodifiedCheckOutsList; @@ -246,21 +249,20 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont @Override public void run(TaskMonitor monitor) { + DataTypeArchiveGTree gTree = dtmPlugin.getProvider().getGTree(); + GTreeState treeState = gTree.getTreeState(); try { - for (DomainFileArchive archive : unmodifiedCheckOutsList) { + ArchiveManager archiveManager = dtmPlugin.getArchiveManager(); + for (ProjectDataTypeArchive archive : unmodifiedCheckOutsList) { DomainFile df = archive.getDomainFile(); if (df.isCheckedOut() && (dtmPlugin != null)) { - // TODO Need to close archive here if it is open. - archive.close(); - + archiveManager.closeArchive(archive); df.undoCheckout(false); - - // TODO Need to open the archive here if it got closed above. - dtmPlugin.openArchive(df); - + archiveManager.openProjectArchiveInTask(df, DomainFile.DEFAULT_VERSION, + Upgrade.ASK, Recover.ASK, false); } } - for (DomainFileArchive currentArchive : modifiedCheckedOutFiles) { + for (ProjectDataTypeArchive currentArchive : modifiedCheckedOutFiles) { monitor.checkCancelled(); DomainFile currentDF = currentArchive.getDomainFile(); @@ -271,14 +273,10 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont monitor.setMessage("Undoing Check Out " + currentDF.getName()); - // TODO Need to close archive here if it is open. - currentArchive.close(); - + archiveManager.closeArchive(currentArchive); currentDF.undoCheckout(saveCopy); - - // TODO Need to open the archive here if it got closed above. - dtmPlugin.openArchive(currentDF); - + archiveManager.openProjectArchiveInTask(currentDF, DomainFile.DEFAULT_VERSION, + Upgrade.ASK, Recover.ASK, true); } } catch (CancelledException e) { @@ -287,6 +285,7 @@ public class VersionControlDataTypeArchiveUndoCheckoutAction extends VersionCont catch (IOException e) { ClientUtil.handleException(repository, e, "Undo Check Out", tool.getToolFrame()); } + gTree.restoreTreeState(treeState); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/CreateProjectArchiveDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/CreateProjectArchiveDialog.java new file mode 100644 index 0000000000..a584324e3d --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/CreateProjectArchiveDialog.java @@ -0,0 +1,87 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.datamgr; + +import java.io.IOException; + +import ghidra.app.util.HelpTopics; +import ghidra.framework.main.DataTreeDialog; +import ghidra.framework.main.DataTreeDialogType; +import ghidra.framework.model.DomainFile; +import ghidra.framework.model.DomainFolder; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; +import ghidra.util.*; +import ghidra.util.exception.DuplicateNameException; + +public class CreateProjectArchiveDialog extends DataTreeDialog { + + private Object consumer; + private ProjectDataTypeArchive archive; + + public CreateProjectArchiveDialog(Object consumer) { + super(null, "Create Project Archive", DataTreeDialogType.CREATE); + this.consumer = consumer; + setHelpLocation(new HelpLocation(HelpTopics.DATA_MANAGER, "New_Project_Data_Type_Archive")); + } + + public ProjectDataTypeArchive getArchive() { + return archive; + } + + @Override + protected void okCallback() { + DomainFolder folder = getDomainFolder(); + String name = getNameText(); + if (name.length() == 0) { + setStatusText("Please enter a name"); + return; + } + if (folder == null) { + setStatusText("Please select a folder"); + return; + } + DomainFile file = folder.getFile(name); + if (file != null) { + setStatusText("Choose a name that doesn't exist"); + return; + } + if (createArchive()) { + close(); + } + } + + private boolean createArchive() { + DomainFolder domainFolder = getDomainFolder(); + String archiveName = getNameText(); + try { + archive = + DataTypeArchiveFactory.createProjectArchive(domainFolder, archiveName, consumer); + return true; + } + catch (DuplicateNameException e) { + setStatusText("Duplicate Name: " + e.getMessage()); + } + catch (InvalidNameException e) { + setStatusText("Invalid Name: " + e.getMessage()); + } + catch (IOException e) { + setStatusText("Unexpected IOException!"); + Msg.showError(null, getComponent(), "Unexpected Exception", e.getMessage(), e); + } + return false; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveSaveTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveSaveTask.java new file mode 100644 index 0000000000..ba5e14c215 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveSaveTask.java @@ -0,0 +1,105 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.datamgr; + +import java.io.IOException; +import java.rmi.ConnectException; +import java.util.List; + +import docking.widgets.OptionDialog; +import ghidra.framework.client.ClientUtil; +import ghidra.framework.client.NotConnectedException; +import ghidra.framework.model.DomainObject; +import ghidra.framework.model.TransactionInfo; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.Task; +import ghidra.util.task.TaskMonitor; + +public class DataTypeArchiveSaveTask extends Task { + private static final String CONTENT_NAME = "Data Type Archive"; + private PersistentDataTypeArchive archive; + private PluginTool tool; + + DataTypeArchiveSaveTask(PersistentDataTypeArchive archive, PluginTool tool) { + super("Save " + archive.getName(), true, true, true); + this.archive = archive; + this.tool = tool; + } + + @Override + public void run(TaskMonitor monitor) { + monitor.setMessage("Saving " + archive.getName() + "..."); + if (acquireSaveLock(archive)) { + try { + archive.save(null, monitor); + } + catch (CancelledException e) { + // O.K., expected + } + catch (NotConnectedException e) { + ClientUtil.promptForReconnect(tool.getProject().getRepository(), + tool.getToolFrame()); + } + catch (ConnectException e) { + ClientUtil.promptForReconnect(tool.getProject().getRepository(), + tool.getToolFrame()); + } + catch (IOException e) { + ClientUtil.handleException(tool.getProject().getRepository(), e, "Save File", + tool.getToolFrame()); + } + finally { + archive.unlock(); + } + } + } + + private boolean acquireSaveLock(DomainObject domainObject) { + if (!domainObject.lock(null)) { + String title = "Save " + CONTENT_NAME + " (Busy)"; + StringBuilder buf = new StringBuilder(); + buf.append("The " + CONTENT_NAME + " is currently being modified by \n"); + buf.append("the following actions:\n "); + TransactionInfo t = domainObject.getCurrentTransactionInfo(); + List list = t.getOpenSubTransactions(); + for (String element : list) { + buf.append("\n "); + buf.append(element); + } + buf.append("\n \n"); + buf.append( + "WARNING! The above task(s) should be cancelled before attempting a Save.\n"); + buf.append("Only proceed if unable to cancel them.\n \n"); + buf.append( + "If you continue, all changes made by these tasks, as well as any other overlapping task,\n"); + buf.append( + "will be LOST and subsequent transaction errors may occur while these tasks remain active.\n \n"); + + int result = OptionDialog.showOptionDialog(tool.getToolFrame(), title, buf.toString(), + "Save Archive!", OptionDialog.WARNING_MESSAGE); + + if (result == OptionDialog.OPTION_ONE) { + domainObject.forceLock(true, "Save Archive"); + return true; + } + return false; + } + return true; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeIndexer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeIndexer.java similarity index 99% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeIndexer.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeIndexer.java index 057579e4a1..c1b9875a62 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeIndexer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeIndexer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.datamgr.archive; +package ghidra.app.plugin.core.datamgr; import java.util.*; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java index 555cec383b..840ed472c6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java @@ -32,6 +32,7 @@ import docking.Tool; import docking.action.*; import docking.action.builder.ActionBuilder; import docking.actions.PopupActionProvider; +import docking.widgets.OptionDialog; import docking.widgets.tree.GTreeNode; import generic.jar.ResourceFile; import generic.util.Path; @@ -39,35 +40,32 @@ import ghidra.app.CorePluginPackage; import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.ProgramPlugin; import ghidra.app.plugin.core.datamgr.actions.RecentlyOpenedArchiveAction; -import ghidra.app.plugin.core.datamgr.actions.UpdateSourceArchiveNamesAction; import ghidra.app.plugin.core.datamgr.actions.associate.*; -import ghidra.app.plugin.core.datamgr.archive.*; +import ghidra.app.plugin.core.datamgr.archive.DuplicateIdException; +import ghidra.app.plugin.core.datamgr.archive.InvalidArchive; import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager; -import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; +import ghidra.app.plugin.core.datamgr.tree.DataTypeStoreNode; import ghidra.app.plugin.core.datamgr.tree.DtFilterState; -import ghidra.app.plugin.core.datamgr.util.DataDropOnBrowserHandler; -import ghidra.app.plugin.core.datamgr.util.DataTypeChooserDialog; +import ghidra.app.plugin.core.datamgr.util.*; import ghidra.app.services.*; -import ghidra.app.util.HelpTopics; import ghidra.app.util.datatype.DataTypeSelectionDialog; import ghidra.framework.Application; -import ghidra.framework.main.OpenVersionedFileDialog; import ghidra.framework.model.*; import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.PluginInfo; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.util.PluginStatus; +import ghidra.framework.store.FileSystem; import ghidra.program.database.data.ProgramDataTypeManager; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.data.*; -import ghidra.program.model.listing.DataTypeArchive; +import ghidra.program.model.dtarchive.*; import ghidra.program.model.listing.Program; import ghidra.util.*; import ghidra.util.data.DataTypeParser.AllowedDataTypes; import ghidra.util.datastruct.LRUMap; import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskLauncher; import ghidra.util.task.TaskMonitor; /** @@ -95,8 +93,13 @@ public class DataTypeManagerPlugin extends ProgramPlugin private static final String STANDARD_ARCHIVE_MENU = "Standard Archive"; private static final String RECENTLY_OPENED_MENU = "Recently Opened Archive"; + private final static String ARCHIVE_NAMES = "ArchiveNames"; + private static final String PROJECT_NAME_DELIMETER = ":"; + private final static String RECENT_NAMES = "RecentArchiveNames"; + private static final String FAVORITES = "Favorite Dts"; + private static final String RELATIVE_PATH_PREFIX = "."; - private DataTypeManagerHandler dataTypeManagerHandler; + private ArchiveManager archiveManager; private DataTypesProvider provider; private Map recentlyOpenedArchiveMap; @@ -104,6 +107,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin private Clipboard clipboard = new Clipboard(getName()); private DataTypeEditorManager editorManager; private DataTypePropertyManager dataTypePropertyManager; + private RecentlyUsedDataType recentlyUsedDataType = new RecentlyUsedDataType(); public DataTypeManagerPlugin(PluginTool tool) { super(tool); @@ -122,43 +126,59 @@ public class DataTypeManagerPlugin extends ProgramPlugin } }; - dataTypeManagerHandler = new DataTypeManagerHandler(this); + archiveManager = new ArchiveManager(this); dataTypePropertyManager = new DataTypePropertyManager(); provider = new DataTypesProvider(this, "DataTypes Provider"); createActions(); - dataTypeManagerHandler.addArchiveManagerListener(new ArchiveManagerListener() { + archiveManager.addArchiveManagerListener(new ArchiveManagerListener() { @Override - public void archiveClosed(Archive archive) { - if (archive instanceof ProjectArchive) { - ProjectArchive projectArchive = (ProjectArchive) archive; - projectArchive.getDomainObject().removeListener(DataTypeManagerPlugin.this); + public void archiveClosed(PersistentDataTypeArchive archive) { + editorManager.dismissEditors(archive.getDataTypeManager()); + tool.setConfigChanged(true); + if (archive instanceof ProjectDataTypeArchive projectArchive) { + projectArchive.removeListener(DataTypeManagerPlugin.this); } - - provider.archiveClosed(archive.getDataTypeManager()); + provider.archiveClosed(archive); } @Override - public void archiveOpened(Archive archive) { - if (archive instanceof FileArchive) { - addRecentlyOpenedArchiveFile(((FileArchive) archive).getFile()); + public void archiveOpened(PersistentDataTypeArchive archive) { + tool.setConfigChanged(true); + if (archive instanceof FileDataTypeArchive fileArchive) { + addRecentlyOpenedArchiveFile(fileArchive.getFile()); } - else if (archive instanceof ProjectArchive) { - ProjectArchive projectArchive = (ProjectArchive) archive; - projectArchive.getDomainObject().addListener(DataTypeManagerPlugin.this); - addRecentlyOpenedProjectArchive((ProjectArchive) archive); + else if (archive instanceof ProjectDataTypeArchive projectArchive) { + projectArchive.addListener(DataTypeManagerPlugin.this); + addRecentlyOpenedProjectArchive(projectArchive); } } @Override - public void archiveDataTypeManagerChanged(Archive archive) { - provider.archiveChanged(archive); + public void programOpened(Program program) { + // don't care } @Override - public void archiveStateChanged(Archive archive) { - provider.archiveChanged(archive); + public void programClosed(Program program) { + // don't care } + + @Override + public void invalidArchiveAdded(InvalidArchive invalidArchive) { + // don't care + } + + @Override + public void invalidArchiveRemoved(InvalidArchive invalidArchive) { + // don't care + } + + @Override + public void stateChanged(DataTypeStore store) { + provider.archiveChanged(store.getName()); + } + }); editorManager = new DataTypeEditorManager(this); @@ -187,7 +207,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin } Path path = new Path(file); String absoluteFilePath = path.getPathAsString(); - if (!absoluteFilePath.endsWith(FileDataTypeManager.SUFFIX)) { + if (!absoluteFilePath.endsWith(FileDataTypeArchive.SUFFIX)) { // ignore invalid archive files return; } @@ -209,7 +229,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin * @param pathname the pathname */ public void addRecentlyOpenedProjectArchive(String projectName, String pathname) { - String projectPathname = DataTypeManagerHandler.getProjectPathname(projectName, pathname); + String projectPathname = getProjectPathname(projectName, pathname); if (recentlyOpenedArchiveMap.get(projectPathname) != null) { return; } @@ -230,8 +250,8 @@ public class DataTypeManagerPlugin extends ProgramPlugin * remembered). * @param pa project archive */ - public void addRecentlyOpenedProjectArchive(ProjectArchive pa) { - String projectPathname = dataTypeManagerHandler.getProjectPathname(pa, true); + public void addRecentlyOpenedProjectArchive(ProjectDataTypeArchive pa) { + String projectPathname = getProjectPathname(pa, true); if (projectPathname != null) { // projectPathname will be null if we can't remember it DomainFile df = pa.getDomainFile(); addRecentlyOpenedProjectArchive(df.getProjectLocator().getName(), df.getPathname()); @@ -249,48 +269,174 @@ public class DataTypeManagerPlugin extends ProgramPlugin Project project = tool.getProjectManager().getActiveProject(); if (project != null && project.getName().equals(projectName)) { DomainFile df = project.getProjectData().getFile(pathname); - if (df != null && DataTypeArchive.class.isAssignableFrom(df.getDomainObjectClass())) { + if (df != null && + ProjectDataTypeArchive.class.isAssignableFrom(df.getDomainObjectClass())) { return df; } } return null; } - /** - * A collection of files that have recently been opened by the user. - * @return A collection of files that have recently been opened by the user. - */ - public Collection getRecentlyOpenedArchives() { - return Collections.unmodifiableSet(recentlyOpenedArchiveMap.keySet()); - } - @Override public void dispose() { tool.removePopupActionProvider(this); - dataTypeManagerHandler.closeAllArchives(); - dataTypeManagerHandler.dispose(); + archiveManager.dispose(); } @Override public void readConfigState(SaveState saveState) { - dataTypeManagerHandler.restore(saveState); + restoreArchiveNames(saveState); + restoreRecentlyOpenedArchiveNames(saveState); + restoreFavorites(saveState); provider.restore(saveState); } @Override public void writeConfigState(SaveState saveState) { - dataTypeManagerHandler.save(saveState); + saveArchiveNames(saveState); + saveRecentlyOpenedArchiveNames(saveState); + saveFavorites(saveState); provider.save(saveState); } + private void restoreRecentlyOpenedArchiveNames(SaveState saveState) { + String[] recentFilenames = saveState.getStrings(RECENT_NAMES, null); + List archivePaths = resolveArchivePaths(recentFilenames); + for (String path : archivePaths) { + if (path.startsWith(PROJECT_NAME_DELIMETER)) { + String[] projectPathname = parseProjectPathname(path); + if (projectPathname != null) { + addRecentlyOpenedProjectArchive(projectPathname[0], projectPathname[1]); + } + } + else { + ResourceFile file = new ResourceFile(path); + if (file.exists()) { + file = file.getCanonicalFile(); + addRecentlyOpenedArchiveFile(file); + } + } + } + } + + private void restoreFavorites(SaveState saveState) { + String[] names = saveState.getStrings(FAVORITES, new String[0]); + if (names.length == 0) { + return; + } + BuiltInDataTypeManager builtinDtm = BuiltInDataTypeManager.getDataTypeManager(); + Set favorites = new HashSet<>(); + for (String dtName : names) { + DataType dataType = builtinDtm.getDataType(dtName); + if (dataType != null) { + favorites.add(dataType); + } + } + + List currentFavoritesList = builtinDtm.getFavorites(); + for (DataType type : currentFavoritesList) { + if (favorites.contains(type)) { + favorites.remove(type); + } + else { + builtinDtm.setFavorite(type, false); + } + } + for (DataType dataType : favorites) { + builtinDtm.setFavorite(dataType, true); + } + } + + private void restoreArchiveNames(SaveState saveState) { + String[] savedFilenames = saveState.getStrings(ARCHIVE_NAMES, new String[0]); + List archivePaths = resolveArchivePaths(savedFilenames); + openArchives(archivePaths); + + } + + private void openArchives(List archiveFilenames) { + for (String filename : archiveFilenames) { + String[] projectPathname = parseProjectPathname(filename); + if (projectPathname != null) { + DomainFile df = getProjectArchiveFile(projectPathname[0], projectPathname[1]); + if (df != null) { + archiveManager.openProjectArchiveInTask(df, DomainFile.DEFAULT_VERSION, + Upgrade.ASK, Recover.ASK, true); + } + } + else { + File file = new File(filename); + if (!file.exists()) { + continue; // if the file does not exist, skip it. + } + archiveManager.openFileArchiveInTask(new ResourceFile(file), false, Upgrade.ASK, + true); + } + } + } + + private void saveRecentlyOpenedArchiveNames(SaveState saveState) { + List recentMenuList = new ArrayList<>(); + for (String file : recentlyOpenedArchiveMap.keySet()) { + recentMenuList.add(file); + } + saveState.putStrings(RECENT_NAMES, getSaveableArchiveNames(recentMenuList)); + } + + private void saveArchiveNames(SaveState saveState) { + List rememberedArchivePaths = new ArrayList<>(); + Set rememberedArchives = archiveManager.getRememberedArchives(); + for (PersistentDataTypeArchive archive : rememberedArchives) { + String filePath = null; + if (archive instanceof FileDataTypeArchive fileArchive) { + ResourceFile file = fileArchive.getFile(); + rememberedArchivePaths.add(file.getAbsolutePath()); + } + else if (archive instanceof ProjectDataTypeArchive projectArchive) { + filePath = getProjectPathname(projectArchive, true); + if (filePath != null) { + rememberedArchivePaths.add(filePath); + } + } + } + saveState.putStrings(ARCHIVE_NAMES, getSaveableArchiveNames(rememberedArchivePaths)); + } + + private String[] getSaveableArchiveNames(List absoluteFilenameList) { + String[] saveableFilenames = new String[absoluteFilenameList.size()]; + for (int i = 0; i < absoluteFilenameList.size(); i++) { + saveableFilenames[i] = getSaveableArchive(absoluteFilenameList.get(i)); + } + return saveableFilenames; + } + + private String getSaveableArchive(String absoluteFilename) { + if (absoluteFilename.startsWith(PROJECT_NAME_DELIMETER)) { + return absoluteFilename; + } + Path path = new Path(absoluteFilename); + return path.getPathAsString(); + } + + void saveFavorites(SaveState saveState) { + BuiltInDataTypeManager builtInDtm = BuiltInDataTypeManager.getDataTypeManager(); + List favoritesList = builtInDtm.getFavorites(); + String[] names = new String[favoritesList.size()]; + for (int i = 0; i < names.length; i++) { + DataType dataType = favoritesList.get(i); + names[i] = dataType.getPathName(); + } + + saveState.putStrings(FAVORITES, names); + } + @Override public void domainObjectChanged(DomainObjectChangedEvent event) { if (event.contains(DomainObjectEvent.RESTORED)) { Object source = event.getSource(); - if (source instanceof DataTypeManagerDomainObject) { - DataTypeManagerDomainObject domainObject = (DataTypeManagerDomainObject) source; - provider.domainObjectRestored(domainObject); - dataTypePropertyManager.domainObjectRestored(domainObject); + if (source instanceof PersistentDataTypeArchive archive) { + provider.domainObjectRestored(archive); + dataTypePropertyManager.domainObjectRestored(archive); // NOTE: each editor that cares about a restored DataTypeManager must establish // a DataTypeManagerChangeListener and will be notified via the restored method. } @@ -303,7 +449,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin @Override protected void programDeactivated(Program program) { program.removeListener(this); - dataTypeManagerHandler.programClosed(); + archiveManager.setProgram(null); dataTypePropertyManager.programClosed(program); } @@ -311,7 +457,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin protected void programActivated(Program program) { program.addListener(this); provider.programActivated(program); - dataTypeManagerHandler.programOpened(program); + archiveManager.setProgram(program); dataTypePropertyManager.programOpened(program); } @@ -325,9 +471,11 @@ public class DataTypeManagerPlugin extends ProgramPlugin @Override protected boolean canCloseDomainObject(DomainObject dObj) { - if (dObj instanceof DataTypeManagerDomainObject) { - DataTypeManagerDomainObject dtmObject = (DataTypeManagerDomainObject) dObj; - return editorManager.checkEditors(dtmObject.getDataTypeManager(), true); + if (dObj instanceof PersistentDataTypeArchive archive) { + return editorManager.checkEditors(archive.getDataTypeManager(), true); + } + else if (dObj instanceof Program program) { + return editorManager.checkEditors(program.getDataTypeManager(), true); } return true; } @@ -338,21 +486,45 @@ public class DataTypeManagerPlugin extends ProgramPlugin return false; } editorManager.dismissEditors(null); - dataTypeManagerHandler.updateKnownOpenArchives(); - if (!ArchiveUtils.canClose(dataTypeManagerHandler.getAllModifiedFileArchives(), - provider.getComponent())) { - return false; + List archives = archiveManager.getOpenFileArchives(); + for (FileDataTypeArchive archive : archives) { + if (!resolveModifiedArchive(archive)) { + return false; + } } return true; } + /** + * Prompts the user to save any changes to the given archive in preparation for closing. + * @param archive the archive that is to be prompted to save + * @return true if the archive can be closed (The user either saved it or declined to save it). + * Only returns false if the user cancelled the operation. + */ + public boolean resolveModifiedArchive(PersistentDataTypeArchive archive) { + + if (!archive.isChanged()) { + return true; + } + + int result = + OptionDialog.showYesNoCancelDialog(null, "Save Archive?", "Datatype Archive \"" + + archive.getName() + "\" has been changed.\n Do you want to save the changes?"); + + if (result == OptionDialog.YES_OPTION) { + archiveManager.save(archive); + } + + return result != OptionDialog.CANCEL_OPTION; + } + @Override protected void close() { provider.dispose(); } - public DataTypeManagerHandler getDataTypeManagerHandler() { - return dataTypeManagerHandler; + public ArchiveManager getArchiveManager() { + return archiveManager; } public DataTypeEditorManager getEditorManager() { @@ -425,7 +597,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin private void createStandardArchivesMenu() { installArchiveMap = new TreeMap<>(); - String gdt = FileDataTypeManager.SUFFIX; + String gdt = FileDataTypeArchive.SUFFIX; List gdts = Application.findFilesByExtensionInApplication(gdt); for (ResourceFile archiveFile : gdts) { Path path = new Path(archiveFile); @@ -505,7 +677,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin @Override public DataTypeManager getBuiltInDataTypesManager() { - return dataTypeManagerHandler.getBuiltInDataTypesManager(); + return BuiltInDataTypeManager.getDataTypeManager(); } public DataTypeManager getProgramDataTypeManager() { @@ -609,77 +781,79 @@ public class DataTypeManagerPlugin extends ProgramPlugin @Override public DataTypeManager[] getDataTypeManagers() { - return dataTypeManagerHandler.getDataTypeManagers(); + return archiveManager.getDataTypeManagers(); + } + + @Override + public List getDataTypeArchives() { + return archiveManager.getOpenArchives(); } @Override public void closeArchive(DataTypeManager dtm) { - dataTypeManagerHandler.closeArchive(dtm); - provider.archiveClosed(dtm); + DataTypeStore dataStore = dtm.getDataStore(); + if (dataStore instanceof PersistentDataTypeArchive archive) { + archiveManager.closeArchive(archive); + provider.archiveClosed(archive); + } } @Override - public DataTypeManager openDataTypeArchive(String archiveName) - throws IOException, DuplicateIdException { - return dataTypeManagerHandler.openArchive(archiveName); + public void closeArchive(PersistentDataTypeArchive archive) { + archiveManager.closeArchive(archive); + provider.archiveClosed(archive); } @Override - public DataTypeManager openArchive(ResourceFile file, boolean acquireWriteLock) - throws IOException, DuplicateIdException { - Archive archive = openArchive(file.getFile(true), acquireWriteLock); - return archive.getDataTypeManager(); + public FileDataTypeArchive openFileArchive(String archiveName, TaskMonitor monitor) + throws IOException, DuplicateIdException, CancelledException { + ResourceFile file = DataTypeArchiveUtility.findArchiveFile(archiveName); + if (file != null) { + try { + return openFileArchive(file, false, Upgrade.NO, monitor); + } + catch (VersionException e) { + throw new IOException(e); // legacy service method doesn't throw VersionException + } + } + return null; } @Override - public DataTypeManager openArchive(DomainFile domainFile, TaskMonitor monitor) + public FileDataTypeArchive openFileArchive(ResourceFile resourceFile, boolean openForUpdate, + Upgrade upgradeStrategy, TaskMonitor monitor) + throws IOException, VersionException, DuplicateIdException, CancelledException { + + return archiveManager.openFileArchive(resourceFile, openForUpdate, upgradeStrategy, false, + monitor); + } + + @Override + public ProjectDataTypeArchive openProjectArchive(DomainFile domainFile, Upgrade upgradeStrategy, + Recover recoverStrategy, TaskMonitor monitor) throws VersionException, CancelledException, IOException, DuplicateIdException { - DataTypeArchive archive = openArchive(domainFile); - return archive.getDataTypeManager(); - } - - public List getAllArchives() { - return dataTypeManagerHandler.getAllArchives(); - } - - public void openProjectDataTypeArchive() { - - OpenVersionedFileDialog dialog = new OpenVersionedFileDialog<>(tool, - "Open Project Data Type Archive", DataTypeArchive.class); - dialog.setHelpLocation(new HelpLocation(HelpTopics.PROGRAM, "Open_File_Dialog")); - dialog.addOkActionListener(ev -> { - DomainFile domainFile = dialog.getDomainFile(); - int version = dialog.getVersion(); - if (domainFile == null) { - dialog.setStatusText("Please choose a Project Data Type Archive"); - } - else { - dialog.close(); - openArchive(domainFile, version); - } - }); - - tool.showDialog(dialog); + return archiveManager.openProjectArchive(domainFile, DomainFile.DEFAULT_VERSION, + upgradeStrategy, recoverStrategy, false, monitor); } @Override public List getFavorites() { - return dataTypeManagerHandler.getFavoriteDataTypes(); + return archiveManager.getFavoriteDataTypes(); } @Override public DataType getRecentlyUsed() { - return dataTypeManagerHandler.getRecentlyDataType(); + return recentlyUsedDataType.getDataType(); } @Override public List getSortedDataTypeList() { - return dataTypeManagerHandler.getDataTypeIndexer().getSortedDataTypeList(); + return archiveManager.getSortedDataTypeList(); } @Override public List getSortedCategoryPathList() { - return dataTypeManagerHandler.getDataTypeIndexer().getSortedCategoryPathList(); + return archiveManager.getSortedCategoryPathList(); } @Override @@ -710,27 +884,33 @@ public class DataTypeManagerPlugin extends ProgramPlugin @Override public void setRecentlyUsed(DataType dt) { - dataTypeManagerHandler.setRecentlyUsedDataType(dt); + recentlyUsedDataType = new RecentlyUsedDataType(dt); + } @Override public void addDataTypeManagerChangeListener(DataTypeManagerChangeListener listener) { - dataTypeManagerHandler.addDataTypeManagerChangeListener(listener); + archiveManager.addDataTypeManagerChangeListener(listener); } @Override public void removeDataTypeManagerChangeListener(DataTypeManagerChangeListener listener) { - dataTypeManagerHandler.removeDataTypeManagerChangeListener(listener); + archiveManager.removeDataTypeManagerChangeListener(listener); } @Override public Set getPossibleEquateNames(long value) { - return dataTypeManagerHandler.getPossibleEquateNames(value); + Set equateNames = new HashSet<>(); + DataTypeManager[] dataTypeManagers = archiveManager.getDataTypeManagers(); + for (DataTypeManager dtm : dataTypeManagers) { + dtm.findEnumValueNames(value, equateNames); + } + return equateNames; } @Override public Class[] getSupportedDataTypes() { - return new Class[] { DataTypeArchive.class }; + return new Class[] { ProjectDataTypeArchive.class }; } @Override @@ -740,10 +920,12 @@ public class DataTypeManagerPlugin extends ProgramPlugin } boolean addedArchives = false; - for (DomainFile element : data) { - if (element != null && - DataTypeArchive.class.isAssignableFrom(element.getDomainObjectClass())) { - openArchive(element); + for (DomainFile df : data) { + if (df != null && + ProjectDataTypeArchive.class.isAssignableFrom(df.getDomainObjectClass())) { + archiveManager.openProjectArchiveInTask(df, DomainFile.DEFAULT_VERSION, Upgrade.ASK, + Recover.ASK, true); + addedArchives = true; } } @@ -760,44 +942,22 @@ public class DataTypeManagerPlugin extends ProgramPlugin SwingUtilities.invokeLater(() -> tool.toFront(provider)); } - public DataTypeArchive openArchive(DomainFile df) { - return openArchive(df, -1); - } - - public DataTypeArchive openArchive(final DomainFile df, final int version) { - OpenDomainFileTask task = - new OpenDomainFileTask(df, version, tool, DataTypeManagerPlugin.this); - new TaskLauncher(task, tool.getToolFrame(), 0); - return task.getArchive(); - } - public boolean commit(DataType dataType) { - return DataTypeSynchronizer.commit(dataTypeManagerHandler, dataType); + return DataTypeSynchronizer.commit(archiveManager, dataType); } public boolean update(DataType dataType) { - return DataTypeSynchronizer.update(dataTypeManagerHandler, dataType); + return DataTypeSynchronizer.update(archiveManager, dataType); } public boolean revert(DataType dataType) { - return DataTypeSynchronizer.update(dataTypeManagerHandler, dataType); + return DataTypeSynchronizer.update(archiveManager, dataType); } public void disassociate(DataType dataTypes) { DataTypeSynchronizer.disassociate(dataTypes); } - @Override - public Archive openArchive(DataTypeArchive dataTypeArchive) { - return dataTypeManagerHandler.openArchive(dataTypeArchive); - } - - @Override - public Archive openArchive(File file, boolean acquireWriteLock) - throws IOException, DuplicateIdException { - return dataTypeManagerHandler.openArchive(file, acquireWriteLock, false); - } - public AddressSetView getCurrentSelection() { return currentSelection; } @@ -809,15 +969,13 @@ public class DataTypeManagerPlugin extends ProgramPlugin } DataTypesActionContext dtContext = (DataTypesActionContext) context; GTreeNode selectedNode = dtContext.getClickedNode(); - if (!(selectedNode instanceof ArchiveNode)) { + if (!(selectedNode instanceof DataTypeStoreNode archiveNode)) { return null; } List actions = new ArrayList<>(); - ArchiveNode archiveNode = (ArchiveNode) selectedNode; - Archive archive = archiveNode.getArchive(); - DataTypeManager dataTypeManager = archive.getDataTypeManager(); + DataTypeManager dataTypeManager = archiveNode.getDataTypeManager(); if (dataTypeManager == null) { return null; } @@ -825,18 +983,18 @@ public class DataTypeManagerPlugin extends ProgramPlugin String group = "FGroup"; // after 'Edit' List sourceArchives = dataTypeManager.getSourceArchives(); for (SourceArchive sourceArchive : sourceArchives) { - DataTypeManager sourceDTM = dataTypeManagerHandler.getDataTypeManager(sourceArchive); + DataTypeManager sourceDTM = archiveManager.getDataTypeManager(sourceArchive); boolean canUpdate = canUpdate(sourceArchive, sourceDTM); boolean canCommit = canCommit(sourceArchive, sourceDTM); - actions.add(new SyncRefreshAction(this, dataTypeManagerHandler, dataTypeManager, + actions.add(new SyncRefreshAction(this, archiveManager, dataTypeManager, archiveNode, sourceArchive, true)); - actions.add(new CommitAction(this, dataTypeManagerHandler, dataTypeManager, archiveNode, + actions.add(new CommitAction(this, archiveManager, dataTypeManager, archiveNode, sourceArchive, canCommit)); - actions.add(new UpdateAction(this, dataTypeManagerHandler, dataTypeManager, archiveNode, + actions.add(new UpdateAction(this, archiveManager, dataTypeManager, archiveNode, sourceArchive, canUpdate)); - actions.add(new RevertAction(this, dataTypeManagerHandler, dataTypeManager, archiveNode, + actions.add(new RevertAction(this, archiveManager, dataTypeManager, archiveNode, sourceArchive, canCommit)); - actions.add(new DisassociateAction(this, dataTypeManagerHandler, dataTypeManager, + actions.add(new DisassociateAction(this, archiveManager, dataTypeManager, archiveNode, sourceArchive)); } @@ -846,11 +1004,6 @@ public class DataTypeManagerPlugin extends ProgramPlugin String pullRightName = popupData.getMenuPath()[0]; tool.setMenuGroup(new String[] { pullRightName }, group); } - - UpdateSourceArchiveNamesAction action = - new UpdateSourceArchiveNamesAction(this, dataTypeManager); - action.getPopupMenuData().setMenuGroup(group); - actions.add(action); return actions; } @@ -875,10 +1028,10 @@ public class DataTypeManagerPlugin extends ProgramPlugin // Program Manager will take care of programs. // We need to take care of Project Data Type Archives. List domainFileList = new ArrayList<>(); - List allArchives = dataTypeManagerHandler.getAllArchives(); - for (Archive archive : allArchives) { - if (archive instanceof ProjectArchive && archive.isModifiable()) { - domainFileList.add(((ProjectArchive) archive).getDomainFile()); + List allArchives = archiveManager.getProjectArchives(); + for (ProjectDataTypeArchive archive : allArchives) { + if ((archive instanceof ProjectDataTypeArchive pa) && archive.isChangeable()) { + domainFileList.add(pa.getDomainFile()); } } return domainFileList.toArray(new DomainFile[domainFileList.size()]); @@ -886,10 +1039,173 @@ public class DataTypeManagerPlugin extends ProgramPlugin @Override protected boolean saveData() { - if (!ArchiveUtils.canClose(dataTypeManagerHandler.getAllFileOrProjectArchives(), - provider.getComponent())) { - return false; + // Note: project archives are saved via this method and file archives are saved via + // the canClose() method. The reason is to prevent duplicate asks to save an archive. + // + // There is an inconsistency between these methods getting called depending on whether just + // a tool is being closed or the front-end is closing. This method is only called in the + // case when just a tool is closing. When the front-end is closing, this call is skipped + // and instead a generic dialog for any changed project domain object (which includes + // programs and project datatype archives) is called. + // + // So, if we were to try and save file archives here, they wouldn't get saved since this + // method isn't called when the front-end is closed. + + List archives = archiveManager.getProjectArchives(); + for (ProjectDataTypeArchive archive : archives) { + if (!resolveModifiedArchive(archive)) { + return false; + } } return true; } + + /** + * Create project archive path string for recently used project archive + * @param projectName the project name + * @param pathname the pathname used to create the final path + * @return recently used project pathname string + */ + public static String getProjectPathname(String projectName, String pathname) { + if (pathname.length() < 2 || !pathname.startsWith(FileSystem.SEPARATOR)) { + throw new IllegalArgumentException("Absolute project pathname required"); + } + return PROJECT_NAME_DELIMETER + projectName + PROJECT_NAME_DELIMETER + pathname; + } + + /** + * Determine if we can remember the specified project archive using a simple project path + * (e.g., we can't remember specific versions). + * @param pa project archive + * @param activeProjectOnly if true pa must be contained within the + * active project to be remembered. + * @return return project path which can be remembered or null + */ + public String getProjectPathname(ProjectDataTypeArchive pa, boolean activeProjectOnly) { + // Project archives are always opened by a user. + // Only remember it if it is the current version within the current project + DomainFile df = pa.getDomainFile(); + ProjectLocator projectLocator = df.getProjectLocator(); + String projectName = projectLocator.getName(); + boolean remember = df.isInWritableProject(); + if (!remember) { + // handle read-only case + Project project = tool.getProjectManager().getActiveProject(); + remember = (project != null && project.getName().equals(projectName) && + df.getVersion() == DomainFile.DEFAULT_VERSION); + } + return remember ? getProjectPathname(projectName, df.getPathname()) : null; + } + + private static List resolveArchivePaths(String[] savedArchivePaths) { + if (savedArchivePaths == null) { + return Collections.emptyList(); + } + List resolvedPaths = new ArrayList<>(); + for (String archivePath : savedArchivePaths) { + archivePath = adjustArchivePath(archivePath); + if (archivePath != null) { + resolvedPaths.add(archivePath); + } + } + return resolvedPaths; + } + + private static String adjustArchivePath(String pathName) { + if (pathName.startsWith(PROJECT_NAME_DELIMETER)) { + return pathName; + } + if (pathName.startsWith(RELATIVE_PATH_PREFIX)) { + ResourceFile file = DataTypeArchiveUtility.findArchiveFile(pathName); + if (file == null) { + Msg.error(ArchiveManager.class, "Archive not found: " + pathName); + return null; + } + return file.getAbsolutePath(); + } + Path path = new Path(pathName); + return path.getPath().getAbsolutePath(); + } + + /** + * Parse a recently used project pathname string + * @param projectFilePath project pathname string + * @return 2-element String array containing project name and pathname of project archive, or null if path is invalid + */ + public static String[] parseProjectPathname(String projectFilePath) { + if (projectFilePath.startsWith(PROJECT_NAME_DELIMETER)) { + int index = projectFilePath.indexOf(PROJECT_NAME_DELIMETER, 1); + if (index > 0) { + String projectName = projectFilePath.substring(1, index); + String pathname = projectFilePath.substring(index + 1); + if (pathname.length() > 1 && pathname.startsWith(FileSystem.SEPARATOR)) { + return new String[] { projectName, pathname }; + } + } + } + return null; + } + + class RecentlyUsedDataType { + + private String dataTypeManagerName; + private CategoryPath path; + private String dataTypeName; + + RecentlyUsedDataType() { + // default constructor + } + + RecentlyUsedDataType(DataType dt) { + dataTypeName = dt.getName(); + path = dt.getCategoryPath(); + DataTypeManager dtMgr = dt.getDataTypeManager(); + dataTypeManagerName = dtMgr == null ? null : dtMgr.getName(); + + if (dataTypeManagerName == null && currentProgram != null) { + DataTypeManager programDataTypeManager = currentProgram.getDataTypeManager(); + dataTypeManagerName = programDataTypeManager.getName(); + } + } + + public DataType getDataType() { + if (dataTypeName == null) { + return null; + } + DataTypeManager dtMgr = findDataTypeManager(); + Category category = dtMgr.getCategory(path); + if (category != null) { + DataType dt = category.getDataType(dataTypeName); + if (dt != null) { + return dt; + } + } + return getBuiltInDataType(); + } + + private DataType getBuiltInDataType() { + DataTypeManager dtMgr = getBuiltInDataTypesManager(); + Category category = dtMgr.getCategory(path); + if (category != null) { + DataType dt = category.getDataType(dataTypeName); + if (dt != null) { + return dt; + } + } + return null; + } + + private DataTypeManager findDataTypeManager() { + if (currentProgram != null && currentProgram.getName().equals(dataTypeManagerName)) { + return currentProgram.getDataTypeManager(); + } + for (PersistentDataTypeArchive archive : archiveManager.getOpenArchives()) { + if (archive.getName().equals(dataTypeManagerName)) { + return archive.getDataTypeManager(); + } + } + return BuiltInDataTypeManager.getDataTypeManager(); + } + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypePropertyManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypePropertyManager.java index 6ca8fdb80d..b9b85304b2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypePropertyManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypePropertyManager.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,18 +15,19 @@ */ package ghidra.app.plugin.core.datamgr; -import ghidra.program.model.data.*; -import ghidra.program.model.listing.Program; -import ghidra.util.SystemUtilities; -import ghidra.util.datastruct.WeakDataStructureFactory; -import ghidra.util.datastruct.WeakSet; - import java.awt.event.KeyEvent; import javax.swing.KeyStroke; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.program.model.listing.Program; +import ghidra.util.SystemUtilities; +import ghidra.util.datastruct.WeakDataStructureFactory; +import ghidra.util.datastruct.WeakSet; + /** * Manages the attributes for data types; used by the manage data types * dialog to populate the data types tree structure. @@ -110,8 +110,8 @@ public class DataTypePropertyManager { programDataTypesManager = null; } - void domainObjectRestored(DataTypeManagerDomainObject domainObject) { - DataTypeManager dataTypeManager = domainObject.getDataTypeManager(); + void domainObjectRestored(PersistentDataTypeArchive archive) { + DataTypeManager dataTypeManager = archive.getDataTypeManager(); if (dataTypeManager != programDataTypesManager) { return; // Ignore since not our program data type manager. } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java index 610f32d0e6..a9b34cd899 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java @@ -25,7 +25,6 @@ import javax.swing.SwingUtilities; import org.apache.commons.lang3.Strings; import docking.widgets.label.GDHtmlLabel; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.util.ToolTipUtils; import ghidra.app.util.html.HTMLDataTypeRepresentation; import ghidra.app.util.html.MissingArchiveDataTypeHTMLRepresentation; @@ -48,17 +47,17 @@ public class DataTypeSynchronizer { /** * Creates a DataTypeSynchronizer to be used for synchronizing data types between a program * and an archive. - * @param dataTypeManagerHandler the handler that manages all the open data type managers + * @param archiveManager the handler that manages all the open data type managers * whether built-in, program, project data type archive or file data type archive. * @param dataTypeManager the program data type manager. * @param source the data type source archive information indicating the associated archive for * synchronizing. */ - public DataTypeSynchronizer(DataTypeManagerHandler dataTypeManagerHandler, - DataTypeManager dataTypeManager, SourceArchive source) { + public DataTypeSynchronizer(ArchiveManager archiveManager, DataTypeManager dataTypeManager, + SourceArchive source) { this.dataTypeManager = dataTypeManager; this.sourceArchive = source; - this.sourceDTM = dataTypeManagerHandler.getDataTypeManager(source); + this.sourceDTM = archiveManager.getDataTypeManager(source); } public List findOutOfSynchDataTypes() { @@ -150,13 +149,13 @@ public class DataTypeSynchronizer { /** * Commits a single program data type's changes to the associated source data type in the * archive. - * @param dtmHandler the handler that manages data types + * @param archiveManager the archive manager * @param dt the program data type * @return true if the commit succeeds */ - public static boolean commit(DataTypeManagerHandler dtmHandler, DataType dt) { + public static boolean commit(ArchiveManager archiveManager, DataType dt) { SourceArchive sourceArchive = dt.getSourceArchive(); - DataTypeManager sourceDTM = dtmHandler.getDataTypeManager(sourceArchive); + DataTypeManager sourceDTM = archiveManager.getDataTypeManager(sourceArchive); if (sourceDTM == null) { return false; } @@ -167,14 +166,14 @@ public class DataTypeSynchronizer { /** * Updates a single data type in the program to match the associated source data type from the * archive. - * @param dtmHandler the handler that manages data types + * @param archiveManager the handler that manages data types * @param dt the data type * @return true if the update succeeds */ - public static boolean update(DataTypeManagerHandler dtmHandler, DataType dt) { + public static boolean update(ArchiveManager archiveManager, DataType dt) { DataTypeManager dataTypeManager = dt.getDataTypeManager(); SourceArchive sourceArchive = dt.getSourceArchive(); - DataTypeManager sourceDtm = dtmHandler.getDataTypeManager(sourceArchive); + DataTypeManager sourceDtm = archiveManager.getDataTypeManager(sourceArchive); if (dataTypeManager == null || sourceDtm == null) { return false; } @@ -275,7 +274,7 @@ public class DataTypeSynchronizer { } - public static DataTypeSyncState getSyncStatus(DataTypeManagerHandler handler, + public static DataTypeSyncState getSyncStatus(ArchiveManager archiveManager, DataType dataType) { DataTypeManager dataTypeManager = dataType.getDataTypeManager(); SourceArchive sourceArchive = dataType.getSourceArchive(); @@ -287,7 +286,7 @@ public class DataTypeSynchronizer { boolean hasChangedLocally = dataType.getLastChangeTime() != dataType.getLastChangeTimeInSourceArchive(); - DataTypeManager sourceDTM = handler.getDataTypeManager(sourceArchive); + DataTypeManager sourceDTM = archiveManager.getDataTypeManager(sourceArchive); DataTypeSyncInfo syncInfo = new DataTypeSyncInfo(dataType, sourceDTM); if (sourceDTM == null) { return hasChangedLocally ? DataTypeSyncState.COMMIT : DataTypeSyncState.IN_SYNC; @@ -295,7 +294,7 @@ public class DataTypeSynchronizer { return syncInfo.getSyncState(); } - public static String getDiffToolTip(DataTypeManagerHandler handler, DataType dataType) { + public static String getDiffToolTip(ArchiveManager archiveManager, DataType dataType) { DataTypeManager dataTypeManager = dataType.getDataTypeManager(); SourceArchive sourceArchive = dataType.getSourceArchive(); UniversalID dataTypeID = dataType.getUniversalID(); @@ -304,7 +303,7 @@ public class DataTypeSynchronizer { return null; } - DataTypeManager sourceDTM = handler.getDataTypeManager(sourceArchive); + DataTypeManager sourceDTM = archiveManager.getDataTypeManager(sourceArchive); boolean hasChangedLocally = dataType.getLastChangeTime() != dataType.getLastChangeTimeInSourceArchive(); DataType sourceDT = null; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesActionContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesActionContext.java index 27868baaab..edf9a2837c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesActionContext.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesActionContext.java @@ -26,11 +26,11 @@ import docking.widgets.tree.GTreeNode; import docking.widgets.tree.support.GTreeNodeTransferable; import ghidra.app.context.ProgramActionContext; import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive; -import ghidra.app.plugin.core.datamgr.archive.ProjectArchive; import ghidra.app.plugin.core.datamgr.tree.*; import ghidra.framework.main.datatable.DomainFileContext; import ghidra.framework.model.DomainFile; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; import ghidra.program.model.listing.Program; public class DataTypesActionContext extends ProgramActionContext implements DomainFileContext { @@ -90,11 +90,11 @@ public class DataTypesActionContext extends ProgramActionContext implements Doma domainFiles = new ArrayList(); for (TreePath path : selectionPaths) { Object lastPathComponent = path.getLastPathComponent(); - if (lastPathComponent instanceof ProjectArchiveNode) { - ProjectArchiveNode node = (ProjectArchiveNode) lastPathComponent; - ProjectArchive archive = (ProjectArchive) node.getArchive(); - DomainFile originalDomainFile = archive.getDomainFile(); - domainFiles.add(originalDomainFile); + if (lastPathComponent instanceof ProjectArchiveNode node) { + DomainFile originalDomainFile = node.getOriginalDomainFile(); + if (originalDomainFile != null) { + domainFiles.add(originalDomainFile); + } } } } @@ -156,4 +156,17 @@ public class DataTypesActionContext extends ProgramActionContext implements Doma return dataTypeNode; } + public List getSelectedProjectArchives() { + List list = new ArrayList<>(); + + List selectedNodes = getSelectedNodes(); + for (GTreeNode node : selectedNodes) { + if (node instanceof ProjectArchiveNode projectNode) { + ProjectDataTypeArchive archive = projectNode.getArchive(); + list.add(archive); + } + } + return list; + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java index 84fc1f2832..fe001d375f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java @@ -38,19 +38,18 @@ import generic.theme.GIcon; import generic.theme.GThemeDefaults.Colors; import ghidra.app.plugin.core.datamgr.actions.*; import ghidra.app.plugin.core.datamgr.actions.associate.*; -import ghidra.app.plugin.core.datamgr.archive.*; import ghidra.app.plugin.core.datamgr.tree.*; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; import ghidra.app.util.ToolTipUtils; import ghidra.app.util.datatype.DataTypeUrl; -import ghidra.framework.main.datatree.ArchiveProvider; -import ghidra.framework.main.datatree.VersionControlDataTypeArchiveUndoCheckoutAction; import ghidra.framework.main.projectdata.actions.*; +import ghidra.framework.model.DomainFile; import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.program.model.data.*; import ghidra.program.model.data.DataTypeConflictHandler.ConflictResolutionPolicy; -import ghidra.program.model.listing.DataTypeArchive; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.program.model.listing.Program; import ghidra.util.*; import ghidra.util.task.SwingUpdateManager; @@ -172,12 +171,13 @@ public class DataTypesProvider extends ComponentProviderAdapter { // File group addLocalAction(new SaveArchiveAction(plugin)); // Archive + addLocalAction(new SaveArchiveAsAction(plugin)); addLocalAction(new CloseArchiveAction(plugin)); // Archive addLocalAction(new RemoveInvalidArchiveFromProgramAction(plugin)); // Archive // FileEdit group - addLocalAction(new LockArchiveAction(plugin)); // Archive - addLocalAction(new UnlockArchiveAction(plugin)); // Archive + addLocalAction(new OpenArchiveForEditingAction(plugin)); // Archive + addLocalAction(new CloseArchiveForEditingAction(plugin)); // Archive addLocalAction(new UndoArchiveTransactionAction(plugin)); // Archive addLocalAction(new RedoArchiveTransactionAction(plugin)); // Archive @@ -247,20 +247,6 @@ public class DataTypesProvider extends ComponentProviderAdapter { private void addVersionControlActions() { - ArchiveProvider archiveProvider = () -> { - TreePath[] selectionPaths = archiveGTree.getSelectionPaths(); - List selectedArchives = new ArrayList<>(); - for (TreePath path : selectionPaths) { - Object lastPathComponent = path.getLastPathComponent(); - if (lastPathComponent instanceof ProjectArchiveNode) { - ProjectArchiveNode node = (ProjectArchiveNode) lastPathComponent; - ProjectArchive archive = (ProjectArchive) node.getArchive(); - selectedArchives.add(archive); - } - } - return selectedArchives; - }; - VersionControlAddAction addAction = new VersionControlAddAction(plugin); addAction.setEnabled(false); @@ -274,8 +260,8 @@ public class DataTypesProvider extends ComponentProviderAdapter { new VersionControlCheckInAction(plugin, archiveGTree); checkInAction.setEnabled(false); - VersionControlDataTypeArchiveUndoCheckoutAction undoCheckOutAction = - new VersionControlDataTypeArchiveUndoCheckoutAction(plugin, archiveProvider); + ArchiveUndoCheckoutTask undoCheckOutAction = + new ArchiveUndoCheckoutTask(plugin); undoCheckOutAction.setEnabled(false); VersionControlShowHistoryAction showHistoryAction = @@ -572,35 +558,16 @@ public class DataTypesProvider extends ComponentProviderAdapter { } private static DataType updateDataType(CategoryPath path, String dataTypeName, - ArchiveNode archiveNode) { - DataTypeManager dataTypeManager = archiveNode.getArchive().getDataTypeManager(); + PersistentDataTypeArchive archive) { + if (archive == null) { + return null; + } + DataTypeManager dataTypeManager = archive.getDataTypeManager(); Category category = dataTypeManager.getCategory(path); return category.getDataType(dataTypeName); } - private boolean getWriteLock(DataTypeManagerPlugin dataTypePlugin, ArchiveNode archiveNode) { - if (!isOkToLock()) { - return false; - } - GTree tree = dataTypePlugin.getProvider().getGTree(); - GTreeState state = tree.getTreeState(); - if (!ArchiveUtils.lockArchive((FileArchive) archiveNode.getArchive())) { - return false; - } - tree.restoreTreeState(state); - - return true; - } - - private boolean needsWriteLock(ArchiveNode archiveNode) { - if (archiveNode instanceof FileArchiveNode) { - FileArchiveNode fileArchiveNode = (FileArchiveNode) archiveNode; - return !fileArchiveNode.hasWriteLock(); - } - return false; - } - - private boolean isOkToLock() { + private boolean askToOpenArchiveForUpdate() { return (OptionDialog.showYesNoDialog(archiveGTree, "Open Archive for Edit?", "Archive file is not modifiable.\nDo you want to open for edit?") == OptionDialog.OPTION_ONE); } @@ -623,30 +590,55 @@ public class DataTypesProvider extends ComponentProviderAdapter { dataType = DataTypeUtils.getBaseDataType(dataType); CategoryPath path = dataType.getCategoryPath(); String dataTypeName = dataType.getName(); - ArchiveNode archiveNode = dataTypeNode.getArchiveNode(); + DataTypeStoreNode archiveNode = dataTypeNode.getArchiveNode(); - if (archiveNode instanceof ProjectArchiveNode && !archiveNode.isModifiable()) { - ProjectArchiveNode projectArchive = (ProjectArchiveNode) archiveNode; - if (projectArchive.getDomainFile().isReadOnly()) { - Msg.showInfo(getClass(), archiveGTree, "Read-Only Archive", - "You may not edit data type within a read-only project archive."); - } - else { - Msg.showInfo(getClass(), archiveGTree, "Archive Not Checked Out", - "You must checkout this archive before you may edit data types."); - } + if (isUnmodifiableProjectArchive(archiveNode)) { return; } - // must get write lock before we can edit - if (needsWriteLock(archiveNode)) { - if (!getWriteLock(plugin, archiveNode)) { - return; + if (archiveNode instanceof FileArchiveNode fileNode) { + FileDataTypeArchive archive = fileNode.getArchive(); + if (!archive.isChangeable()) { + if (!askToOpenArchiveForUpdate()) { + return; + } + archive = openForUpdate(archive); + dataType = updateDataType(path, dataTypeName, archive); } - dataType = updateDataType(path, dataTypeName, archiveNode); + } + if (dataType != null) { + plugin.getEditorManager().edit(dataType); + } + } + + private FileDataTypeArchive openForUpdate(FileDataTypeArchive archive) { + GTree tree = plugin.getProvider().getGTree(); + GTreeState state = tree.getTreeState(); + archive = plugin.getArchiveManager().reopenFileArchive(archive, true); + tree.restoreTreeState(state); + return archive; + } + + private boolean isUnmodifiableProjectArchive(DataTypeStoreNode archiveNode) { + if (!(archiveNode instanceof ProjectArchiveNode projectNode)) { + return false; } - plugin.getEditorManager().edit(dataType); + if (archiveNode.isModifiable()) { + return false; + } + DomainFile domainFile = projectNode.getDomainFile(); + DomainFile originalDomainFile = projectNode.getOriginalDomainFile(); + if (domainFile.getVersion() == originalDomainFile.getLatestVersion() && + originalDomainFile.canCheckout()) { + Msg.showInfo(getClass(), archiveGTree, "Archive Not Checked Out", + "You must checkout this archive before you may edit data types."); + } + else { + Msg.showInfo(getClass(), archiveGTree, "Archive Opened Read-Only", + "You may not edit data type within a read-only project archive."); + } + return true; } void restore(SaveState saveState) { @@ -692,55 +684,26 @@ public class DataTypesProvider extends ComponentProviderAdapter { return archiveGTree; } - void domainObjectRestored(DataTypeManagerDomainObject domainObject) { + void domainObjectRestored(PersistentDataTypeArchive archive) { if (archiveGTree == null) { return; // nothing to update } - if (domainObject instanceof Program) { - Program program = (Program) domainObject; - Program programInTree = plugin.getProgram(); // may be null - if (program == programInTree) { - DataTypeArchiveGTree gTree = getGTree(); - ArchiveNode node = getProgramArchiveNode(); - if (node != null) { - GTreeState state = gTree.getTreeState(node); - node.structureChanged(); - gTree.restoreTreeState(state); - } - } - } - else if (domainObject instanceof DataTypeArchive) { - DataTypeArchive dataTypeArchive = (DataTypeArchive) domainObject; - DataTypeArchiveGTree gTree = getGTree(); - ArchiveNode node = getDataTypeArchiveNode(dataTypeArchive); - if (node != null) { - GTreeState state = gTree.getTreeState(node); - node.structureChanged(); - gTree.restoreTreeState(state); - } + DataTypeArchiveGTree gTree = getGTree(); + ArchiveNode node = getDataTypeArchiveNode(archive); + if (node != null) { + GTreeState state = gTree.getTreeState(node); + node.structureChanged(); + gTree.restoreTreeState(state); } } - private ProgramArchiveNode getProgramArchiveNode() { + private ArchiveNode getDataTypeArchiveNode(PersistentDataTypeArchive dataTypeArchive) { GTreeNode rootNode = getGTree().getModelRoot(); List children = rootNode.getChildren(); for (GTreeNode node : children) { - if (node instanceof ProgramArchiveNode programNode) { - return programNode; - } - } - return null; - } - - private ArchiveNode getDataTypeArchiveNode(DataTypeArchive dataTypeArchive) { - GTreeNode rootNode = getGTree().getModelRoot(); - List children = rootNode.getChildren(); - for (GTreeNode node : children) { - ArchiveNode archiveNode = (ArchiveNode) node; - Archive archive = archiveNode.getArchive(); - if (archive instanceof ProjectArchive) { - ProjectArchive projectArchive = (ProjectArchive) archive; - if (projectArchive.getDataTypeManager() == dataTypeArchive.getDataTypeManager()) { + if (node instanceof ArchiveNode archiveNode) { + PersistentDataTypeArchive archive = archiveNode.getArchive(); + if (archive == dataTypeArchive) { return archiveNode; } } @@ -774,7 +737,7 @@ public class DataTypesProvider extends ComponentProviderAdapter { Category category = dataTypeManager.getCategory(dataType.getCategoryPath()); ArchiveRootNode rootNode = (ArchiveRootNode) gTree.getViewRoot(); - ArchiveNode archiveNode = rootNode.getNodeForManager(dataTypeManager); + DataTypeStoreNode archiveNode = rootNode.getNodeForManager(dataTypeManager); if (archiveNode == null) { plugin.setStatus("Cannot find archive '" + dataTypeManager.getName() + "'. It may " + "be filtered out of view or may have been closed (Data Type Manager)"); @@ -824,7 +787,7 @@ public class DataTypesProvider extends ComponentProviderAdapter { } ArchiveRootNode rootNode = (ArchiveRootNode) gTree.getViewRoot(); - ArchiveNode archiveNode = rootNode.getNodeForManager(dataTypeManager); + DataTypeStoreNode archiveNode = rootNode.getNodeForManager(dataTypeManager); if (archiveNode == null) { plugin.setStatus("Cannot find archive '" + dataTypeManager.getName() + "'. It may " + "be filtered out of view or may have been closed (Data Type Manager)"); @@ -924,8 +887,7 @@ public class DataTypesProvider extends ComponentProviderAdapter { } private void restoreProgramTreeState(ProgramArchiveNode programNode) { - ProgramArchive programArchive = (ProgramArchive) programNode.getArchive(); - Program program = programArchive.getProgram(); + Program program = programNode.getProgram(); long id = program.getUniqueProgramID(); TreePath selectedPath = programTreeState.get(id); if (selectedPath == null) { @@ -946,8 +908,7 @@ public class DataTypesProvider extends ComponentProviderAdapter { // of the program node, so only save the path if there is a single selection. This will // be helpful in the case that the user was working with a single data type in the program. // - ProgramArchive programArchive = (ProgramArchive) programNode.getArchive(); - Program program = programArchive.getProgram(); + Program program = programNode.getProgram(); long id = program.getUniqueProgramID(); GTreeState state = archiveGTree.getTreeState(); List paths = state.getSelectedPaths(); @@ -987,16 +948,11 @@ public class DataTypesProvider extends ComponentProviderAdapter { clearDataTypePreview(); } - void archiveClosed(DataTypeManager dtm) { - dataTypeManagerChanged(dtm); + void archiveClosed(PersistentDataTypeArchive archive) { + archiveChanged(archive.getName()); } - void archiveChanged(Archive archive) { - DataTypeManager dtm = archive.getDataTypeManager(); - dataTypeManagerChanged(dtm); - } - - private void dataTypeManagerChanged(DataTypeManager dtm) { + void archiveChanged(String name) { if (lastPreviewNode == null || !(lastPreviewNode instanceof DataTypeNode)) { return; @@ -1005,9 +961,10 @@ public class DataTypesProvider extends ComponentProviderAdapter { DataTypeNode dtNode = (DataTypeNode) lastPreviewNode; DataType dt = dtNode.getDataType(); DataTypeManager dtManager = dt.getDataTypeManager(); - - // note: compare using name; an equality check will fail if the manager is reloaded - if (dtm.getName().equals(dtManager.getName())) { + // notes: the archive name and the datatypeManager name are the same. If the archive + // changed because it was closed, then its datatype manager will be null. So just + // compare the datatype's manager's name with the archive's name. + if (name.equals(dtManager.getName())) { lastPreviewNode = null; } } @@ -1016,9 +973,8 @@ public class DataTypesProvider extends ComponentProviderAdapter { ArchiveRootNode rootNode = (ArchiveRootNode) archiveGTree.getModelRoot(); List allChildren = rootNode.getChildren(); for (GTreeNode node : allChildren) { - ArchiveNode archiveNode = (ArchiveNode) node; - if (archiveNode.getArchive() instanceof ProgramArchive) { - archiveNode.nodeChanged(); + if (node instanceof ProgramArchiveNode programNode) { + programNode.nodeChanged(); return; } } @@ -1087,14 +1043,14 @@ public class DataTypesProvider extends ComponentProviderAdapter { private class ProgramNodeUpdateListener implements ArchiveRootNodeListener { @Override - public void archiveNodeAdded(ArchiveNode node) { + public void archiveNodeAdded(DataTypeStoreNode node) { if (node instanceof ProgramArchiveNode programNode) { restoreProgramTreeState(programNode); } } @Override - public void archiveNodeRemoved(ArchiveNode node) { + public void archiveNodeRemoved(DataTypeStoreNode node) { if (node instanceof ProgramArchiveNode programNode) { saveProgramTreeState(programNode); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenDomainFileTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenDomainFileTask.java deleted file mode 100644 index b3c0bc96d7..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenDomainFileTask.java +++ /dev/null @@ -1,239 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.datamgr; - -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.List; - -import javax.swing.SwingUtilities; - -import docking.widgets.OptionDialog; -import docking.widgets.tree.GTree; -import docking.widgets.tree.GTreeNode; -import ghidra.app.plugin.core.datamgr.archive.*; -import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; -import ghidra.framework.client.ClientUtil; -import ghidra.framework.client.RepositoryAdapter; -import ghidra.framework.main.AppInfo; -import ghidra.framework.model.DomainFile; -import ghidra.framework.plugintool.PluginTool; -import ghidra.program.model.listing.DataTypeArchive; -import ghidra.util.*; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.VersionException; -import ghidra.util.task.Task; -import ghidra.util.task.TaskMonitor; - -class OpenDomainFileTask extends Task { - private DomainFile domainFile; - private int version; - private DataTypeManagerPlugin dtmPlugin; - private DataTypeManagerHandler dtmHandler; - private PluginTool tool; - private DataTypeArchive dtArchive = null; - - OpenDomainFileTask(DomainFile domainFile, int version, PluginTool tool, - DataTypeManagerPlugin dtmPlugin) { - - super("Open Project Data Type Archive", true, true, true); - this.domainFile = domainFile; - this.dtmPlugin = dtmPlugin; - this.dtmHandler = dtmPlugin.getDataTypeManagerHandler(); - this.tool = tool; - this.version = version; - } - - DataTypeArchive getArchive() { - return dtArchive; - } - - /* (non-Javadoc) - * @see ghidra.util.task.Task#run(ghidra.util.task.TaskMonitor) - */ - @Override - public void run(TaskMonitor monitor) { - - if (isFileOpen()) { - return; - } - boolean associateWithOriginalDomainFile = true; - if (version != DomainFile.DEFAULT_VERSION) { - openReadOnlyFile(monitor); - associateWithOriginalDomainFile = false; - } - else if (domainFile.isReadOnly()) { - openReadOnlyFile(monitor); - } - else if (domainFile.isVersioned() && !domainFile.isCheckedOut()) { - openReadOnlyFile(monitor); - } - else { - openUnversionedFile(monitor); - } - if (dtArchive != null) { - openFileInTree(associateWithOriginalDomainFile); - dtArchive.release(this); - } - } - - private boolean isFileOpen() { - List dtArchiveList = dtmHandler.getAllArchives(); - for (Archive archive : dtArchiveList) { - if (archive instanceof ProjectArchive) { - ProjectArchive projectArchive = (ProjectArchive) archive; - DomainFile archiveDomainFile = projectArchive.getDomainFile(); - if (filesMatch(domainFile, archiveDomainFile)) { - // archive = projectArchive; - // dtmHandler.open // TODO - return true; - } - } - } - - return false; - } - - private boolean filesMatch(DomainFile file1, DomainFile file2) { - if (!file1.getPathname().equals(file2.getPathname())) { - return false; - } - - if (file1.isCheckedOut() != file2.isCheckedOut()) { - return false; - } - - if (!SystemUtilities.isEqual(file1.getProjectLocator(), file2.getProjectLocator())) { - return false; - } - - int otherVersion = file2.isReadOnly() ? file2.getVersion() : -1; - return version == otherVersion; - } - - /** - * Open archive in an immutable fashion. Unlike ProgramDB, we do not want to - * allow upgrade or modification of a read-only archve (e.g., not-checked-out). - * @param monitor task monitor - */ - private void openReadOnlyFile(TaskMonitor monitor) { - String fileDescr = - ((version != DomainFile.DEFAULT_VERSION) ? "version " + version + " of " : "") + - domainFile.getName(); - String contentType = null; - try { - monitor.setMessage("Opening " + fileDescr); - contentType = domainFile.getContentType(); - dtArchive = - (DataTypeArchive) domainFile.getImmutableDomainObject(this, version, monitor); - } - catch (CancelledException e) { - // we don't care, the task has been canceled - } - catch (IOException e) { - if (domainFile.isVersioned() && domainFile.isInWritableProject()) { - ClientUtil.handleException(AppInfo.getActiveProject().getRepository(), e, - "Get Versioned Object", null); - } - else { - Msg.showError(this, null, "Project Archive Open Error", - "Error occurred while opening " + fileDescr, e); - } - } - catch (VersionException e) { - VersionExceptionHandler.showVersionError(tool.getToolFrame(), domainFile.getName(), - contentType, "Open", false, e); - } - } - - private void openUnversionedFile(TaskMonitor monitor) { - monitor.setMessage("Opening " + domainFile.getName()); - String contentType = null; - try { - final boolean recoverFile = isRecoveryOK(domainFile); - contentType = domainFile.getContentType(); - try { - dtArchive = - (DataTypeArchive) domainFile.getDomainObject(this, false, recoverFile, monitor); - } - catch (VersionException e) { - if (VersionExceptionHandler.isUpgradeOK(null, domainFile, "Open", e)) { - dtArchive = (DataTypeArchive) domainFile.getDomainObject(this, true, - recoverFile, monitor); - } - } - } - catch (VersionException e) { - VersionExceptionHandler.showVersionError(null, domainFile.getName(), contentType, - "Open", false, e); - } - catch (CancelledException e) { - // we don't care, the task has been canceled - } - catch (Exception e) { - if (domainFile.isInWritableProject() && (e instanceof IOException)) { - RepositoryAdapter repo = domainFile.getParent().getProjectData().getRepository(); - ClientUtil.handleException(repo, e, "Open File", null); - } - else { - Msg.showError(this, null, "Error Opening " + domainFile.getName(), - "Opening data type archive failed.\n" + e.getMessage()); - } - } - } - - private boolean isRecoveryOK(final DomainFile dfile) - throws InterruptedException, InvocationTargetException { - final boolean[] recoverFile = new boolean[] { false }; - if (dfile.isInWritableProject() && dfile.canRecover()) { - Runnable r = () -> { - int option = OptionDialog.showYesNoDialog(null, "Crash Recovery Data Found", - "" + HTMLUtilities.escapeHTML(dfile.getName()) + " has crash data.
" + - "Would you like to recover unsaved changes?"); - recoverFile[0] = (option == OptionDialog.OPTION_ONE); - }; - SwingUtilities.invokeAndWait(r); - } - return recoverFile[0]; - } - - private void openFileInTree(boolean associatedWithOriginalDomainFile) { - DataTypesProvider provider = dtmPlugin.getProvider(); - GTree tree = provider.getGTree(); - DataTypeManagerHandler manager = dtmPlugin.getDataTypeManagerHandler(); - DomainFile df = associatedWithOriginalDomainFile ? domainFile : dtArchive.getDomainFile(); - Archive archive = manager.openArchive(dtArchive, df); - GTreeNode node = getNodeForArchive(tree, archive); - if (node != null) { - tree.setSelectedNode(node); - } - } - - private GTreeNode getNodeForArchive(GTree tree, Archive archive) { - GTreeNode rootNode = tree.getModelRoot(); - for (GTreeNode node : rootNode.getChildren()) { - if (node instanceof ArchiveNode) { - ArchiveNode archiveNode = (ArchiveNode) node; - if (archiveNode.getArchive() == archive) { - return archiveNode; - } - } - } - - return null; - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenFileArchiveTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenFileArchiveTask.java new file mode 100644 index 0000000000..d98bbd177d --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenFileArchiveTask.java @@ -0,0 +1,117 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.datamgr; + +import java.io.FileNotFoundException; +import java.io.IOException; + +import docking.widgets.OptionDialog; +import generic.jar.ResourceFile; +import ghidra.app.services.Upgrade; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; +import ghidra.program.model.dtarchive.FileDataTypeArchive; +import ghidra.util.Msg; +import ghidra.util.VersionExceptionHandler; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.Task; +import ghidra.util.task.TaskMonitor; + +class OpenFileArchiveTask extends Task { + private ResourceFile file; + private FileDataTypeArchive archive = null; + private Object consumer; + private Upgrade upgradeStrategy; + private boolean openForUpdate; + + OpenFileArchiveTask(ResourceFile file, boolean openForUpdate, + Upgrade upgradeStrategy, Object consumer) { + super("Opening File DataType Archive " + file.getName(), false, false, true); + this.file = file; + this.openForUpdate = openForUpdate; + this.upgradeStrategy = upgradeStrategy; + this.consumer = consumer; + } + + FileDataTypeArchive getArchive() { + return archive; + } + + @Override + public void run(TaskMonitor monitor) { + try { + archive = openArchive(monitor); + } + catch (FileNotFoundException e) { + Msg.showError(this, null, "Open File Archive Failed", + file.getAbsolutePath() + " not found!"); + } + catch (IOException e) { + Msg.showError(this, null, "Open File Archive Failed", + e.getMessage() + ": " + file.getName()); + } + catch (VersionException e) { + VersionExceptionHandler.showVersionError(null, file.getName(), "Archive", "open", false, + e); + } + catch (CancelledException e) { + // user cancelled, nothing to report + } + + } + + public FileDataTypeArchive openArchive(TaskMonitor monitor) + throws VersionException, IOException, CancelledException { + + if (!openForUpdate) { + return DataTypeArchiveFactory.openReadOnly(file, consumer, monitor); + } + try { + return DataTypeArchiveFactory.openForUpdate(file, false, consumer, monitor); + } + catch (VersionException e) { + if (shouldUpgrade(e)) { + return DataTypeArchiveFactory.openForUpdate(file, true, consumer, monitor); + } + throw e; + } + } + + private boolean shouldUpgrade(VersionException e) { + if (!e.isUpgradable()) { + return false; + } + switch (upgradeStrategy) { + case YES: + return true; + case NO: + return false; + case ASK: + default: + return askToUpgrade(); + + } + } + + private boolean askToUpgrade() { + return OptionDialog.showOptionDialog(null, + "Upgrade File Archive: " + file.getName(), + "File archive is an older version.\n" + + "Do you want to upgrade it to the latest version?", + "Upgrade", + OptionDialog.QUESTION_MESSAGE) == OptionDialog.YES_OPTION; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenProjectArchiveTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenProjectArchiveTask.java new file mode 100644 index 0000000000..aa68933661 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenProjectArchiveTask.java @@ -0,0 +1,162 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.datamgr; + +import java.io.IOException; + +import docking.widgets.OptionDialog; +import ghidra.app.services.Recover; +import ghidra.app.services.Upgrade; +import ghidra.framework.client.ClientUtil; +import ghidra.framework.main.AppInfo; +import ghidra.framework.model.DomainFile; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; +import ghidra.util.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.Task; +import ghidra.util.task.TaskMonitor; + +class OpenProjectArchiveTask extends Task { + private DomainFile file; + private int version; + private ProjectDataTypeArchive archive = null; + private Object consumer; + private Recover recoverStrategy; + private Upgrade upgradeStrategy; + + OpenProjectArchiveTask(DomainFile file, int version, Upgrade upgradeStrategy, + Recover recoverStrategy, Object consumer) { + super("Open Project Data Type Archive", true, true, true); + this.file = file; + this.upgradeStrategy = upgradeStrategy; + this.recoverStrategy = recoverStrategy; + this.consumer = consumer; + this.version = version; + } + + ProjectDataTypeArchive getArchive() { + return archive; + } + + @Override + public void run(TaskMonitor monitor) { + monitor.setMessage(getDescription()); + try { + archive = openArchive(monitor); + } + catch (VersionException e) { + String contentType = file.getContentType(); + VersionExceptionHandler.showVersionError(null, file.getName(), + contentType, "Open", false, e); + } + catch (CancelledException e) { + // do nothing, user cancelled + } + catch (IOException e) { + if (file.isVersioned() && file.isInWritableProject()) { + ClientUtil.handleException(AppInfo.getActiveProject().getRepository(), e, + "Get Versioned Object", null); + } + else { + Msg.showError(this, null, "Project Archive Open Error", + "Error occurred while opening " + file.getName(), e); + } + } + + } + + private String getDescription() { + String description = "Opening " + file.getName(); + if (version != DomainFile.DEFAULT_VERSION) { + description += " (version " + version + ")"; + } + return description; + } + + public ProjectDataTypeArchive openArchive(TaskMonitor monitor) + throws VersionException, IOException, CancelledException { + + if (shouldOpenImmutable(file)) { + return DataTypeArchiveFactory.openReadOnly(file, version, consumer, monitor); + } + boolean okToRecover = shouldRecover(); + try { + return DataTypeArchiveFactory.openForUpdate(file, consumer, false, okToRecover, + monitor); + } + catch (VersionException e) { + if (shouldUpgrade(e)) { + return DataTypeArchiveFactory.openForUpdate(file, consumer, true, okToRecover, + monitor); + } + throw e; + } + } + + private boolean shouldUpgrade(VersionException e) { + if (!e.isUpgradable()) { + return false; + } + switch (upgradeStrategy) { + case YES: + return true; + case NO: + return false; + case ASK: + default: + return VersionExceptionHandler.isUpgradeOK(null, file, "Open File Archive", e); + + } + } + + private boolean shouldOpenImmutable(DomainFile domainFile) { + if (version != DomainFile.DEFAULT_VERSION) { + return true; + } + if (domainFile.isReadOnly()) { + return true; + } + return domainFile.isVersioned() && !domainFile.isCheckedOut(); + } + + private boolean shouldRecover() { + if (!file.isInWritableProject()) { + return false; + } + switch (recoverStrategy) { + case YES: + return true; + case NO: + return false; + case ASK: + default: + return askToRecover(); + } + } + + private boolean askToRecover() { + if (!file.canRecover()) { + return false; + } + int option = OptionDialog.showYesNoDialog(null, "Crash Recovery Data Found", + "" + HTMLUtilities.escapeHTML(file.getName()) + " has crash data.
" + + "Would you like to recover unsaved changes?"); + return (option == OptionDialog.OPTION_ONE); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ProjectArchiveSaveAsDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ProjectArchiveSaveAsDialog.java new file mode 100644 index 0000000000..78c1cdd6aa --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/ProjectArchiveSaveAsDialog.java @@ -0,0 +1,137 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.datamgr; + +import java.io.IOException; +import java.util.List; + +import docking.widgets.OptionDialog; +import ghidra.app.util.HelpTopics; +import ghidra.framework.main.DataTreeDialog; +import ghidra.framework.main.DataTreeDialogType; +import ghidra.framework.model.*; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; +import ghidra.util.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskLauncher; + +public class ProjectArchiveSaveAsDialog extends DataTreeDialog { + private static final String CONTENT_NAME = "Data Type Archive"; + + private ProjectDataTypeArchive archive; + + private PluginTool tool; + + public ProjectArchiveSaveAsDialog(PluginTool tool, ProjectDataTypeArchive archive) { + super(null, "Save Project Archive As", DataTreeDialogType.SAVE); + this.tool = tool; + this.archive = archive; + setHelpLocation(new HelpLocation(HelpTopics.PROGRAM, "Save_As_File")); + } + + @Override + protected void okCallback() { + DomainFolder folder = getDomainFolder(); + String name = getNameText(); + if (name.length() == 0) { + setStatusText("Please enter a name"); + return; + } + if (folder == null) { + setStatusText("Please select a folder"); + return; + } + DomainFile file = folder.getFile(name); + if (file != null) { + setStatusText("Choose a name that doesn't exist"); + return; + } + close(); + doSaveAs(); + } + + private void doSaveAs() { + if (!getSaveAsLock(archive)) { + return; + } + try { + DomainFolder folder = getDomainFolder(); + String newName = getNameText(); + tool.prepareToSave(archive); + TaskLauncher.launchModal("Save As", monitor -> { + try { + folder.createFile(newName, archive, monitor); + } + catch (InvalidNameException e) { + Msg.showError(this, null, "Invalid Name", e.getMessage(), e); + } + catch (CancelledException e) { + // user cancelled, nothing to do + } + catch (IOException e) { + Msg.showError(this, null, "I/O error ", e.getMessage(), e); + } + }); + } + finally { + archive.unlock(); + } + } + + private boolean getSaveAsLock(DomainObject domainObject) { + if (!domainObject.lock(null)) { + String title = "Save " + CONTENT_NAME + " As (Busy)"; + StringBuffer buf = new StringBuffer(); + buf.append("The " + CONTENT_NAME + + " is currently being modified by the following actions/tasks:\n \n"); + TransactionInfo t = domainObject.getCurrentTransactionInfo(); + List list = t.getOpenSubTransactions(); + for (String element : list) { + buf.append("\n "); + buf.append(element); + } + buf.append("\n \n"); + buf.append( + "WARNING! The above task(s) should be cancelled before attempting a Save As...\n"); + buf.append("Only proceed if unable to cancel them.\n \n"); + buf.append( + "If you click 'Save Archive As (Rollback)' {recommended}, all changes made\n"); + buf.append("by these tasks, as well as any other overlapping task, will be LOST!\n"); + buf.append( + "If you click 'Save As (As Is)', the archive will be saved in its current\n"); + buf.append("state which may contain some incomplete data.\n"); + buf.append("Any forced save may also result in subsequent transaction errors while\n"); + buf.append("the above tasks remain active.\n "); + + int result = OptionDialog.showOptionDialog(null, title, buf.toString(), + "Save Archive As (Rollback)!", "Save Archive As (As Is)!", + OptionDialog.WARNING_MESSAGE); + + if (result == OptionDialog.OPTION_ONE) { + domainObject.forceLock(true, "Save Archive As"); + return true; + } + else if (result == OptionDialog.OPTION_TWO) { + domainObject.forceLock(false, "Save Archive As"); + return true; + } + return false; + } + return true; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/AbstractUndoRedoArchiveTransactionAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/AbstractUndoRedoArchiveTransactionAction.java index 1d36502b2d..5879ca52f8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/AbstractUndoRedoArchiveTransactionAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/AbstractUndoRedoArchiveTransactionAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,8 +27,7 @@ import docking.widgets.tree.GTreeNode; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import ghidra.app.plugin.core.datamgr.DataTypesActionContext; import ghidra.app.plugin.core.datamgr.tree.*; -import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.StandAloneDataTypeManager; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; public abstract class AbstractUndoRedoArchiveTransactionAction extends DockingAction { @@ -62,28 +61,28 @@ public abstract class AbstractUndoRedoArchiveTransactionAction extends DockingAc } TreePath[] selectionPaths = getSelectionPaths(context); - return getModifiableProjectOrFileDTM(selectionPaths) != null; + return getModifiableProjectOrFileArchive(selectionPaths) != null; } /** * Determine if the corresponding undo/redo can be performed - * @param dtm archive datatype manager + * @param archive datatype archive * @return true if action can be performed on archive */ - abstract protected boolean canExecute(StandAloneDataTypeManager dtm); + abstract protected boolean canExecute(PersistentDataTypeArchive archive); /** * Determine the next undo/redo transaction name - * @param dtm archive datatype manager + * @param archive datatype archive * @return next undo/redo transaction name */ - abstract protected String getNextName(StandAloneDataTypeManager dtm); + abstract protected String getNextName(PersistentDataTypeArchive archive); /** - * Execute the undo/redo operation on the specified archive datatype manager. - * @param dtm archive datatype manager + * Execute the undo/redo operation on the specified archive. + * @param archive datatype archive */ - abstract protected void execute(StandAloneDataTypeManager dtm); + abstract protected void execute(PersistentDataTypeArchive archive); @Override public boolean isEnabledForContext(ActionContext context) { @@ -92,9 +91,9 @@ public abstract class AbstractUndoRedoArchiveTransactionAction extends DockingAc } TreePath[] selectionPaths = getSelectionPaths(context); - StandAloneDataTypeManager dtm = getModifiableProjectOrFileDTM(selectionPaths); - if (dtm != null && canExecute(dtm)) { - setPopupMenuData(getMenuData(getNextName(dtm))); + PersistentDataTypeArchive dta = getModifiableProjectOrFileArchive(selectionPaths); + if (dta != null && canExecute(dta)) { + setPopupMenuData(getMenuData(getNextName(dta))); return true; } setPopupMenuData(getMenuData(null)); @@ -109,9 +108,9 @@ public abstract class AbstractUndoRedoArchiveTransactionAction extends DockingAc } TreePath[] selectionPaths = getSelectionPaths(context); - StandAloneDataTypeManager dtm = getModifiableProjectOrFileDTM(selectionPaths); - if (dtm != null && canExecute(dtm)) { - execute(dtm); + PersistentDataTypeArchive dta = getModifiableProjectOrFileArchive(selectionPaths); + if (dta != null && canExecute(dta)) { + execute(dta); } } @@ -122,7 +121,7 @@ public abstract class AbstractUndoRedoArchiveTransactionAction extends DockingAc return selectionPaths; } - private StandAloneDataTypeManager getModifiableProjectOrFileDTM(TreePath[] selectionPaths) { + private PersistentDataTypeArchive getModifiableProjectOrFileArchive(TreePath[] selectionPaths) { // only valid if single file or project archive node is selected if (selectionPaths.length != 1) { return null; @@ -140,9 +139,9 @@ public abstract class AbstractUndoRedoArchiveTransactionAction extends DockingAc ArchiveNode archiveNode = (ArchiveNode) node; if (archiveNode.isModifiable()) { - DataTypeManager dtm = archiveNode.getArchive().getDataTypeManager(); - if (dtm instanceof StandAloneDataTypeManager archiveDtm) { - return archiveDtm; + PersistentDataTypeArchive archive = archiveNode.getArchive(); + if (archive instanceof PersistentDataTypeArchive dta) { + return dta; } } return null; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CaptureFunctionDataTypesAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CaptureFunctionDataTypesAction.java index 130cd0de55..5709dced4b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CaptureFunctionDataTypesAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CaptureFunctionDataTypesAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -78,8 +78,8 @@ public class CaptureFunctionDataTypesAction extends DockingAction { public void actionPerformed(ActionContext context) { GTree gTree = (GTree) context.getContextObject(); TreePath selectionPath = gTree.getSelectionPath(); - ArchiveNode node = (ArchiveNode) selectionPath.getLastPathComponent(); - if (!node.getArchive().isModifiable()) { + DataTypeStoreNode node = (DataTypeStoreNode) selectionPath.getLastPathComponent(); + if (!node.getDataTypeStore().isChangeable()) { informNotModifiable(node); return; } @@ -89,7 +89,7 @@ public class CaptureFunctionDataTypesAction extends DockingAction { if (currentSelection == null || currentSelection.isEmpty()) { currentSelection = program.getMemory(); } - final DataTypeManager manager = node.getArchive().getDataTypeManager(); + final DataTypeManager manager = node.getDataTypeManager(); final PluginTool tool = plugin.getTool(); CaptureFunctionDataTypesCmd cmd = new CaptureFunctionDataTypesCmd(manager, currentSelection, @@ -105,7 +105,7 @@ public class CaptureFunctionDataTypesAction extends DockingAction { tool.executeBackgroundCommand(cmd, program); } - private void informNotModifiable(ArchiveNode node) { + private void informNotModifiable(DataTypeStoreNode node) { String message; if (node instanceof ProgramArchiveNode) { message = "The program \"" + node.getName() + "\" isn't modifiable."; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ClearArchiveArchitectureAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ClearArchiveArchitectureAction.java index f88e1aea76..24965991a9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ClearArchiveArchitectureAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ClearArchiveArchitectureAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,8 +15,6 @@ */ package ghidra.app.plugin.core.datamgr.actions; -import java.io.IOException; - import javax.swing.tree.TreePath; import docking.ActionContext; @@ -26,13 +24,11 @@ import docking.widgets.OptionDialog; import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; import generic.theme.GThemeDefaults.Colors.Messages; -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.DataTypesActionContext; -import ghidra.app.plugin.core.datamgr.archive.*; +import ghidra.app.plugin.core.datamgr.*; import ghidra.app.plugin.core.datamgr.tree.*; -import ghidra.framework.model.DomainFile; -import ghidra.framework.store.LockException; -import ghidra.program.model.data.StandAloneDataTypeManager; +import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; import ghidra.util.Msg; import ghidra.util.exception.CancelledException; import ghidra.util.task.*; @@ -71,8 +67,7 @@ public class ClearArchiveArchitectureAction extends DockingAction { return false; } ArchiveNode archiveNode = (ArchiveNode) node; - StandAloneDataTypeManager dtm = - (StandAloneDataTypeManager) archiveNode.getArchive().getDataTypeManager(); + DataTypeManager dtm = archiveNode.getDataTypeManager(); return dtm.getProgramArchitectureSummary() != null && dtm.isUpdatable(); } @@ -91,7 +86,7 @@ public class ClearArchiveArchitectureAction extends DockingAction { if (node instanceof ProjectArchiveNode) { ProjectArchiveNode paNode = (ProjectArchiveNode) node; - ProjectArchive pa = (ProjectArchive) paNode.getArchive(); + ProjectDataTypeArchive pa = paNode.getArchive(); if (!pa.hasExclusiveAccess()) { Msg.showError(this, null, "Clear Program Architecture Failed", "Clearing program-architecture on Project Archive requires exclusive checkout."); @@ -100,10 +95,9 @@ public class ClearArchiveArchitectureAction extends DockingAction { } ArchiveNode archiveNode = (ArchiveNode) node; - StandAloneDataTypeManager dtm = - (StandAloneDataTypeManager) archiveNode.getArchive().getDataTypeManager(); + PersistentDataTypeArchive archive = archiveNode.getArchive(); - if (dtm.isChanged()) { + if (archive.isChanged()) { if (OptionDialog.OPTION_ONE != OptionDialog.showOptionDialogWithCancelAsDefaultButton( null, "Save Archive Changes", "Archive has unsaved changes which must be saved before continuing." + @@ -111,20 +105,14 @@ public class ClearArchiveArchitectureAction extends DockingAction { "Save")) { return; } - try { - archiveNode.getArchive().save(); - } - catch (IOException e) { - Msg.showError(this, null, "Save Archive Failed", - "Failed to save changes for Archive: " + dtm.getName() + "\n" + e.getMessage()); - return; - } + ArchiveManager archiveManager = plugin.getArchiveManager(); + archiveManager.save(archive); } // TODO: Update message indicating that custom storage specification will not be // retained/permitted (once supported) String msg = "Clear program-architecture for Archive?
" + dtm.getPath() + + Messages.NORMAL + "\">" + archive.getPath() + "

Archive will revert to using default data organization."; int response = OptionDialog.showOptionDialogWithCancelAsDefaultButton(null, "Confirm Clearing Archive Architecture", msg, "Clear Architecture", @@ -133,25 +121,23 @@ public class ClearArchiveArchitectureAction extends DockingAction { return; } - new TaskLauncher(new ClearProgramArchitectureTask(archiveNode.getArchive(), dtm)); + new TaskLauncher(new ClearProgramArchitectureTask(archive)); } private class ClearProgramArchitectureTask extends Task { - private final Archive archive; - private final StandAloneDataTypeManager dtm; + private final PersistentDataTypeArchive archive; - public ClearProgramArchitectureTask(Archive archive, StandAloneDataTypeManager dtm) { + public ClearProgramArchitectureTask(PersistentDataTypeArchive archive) { super("Clearing Program-Architecture for Archive", true, false, true, false); this.archive = archive; - this.dtm = dtm; } @Override public void run(TaskMonitor monitor) throws CancelledException { boolean success = false; try { - dtm.clearProgramArchitecture(monitor); + archive.clearProgramArchitecture(monitor); success = true; } catch (CancelledException e) { @@ -159,25 +145,13 @@ public class ClearArchiveArchitectureAction extends DockingAction { } catch (Exception e) { Msg.showError(this, null, "Archive Update Failed", - "Failed to clear program-architecture for Archive: " + dtm.getName() + "\n" + + "Failed to clear program-architecture for Archive: " + archive.getName() + + "\n" + e.getMessage()); } finally { if (!success) { - if (archive instanceof FileArchive) { - try { - ((FileArchive) archive).releaseWriteLock(); - ((FileArchive) archive).acquireWriteLock(); - } - catch (LockException | IOException e) { - archive.close(); - } - } - else { // if (archive instanceof ProjectArchive) { - archive.close(); - DomainFile df = ((ProjectArchive) archive).getDomainFile(); - plugin.openArchive(df); - } + plugin.getArchiveManager().reopenArchive(archive); } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CloseArchiveAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CloseArchiveAction.java index 5207353914..2d07c542f0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CloseArchiveAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CloseArchiveAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,19 +18,15 @@ package ghidra.app.plugin.core.datamgr.actions; import java.util.ArrayList; import java.util.List; -import javax.swing.tree.TreePath; - import docking.ActionContext; import docking.action.DockingAction; import docking.action.MenuData; -import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.DataTypesActionContext; -import ghidra.app.plugin.core.datamgr.archive.Archive; -import ghidra.app.plugin.core.datamgr.archive.ArchiveUtils; +import ghidra.app.plugin.core.datamgr.*; import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager; import ghidra.app.plugin.core.datamgr.tree.*; +import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; public class CloseArchiveAction extends DockingAction { @@ -42,28 +38,25 @@ public class CloseArchiveAction extends DockingAction { setPopupMenuData(new MenuData(new String[] { "Close Archive" }, null, "File")); - setDescription("Closes a data type archive and removes it from the tool " - + "(does not affect program file associations)."); + setDescription("Closes a data type archive and removes it from the tool " + + "(does not affect program file associations)."); setEnabled(true); } @Override public boolean isEnabledForContext(ActionContext context) { - if (!(context instanceof DataTypesActionContext)) { + if (!(context instanceof DataTypesActionContext dtac)) { return false; } - Object contextObject = context.getContextObject(); - GTree gtree = (GTree) contextObject; - TreePath[] selectionPaths = gtree.getSelectionPaths(); + List selectedNodes = dtac.getSelectedNodes(); - if (selectionPaths.length == 0) { + if (selectedNodes.isEmpty()) { return false; } - for (TreePath path : selectionPaths) { - GTreeNode node = (GTreeNode) path.getLastPathComponent(); + for (GTreeNode node : selectedNodes) { if (!(node instanceof FileArchiveNode) && !(node instanceof InvalidArchiveNode) && !(node instanceof ProjectArchiveNode)) { return false; @@ -74,38 +67,40 @@ public class CloseArchiveAction extends DockingAction { @Override public void actionPerformed(ActionContext context) { - GTree gtree = (GTree) context.getContextObject(); - TreePath[] selectionPaths = gtree.getSelectionPaths(); - DataTypeEditorManager editorManager = plugin.getEditorManager(); - List archives = new ArrayList(); - for (TreePath path : selectionPaths) { - Object pathComponent = path.getLastPathComponent(); - if (pathComponent instanceof InvalidArchiveNode) { - InvalidArchiveNode invalidArchiveNode = (InvalidArchiveNode) pathComponent; - Archive archive = invalidArchiveNode.getArchive(); - archive.close(); - continue; - } + DataTypesActionContext dtac = (DataTypesActionContext) context; + ArchiveManager archiveManager = plugin.getArchiveManager(); - Archive archive = null; - Object node = path.getLastPathComponent(); - if (node instanceof ArchiveNode) { - ArchiveNode archiveNode = (ArchiveNode) node; - archive = archiveNode.getArchive(); + List archives = new ArrayList<>(); + List selectedNodes = dtac.getSelectedNodes(); + for (GTreeNode node : selectedNodes) { + if (node instanceof InvalidArchiveNode invalidNode) { + archiveManager.closeInvalidArchive(invalidNode.getInvalidArchive()); } - if (archive != null) { + else if (node instanceof ArchiveNode archiveNode) { + PersistentDataTypeArchive archive = archiveNode.getArchive(); archives.add(archive); - if (!editorManager.checkEditors(archive.getDataTypeManager(), true)) { - return; - } } } - if (ArchiveUtils.canClose(archives, gtree)) { - for (Archive archive : archives) { - editorManager.dismissEditors(archive.getDataTypeManager()); - archive.close(); + if (checkEditors(archives)) { + return; + } + for (PersistentDataTypeArchive archive : archives) { + if (!plugin.resolveModifiedArchive(archive)) { + return; } + archiveManager.closeArchive(archive); } } + private boolean checkEditors(List archives) { + DataTypeEditorManager editorManager = plugin.getEditorManager(); + for (PersistentDataTypeArchive archive : archives) { + DataTypeManager dtm = archive.getDataTypeManager(); + if (!editorManager.checkEditors(dtm, true)) { + return true; + } + } + return false; + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UnlockArchiveAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CloseArchiveForEditingAction.java similarity index 66% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UnlockArchiveAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CloseArchiveForEditingAction.java index 3acea700ce..8eb27f5b78 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UnlockArchiveAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CloseArchiveForEditingAction.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,31 +15,28 @@ */ package ghidra.app.plugin.core.datamgr.actions; -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.DataTypesActionContext; -import ghidra.app.plugin.core.datamgr.archive.ArchiveUtils; -import ghidra.app.plugin.core.datamgr.archive.FileArchive; -import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager; -import ghidra.app.plugin.core.datamgr.tree.FileArchiveNode; -import ghidra.program.model.data.DataTypeManager; -import ghidra.util.Msg; - -import java.awt.Component; -import java.io.IOException; - import javax.swing.tree.TreePath; import docking.ActionContext; import docking.action.DockingAction; import docking.action.MenuData; -import docking.widgets.tree.*; +import docking.widgets.tree.GTree; +import docking.widgets.tree.GTreeNode; +import ghidra.app.plugin.core.datamgr.*; +import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager; +import ghidra.app.plugin.core.datamgr.tree.FileArchiveNode; +import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.dtarchive.FileDataTypeArchive; -public class UnlockArchiveAction extends DockingAction { - public static final String ACTION_NAME = "Unlock Archive"; +/** + * Action to set an archive to be read only + */ +public class CloseArchiveForEditingAction extends DockingAction { + public static final String ACTION_NAME = "Close Archive For Editing"; private final DataTypeManagerPlugin plugin; - public UnlockArchiveAction(DataTypeManagerPlugin plugin) { + public CloseArchiveForEditingAction(DataTypeManagerPlugin plugin) { super(ACTION_NAME, plugin.getName()); this.plugin = plugin; @@ -89,7 +85,7 @@ public class UnlockArchiveAction extends DockingAction { // only valid if all selected paths are file archives for (TreePath path : selectionPaths) { GTreeNode node = (GTreeNode) path.getLastPathComponent(); - if (hasWriteLock((FileArchiveNode) node)) { + if (isModifiable((FileArchiveNode) node)) { return true; } } @@ -103,9 +99,9 @@ public class UnlockArchiveAction extends DockingAction { return selectionPaths; } - private boolean hasWriteLock(FileArchiveNode fileArchiveNode) { - FileArchive archive = (FileArchive) fileArchiveNode.getArchive(); - return archive.hasWriteLock(); + private boolean isModifiable(FileArchiveNode fileArchiveNode) { + FileDataTypeArchive archive = fileArchiveNode.getArchive(); + return archive.isChangeable(); } @Override @@ -113,48 +109,22 @@ public class UnlockArchiveAction extends DockingAction { GTree gTree = (GTree) context.getContextObject(); TreePath[] selectionPaths = gTree.getSelectionPaths(); - GTreeState treeState = gTree.getTreeState(); - DataTypeEditorManager editorManager = plugin.getEditorManager(); for (TreePath path : selectionPaths) { FileArchiveNode node = (FileArchiveNode) path.getLastPathComponent(); - FileArchive archive = (FileArchive) node.getArchive(); - if (!canReleaseLock(archive, gTree)) { - continue; - } + FileDataTypeArchive archive = node.getArchive(); + DataTypeManager dataTypeManager = archive.getDataTypeManager(); if (!editorManager.checkEditors(dataTypeManager, true)) { return; } // check for changes and save if desired by user. If cancelled, get out. - if (!ArchiveUtils.canClose(archive, gTree)) { + if (!plugin.resolveModifiedArchive(archive)) { return; } editorManager.dismissEditors(dataTypeManager); - - releaseLock(archive); - } - gTree.restoreTreeState(treeState); - } - - private boolean canReleaseLock(FileArchive archive, Component parent) { - if (archive.getFile() == null) { - Msg.showInfo(getClass(), parent, - "Unsaved Archive", "Unsaved Archives must first be saved."); - return false; - } - return true; - } - - private void releaseLock(FileArchive archive) { - try { - archive.releaseWriteLock(); - } - catch (IOException ioe) { - Msg.showError(this, plugin.getProvider().getComponent(), - "Unable to Release Write Lock", - "Problem attempting to release write lock for archive: " + archive.getName() + - "\nMessage: " + ioe.getMessage(), ioe); + ArchiveManager archiveManager = plugin.getArchiveManager(); + archiveManager.reopenFileArchive(archive, false); } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CopyAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CopyAction.java index ea8ed82e6c..734ba09414 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CopyAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CopyAction.java @@ -71,7 +71,7 @@ public class CopyAction extends DockingAction { if (node instanceof ArchiveRootNode) { return true; } - else if (node instanceof ArchiveNode) { + else if (node instanceof DataTypeStoreNode) { return true; } else if (node instanceof CategoryNode) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateArchiveAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateArchiveAction.java index f72b57d0ef..c2709175e5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateArchiveAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateArchiveAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,10 +22,10 @@ import docking.action.DockingAction; import docking.action.MenuData; import docking.widgets.OptionDialog; import docking.widgets.tree.GTreeNode; +import ghidra.app.plugin.core.datamgr.ArchiveFileChooser; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.archive.Archive; -import ghidra.app.plugin.core.datamgr.archive.ArchiveFileChooser; import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.util.Msg; public class CreateArchiveAction extends DockingAction { @@ -71,14 +71,14 @@ public class CreateArchiveAction extends DockingAction { Msg.trace(this, "\toverwriting file!"); file.delete(); } - Archive newArchive = plugin.getDataTypeManagerHandler().createArchive(file); + FileDataTypeArchive newArchive = plugin.getArchiveManager().createFileArchive(file); if (newArchive != null) { Msg.trace(this, "Created new archive: " + newArchive.getName()); selectNewArchive(newArchive, gTree); } } - private void selectNewArchive(final Archive archive, final DataTypeArchiveGTree gTree) { + private void selectNewArchive(FileDataTypeArchive archive, DataTypeArchiveGTree gTree) { GTreeNode rootNode = gTree.getModelRoot(); gTree.setSelectedNodeByNamePath(new String[] { rootNode.getName(), archive.getName() }); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateCategoryAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateCategoryAction.java index 02853f7224..f7b5f42678 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateCategoryAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateCategoryAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,9 +24,9 @@ import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import ghidra.app.plugin.core.datamgr.DataTypesActionContext; -import ghidra.app.plugin.core.datamgr.archive.Archive; import ghidra.app.plugin.core.datamgr.tree.*; -import ghidra.program.model.data.*; +import ghidra.program.model.data.Category; +import ghidra.program.model.data.DataTypeManager; import ghidra.util.InvalidNameException; public class CreateCategoryAction extends DockingAction { @@ -85,9 +85,8 @@ public class CreateCategoryAction extends DockingAction { TreePath[] selectionPaths = gtree.getSelectionPaths(); final CategoryNode node = (CategoryNode) selectionPaths[0].getLastPathComponent(); Category category = node.getCategory(); - ArchiveNode archiveNode = node.getArchiveNode(); - Archive archive = archiveNode.getArchive(); - DataTypeManager dataTypeManager = archive.getDataTypeManager(); + DataTypeStoreNode dataStoreNode = node.getArchiveNode(); + DataTypeManager dataTypeManager = dataStoreNode.getDataTypeManager(); String newNodeName = getUniqueCategoryName(category); String path = category.toString() + newNodeName; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreatePointerAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreatePointerAction.java index cf35d16fe3..27e8c547e9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreatePointerAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreatePointerAction.java @@ -96,7 +96,7 @@ public class CreatePointerAction extends DockingAction { return false; } - ArchiveNode archiveNode = node.getArchiveNode(); + DataTypeStoreNode archiveNode = node.getArchiveNode(); if (archiveNode == null) { // this can happen as the tree is changing return false; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateProjectArchiveAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateProjectArchiveAction.java index 78e10e8bf2..c0d224c77c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateProjectArchiveAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateProjectArchiveAction.java @@ -22,9 +22,8 @@ import docking.action.DockingAction; import docking.action.MenuData; import docking.widgets.tree.GTreeNode; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.archive.Archive; import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; -import ghidra.util.exception.CancelledException; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; public class CreateProjectArchiveAction extends DockingAction { private final DataTypeManagerPlugin plugin; @@ -40,17 +39,13 @@ public class CreateProjectArchiveAction extends DockingAction { @Override public void actionPerformed(ActionContext context) { - try { - Archive newArchive = plugin.getDataTypeManagerHandler().createProjectArchive(); - DataTypeArchiveGTree gTree = plugin.getProvider().getGTree(); - selectNewArchive(newArchive, gTree); - } - catch (CancelledException ce) { - plugin.getTool().setStatusInfo("Create project archive was cancelled."); - } + ProjectDataTypeArchive newArchive = plugin.getArchiveManager().createProjectArchive(); + DataTypeArchiveGTree gTree = plugin.getProvider().getGTree(); + selectNewArchive(newArchive, gTree); } - private void selectNewArchive(final Archive archive, final DataTypeArchiveGTree gTree) { + private void selectNewArchive(ProjectDataTypeArchive archive, + final DataTypeArchiveGTree gTree) { GTreeNode root = gTree.getViewRoot(); gTree.whenNodeIsReady(root, archive.getName(), archiveNode -> { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateTypeDefAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateTypeDefAction.java index 2e6a1793a7..bb7dcc5e21 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateTypeDefAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateTypeDefAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -62,7 +62,7 @@ public class CreateTypeDefAction extends AbstractTypeDefAction { return false; } - ArchiveNode archiveNode = node.getArchiveNode(); + DataTypeStoreNode archiveNode = node.getArchiveNode(); if (archiveNode == null) { // this can happen as the tree is changing return false; @@ -113,7 +113,6 @@ public class CreateTypeDefAction extends AbstractTypeDefAction { DataTypeNode dataTypeNode = (DataTypeNode) selectionPaths[0].getLastPathComponent(); DataType dataType = dataTypeNode.getDataType(); - DerivativeDataTypeInfo info = new DerivativeDataTypeInfo(plugin, gTree, dataTypeNode, dataType); @@ -132,5 +131,4 @@ public class CreateTypeDefAction extends AbstractTypeDefAction { gTree.startEditing(finalParentNode, newNodeName); } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteAction.java index 51e1c2bd02..487d9748ad 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteAction.java @@ -28,7 +28,7 @@ import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import ghidra.app.plugin.core.datamgr.DataTypesActionContext; -import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; +import ghidra.app.plugin.core.datamgr.tree.DataTypeStoreNode; import ghidra.app.plugin.core.datamgr.tree.DataTypeTreeNode; import ghidra.app.plugin.core.datamgr.util.DataTypeTreeDeleteTask; @@ -84,7 +84,7 @@ public class DeleteAction extends DockingAction { private boolean containsUndeletableNodes(TreePath[] selectionPaths) { for (TreePath path : selectionPaths) { DataTypeTreeNode node = (DataTypeTreeNode) path.getLastPathComponent(); - if (!node.canDelete() || (node instanceof ArchiveNode)) { + if (!node.canDelete() || (node instanceof DataTypeStoreNode)) { return true; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteArchiveAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteArchiveAction.java index 708eb47577..4df1dc6a95 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteArchiveAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteArchiveAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,15 +27,18 @@ import docking.widgets.tree.GTree; import generic.theme.GThemeDefaults.Colors.Messages; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import ghidra.app.plugin.core.datamgr.DataTypesActionContext; -import ghidra.app.plugin.core.datamgr.archive.FileArchive; import ghidra.app.plugin.core.datamgr.tree.*; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.util.HTMLUtilities; import ghidra.util.Msg; public class DeleteArchiveAction extends DockingAction { + private DataTypeManagerPlugin plugin; + public DeleteArchiveAction(DataTypeManagerPlugin plugin) { super("Delete Archive", plugin.getName()); + this.plugin = plugin; setPopupMenuData(new MenuData(new String[] { "Delete Archive" }, null, "File")); @@ -61,7 +64,7 @@ public class DeleteArchiveAction extends DockingAction { if (!(node instanceof FileArchiveNode)) { return false; } - return ((ArchiveNode) node).isModifiable(); + return ((DataTypeStoreNode) node).isModifiable(); } @Override @@ -102,7 +105,9 @@ public class DeleteArchiveAction extends DockingAction { } try { - ((FileArchive) node.getArchive()).delete(); + FileDataTypeArchive archive = node.getArchive(); + plugin.getArchiveManager().closeArchive(archive); + archive.delete(); } catch (IOException e1) { Msg.showError(this, null, "Error", "Error deleting data type archive.", e1); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DerivativeDataTypeInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DerivativeDataTypeInfo.java index bc9591ae23..17f2197c8b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DerivativeDataTypeInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DerivativeDataTypeInfo.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,9 +19,8 @@ import java.util.List; import docking.widgets.tree.GTreeNode; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.archive.Archive; -import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; +import ghidra.app.plugin.core.datamgr.tree.ProgramArchiveNode; import ghidra.program.model.data.*; import ghidra.util.SystemUtilities; import ghidra.util.exception.AssertException; @@ -76,16 +75,17 @@ class DerivativeDataTypeInfo { return parentNode; } - private ArchiveNode getProgramArchiveNode(DataTypeArchiveGTree tree) { + private ProgramArchiveNode getProgramArchiveNode(DataTypeArchiveGTree tree) { DataTypeManager manager = plugin.getProgramDataTypeManager(); GTreeNode rootNode = tree.getModelRoot(); List children = rootNode.getChildren(); for (GTreeNode node : children) { - ArchiveNode archiveNode = (ArchiveNode) node; - Archive archive = archiveNode.getArchive(); - if (archive.getDataTypeManager() == manager) { - return archiveNode; + if (node instanceof ProgramArchiveNode programNode) { + DataTypeManager dtm = programNode.getDataTypeManager(); + if (dtm == manager) { + return programNode; + } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/EditArchivePathAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/EditArchivePathAction.java index 64ccd6c157..5b7b3ef4f9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/EditArchivePathAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/EditArchivePathAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,8 +21,8 @@ import docking.action.DockingAction; import docking.action.MenuData; import docking.widgets.filechooser.GhidraFileChooserMode; import docking.widgets.pathmanager.PathManager; +import ghidra.app.plugin.core.datamgr.ArchiveManager; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.framework.preferences.Preferences; import ghidra.util.HelpLocation; @@ -61,8 +61,8 @@ public class EditArchivePathAction extends DockingAction { false, null); setHelpLocation(new HelpLocation("DataTypeManagerPlugin", "Edit_Archive_Paths_Dialog")); - pathManager.restoreFromPreferences(DataTypeManagerHandler.DATA_TYPE_ARCHIVE_PATH_KEY, - null, DataTypeManagerHandler.DISABLED_DATA_TYPE_ARCHIVE_PATH_KEY); + pathManager.restoreFromPreferences(ArchiveManager.DATA_TYPE_ARCHIVE_PATH_KEY, + null, ArchiveManager.DISABLED_DATA_TYPE_ARCHIVE_PATH_KEY); addWorkPanel(pathManager.getComponent()); addOKButton(); addCancelButton(); @@ -70,8 +70,8 @@ public class EditArchivePathAction extends DockingAction { @Override protected void okCallback() { - pathManager.saveToPreferences(DataTypeManagerHandler.DATA_TYPE_ARCHIVE_PATH_KEY, - DataTypeManagerHandler.DISABLED_DATA_TYPE_ARCHIVE_PATH_KEY); + pathManager.saveToPreferences(ArchiveManager.DATA_TYPE_ARCHIVE_PATH_KEY, + ArchiveManager.DISABLED_DATA_TYPE_ARCHIVE_PATH_KEY); close(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/OpenArchiveAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/OpenArchiveAction.java index e30084731a..2ca77adfb5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/OpenArchiveAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/OpenArchiveAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -26,15 +26,14 @@ import docking.widgets.filechooser.GhidraFileChooser; import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; import generic.jar.ResourceFile; -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.DataTypesProvider; -import ghidra.app.plugin.core.datamgr.archive.Archive; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; +import ghidra.app.plugin.core.datamgr.*; import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; +import ghidra.app.services.Upgrade; import ghidra.framework.Application; import ghidra.framework.GenericRunInfo; import ghidra.framework.preferences.Preferences; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.util.Msg; public class OpenArchiveAction extends DockingAction { @@ -58,47 +57,43 @@ public class OpenArchiveAction extends DockingAction { GhidraFileChooser fileChooser = new GhidraFileChooser(tree); File archiveDirectory = getArchiveDirectory(); - fileChooser.setFileFilter(FileDataTypeManager.GDT_FILEFILTER); + fileChooser.setFileFilter(FileDataTypeArchive.GDT_FILEFILTER); fileChooser.setCurrentDirectory(archiveDirectory); fileChooser.setApproveButtonText("Open DataType Archive File"); fileChooser.setApproveButtonToolTipText("Open DataType Archive File"); - DataTypeManagerHandler manager = plugin.getDataTypeManagerHandler(); + ArchiveManager manager = plugin.getArchiveManager(); File file = fileChooser.getSelectedFile(); fileChooser.dispose(); if (file == null) { return; } - if (!file.getName().endsWith(FileDataTypeManager.EXTENSION)) { - file = new File(file.getParent(), file.getName() + "." + FileDataTypeManager.EXTENSION); + if (!file.getName().endsWith(FileDataTypeArchive.EXTENSION)) { + file = new File(file.getParent(), file.getName() + "." + FileDataTypeArchive.EXTENSION); } File lastOpenedDir = file.getParentFile(); Preferences.setProperty(Preferences.LAST_OPENED_ARCHIVE_DIRECTORY, lastOpenedDir.getAbsolutePath()); - - try { - Archive archive = manager.openArchive(file, false, true); + ResourceFile resourceFile = new ResourceFile(file); + PersistentDataTypeArchive archive = + manager.openFileArchiveInTask(resourceFile, false, Upgrade.ASK, true); + if (archive != null) { GTreeNode node = getNodeForArchive(tree, archive); - if (node != null) { - tree.setSelectedNode(node); - } - } - catch (Throwable t) { - DataTypeManagerHandler.handleArchiveFileException(plugin, new ResourceFile(file), t); + tree.setSelectedNode(node); } } - private GTreeNode getNodeForArchive(GTree tree, Archive archive) { + private GTreeNode getNodeForArchive(GTree tree, PersistentDataTypeArchive archive) { GTreeNode rootNode = tree.getModelRoot(); List allChildren = rootNode.getChildren(); for (GTreeNode node : allChildren) { - ArchiveNode archiveNode = (ArchiveNode) node; - if (archiveNode.getArchive() == archive) { - return archiveNode; + if (node instanceof ArchiveNode archiveNode) { + if (archiveNode.getArchive() == archive) { + return archiveNode; + } } } - return null; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/LockArchiveAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/OpenArchiveForEditingAction.java similarity index 77% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/LockArchiveAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/OpenArchiveForEditingAction.java index 4b78cf7344..c08009d8c0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/LockArchiveAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/OpenArchiveForEditingAction.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,29 +15,27 @@ */ package ghidra.app.plugin.core.datamgr.actions; -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.DataTypesActionContext; -import ghidra.app.plugin.core.datamgr.archive.ArchiveUtils; -import ghidra.app.plugin.core.datamgr.archive.FileArchive; -import ghidra.app.plugin.core.datamgr.tree.FileArchiveNode; -import ghidra.program.model.data.FileDataTypeManager; - import javax.swing.tree.TreePath; import docking.ActionContext; import docking.action.DockingAction; import docking.action.MenuData; import docking.widgets.tree.*; +import ghidra.app.plugin.core.datamgr.*; +import ghidra.app.plugin.core.datamgr.tree.FileArchiveNode; +import ghidra.program.model.dtarchive.FileDataTypeArchive; -public class LockArchiveAction extends DockingAction { - public static final String ACTION_NAME = "Lock Archive"; +/** + * Action to convert a read-only archive to be open for editing + */ +public class OpenArchiveForEditingAction extends DockingAction { + public static final String ACTION_NAME = "Open Archive For Editing"; + private DataTypeManagerPlugin plugin; - public LockArchiveAction(DataTypeManagerPlugin plugin) { + public OpenArchiveForEditingAction(DataTypeManagerPlugin plugin) { super(ACTION_NAME, plugin.getName()); - -// ACTIONS - auto generated + this.plugin = plugin; setPopupMenuData(new MenuData(new String[] { "Open For Editing" }, null, "FileEdit")); - setEnabled(true); } @@ -83,7 +80,7 @@ public class LockArchiveAction extends DockingAction { // only valid if all selected paths are file archives for (TreePath path : selectionPaths) { GTreeNode node = (GTreeNode) path.getLastPathComponent(); - if (canLock(node)) { + if (canOpenForEditing(node)) { return true; } } @@ -97,14 +94,14 @@ public class LockArchiveAction extends DockingAction { return selectionPaths; } - private boolean canLock(GTreeNode node) { + private boolean canOpenForEditing(GTreeNode node) { FileArchiveNode fileNode = (FileArchiveNode) node; - FileArchive archive = (FileArchive) fileNode.getArchive(); + FileDataTypeArchive archive = fileNode.getArchive(); String fname = archive.getFile().getName(); - if (!fname.endsWith(FileDataTypeManager.SUFFIX)) { + if (!fname.endsWith(FileDataTypeArchive.SUFFIX)) { return false; } - return !archive.hasWriteLock(); + return !archive.isChangeable(); } @Override @@ -116,9 +113,10 @@ public class LockArchiveAction extends DockingAction { for (TreePath path : selectionPaths) { FileArchiveNode node = (FileArchiveNode) path.getLastPathComponent(); - FileArchive archive = (FileArchive) node.getArchive(); - if (!archive.hasWriteLock()) { - ArchiveUtils.lockArchive(archive); + FileDataTypeArchive archive = node.getArchive(); + if (!archive.isChangeable()) { + ArchiveManager archiveManager = plugin.getArchiveManager(); + archiveManager.reopenFileArchive(archive, true); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/OpenProjectArchiveAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/OpenProjectArchiveAction.java index d061b75a2a..6ea136f2b5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/OpenProjectArchiveAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/OpenProjectArchiveAction.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,11 +15,23 @@ */ package ghidra.app.plugin.core.datamgr.actions; -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import docking.ActionContext; import docking.action.DockingAction; import docking.action.MenuData; +import ghidra.app.plugin.core.datamgr.ArchiveManager; +import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; +import ghidra.app.services.Recover; +import ghidra.app.services.Upgrade; +import ghidra.app.util.HelpTopics; +import ghidra.framework.main.OpenVersionedFileDialog; +import ghidra.framework.model.DomainFile; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; +import ghidra.util.HelpLocation; +/** + * Action for opening file datatype archives. + */ public class OpenProjectArchiveAction extends DockingAction { private final DataTypeManagerPlugin plugin; @@ -38,7 +49,25 @@ public class OpenProjectArchiveAction extends DockingAction { @Override public void actionPerformed(ActionContext context) { - plugin.openProjectDataTypeArchive(); + PluginTool tool = plugin.getTool(); + ArchiveManager archiveManager = plugin.getArchiveManager(); + OpenVersionedFileDialog dialog = new OpenVersionedFileDialog<>(tool, + "Open Project Data Type Archive", ProjectDataTypeArchive.class); + dialog.setHelpLocation(new HelpLocation(HelpTopics.PROGRAM, "Open_File_Dialog")); + dialog.addOkActionListener(ev -> { + DomainFile domainFile = dialog.getDomainFile(); + int version = dialog.getVersion(); + if (domainFile == null) { + dialog.setStatusText("Please choose a Project Data Type Archive"); + } + else { + dialog.close(); + archiveManager.openProjectArchiveInTask(domainFile, version, Upgrade.ASK, + Recover.ASK, true); + } + }); + + tool.showDialog(dialog); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PasteAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PasteAction.java index c7f6710710..8db62bbb77 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PasteAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PasteAction.java @@ -118,10 +118,10 @@ public class PasteAction extends DockingAction { } // can't cut nodes from one archive and paste into another - ArchiveNode destinationArchiveNode = destinationNode.getArchiveNode(); + DataTypeStoreNode destinationArchiveNode = destinationNode.getArchiveNode(); for (GTreeNode cutNode : nodeList) { DataTypeTreeNode dataTypeTreeNode = (DataTypeTreeNode) cutNode; - ArchiveNode archiveNode = dataTypeTreeNode.getArchiveNode(); + DataTypeStoreNode archiveNode = dataTypeTreeNode.getArchiveNode(); if (!Objects.equals(archiveNode, destinationArchiveNode)) { return true; // is invalid } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RecentlyOpenedArchiveAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RecentlyOpenedArchiveAction.java index e6268c91c3..a8319a2e81 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RecentlyOpenedArchiveAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RecentlyOpenedArchiveAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,13 +18,14 @@ package ghidra.app.plugin.core.datamgr.actions; import docking.ActionContext; import docking.action.DockingAction; import docking.action.MenuData; +import generic.jar.ResourceFile; import generic.util.Path; +import ghidra.app.plugin.core.datamgr.ArchiveManager; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; +import ghidra.app.services.Recover; +import ghidra.app.services.Upgrade; import ghidra.framework.model.DomainFile; import ghidra.util.Msg; -import ghidra.util.task.Task; -import ghidra.util.task.TaskLauncher; /** * Class for action to open a recently opened data type archive. @@ -41,7 +42,7 @@ public class RecentlyOpenedArchiveAction extends DockingAction { String menuGroup) { super(menuGroup + ": \"" + archivePath + "\"", plugin.getName(), false); this.plugin = plugin; - String[] projectPathname = DataTypeManagerHandler.parseProjectPathname(archivePath); + String[] projectPathname = DataTypeManagerPlugin.parseProjectPathname(archivePath); if (projectPathname == null) { this.projectName = null; this.archivePath = archivePath; @@ -64,7 +65,7 @@ public class RecentlyOpenedArchiveAction extends DockingAction { return getTypeInfoRelativeName(filepath); } - String[] projectPathname = DataTypeManagerHandler.parseProjectPathname(filepath); + String[] projectPathname = DataTypeManagerPlugin.parseProjectPathname(filepath); if (projectPathname == null) { return filepath; } @@ -111,16 +112,17 @@ public class RecentlyOpenedArchiveAction extends DockingAction { @Override public void actionPerformed(ActionContext context) { + ArchiveManager archiveManager = plugin.getArchiveManager(); if (projectName == null) { - DataTypeManagerHandler archiveManager = plugin.getDataTypeManagerHandler(); Path path = new Path(archivePath); - OpenArchiveTask task = new OpenArchiveTask(archiveManager, path); - new TaskLauncher(task, plugin.getProvider().getComponent()); + ResourceFile file = path.getPath(); + archiveManager.openFileArchiveInTask(file, false, Upgrade.ASK, true); } else { DomainFile df = plugin.getProjectArchiveFile(projectName, archivePath); if (df != null) { - plugin.openArchive(df); + archiveManager.openProjectArchiveInTask(df, DomainFile.DEFAULT_VERSION, Upgrade.ASK, + Recover.ASK, true); } else { Msg.showError(this, null, "Project Archive Open Error", @@ -129,25 +131,4 @@ public class RecentlyOpenedArchiveAction extends DockingAction { } } - private class OpenArchiveTask extends Task { - private final Path taskArchivePath; - private final DataTypeManagerHandler archiveManager; - - OpenArchiveTask(DataTypeManagerHandler archiveManager, Path archivePath) { - super("Opening Archive " + archivePath.getPath().getName(), false, false, true); - this.archiveManager = archiveManager; - this.taskArchivePath = archivePath; - } - - @Override - public void run(ghidra.util.task.TaskMonitor monitor) { - try { - archiveManager.openArchive(taskArchivePath.getPath(), false, true); - } - catch (Exception e) { - DataTypeManagerHandler.handleArchiveFileException(plugin, taskArchivePath.getPath(), - e); - } - } - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RedoArchiveTransactionAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RedoArchiveTransactionAction.java index cf9345eadf..1284e5b711 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RedoArchiveTransactionAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RedoArchiveTransactionAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,8 +15,11 @@ */ package ghidra.app.plugin.core.datamgr.actions; +import java.io.IOException; + import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.program.model.data.StandAloneDataTypeManager; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.util.Msg; public class RedoArchiveTransactionAction extends AbstractUndoRedoArchiveTransactionAction { @@ -28,18 +31,24 @@ public class RedoArchiveTransactionAction extends AbstractUndoRedoArchiveTransac } @Override - protected boolean canExecute(StandAloneDataTypeManager dtm) { - return dtm.canRedo(); + protected boolean canExecute(PersistentDataTypeArchive dta) { + return dta.canRedo(); } @Override - protected String getNextName(StandAloneDataTypeManager dtm) { - return dtm.getRedoName(); + protected String getNextName(PersistentDataTypeArchive dta) { + return dta.getRedoName(); } @Override - protected void execute(StandAloneDataTypeManager dtm) { - dtm.redo(); + protected void execute(PersistentDataTypeArchive dta) { + try { + dta.redo(); + } + catch (IOException e) { + Msg.showError(this, null, "Archive Redo Failed", + "Failed to redo last transaction: " + dta.getName(), e); + } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RemoveInvalidArchiveFromProgramAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RemoveInvalidArchiveFromProgramAction.java index 634c2dc991..a6e1fcc4c4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RemoveInvalidArchiveFromProgramAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RemoveInvalidArchiveFromProgramAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,12 +24,15 @@ import docking.widgets.OptionDialog; import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; import generic.theme.GThemeDefaults.Colors.Messages; -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.DataTypesActionContext; -import ghidra.app.plugin.core.datamgr.archive.*; +import ghidra.app.plugin.core.datamgr.*; +import ghidra.app.plugin.core.datamgr.archive.InvalidArchive; import ghidra.app.plugin.core.datamgr.tree.InvalidArchiveNode; import ghidra.util.HTMLUtilities; +/** + * Removes a SourceArchive from a program and removes any datatype associations to that source + * archive. + */ public class RemoveInvalidArchiveFromProgramAction extends DockingAction { private DataTypeManagerPlugin plugin; @@ -86,8 +89,8 @@ public class RemoveInvalidArchiveFromProgramAction extends DockingAction { return; } - Archive archive = invalidArchiveNode.getArchive(); - DataTypeManagerHandler dataTypeManagerHandler = plugin.getDataTypeManagerHandler(); - dataTypeManagerHandler.removeInvalidArchive((InvalidFileArchive) archive); + InvalidArchive archive = invalidArchiveNode.getInvalidArchive(); + ArchiveManager archiveManager = plugin.getArchiveManager(); + archiveManager.removeInvalidArchiveFromProgram(archive); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RenameAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RenameAction.java index fcd05afe75..084c645d98 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RenameAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RenameAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -26,6 +26,9 @@ import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import ghidra.app.plugin.core.datamgr.DataTypesActionContext; import ghidra.app.plugin.core.datamgr.tree.*; +/** + * Renames objects in the datatypes tree if they support being renamed + */ public class RenameAction extends DockingAction { public RenameAction(DataTypeManagerPlugin plugin) { @@ -37,7 +40,8 @@ public class RenameAction extends DockingAction { @Override public boolean isAddToPopup(ActionContext context) { GTreeNode node = getSelectedNode(context); - if (node == null || node instanceof ArchiveRootNode || node instanceof ArchiveNode) { + if (node == null || node instanceof ArchiveRootNode || node instanceof DataTypeStoreNode || + node instanceof InvalidArchiveNode) { return false; } return true; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ReplaceDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ReplaceDataTypeAction.java index 79f65c4454..bf46f80c47 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ReplaceDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ReplaceDataTypeAction.java @@ -24,15 +24,13 @@ import docking.action.MenuData; import docking.widgets.label.GLabel; import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.DataTypesActionContext; -import ghidra.app.plugin.core.datamgr.archive.Archive; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; +import ghidra.app.plugin.core.datamgr.*; import ghidra.app.plugin.core.datamgr.tree.*; import ghidra.app.util.datatype.DataTypeSelectionDialog; import ghidra.app.util.datatype.DataTypeSelectionEditor; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.DataTypeStore; import ghidra.util.Msg; import ghidra.util.data.DataTypeParser.AllowedDataTypes; import ghidra.util.layout.VerticalLayout; @@ -134,14 +132,14 @@ public class ReplaceDataTypeAction extends DockingAction { return; // cancelled } - DataTypeManagerHandler dtmHandler = plugin.getDataTypeManagerHandler(); + ArchiveManager archiveManager = plugin.getArchiveManager(); DataTypeManager newDtm = newDt.getDataTypeManager(); - Archive sourceArchive = dtmHandler.getArchive(newDtm); - Archive destinationArchive = findArchive(node); + DataTypeStore sourceStore = newDtm.getDataStore(); + DataTypeStore destinationStore = findArchive(node); DataType oldDt = ((DataTypeNode) node).getDataType(); DataTypeManager dtm = oldDt.getDataTypeManager(); - if (sourceArchive != destinationArchive) { + if (sourceStore != destinationStore) { oldDt = oldDt.clone(oldDt.getDataTypeManager()); } @@ -158,10 +156,10 @@ public class ReplaceDataTypeAction extends DockingAction { } } - private Archive findArchive(GTreeNode node) { + private DataTypeStore findArchive(GTreeNode node) { while (node != null) { - if (node instanceof ArchiveNode) { - return ((ArchiveNode) node).getArchive(); + if (node instanceof DataTypeStoreNode archiveNode) { + return archiveNode.getDataTypeStore(); } node = node.getParent(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SaveArchiveAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SaveArchiveAction.java index 51fa66a5f6..de804bf69b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SaveArchiveAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SaveArchiveAction.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,14 +15,6 @@ */ package ghidra.app.plugin.core.datamgr.actions; -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.DataTypesActionContext; -import ghidra.app.plugin.core.datamgr.archive.*; -import ghidra.app.plugin.core.datamgr.tree.*; -import ghidra.util.Msg; - -import java.io.IOException; - import javax.swing.tree.TreePath; import docking.ActionContext; @@ -31,7 +22,13 @@ import docking.action.DockingAction; import docking.action.MenuData; import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; +import ghidra.app.plugin.core.datamgr.*; +import ghidra.app.plugin.core.datamgr.tree.*; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +/** + * Actions for saving datatype archives that support saving. + */ public class SaveArchiveAction extends DockingAction { private final DataTypeManagerPlugin plugin; @@ -39,7 +36,6 @@ public class SaveArchiveAction extends DockingAction { super("Save", plugin.getName()); this.plugin = plugin; -// ACTIONS - auto generated setPopupMenuData(new MenuData(new String[] { "Save Archive" }, null, "File")); setEnabled(true); @@ -97,10 +93,9 @@ public class SaveArchiveAction extends DockingAction { } private boolean canSave(GTreeNode node) { - if ((node instanceof FileArchiveNode) || (node instanceof ProjectArchiveNode)) { - ArchiveNode archiveNode = (ArchiveNode) node; - Archive archive = archiveNode.getArchive(); - return archive.isChanged() && archive.isSavable(); + if (node instanceof ArchiveNode archiveNode) { + PersistentDataTypeArchive archive = archiveNode.getArchive(); + return archive.isChanged(); } return false; } @@ -112,41 +107,13 @@ public class SaveArchiveAction extends DockingAction { TreePath[] selectionPaths = gTree.getSelectionPaths(); for (TreePath path : selectionPaths) { Object node = path.getLastPathComponent(); - if (node instanceof ArchiveNode) { - ArchiveNode archiveNode = (ArchiveNode) node; - Archive archive = archiveNode.getArchive(); + if (node instanceof ArchiveNode archiveNode) { + PersistentDataTypeArchive archive = archiveNode.getArchive(); if (archive.isChanged()) { - saveArchive(archive); + ArchiveManager archiveManager = plugin.getArchiveManager(); + archiveManager.save(archive); } } } } - - private void saveFileArchive(FileArchive archive) { - try { - archive.save(); - } - catch (IOException ioe) { - Msg.showError(this, plugin.getProvider().getComponent(), "Unable to Save File", - "Unexpected exception attempting to save archive: " + archive, ioe); - } - } - - private void saveProjectArchive(ProjectArchive archive) { - DataTypeManagerHandler dtmHandler = plugin.getDataTypeManagerHandler(); - dtmHandler.save(archive.getDomainObject()); - } - - private void saveArchive(Archive archive) { - if (archive instanceof ProjectArchive) { - saveProjectArchive((ProjectArchive) archive); - } - else if (archive instanceof FileArchive) { - saveFileArchive((FileArchive) archive); - } - else { - throw new IllegalArgumentException(archive.getName() + - " must be Project or File archive."); - } - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SaveAsAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SaveArchiveAsAction.java similarity index 59% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SaveAsAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SaveArchiveAsAction.java index 309139f4a9..57fe814065 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SaveAsAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SaveArchiveAsAction.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,15 +15,6 @@ */ package ghidra.app.plugin.core.datamgr.actions; -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.DataTypesActionContext; -import ghidra.app.plugin.core.datamgr.archive.*; -import ghidra.app.plugin.core.datamgr.tree.*; -import ghidra.util.Msg; -import ghidra.util.exception.DuplicateFileException; - -import java.io.IOException; - import javax.swing.tree.TreePath; import docking.ActionContext; @@ -32,11 +22,17 @@ import docking.action.DockingAction; import docking.action.MenuData; import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; +import ghidra.app.plugin.core.datamgr.*; +import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; +import ghidra.program.model.dtarchive.*; -public class SaveAsAction extends DockingAction { +/** + * Action for saving a ProjectDataTypeArchive or a FileDataTypeArchive to a new storage location. + */ +public class SaveArchiveAsAction extends DockingAction { private DataTypeManagerPlugin plugin; - public SaveAsAction(DataTypeManagerPlugin plugin) { + public SaveArchiveAsAction(DataTypeManagerPlugin plugin) { super("Save As", plugin.getName()); this.plugin = plugin; @@ -58,7 +54,7 @@ public class SaveAsAction extends DockingAction { } GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent(); - if ((node instanceof FileArchiveNode) || (node instanceof ProjectArchiveNode)) { + if (node instanceof ArchiveNode) { return true; } @@ -68,29 +64,17 @@ public class SaveAsAction extends DockingAction { @Override public void actionPerformed(ActionContext context) { GTree gtree = (GTree) context.getContextObject(); - + ArchiveManager archiveManager = plugin.getArchiveManager(); TreePath[] selectionPaths = gtree.getSelectionPaths(); ArchiveNode node = (ArchiveNode) selectionPaths[0].getLastPathComponent(); - Archive archive = node.getArchive(); + PersistentDataTypeArchive archive = node.getArchive(); - try { - if (node instanceof FileArchiveNode) { - FileArchive fa = (FileArchive) archive; - ArchiveUtils.saveAs(gtree, fa); - plugin.addRecentlyOpenedArchiveFile(fa.getFile()); - } - else if (node instanceof ProjectArchiveNode) { - ProjectArchive pa = (ProjectArchive) archive; - pa.saveAs(gtree); - plugin.addRecentlyOpenedProjectArchive(pa); - } + archiveManager.saveAs(archive); + if (archive instanceof FileDataTypeArchive fa) { + plugin.addRecentlyOpenedArchiveFile(fa.getFile()); } - catch (DuplicateFileException de) { - Msg.showError(this, gtree, "Unable to Save File", "Archive already exists: " + archive); - } - catch (IOException ioe) { - Msg.showError(this, gtree, "Unable to Save File", - "Unexpected exception attempting to save archive: " + archive, ioe); + else if (archive instanceof ProjectDataTypeArchive pa) { + plugin.addRecentlyOpenedProjectArchive(pa); } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SetArchiveArchitectureAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SetArchiveArchitectureAction.java index 0520cf2ad0..0107334ab1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SetArchiveArchitectureAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SetArchiveArchitectureAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,8 +15,6 @@ */ package ghidra.app.plugin.core.datamgr.actions; -import java.io.IOException; - import javax.swing.tree.TreePath; import docking.ActionContext; @@ -26,23 +24,23 @@ import docking.widgets.OptionDialog; import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; import generic.theme.GThemeDefaults.Colors.Messages; -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.DataTypesActionContext; -import ghidra.app.plugin.core.datamgr.archive.*; -import ghidra.app.plugin.core.datamgr.tree.*; +import ghidra.app.plugin.core.datamgr.*; +import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; +import ghidra.app.plugin.core.datamgr.tree.ProjectArchiveNode; import ghidra.app.plugin.core.processors.SetLanguageDialog; -import ghidra.framework.model.DomainFile; -import ghidra.framework.store.LockException; -import ghidra.program.model.data.StandAloneDataTypeManager; -import ghidra.program.model.data.StandAloneDataTypeManager.LanguageUpdateOption; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; +import ghidra.program.model.dtarchive.DataTypeArchive.LanguageUpdateOption; import ghidra.program.model.lang.*; import ghidra.program.model.listing.IncompatibleLanguageException; import ghidra.program.util.DefaultLanguageService; import ghidra.util.Msg; -import ghidra.util.Swing; import ghidra.util.exception.CancelledException; import ghidra.util.task.*; +/** + * Action for setting the Architecture for a DataTypeAchive. + */ public class SetArchiveArchitectureAction extends DockingAction { private final DataTypeManagerPlugin plugin; @@ -78,13 +76,10 @@ public class SetArchiveArchitectureAction extends DockingAction { return false; } GTreeNode node = (GTreeNode) selectionPath.getLastPathComponent(); - if (!(node instanceof FileArchiveNode) && !(node instanceof ProjectArchiveNode)) { - return false; + if (node instanceof ArchiveNode archiveNode) { + return archiveNode.getArchive().isUpdatable(); } - ArchiveNode archiveNode = (ArchiveNode) node; - StandAloneDataTypeManager dtm = - (StandAloneDataTypeManager) archiveNode.getArchive().getDataTypeManager(); - return dtm.isUpdatable(); + return false; } @Override @@ -94,13 +89,12 @@ public class SetArchiveArchitectureAction extends DockingAction { return; } GTreeNode node = (GTreeNode) selectionPath.getLastPathComponent(); - if (!(node instanceof FileArchiveNode) && !(node instanceof ProjectArchiveNode)) { + if (!(node instanceof ArchiveNode archiveNode)) { return; } - if (node instanceof ProjectArchiveNode) { - ProjectArchiveNode paNode = (ProjectArchiveNode) node; - ProjectArchive pa = (ProjectArchive) paNode.getArchive(); + if (node instanceof ProjectArchiveNode projectNode) { + ProjectDataTypeArchive pa = projectNode.getArchive(); if (!pa.hasExclusiveAccess()) { Msg.showError(this, null, "Set Program Architecture Failed", "Setting program-architecture on Project Archive requires exclusive checkout."); @@ -108,11 +102,8 @@ public class SetArchiveArchitectureAction extends DockingAction { } } - ArchiveNode archiveNode = (ArchiveNode) node; - StandAloneDataTypeManager dtm = - (StandAloneDataTypeManager) archiveNode.getArchive().getDataTypeManager(); - - if (dtm.isChanged()) { + PersistentDataTypeArchive archive = archiveNode.getArchive(); + if (archive.isChanged()) { if (OptionDialog.OPTION_ONE != OptionDialog.showOptionDialogWithCancelAsDefaultButton( null, "Save Archive Changes", "Archive has unsaved changes which must be saved before continuing." + @@ -120,19 +111,12 @@ public class SetArchiveArchitectureAction extends DockingAction { "Save")) { return; } - try { - archiveNode.getArchive().save(); - } - catch (IOException e) { - Msg.showError(this, null, "Save Archive Failed", - "Failed to save changes for Archive: " + dtm.getName() + "\n" + e.getMessage()); - return; - } + plugin.getArchiveManager().save(archiveNode.getArchive()); } SetLanguageDialog dialog = new SetLanguageDialog(plugin.getTool(), - dtm.getProgramArchitecture(), - "Select Program Architecture for Archive: " + dtm.getName()); + archive.getProgramArchitecture(), + "Select Program Architecture for Archive: " + archive.getName()); LanguageID languageId = dialog.getLanguageDescriptionID(); CompilerSpecID compilerSpecId = dialog.getCompilerSpecDescriptionID(); if ((languageId == null) || (compilerSpecId == null)) { @@ -147,15 +131,15 @@ public class SetArchiveArchitectureAction extends DockingAction { buf.append(compilerSpecId.getIdAsString()); String newProgramArchitectureSummary = buf.toString(); - String programArchitectureSummary = dtm.getProgramArchitectureSummary(); + String programArchitectureSummary = archive.getProgramArchitectureSummary(); String msg = "Set program-architecture for Archive?
" + dtm.getPath() + "

";
+					"\">" + archive.getPath() + "
";
 			if (programArchitectureSummary != null) {
 				msg +=
 					"\nChange Language/Compiler\n  from:  " +
-					programArchitectureSummary + "\n    to:  ";
+						programArchitectureSummary + "\n    to:  ";
 			}
 			else {
 				msg += "\n\nLanguage/Compiler: ";
@@ -170,46 +154,44 @@ public class SetArchiveArchitectureAction extends DockingAction {
 				return;
 			}
 
-			new TaskLauncher(new SetProgramArchitectureTask(archiveNode.getArchive(), dtm, language,
+			new TaskLauncher(new SetProgramArchitectureTask(archive, language,
 				compilerSpecId));
 		}
 		catch (LanguageNotFoundException e) {
 			Msg.showError(this, null, "Archive Update Failed",
-				"Failed to set program-architecture for Archive: " + dtm.getName() + "\n" +
+				"Failed to set program-architecture for Archive: " + archive.getName() + "\n" +
 					e.getMessage());
 		}
 	}
 
 	private class SetProgramArchitectureTask extends Task {
 
-		private final Archive archive;
-		private final StandAloneDataTypeManager dtm;
+		private final PersistentDataTypeArchive archive;
 		private final Language language;
 		private final CompilerSpecID compilerSpecId;
 
-		public SetProgramArchitectureTask(Archive archive, StandAloneDataTypeManager dtm,
-				Language language, CompilerSpecID compilerSpecId) {
+		public SetProgramArchitectureTask(PersistentDataTypeArchive archive, Language language,
+				CompilerSpecID compilerSpecId) {
 			super("Updating Program-Architecture for Archive", true, false, true, false);
 			this.archive = archive;
-			this.dtm = dtm;
 			this.language = language;
 			this.compilerSpecId = compilerSpecId;
 		}
 
 		@Override
 		public void run(TaskMonitor monitor) throws CancelledException {
-			
+
 			boolean success = false;
 			try {
 				try {
-					dtm.setProgramArchitecture(language, compilerSpecId,
+					archive.setProgramArchitecture(language, compilerSpecId,
 						LanguageUpdateOption.TRANSLATE, monitor);
 					success = true;
 				}
 				catch (IncompatibleLanguageException e) {
 					int resp = OptionDialog.showOptionDialog(null, "Archive Architecture Change",
 						"Unable to translate storage for specified architecture change.
" + dtm.getPath() + + Messages.NORMAL + "\">" + archive.getPath() + "

Would you like to Clear custom storage information or Cancel change?", "Clear"); if (resp == OptionDialog.CANCEL_OPTION) { @@ -220,7 +202,7 @@ public class SetArchiveArchitectureAction extends DockingAction { if (resp == OptionDialog.OPTION_TWO) { updateOption = LanguageUpdateOption.UNCHANGED; } - dtm.setProgramArchitecture(language, compilerSpecId, updateOption, monitor); + archive.setProgramArchitecture(language, compilerSpecId, updateOption, monitor); success = true; } } @@ -229,27 +211,18 @@ public class SetArchiveArchitectureAction extends DockingAction { } catch (Exception e) { Msg.showError(this, null, "Archive Update Failed", - "Failed to set program-architecture for Archive: " + dtm.getName() + "\n" + + "Failed to set program-architecture for Archive: " + archive.getName() + "\n" + e.getMessage()); } finally { if (!success) { - Swing.runNow(() -> { - /* flush event queue before closing archive */ }); - if (archive instanceof FileArchive) { - try { - ((FileArchive) archive).releaseWriteLock(); - ((FileArchive) archive).acquireWriteLock(); - } - catch (LockException | IOException e) { - archive.close(); - } - } - else { // if (archive instanceof ProjectArchive) { - archive.close(); - DomainFile df = ((ProjectArchive) archive).getDomainFile(); - plugin.openArchive(df); - } + // not sure why this is needed; put back if so and explain why + // Swing.runNow(() -> { + // /* flush event queue before closing archive */ + // }); + + ArchiveManager archiveManager = plugin.getArchiveManager(); + archiveManager.reopenArchive(archive); } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UndoArchiveTransactionAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UndoArchiveTransactionAction.java index bc16e07226..7b16d0ed07 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UndoArchiveTransactionAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UndoArchiveTransactionAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,8 +15,11 @@ */ package ghidra.app.plugin.core.datamgr.actions; +import java.io.IOException; + import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.program.model.data.StandAloneDataTypeManager; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.util.Msg; public class UndoArchiveTransactionAction extends AbstractUndoRedoArchiveTransactionAction { @@ -28,18 +31,24 @@ public class UndoArchiveTransactionAction extends AbstractUndoRedoArchiveTransac } @Override - protected boolean canExecute(StandAloneDataTypeManager dtm) { - return dtm.canUndo(); + protected boolean canExecute(PersistentDataTypeArchive dta) { + return dta.canUndo(); } @Override - protected String getNextName(StandAloneDataTypeManager dtm) { - return dtm.getUndoName(); + protected String getNextName(PersistentDataTypeArchive dta) { + return dta.getUndoName(); } @Override - protected void execute(StandAloneDataTypeManager dtm) { - dtm.undo(); + protected void execute(PersistentDataTypeArchive dta) { + try { + dta.undo(); + } + catch (IOException e) { + Msg.showError(this, null, "Archive Undo Failed", + "Failed to undo last transaction: " + dta.getName(), e); + } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UpdateSourceArchiveNamesAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UpdateSourceArchiveNamesAction.java deleted file mode 100644 index fa0b35dcfc..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UpdateSourceArchiveNamesAction.java +++ /dev/null @@ -1,96 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.datamgr.actions; - -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.DataTypesActionContext; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; -import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.SourceArchive; -import ghidra.util.HelpLocation; -import docking.ActionContext; -import docking.action.DockingAction; -import docking.action.MenuData; - -public class UpdateSourceArchiveNamesAction extends DockingAction { - - public static final String NAME = "Update Source Archive Names"; - - private final DataTypeManagerPlugin plugin; - private final DataTypeManager dtm; - - public UpdateSourceArchiveNamesAction(DataTypeManagerPlugin plugin, DataTypeManager dtm) { - super(NAME, plugin.getName()); - this.plugin = plugin; - this.dtm = dtm; - - setPopupMenuData(new MenuData(new String[] { NAME })); - setHelpLocation(new HelpLocation(plugin.getName(), "Update_Source_Archive_Names")); - } - - @Override - public boolean isEnabledForContext(ActionContext context) { - if (!(context instanceof DataTypesActionContext)) { - return false; - } - - DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler(); - - // TODO: nothing can be done to protect against dtm being closed - // Must ensure that dtm closing must trigger associated Archive - // close and cleanup from tree - - for (SourceArchive archive : dtm.getSourceArchives()) { - DataTypeManager archiveDtm = handler.getDataTypeManager(archive); - if (archiveDtm == null) { - continue; - } - String archiveName = archiveDtm.getName(); - if (!archive.getName().equals(archiveName)) { - return true; - } - } - return false; - } - - @Override - public void actionPerformed(ActionContext context) { - - DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler(); - - // TODO: nothing can be done to protect against dtm being closed - // Must ensure that dtm closing must trigger associated Archive - // close and cleanup from tree - - int txId = dtm.startTransaction(NAME); - try { - for (SourceArchive archive : dtm.getSourceArchives()) { - DataTypeManager archiveDtm = handler.getDataTypeManager(archive); - if (archiveDtm == null) { - continue; - } - String archiveName = archiveDtm.getName(); - if (!archive.getName().equals(archiveName)) { - archive.setName(archiveName); - } - } - } - finally { - dtm.endTransaction(txId, true); - } - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/AssociateDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/AssociateDataTypeAction.java index 05651aa52d..3c587cffc5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/AssociateDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/AssociateDataTypeAction.java @@ -34,13 +34,15 @@ import docking.widgets.list.GComboBoxCellRenderer; import docking.widgets.tree.GTreeNode; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import ghidra.app.plugin.core.datamgr.DataTypesActionContext; -import ghidra.app.plugin.core.datamgr.archive.*; -import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; +import ghidra.app.plugin.core.datamgr.tree.DataTypeStoreNode; import ghidra.app.plugin.core.datamgr.util.DataTypeTreeCopyMoveTask; import ghidra.app.plugin.core.datamgr.util.DataTypeTreeCopyMoveTask.ActionType; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.program.model.dtarchive.DataTypeStore; +import ghidra.program.model.listing.Program; import ghidra.util.Msg; import ghidra.util.layout.PairLayout; import ghidra.util.task.TaskLauncher; @@ -70,12 +72,12 @@ public class AssociateDataTypeAction extends DockingAction { if (dtNodes.isEmpty()) { return false; } - Archive singleDTArchive = getSingleDTArchive(dtNodes); + DataTypeStore singleDataTypeStore = getSingleDataTypeStore(dtNodes); // NOTE: We only support program-to-archive since other cases become rather complicated - // when considering dependencies that must get copied and how their associations shuold be + // when considering dependencies that must get copied and how their associations should be // handled. - return singleDTArchive instanceof ProgramArchive; + return singleDataTypeStore instanceof Program; } private List getDataTypeNodes(DataTypesActionContext ctx) { @@ -91,11 +93,11 @@ public class AssociateDataTypeAction extends DockingAction { return !nodes.isEmpty(); } - private Archive getSingleDTArchive(List nodes) { + private DataTypeStore getSingleDataTypeStore(List nodes) { - Archive dtArchive = null; + DataTypeStore dtArchive = null; for (GTreeNode node : nodes) { - Archive archive = findArchive(node); + DataTypeStore archive = findDataStore(node); if (dtArchive == null) { dtArchive = archive; continue; @@ -108,23 +110,21 @@ public class AssociateDataTypeAction extends DockingAction { return dtArchive; } - private static Archive findArchive(GTreeNode node) { + private static DataTypeStore findDataStore(GTreeNode node) { while (node != null) { - if (node instanceof ArchiveNode) { - return ((ArchiveNode) node).getArchive(); + if (node instanceof DataTypeStoreNode archiveNode) { + return archiveNode.getDataTypeStore(); } node = node.getParent(); } return null; } - private List getDestinationArchives(Archive excludedArchive) { + private List getDestinationArchives(DataTypeStore excluded) { - List archives = plugin.getAllArchives(); - List destArchives = archives.stream() - .filter(a -> !(a instanceof ProgramArchive)) - .filter(a -> !(a instanceof BuiltInArchive)) - .filter(a -> !a.equals(excludedArchive)) + List archives = plugin.getArchiveManager().getOpenArchives(); + List destArchives = archives.stream() + .filter(a -> !a.equals(excluded)) .sorted((a1, a2) -> a1.getName().compareToIgnoreCase(a2.getName())) .collect(Collectors.toList()); @@ -138,14 +138,14 @@ public class AssociateDataTypeAction extends DockingAction { if (dtNodes.isEmpty()) { return; } - Archive dtArchive = getSingleDTArchive(dtNodes); - if (dtArchive == null) { + DataTypeStore dtStore = getSingleDataTypeStore(dtNodes); + if (dtStore == null) { return; } - if (!dtArchive.isModifiable()) { + if (!dtStore.isChangeable()) { DataTypeUtils.showUnmodifiableArchiveErrorMessage(context.getSourceComponent(), - "Disassociate Failed", dtArchive.getDataTypeManager()); + "Disassociate Failed", dtStore.getDataTypeManager()); return; } @@ -156,7 +156,7 @@ public class AssociateDataTypeAction extends DockingAction { return; } - List archives = getDestinationArchives(dtArchive); + List archives = getDestinationArchives(dtStore); if (archives.isEmpty()) { Msg.showInfo(this, getProviderComponent(), "No Source Archives Open", "No source archives open. Please open the desired source archive."); @@ -169,7 +169,7 @@ public class AssociateDataTypeAction extends DockingAction { return; } - Archive destinationArchive = dialog.getArchive(); + PersistentDataTypeArchive destinationArchive = dialog.getArchive(); Category destinationCategory = dialog.getCategory(); DataTypeTreeCopyMoveTask task = @@ -186,15 +186,15 @@ public class AssociateDataTypeAction extends DockingAction { private class ChooseArchiveDialog extends DialogComponentProvider { private Category category; - private Archive archive; + private PersistentDataTypeArchive archive; // default to true to handle the case the user presses Escape or presses the x button private boolean isCancelled = true; - private GhidraComboBox archivesBox = new GhidraComboBox<>(); + private GhidraComboBox archivesBox = new GhidraComboBox<>(); private JTextField categoryField = new JTextField(20); - ChooseArchiveDialog(List archives) { + ChooseArchiveDialog(List archives) { super("Choose New Source Archive", true); addWorkPanel(buildWorkPanel()); @@ -211,12 +211,14 @@ public class AssociateDataTypeAction extends DockingAction { archivesBox.setRenderer(new GComboBoxCellRenderer<>() { @Override - public Component getListCellRendererComponent(JList list, - Archive value, int index, boolean isSelected, boolean cellHasFocus) { + public Component getListCellRendererComponent( + JList list, + PersistentDataTypeArchive value, int index, boolean isSelected, + boolean cellHasFocus) { JLabel renderer = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - Archive a = value; + PersistentDataTypeArchive a = value; renderer.setText(a.getName()); return renderer; } @@ -244,13 +246,13 @@ public class AssociateDataTypeAction extends DockingAction { clearStatusText(); - archive = (Archive) archivesBox.getSelectedItem(); + archive = archivesBox.getSelectedItem(); if (archive == null) { setStatusText("Please choose an archive"); return; } - if (!archive.isModifiable()) { + if (!archive.isChangeable()) { setStatusText( "Archive is not modifiable. You must first open this archive for edit."); return; @@ -318,7 +320,7 @@ public class AssociateDataTypeAction extends DockingAction { DockingWindowManager.showDialog(parent, this); } - Archive getArchive() { + PersistentDataTypeArchive getArchive() { return archive; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/CommitAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/CommitAction.java index 821b306751..6b7f8c8f1b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/CommitAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/CommitAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,8 +19,7 @@ import java.util.List; import docking.action.MenuData; import ghidra.app.plugin.core.datamgr.*; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; -import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; +import ghidra.app.plugin.core.datamgr.tree.DataTypeStoreNode; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.SourceArchive; import ghidra.util.HelpLocation; @@ -29,11 +28,11 @@ public class CommitAction extends SyncAction { public static final String MENU_NAME = "Commit Data Types To"; - public CommitAction(DataTypeManagerPlugin plugin, DataTypeManagerHandler dataTypeManagerHandler, - DataTypeManager dtm, ArchiveNode archiveNode, SourceArchive sourceArchive, + public CommitAction(DataTypeManagerPlugin plugin, ArchiveManager archiveManager, + DataTypeManager dtm, DataTypeStoreNode archiveNode, SourceArchive sourceArchive, boolean isEnabled) { - super("Commit Changes To Archive", plugin, dataTypeManagerHandler, dtm, archiveNode, + super("Commit Changes To Archive", plugin, archiveManager, dtm, archiveNode, sourceArchive, isEnabled); setPopupMenuData(new MenuData(new String[] { MENU_NAME, sourceArchive.getName() })); setHelpLocation(new HelpLocation(plugin.getName(), getHelpTopic())); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/CommitSingleDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/CommitSingleDataTypeAction.java index 4fe152dbc8..b497af044b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/CommitSingleDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/CommitSingleDataTypeAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -26,7 +26,6 @@ import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; import generic.theme.GIcon; import ghidra.app.plugin.core.datamgr.*; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; import ghidra.program.model.data.*; @@ -70,8 +69,8 @@ public class CommitSingleDataTypeAction extends DockingAction { } DataTypeNode dataTypeNode = (DataTypeNode) node; DataType dataType = dataTypeNode.getDataType(); - DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler(); - DataTypeSyncState syncStatus = DataTypeSynchronizer.getSyncStatus(handler, dataType); + ArchiveManager archiveManager = plugin.getArchiveManager(); + DataTypeSyncState syncStatus = DataTypeSynchronizer.getSyncStatus(archiveManager, dataType); switch (syncStatus) { case UNKNOWN: @@ -101,8 +100,8 @@ public class CommitSingleDataTypeAction extends DockingAction { DataTypeNode dataTypeNode = (DataTypeNode) node; DataType dataType = dataTypeNode.getDataType(); DataTypeManager dtm = dataType.getDataTypeManager(); - DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler(); - DataTypeSyncState syncStatus = DataTypeSynchronizer.getSyncStatus(handler, dataType); + ArchiveManager archiveManager = plugin.getArchiveManager(); + DataTypeSyncState syncStatus = DataTypeSynchronizer.getSyncStatus(archiveManager, dataType); if (syncStatus == DataTypeSyncState.CONFLICT) { int result = OptionDialog.showOptionDialog(gTree, "Lose Changes in Archive?", @@ -115,7 +114,7 @@ public class CommitSingleDataTypeAction extends DockingAction { } SourceArchive sourceArchive = dataType.getSourceArchive(); DataTypeManager sourceDTM = - plugin.getDataTypeManagerHandler().getDataTypeManager(sourceArchive); + plugin.getArchiveManager().getDataTypeManager(sourceArchive); if (sourceDTM == null) { Msg.showInfo(getClass(), gTree, "Commit Failed", "Source Archive not open: " + sourceArchive.getName()); @@ -133,7 +132,8 @@ public class CommitSingleDataTypeAction extends DockingAction { plugin.commit(dataType); // Source archive data type manager was already checked for null above. - DataTypeSynchronizer synchronizer = new DataTypeSynchronizer(handler, dtm, sourceArchive); + DataTypeSynchronizer synchronizer = + new DataTypeSynchronizer(archiveManager, dtm, sourceArchive); synchronizer.reSyncOutOfSyncInTimeOnlyDataTypes(); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/DisassociateAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/DisassociateAction.java index d26a97de9b..d27b8cadae 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/DisassociateAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/DisassociateAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,8 +25,7 @@ import docking.action.MenuData; import docking.widgets.OptionDialog; import docking.widgets.tree.GTreeState; import ghidra.app.plugin.core.datamgr.*; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; -import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; +import ghidra.app.plugin.core.datamgr.tree.DataTypeStoreNode; import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.SourceArchive; @@ -39,15 +38,15 @@ public class DisassociateAction extends DockingAction { private final SourceArchive sourceArchive; private final DataTypeManager dtm; - private final DataTypeManagerHandler handler; + private final ArchiveManager achiveManager; private final DataTypeManagerPlugin plugin; - private final ArchiveNode archiveNode; + private final DataTypeStoreNode archiveNode; - public DisassociateAction(DataTypeManagerPlugin plugin, DataTypeManagerHandler handler, - DataTypeManager dtm, ArchiveNode archiveNode, SourceArchive sourceArchive) { + public DisassociateAction(DataTypeManagerPlugin plugin, ArchiveManager archiveManager, + DataTypeManager dtm, DataTypeStoreNode archiveNode, SourceArchive sourceArchive) { super("Disassociate Archive", plugin.getName()); this.plugin = plugin; - this.handler = handler; + this.achiveManager = archiveManager; this.dtm = dtm; this.archiveNode = archiveNode; this.sourceArchive = sourceArchive; @@ -70,7 +69,8 @@ public class DisassociateAction extends DockingAction { @Override public void actionPerformed(ActionContext context) { - DataTypeSynchronizer synchronizer = new DataTypeSynchronizer(handler, dtm, sourceArchive); + DataTypeSynchronizer synchronizer = + new DataTypeSynchronizer(achiveManager, dtm, sourceArchive); if (!dtm.isUpdatable()) { showRequiresArchiveOpenMessage(dtm.getName()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/DisassociateDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/DisassociateDataTypeAction.java index 5f1c471932..8a6a111d38 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/DisassociateDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/DisassociateDataTypeAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -29,7 +29,6 @@ import docking.widgets.OptionDialog; import docking.widgets.tree.*; import ghidra.app.plugin.core.datamgr.*; import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; @@ -217,11 +216,12 @@ public class DisassociateDataTypeAction extends DockingAction { monitor.setMessage("Disassociating types from " + dtm.getName()); monitor.initialize(dataTypes.size()); - DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler(); + ArchiveManager archiveManager = plugin.getArchiveManager(); for (Entry> entry : sourceToTypes.entrySet()) { SourceArchive source = entry.getKey(); List types = entry.getValue(); - DataTypeSynchronizer synchronizer = new DataTypeSynchronizer(handler, dtm, source); + DataTypeSynchronizer synchronizer = + new DataTypeSynchronizer(archiveManager, dtm, source); disassociate(synchronizer, dtm, types, monitor); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/RevertAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/RevertAction.java index 7d10931cde..87da4c2aee 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/RevertAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/RevertAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,10 +18,8 @@ package ghidra.app.plugin.core.datamgr.actions.associate; import java.util.List; import docking.action.MenuData; -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.DataTypeSyncInfo; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; -import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; +import ghidra.app.plugin.core.datamgr.*; +import ghidra.app.plugin.core.datamgr.tree.DataTypeStoreNode; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.SourceArchive; import ghidra.util.HelpLocation; @@ -30,11 +28,11 @@ public class RevertAction extends SyncAction { public static final String MENU_NAME = "Revert Data Types From"; - public RevertAction(DataTypeManagerPlugin plugin, DataTypeManagerHandler dataTypeManagerHandler, - DataTypeManager dtm, ArchiveNode archiveNode, SourceArchive sourceArchive, + public RevertAction(DataTypeManagerPlugin plugin, ArchiveManager archiveManager, + DataTypeManager dtm, DataTypeStoreNode archiveNode, SourceArchive sourceArchive, boolean isEnabled) { - super("Revert Data Type Changes", plugin, dataTypeManagerHandler, dtm, archiveNode, + super("Revert Data Type Changes", plugin, archiveManager, dtm, archiveNode, sourceArchive, isEnabled); setPopupMenuData(new MenuData(new String[] { MENU_NAME, sourceArchive.getName() })); setHelpLocation(new HelpLocation(plugin.getName(), getHelpTopic())); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/RevertDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/RevertDataTypeAction.java index 8367123c41..4f30c65a3e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/RevertDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/RevertDataTypeAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,7 +23,6 @@ import docking.action.MenuData; import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; import ghidra.app.plugin.core.datamgr.*; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; import ghidra.program.model.data.*; @@ -60,8 +59,8 @@ public class RevertDataTypeAction extends DockingAction { DataTypeNode dataTypeNode = (DataTypeNode) node; DataType dataType = dataTypeNode.getDataType(); - DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler(); - DataTypeSyncState syncStatus = DataTypeSynchronizer.getSyncStatus(handler, dataType); + ArchiveManager archiveManager = plugin.getArchiveManager(); + DataTypeSyncState syncStatus = DataTypeSynchronizer.getSyncStatus(archiveManager, dataType); switch (syncStatus) { case UNKNOWN: @@ -90,13 +89,13 @@ public class RevertDataTypeAction extends DockingAction { DataTypeNode dataTypeNode = (DataTypeNode) node; DataType dataType = dataTypeNode.getDataType(); DataTypeManager dtm = dataType.getDataTypeManager(); - DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler(); + ArchiveManager archiveManager = plugin.getArchiveManager(); SourceArchive sourceArchive = dataType.getSourceArchive(); if (!dtm.isUpdatable()) { DataTypeUtils.showUnmodifiableArchiveErrorMessage(gTree, "Revert Failed", dtm); return; } - DataTypeManager sourceDTM = handler.getDataTypeManager(sourceArchive); + DataTypeManager sourceDTM = archiveManager.getDataTypeManager(sourceArchive); if (sourceDTM == null) { Msg.showInfo(getClass(), gTree, "Revert Failed", "Source Archive not open: " + sourceArchive.getName()); @@ -105,7 +104,8 @@ public class RevertDataTypeAction extends DockingAction { plugin.revert(dataType); // Source archive data type manager was already checked for null above. - DataTypeSynchronizer synchronizer = new DataTypeSynchronizer(handler, dtm, sourceArchive); + DataTypeSynchronizer synchronizer = + new DataTypeSynchronizer(archiveManager, dtm, sourceArchive); synchronizer.reSyncOutOfSyncInTimeOnlyDataTypes(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/SyncAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/SyncAction.java index 7f228feb6c..4a27bb9932 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/SyncAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/SyncAction.java @@ -24,8 +24,7 @@ import docking.action.DockingAction; import docking.widgets.OptionDialog; import docking.widgets.tree.GTreeState; import ghidra.app.plugin.core.datamgr.*; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; -import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; +import ghidra.app.plugin.core.datamgr.tree.DataTypeStoreNode; import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.SourceArchive; @@ -38,16 +37,16 @@ public abstract class SyncAction extends DockingAction implements Comparable { - - /** - * Gets the name for this data type archive. - * This is the name to be presented to the user for this archive. - * @return the name or null if closed - */ - public String getName(); - - /** - * Closes this archive. Some archives cannot be closed (i.e. BuiltIn data type archive.) - */ - public void close(); - - /** - * Determines if this is a modifiable archive like a program archive, a non-versioned - * project archive, a checked out versioned project archive or a locked (open for editing) - * file archive. - * @return true if it is a modifiable archive and can have its contents changed. - */ - public boolean isModifiable(); - - /** - * Determines if this archive can be saved. Some archives cannot be saved. - * @return true if the archive can be saved. - */ - public boolean isSavable(); - - /** - * Determines if this archive has been changed. Some archives cannot be changed. - * @return true if the archive contains unsaved changes. - */ - public boolean isChanged(); - - /** - * Saves this archive. Some archives cannot be saved. - * @throws DuplicateFileException if there is an exception saving - * @throws IOException if there is an exception saving - */ - public void save() throws DuplicateFileException, IOException; - - /** - * Saves this archive to a newly named file. - * @param component the parent component the any dialogs shown - * @throws IOException if there is an exception saving - */ - public void saveAs(Component component) throws IOException; - - /** - * Gets the icon representing this archive. - * @param expanded true means show the icon for this archive as an expanded (open) tree node. - * false indicates the node is closed. - * @return the archive's icon. - */ - public Icon getIcon(boolean expanded); -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ArchiveManagerListener.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ArchiveManagerListener.java deleted file mode 100644 index e5a913ec8c..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ArchiveManagerListener.java +++ /dev/null @@ -1,44 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.datamgr.archive; - -import ghidra.program.model.data.DataTypeManager; - -public interface ArchiveManagerListener { - /** - * Called when a new Archive is opened. - * @param archive the new archive that was opened. - */ - void archiveOpened(Archive archive); - - /** - * Called when an archive is closed. - * @param archive the archive that was closed. - */ - void archiveClosed(Archive archive); - - /** - * Called when the edited state of the archive has changed, for example, when an archive - * has had a data type or category added or removed. - */ - public void archiveStateChanged(Archive archive); - - /** - * Called when the {@link DataTypeManager} of the archive has changed. This can happen when - * an archive is locked or unlocked. - */ - public void archiveDataTypeManagerChanged(Archive archive); -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ArchiveUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ArchiveUtils.java deleted file mode 100644 index 590fad3050..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ArchiveUtils.java +++ /dev/null @@ -1,215 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.datamgr.archive; - -import java.awt.Component; -import java.io.File; -import java.io.IOException; -import java.util.List; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import docking.widgets.OptionDialog; -import ghidra.framework.store.LockException; -import ghidra.program.model.data.FileDataTypeManager; -import ghidra.util.Msg; -import ghidra.util.ReadOnlyException; -import ghidra.util.exception.DuplicateFileException; - -public class ArchiveUtils { - static final Logger log = LogManager.getLogger(ArchiveUtils.class); - - private ArchiveUtils() { - // utils class - } - - /** - * Saves the given archive to a file of the user's choosing. - * - * @param component The component over which GUI message will be shown when required. - * @param archive The archive to save. - * @throws DuplicateFileException thrown in the case of a naming conflict. - * @throws IOException If the sky is falling. - */ - public static void saveAs(Component component, FileArchive archive) - throws DuplicateFileException, IOException { - File saveAsFile = getFile(component, archive); - if (saveAsFile == null) { - return; - } - - if (saveAsFile.equals(archive.getFile())) { - archive.save(); - } - else { - archive.saveAs(saveAsFile); - } - } - - /** - * This method will perform a save on the given archive. However, the one caveat is that if - * the archive has never been saved, then a filename needs to be chosen. Under this 'special' - * condition this method logically turns into a {@link #saveAs(Component, FileArchive)}. - * - * @param component The component over which GUI message will be shown when required. - * @param archive The archive to save. - * @throws DuplicateFileException thrown only in the {@link #saveAs(Component, FileArchive)} - * condition in the case of a naming conflict. - * @throws IOException If the sky is falling. - */ - public static void save(Component component, Archive archive) - throws DuplicateFileException, IOException { - if (!archive.isSavable()) { - archive.saveAs(component); - } - else { - archive.save(); - } - } - - public static boolean canClose(List archiveList, Component component) { - for (Archive archive : archiveList) { - if (!canClose(archive, component)) { - return false; - } - } - return true; - } - - public static boolean lockArchive(FileArchive archive) { - try { - archive.acquireWriteLock(); - return true; - } - catch (ReadOnlyException e) { - Msg.showError(log, null, "Unable to Lock Archive for Writing", e.getMessage()); - } - catch (LockException exc) { - Msg.showError(log, null, "Unable to Lock Archive for Writing", - "Unable to obtain lock for archive: " + archive.getName() + "\n" + - exc.getMessage()); - } - catch (IOException ioe) { - Throwable cause = ioe.getCause(); - if (cause == null) { - cause = ioe; - } - Msg.showError(log, null, "Unable to Lock Archive for Writing", - "Problem attempting to open archive for update: " + archive.getName() + "\n" + - cause.getMessage()); - } - return false; - } - -// ================================================================================================== -// Private methods -//================================================================================================== - - static File getFile(Component component, FileArchive archive) { - ArchiveFileChooser fileChooser = new ArchiveFileChooser(component); - String archiveName = archive.getName(); - File file = fileChooser.promptUserForFile(archiveName); - fileChooser.dispose(); - if (file == null) { - return null; - } - if (file.equals(archive.getFile().getFile(false))) { - return file; - } - - if (archive.getArchiveManager().isInUse(file)) { - Msg.showInfo(ArchiveUtils.class, component, "Cannot Perform Save As", - "Cannot save archive to " + file.getName() + "\nbecause " + file.getName() + - " is in use."); - return null; - } - - if (file.exists()) { - if (OptionDialog.showYesNoDialogWithNoAsDefaultButton(component, - "Overwrite Existing File?", "Do you want to overwrite existing file\n" + - file.getAbsolutePath()) != OptionDialog.OPTION_ONE) { - return null; - } - - if (!deleteArchiveFile(file)) { - Msg.showError(log, component, "Error Deleting File", - "Could not delete file " + file.getAbsolutePath()); - - return null; - } - } - - return file; - } - - private static boolean deleteArchiveFile(File file) { - try { - FileDataTypeManager.delete(file); - return true; - } - catch (Exception e) { - // try below - } - return file.delete(); - } - - public static boolean canClose(Archive archive, Component component) { - if (!archive.isChanged()) { - return true; - } - int result = - OptionDialog.showYesNoCancelDialog(component, "Save Archive?", "Datatype Archive \"" + - archive.getName() + "\" has been changed.\n Do you want to save the changes?"); - - if (result == OptionDialog.CANCEL_OPTION) { - return false; - } - if (result == OptionDialog.NO_OPTION) { - if (archive instanceof FileArchive) { - // Release the write lock without saving. Returns file archive to its unedited state. - try { - ((FileArchive) archive).releaseWriteLock(); - } - catch (IOException e) { - Msg.showError(log, component, "Unable to release File Archive write lock.", - e.getMessage(), e); - return false; - } - } - return true; - } - - return saveArchive(archive, component); - } - - private static boolean saveArchive(Archive archive, Component component) { - try { - ArchiveUtils.save(component, archive); - return true; - } - catch (DuplicateFileException e) { - Msg.showError(log, component, "Unable to Save Archive", - "Archive already exists: " + archive, e); - } - catch (IOException e) { - Msg.showError(log, component, "Unable to Save Archive", - "Unexpected exception attempting to save archive: " + archive, e); - } - return false; - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/BasicDataTypeArchiveService.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/BasicDataTypeArchiveService.java new file mode 100644 index 0000000000..3596e27ecd --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/BasicDataTypeArchiveService.java @@ -0,0 +1,215 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.datamgr.archive; + +import java.io.IOException; +import java.util.*; + +import generic.jar.ResourceFile; +import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility; +import ghidra.app.services.*; +import ghidra.framework.model.DomainFile; +import ghidra.program.database.data.ProgramDataTypeManager; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; +import ghidra.program.model.data.BuiltInDataTypeManager; +import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.dtarchive.*; +import ghidra.util.Msg; +import ghidra.util.UniversalID; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; + +/** + * Simple, non-ui implementation of the {@link DataTypeArchiveService} interface. This is only + * used in headless mode (and some tests). The methods of this class are synchronized to protect + * the openArchives structure and to prevent multiple threads from attempting to open the same + * archives at the same time. Since this is used in headless mode, they may be multiple threads + * accessing this service at the same time. + */ +public class BasicDataTypeArchiveService implements DataTypeArchiveService { + + private Map openArchives = new HashMap<>(); + protected BuiltInDataTypeManager builtInDataTypesManager; + + public BasicDataTypeArchiveService() { + this.builtInDataTypesManager = BuiltInDataTypeManager.getDataTypeManager(); + } + + public synchronized void dispose() { + for (PersistentDataTypeArchive archive : new ArrayList<>(openArchives.values())) { + closeArchive(archive); // mutates openArchives map + } + } + + @Override + public synchronized DataTypeManager getBuiltInDataTypesManager() { + return builtInDataTypesManager; + } + + @Override + public synchronized DataTypeManager[] getDataTypeManagers() { + List list = new ArrayList<>(); + list.add(builtInDataTypesManager); + for (PersistentDataTypeArchive archive : openArchives.values()) { + list.add(archive.getDataTypeManager()); + } + return list.toArray(DataTypeManager[]::new); + } + + @Override + public synchronized List getDataTypeArchives() { + return new ArrayList(openArchives.values()); + } + + @Override + public synchronized void closeArchive(DataTypeManager dtm) { + if (dtm instanceof BuiltInDataTypeManager) { + Msg.info(this, "Cannot close the built-in Data Type Manager"); + return; + } + + if (dtm instanceof ProgramDataTypeManager) { + Msg.info(this, "Cannot close the Program's Data Type Manager"); + return; + } + + PersistentDataTypeArchive archive = openArchives.get(dtm.getUniversalID()); + if (archive == null) { + Msg.info(this, "Unable close archive; archive not open: '%s'".formatted(dtm.getName())); + return; + } + closeArchive(archive); + } + + @Override + public synchronized void closeArchive(PersistentDataTypeArchive archive) { + openArchives.remove(archive.getDataTypeManager().getUniversalID()); + archive.release(this); + } + + @Override + public synchronized FileDataTypeArchive openFileArchive(String archiveName, TaskMonitor monitor) + throws IOException, DuplicateIdException, CancelledException { + ResourceFile file = DataTypeArchiveUtility.findArchiveFile(archiveName); + if (file != null) { + return openFileArchive(file, false, Upgrade.NO, monitor); + } + return null; + } + + @Override + public synchronized ProjectDataTypeArchive openProjectArchive(DomainFile domainFile, + Upgrade upgradeStrategy, Recover recoverStrategy, TaskMonitor monitor) + throws VersionException, CancelledException, IOException, DuplicateIdException { + + if (!ProjectDataTypeArchive.class.isAssignableFrom(domainFile.getDomainObjectClass())) { + throw new IOException("Unable to open domain file: '%s', not a data type archive" + .formatted(domainFile.getName())); + } + + ProjectDataTypeArchive archive = getOpenArchive(domainFile); + if (archive == null) { + boolean okToUpgrade = upgradeStrategy == Upgrade.YES; + boolean okToRecover = recoverStrategy == Recover.YES; + archive = openDomainFile(domainFile, okToUpgrade, okToRecover, monitor); + addArchive(archive); + } + + return archive; + } + + private ProjectDataTypeArchive openDomainFile(DomainFile domainFile, boolean okToUpgrade, + boolean okToRecover, TaskMonitor monitor) + throws VersionException, CancelledException, IOException { + ProjectDataTypeArchive dta = + (ProjectDataTypeArchive) domainFile.getDomainObject(this, false, false, monitor); + return dta; + } + + @Override + public synchronized FileDataTypeArchive openFileArchive(ResourceFile file, + boolean openForUpdate, Upgrade upgradeStrategy, TaskMonitor monitor) + throws IOException, DuplicateIdException, CancelledException { + file = file.getCanonicalFile(); + FileDataTypeArchive archive = getOpenArchive(file); + if (archive == null) { + boolean okToUpgrade = upgradeStrategy == Upgrade.YES; + archive = doOpenFileArchive(file, openForUpdate, okToUpgrade, monitor); + addArchive(archive); + } + return archive; + } + + private FileDataTypeArchive doOpenFileArchive(ResourceFile file, boolean openForUpdate, + boolean okToUpgrade, TaskMonitor monitor) throws IOException, CancelledException { + try { + if (!openForUpdate) { + return DataTypeArchiveFactory.openReadOnly(file, this, monitor); + } + return DataTypeArchiveFactory.openForUpdate(file, okToUpgrade, this, monitor); + } + catch (VersionException e) { + throw new IOException(e); + } + } + + private void addArchive(PersistentDataTypeArchive archive) + throws DuplicateIdException { + UniversalID id = archive.getDataTypeManager().getUniversalID(); + PersistentDataTypeArchive existing = openArchives.get(id); + if (existing != null) { + if (existing.isClosed()) { + openArchives.remove(id); + } + else { + archive.release(this); + throw new DuplicateIdException(archive.getName(), existing.getName()); + } + } + openArchives.put(id, archive); + } + + private FileDataTypeArchive getOpenArchive(ResourceFile file) { + for (PersistentDataTypeArchive archive : openArchives.values()) { + if (archive instanceof FileDataTypeArchive fileArchive) { + if (fileArchive.getFile().equals(file)) { + if (fileArchive.isClosed()) { + openArchives.remove(fileArchive.getDataTypeManager().getUniversalID()); + return null; + } + return fileArchive; + } + } + } + return null; + } + + private ProjectDataTypeArchive getOpenArchive(DomainFile projectFile) { + for (PersistentDataTypeArchive archive : openArchives.values()) { + if (archive instanceof ProjectDataTypeArchive projectArchive) { + if (archive.getDomainFile().equals(projectFile)) { + if (archive.isClosed()) { + openArchives.remove(archive.getDataTypeManager().getUniversalID()); + return null; + } + return projectArchive; + } + } + } + return null; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/BuiltInArchive.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/BuiltInArchive.java deleted file mode 100644 index 9a874a75aa..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/BuiltInArchive.java +++ /dev/null @@ -1,89 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.datamgr.archive; - -import java.awt.Component; -import java.io.IOException; - -import javax.swing.Icon; - -import generic.theme.GIcon; -import ghidra.program.model.data.BuiltInDataTypeManager; -import ghidra.program.model.data.DataTypeManager; -import ghidra.util.exception.DuplicateFileException; - -public class BuiltInArchive implements Archive { - - private static Icon CLOSED_ICON = new GIcon("icon.plugin.datatypes.archive.built.in.closed"); - private static Icon OPEN_ICON = new GIcon("icon.plugin.datatypes.archive.built.in.open"); - private DataTypeManagerHandler archiveManager; - private BuiltInDataTypeManager dataTypeManager; - - BuiltInArchive(DataTypeManagerHandler archiveManager, BuiltInDataTypeManager dataTypeManager) { - this.archiveManager = archiveManager; - this.dataTypeManager = dataTypeManager; - } - - @Override - public DataTypeManager getDataTypeManager() { - return dataTypeManager; - } - - @Override - public String getName() { - return dataTypeManager.getName(); - } - - @Override - public int compareTo(Archive archive) { - return -1; // Built-ins are always at the top - } - - @Override - public boolean isModifiable() { - return false; // Can't change the data types that are in Built-Ins. - } - - @Override - public void close() { - // Not allowed to close the Built In Data Type Manager. - } - - @Override - public boolean isChanged() { - return false; // Can't change. - } - - @Override - public boolean isSavable() { - return false; // Can't save. - } - - @Override - public void save() throws DuplicateFileException, IOException { - // Can't "Save" so do nothing. - } - - @Override - public void saveAs(Component component) throws IOException { - // Can't "Save As" so do nothing. - } - - @Override - public Icon getIcon(boolean expanded) { - return expanded ? OPEN_ICON : CLOSED_ICON; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java deleted file mode 100644 index 28baeeba2b..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java +++ /dev/null @@ -1,1810 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.datamgr.archive; - -import static ghidra.framework.main.DataTreeDialogType.*; - -import java.awt.Component; -import java.awt.event.ActionListener; -import java.io.*; -import java.rmi.ConnectException; -import java.util.*; - -import docking.widgets.OptionDialog; -import docking.widgets.pathmanager.PathManager; -import generic.jar.ResourceFile; -import generic.util.Path; -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager; -import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility; -import ghidra.app.util.HelpTopics; -import ghidra.framework.client.*; -import ghidra.framework.main.DataTreeDialog; -import ghidra.framework.main.DataTreeDialogType; -import ghidra.framework.model.*; -import ghidra.framework.options.SaveState; -import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.store.FileSystem; -import ghidra.program.database.DataTypeArchiveContentHandler; -import ghidra.program.database.DataTypeArchiveDB; -import ghidra.program.database.data.ProgramDataTypeManager; -import ghidra.program.model.data.*; -import ghidra.program.model.listing.DataTypeArchive; -import ghidra.program.model.listing.Program; -import ghidra.util.*; -import ghidra.util.exception.*; -import ghidra.util.task.*; - -/** - * Helper class to manage the archive files. - */ - -public class DataTypeManagerHandler { - - private static final String CONTENT_NAME = "Data Type Archive"; - private final static String ARCHIVE_NAMES = "ArchiveNames"; - private static final String RELATIVE_PATH_PREFIX = "."; - private static final String PROJECT_NAME_DELIMETER = ":"; - private final static String RECENT_NAMES = "RecentArchiveNames"; - private static final String FAVORITES = "Favorite Dts"; - - private static final String[] UNALLOWED_ARCHIVE_PATH_FRAGMENTS = new String[] { - "/Ghidra/Extensions/", "/Ghidra/docs/", "/Ghidra/Features/", "/Ghidra/Test/" }; - - public static final String OLD_DATA_TYPE_ARCHIVE_PATH_KEY = "DATA_TYPE_ARCHIVE_PATH"; - public static final String DATA_TYPE_ARCHIVE_PATH_KEY = "DATATYPE_ARCHIVE_PATHS"; - public static final String DISABLED_DATA_TYPE_ARCHIVE_PATH_KEY = - "DISABLED_DATA_TYPE_ARCHIVE_PATH"; - - private ProgramArchive programArchive; - private List openArchives = new ArrayList<>(); - private Set initiallyOpenedFileArchiveNames = new HashSet<>(); - private Set userOpenedFileArchiveNames = new HashSet<>(); - private Set knownOpenFileArchiveNames = new HashSet<>(); - private Map invalidArchives = new HashMap<>(); - - private boolean treeDialogCancelled = false; - private DomainFileFilter archiveFileFilter; - - private DataTypeIndexer dataTypeIndexer; - private List archiveManagerlisteners = new ArrayList<>(); - private List dataTypeManagerListeners = new ArrayList<>(); - - private RecentlyUsedDataType recentlyUsedDataType = new RecentlyUsedDataType(); - - private BuiltInDataTypeManager builtInDataTypesManager; - private final DataTypeManagerPlugin plugin; - private PluginTool tool; - private DataTypeManagerListenerDelegate listenerDelegate; - private MyFolderListener folderListener; - - // Updated anytime any datatype or category changes in any open archive, including the program - // archive. Currently used by data type tree nodes to know if their tooltip cache is stale. - private long modCount = 0; - - public DataTypeManagerHandler(DataTypeManagerPlugin plugin) { - this.plugin = plugin; - this.tool = plugin.getTool(); - listenerDelegate = new DataTypeManagerListenerDelegate(); - builtInDataTypesManager = BuiltInDataTypeManager.getDataTypeManager(); - builtInDataTypesManager.addDataTypeManagerListener(listenerDelegate); - initializeFavorites(); - - dataTypeIndexer = new DataTypeIndexer(); - dataTypeIndexer.addDataTypeManager(builtInDataTypesManager); - openArchives.add(new BuiltInArchive(this, builtInDataTypesManager)); - - archiveFileFilter = new DefaultDomainFileFilter(DataTypeArchive.class, true); - - folderListener = new MyFolderListener(); - tool.getProject().getProjectData().addDomainFolderChangeListener(folderListener); - } - - public void dispose() { - dataTypeIndexer.removeDataTypeManager(builtInDataTypesManager); - builtInDataTypesManager.removeDataTypeManagerListener(listenerDelegate); - tool.getProject().getProjectData().removeDomainFolderChangeListener(folderListener); - } - - /** - * Returns the current modification count which is incremented anytime any archive or any - * category or datatype it contains is changed in any way. This includes the datatypes in - * the current program. - * @return the current modification id. - */ - public long getModificationCount() { - return modCount; - } - - /** - * Notification that the given program is open. Add the root category - * for the program to any provider that is open. - * @param program the program - */ - public void programOpened(Program program) { - boolean wasChanged = tool.hasConfigChanged(); - programArchive = new ProgramArchive(program); - openProgramArchives(program); - addArchive(programArchive); - - // Do not mark tool configs as changed when restoring archives used by the program. The - // archives opened by the program are considered transient. Alternatively, archives opened - // by the user will trigger config state changes. - tool.setConfigChanged(wasChanged); - } - - /** - * Notification that the program is closed. Remove the root category - * for the program from the provider. - */ - public void programClosed() { - - programArchive.getDataTypeManager().removeDataTypeManagerListener(listenerDelegate); - dataTypeIndexer.removeDataTypeManager(programArchive.getDataTypeManager()); - openArchives.remove(programArchive); - fireArchiveClosed(programArchive); - programArchive = null; - } - - private void openProgramArchives(Program program) { - DataTypeManager programDataTypeManager = program.getDataTypeManager(); - List sources = programDataTypeManager.getSourceArchives(); - for (SourceArchive dataTypeSource : sources) { - if (isOpen(dataTypeSource) || isKnownInvalidArchive(dataTypeSource)) { - continue; - } - switch (dataTypeSource.getArchiveType()) { - case PROJECT: - openProjectArchive(dataTypeSource); - break; - case FILE: - openFileArchive(dataTypeSource); - break; - default: - // all other cases do nothing - break; - } - - } - } - - private boolean isOpen(SourceArchive dataTypeSource) { - List allArchives = getAllArchives(); - for (Archive archive : allArchives) { - DataTypeManager dataTypeManager = archive.getDataTypeManager(); - UniversalID universalID = dataTypeManager.getUniversalID(); - if ((universalID != null) && universalID.equals(dataTypeSource.getSourceArchiveID())) { - return true; - } - } - return false; - } - - private void openProjectArchive(SourceArchive dataTypeSource) { - String domainFileID = dataTypeSource.getDomainFileID(); - DomainFile domainFile = - plugin.getTool().getProject().getProjectData().getFileByID(domainFileID); - if (domainFile == null) { - createInvalidArchiveNode(dataTypeSource); - return; - } - plugin.openArchive(domainFile); - } - - private void updateSourceArchiveName(DataTypeManager dataTypeManager, String fileID, - String name) { - int transactionID = - dataTypeManager.startTransaction("Update Data Type Source Archive Name"); - try { - dataTypeManager.updateSourceArchiveName(fileID, name); - } - finally { - dataTypeManager.endTransaction(transactionID, true); - } - } - - /** - * @return all archive search paths (both enabled and disabled are included) - */ - private Path[] getArchivePaths() { - return PathManager.getPathsFromPreferences(DATA_TYPE_ARCHIVE_PATH_KEY, null, - DISABLED_DATA_TYPE_ARCHIVE_PATH_KEY); - } - - private FileArchive openFileArchive(String archiveName, UniversalID archiveID, - boolean acquireWriteLock) { - - if (archiveName.endsWith(FileDataTypeManager.SUFFIX)) { - archiveName = archiveName.substring(0, - archiveName.length() - FileDataTypeManager.SUFFIX.length()); - } - - String archiveFileName = archiveName + FileDataTypeManager.SUFFIX; - - List allArchives = getAllArchives(); - for (Archive archive : allArchives) { - if (archive instanceof FileArchive) { - if (archive.getName().equals(archiveName)) { - if (archiveID == null || - archiveID.equals(archive.getDataTypeManager().getUniversalID())) { - if (acquireWriteLock && !archive.isModifiable()) { - // Close archive - hopefully it will get reopened below - closeArchive(archive); - } - break; - } - } - } - } - - Path[] pathsFromPreferences = getArchivePaths(); - for (Path path : pathsFromPreferences) { - if (!path.isEnabled() || !isAllowedArchivePath(path.getPathAsString())) { - continue; - } - ResourceFile archiveFile = new ResourceFile(path.getPath(), archiveFileName); - if (archiveFile.exists()) { - FileArchive archive = openFileArchive(archiveFile, archiveID, acquireWriteLock); - if (archive != null) { - return archive; - } - } - } - - // Look for archive provided with installation (read-only) - ResourceFile archiveFile = DataTypeArchiveUtility.findArchiveFile(archiveFileName); - if (archiveFile == null) { - Msg.showError(this, plugin.getProvider().getComponent(), "Open Archive Failed", - "Archive file not found: " + archiveFileName); - return null; - } - return openFileArchive(archiveFile, archiveID, false); - } - - private FileArchive openFileArchive(ResourceFile archiveFile, UniversalID archiveID, - boolean acquireWriteLock) { - try { - FileArchive archive = new FileArchive(this, archiveFile, acquireWriteLock); - if (archiveID == null || - archiveID.equals(archive.getDataTypeManager().getUniversalID())) { - addArchive(archive); - return archive; - } - archive.close(); - } - catch (Throwable t) { - // Show and/or log error - handleArchiveFileException(plugin, archiveFile, t); - } - return null; - } - - private void openFileArchive(SourceArchive dataTypeSource) { - if (openFileArchive(dataTypeSource.getName(), dataTypeSource.getSourceArchiveID(), - false) == null) { - createInvalidArchiveNode(dataTypeSource); - } - } - - public Archive createArchive(File file) { - try { - Archive archive = new FileArchive(this, file); - addArchivePath(new ResourceFile(file)); - addArchive(archive); - userOpenedFileArchiveNames.add(getSaveableArchive(file.getAbsolutePath())); - return archive; - } - catch (Exception e) { - Msg.showError(this, plugin.getProvider().getComponent(), "Create Archive Failed", - "Error creating archive file (" + file.getName() + "): " + e.getMessage()); - } - return null; - } - - public Archive createProjectArchive() throws CancelledException { - CreateDataTypeArchiveDataTreeDialog dialog = getCreateDialog(); - - treeDialogCancelled = true; - tool.showDialog(dialog); - if (treeDialogCancelled) { - throw new CancelledException(); - } - - DataTypeArchive dataTypeArchive = dialog.getNewDataTypeArchiveDB(); - Archive archive = - new ProjectArchive(this, dataTypeArchive, dataTypeArchive.getDomainFile()); - addArchive(archive); - return archive; - } - - @SuppressWarnings("unused") - private void copyArchive(Archive originalArchive, Archive newArchive, TaskMonitor monitor) { - DataTypeManager originalManager = originalArchive.getDataTypeManager(); - DataTypeManager newManager = newArchive.getDataTypeManager(); - - Category originalRoot = originalManager.getRootCategory(); - Category newRoot = newManager.getRootCategory(); - Category[] categories = originalRoot.getCategories(); - - monitor.setMessage( - "Copy Archive " + originalArchive.getName() + " to " + newArchive.getName()); - monitor.initialize(categories.length); - - int transactionID = newManager.startTransaction("Copy Archive"); - try { - for (Category category : categories) { - newRoot.copyCategory(category, null, monitor); - monitor.incrementProgress(1); - } - DataType[] dataTypes = originalRoot.getDataTypes(); - for (DataType type : dataTypes) { - if (monitor.isCancelled()) { - return; - } - newRoot.addDataType(type, null); - } - } - finally { - newManager.endTransaction(transactionID, true); - } - } - - private void openArchives(String[] archiveFilenames) { - for (String filename : archiveFilenames) { - String[] projectPathname = DataTypeManagerHandler.parseProjectPathname(filename); - if (projectPathname != null) { - DomainFile df = - plugin.getProjectArchiveFile(projectPathname[0], projectPathname[1]); - if (df != null) { - plugin.openArchive(df); - } - } - else { - File file = new File(filename); - if (!file.exists()) { - continue; // if the file does not exist, skip it. - } - try { - openArchive(file, false, false); - } - catch (Throwable t) { - DataTypeManagerHandler.handleArchiveFileException(plugin, - new ResourceFile(file), t); - } - } - } - } - - /** - * Determine if archive path is allowed. - * An attempt is made to disallow any path which appears to be contained - * within a Ghidra installation. - * @param path directory or file archive path - * @return true if path is allowed - */ - public boolean isAllowedArchivePath(String path) { - - if (path.startsWith(Path.GHIDRA_HOME)) { - return false; // Ghidra typeinfo directories will always be searched - } - - for (String unallowed : UNALLOWED_ARCHIVE_PATH_FRAGMENTS) { - if (path.indexOf(unallowed) > 0) { - // Remembering paths to older Ghidra installations is bad - return false; - } - } - - return true; - } - - private void addArchivePath(ResourceFile archiveFilePath) { - - Path newPath = new Path(archiveFilePath.getParentFile()); - if (!isAllowedArchivePath(newPath.getPathAsString())) { - return; - } - - Path[] paths = getArchivePaths(); - for (Path path : paths) { - if (path.equals(newPath)) { - if (!path.isEnabled()) { - path.setEnabled(true); - PathManager.savePathsToPreferences(DATA_TYPE_ARCHIVE_PATH_KEY, - DISABLED_DATA_TYPE_ARCHIVE_PATH_KEY, paths); - } - return; - } - } - - Path[] newPaths = new Path[paths.length + 1]; - System.arraycopy(paths, 0, newPaths, 0, paths.length); - newPaths[paths.length] = newPath; - - PathManager.savePathsToPreferences(DATA_TYPE_ARCHIVE_PATH_KEY, - DISABLED_DATA_TYPE_ARCHIVE_PATH_KEY, newPaths); - } - - public Archive openArchive(File file, boolean acquireWriteLock, boolean isUserAction) - throws IOException, DuplicateIdException { - return openArchive(new ResourceFile(file), acquireWriteLock, isUserAction); - } - - public FileArchive openArchive(ResourceFile file, boolean acquireWriteLock, - boolean isUserAction) throws IOException, DuplicateIdException { - - file = file.getCanonicalFile(); - - FileArchive archive = getArchiveForFile(file); - if (archive == null) { - archive = new FileArchive(this, file, acquireWriteLock); - Archive existingArchive = - findOpenFileArchiveWithID(archive.getDataTypeManager().getUniversalID()); - if (existingArchive != null) { - archive.close(); - throw new DuplicateIdException(archive.getName(), existingArchive.getName()); - } - addArchivePath(file); - addArchive(archive); - } - if (isUserAction) { - userOpenedFileArchiveNames.add(getSaveableArchive(file.getAbsolutePath())); - } - return archive; - } - - private Archive findOpenFileArchiveWithID(UniversalID universalID) { - if (universalID == null) { - return null; - } - List allArchives = getAllArchives(); - for (Archive archive : allArchives) { - if (universalID.equals(archive.getDataTypeManager().getUniversalID())) { - return archive; - } - } - return null; - } - - public Archive openArchive(DomainFile domainFile, boolean okToUpgrade, boolean okToRecover, - TaskMonitor monitor) throws VersionException, CancelledException, IOException { - - Archive archive = getArchiveForDomainFile(domainFile); - if (archive != null) { - return archive; - } - - DataTypeArchive dataTypeArchive = - (DataTypeArchive) domainFile.getDomainObject(tool, okToUpgrade, okToRecover, monitor); - archive = new ProjectArchive(this, dataTypeArchive, domainFile); - addArchive(archive); - return archive; - } - - public Archive openArchive(DataTypeArchive dataTypeArchive) { - return openArchive(dataTypeArchive, dataTypeArchive.getDomainFile()); - } - - public Archive openArchive(DataTypeArchive dataTypeArchive, DomainFile domainFile) { - Archive archive = getArchiveForDomainFile(domainFile); - if (archive != null) { - return archive; // already created - } - - // this file comes to us created from somewhere else, so we must register ourselves as - // a consumer of this file - dataTypeArchive.addConsumer(tool); - - archive = new ProjectArchive(this, dataTypeArchive, domainFile); - addArchive(archive); - return archive; - } - - private void createInvalidArchiveNode(SourceArchive sourceArchive) { - addInvalidArchive(new InvalidFileArchive(this, sourceArchive)); - } - - public DataTypeManager openArchive(String archiveName) - throws IOException, DuplicateIdException { - ResourceFile file = DataTypeArchiveUtility.findArchiveFile(archiveName); - if (file != null) { - Archive archive = openArchive(file, false, false); - if (archive != null) { - return archive.getDataTypeManager(); - } - } - return null; - } - - private FileArchive getArchiveForFile(ResourceFile file) { - for (Archive archive : openArchives) { - if (archive instanceof FileArchive) { - FileArchive fileArchive = (FileArchive) archive; - if (file.equals(fileArchive.getFile())) { - return fileArchive; - } - } - } - return null; - } - - private Archive getArchiveForDomainFile(DomainFile domainFile) { - for (Archive archive : openArchives) { - if (archive instanceof ProjectArchive) { - ProjectArchive projectArchive = (ProjectArchive) archive; - if (domainFile.equals(projectArchive.getDomainFile())) { - return projectArchive; - } - } - } - return null; - } - - private void addArchive(Archive archive) { - updateArchiveNameInfo(archive); - openArchives.add(archive); - archive.getDataTypeManager().addDataTypeManagerListener(listenerDelegate); - dataTypeIndexer.addDataTypeManager(archive.getDataTypeManager()); - if (!(archive instanceof ProgramArchive)) { - tool.setConfigChanged(true); - } - if (archive instanceof ProjectArchive) { - String projectPath = getProjectPathname((ProjectArchive) archive, true); - if (projectPath != null) { - userOpenedFileArchiveNames.add(projectPath); - } - } - fireArchiveOpened(archive); - } - - private void updateArchiveNameInfo(Archive archive) { - - DataTypeManager dataTypeManager = archive.getDataTypeManager(); - int transactionID; - try { - transactionID = - dataTypeManager.startTransaction("Update Data Type Source Archive Names"); - } - catch (Exception e) { - // can't update now - no big deal - return; - } - try { - - // Notify each open archive about this archives ID and name in case it has changed. - for (Archive existingArchive : openArchives) { - - if (!existingArchive.isModifiable()) { - continue; - } - - DataTypeManager existingDataTypeManager = existingArchive.getDataTypeManager(); - - dataTypeManager.updateSourceArchiveName(existingDataTypeManager.getUniversalID(), - existingDataTypeManager.getName()); - - int existingTxID = existingDataTypeManager - .startTransaction("Update Data Type Source Archive Name"); - try { - existingDataTypeManager.updateSourceArchiveName( - dataTypeManager.getUniversalID(), dataTypeManager.getName()); - } - finally { - existingDataTypeManager.endTransaction(existingTxID, true); - } - } - } - finally { - dataTypeManager.endTransaction(transactionID, true); - } - } - - private boolean isKnownInvalidArchive(SourceArchive sourceArchive) { - return invalidArchives.get(sourceArchive.getSourceArchiveID()) != null; - } - - private void addInvalidArchive(InvalidFileArchive archive) { - invalidArchives.put(archive.getUniversalID(), archive); - fireArchiveOpened(archive); - } - - public List getAllArchives() { - return new ArrayList<>(openArchives); - } - - void archiveClosed(Archive archive) { - if (archive instanceof InvalidFileArchive) { - invalidArchives.remove(((InvalidFileArchive) archive).getUniversalID()); - } - else { - if (archive instanceof ProjectArchive) { - ((ProjectArchive) archive).getDomainObject().release(tool); - } - archive.getDataTypeManager().removeDataTypeManagerListener(listenerDelegate); - dataTypeIndexer.removeDataTypeManager(archive.getDataTypeManager()); - openArchives.remove(archive); - } - tool.setConfigChanged(true); - fireArchiveClosed(archive); - } - - void dataTypeManagerChanged(FileArchive archive, DataTypeManager oldManager, - DataTypeManager newManager) { - oldManager.removeDataTypeManagerListener(listenerDelegate); - newManager.addDataTypeManagerListener(listenerDelegate); - dataTypeIndexer.removeDataTypeManager(oldManager); - dataTypeIndexer.addDataTypeManager(newManager); - fireDataTypeManagerChanged(archive); - } - - public void setRecentlyUsedDataType(DataType dataType) { - recentlyUsedDataType = new RecentlyUsedDataType(dataType); - - } - - public DataType getRecentlyDataType() { - return recentlyUsedDataType.getDataType(); - } - - public DataTypeManager getBuiltInDataTypesManager() { - return builtInDataTypesManager; - } - - public DataTypeManager getProgramDataTypeManager() { - if (programArchive != null) { - return programArchive.getDataTypeManager(); - } - return null; - } - - public DataTypeIndexer getDataTypeIndexer() { - return dataTypeIndexer; - } - - public void closeAllArchives() { - // this list will get modified as we close archives, so work from a copy - Archive[] archives = openArchives.toArray(new Archive[openArchives.size()]); - for (Archive archive : archives) { - archive.close(); - } - } - - public void closeArchive(DataTypeManager dtm) { - - if (dtm instanceof BuiltInDataTypeManager) { - Msg.info(this, "Cannot close the built-in Data Type Manager"); - return; - } - - if (dtm instanceof ProgramDataTypeManager) { - Msg.info(this, "Cannot close the Program's Data Type Manager"); - return; - } - - Archive archive = getArchive(dtm); - if (archive == null) { - Msg.info(this, "Unable close archive; archive not open: '" + dtm.getName() + "'"); - } - - closeArchive(archive); - Msg.info(this, "Closed archive: '" + archive.getName() + "'"); - } - - public Archive getArchive(DataTypeManager dtm) { - for (Archive archive : openArchives) { - DataTypeManager dataTypeManager = archive.getDataTypeManager(); - if (dataTypeManager.equals(dtm)) { - return archive; - } - } - return null; - } - - public void closeArchive(Archive archive) { - DataTypeEditorManager editorManager = DataTypeManagerHandler.this.plugin.getEditorManager(); - editorManager.dismissEditors(archive.getDataTypeManager()); - - archive.close(); // this will call us back - } - - public void removeInvalidArchive(InvalidFileArchive archive) { - archive.close(); - - if (programArchive == null) { - return; - } - - ProgramDataTypeManager programDataTypeManager = - (ProgramDataTypeManager) programArchive.getDataTypeManager(); - Program program = programArchive.getProgram(); - int ID = program.startTransaction("Remove Invalid Source Archive From Program"); - try { - UniversalID sourceArchiveID = archive.getUniversalID(); - SourceArchive sourceArchive = programDataTypeManager.getSourceArchive(sourceArchiveID); - if (sourceArchive != null) { - programDataTypeManager.removeSourceArchive(sourceArchive); - } - } - finally { - program.endTransaction(ID, true); - } - } - - public void addArchiveManagerListener(ArchiveManagerListener listener) { - archiveManagerlisteners.add(listener); - } - - public void removeArchiveManagerListener(ArchiveManagerListener listener) { - archiveManagerlisteners.remove(listener); - } - - private void fireArchiveOpened(final Archive archive) { - SystemUtilities.runSwingNow(() -> { - for (ArchiveManagerListener listener : archiveManagerlisteners) { - listener.archiveOpened(archive); - } - }); - } - - private void fireArchiveClosed(final Archive archive) { - SystemUtilities.runSwingNow(() -> { - for (ArchiveManagerListener listener : archiveManagerlisteners) { - listener.archiveClosed(archive); - } - }); - } - - public void fireDataTypeManagerChanged(final FileArchive archive) { - SystemUtilities.runSwingNow(() -> { - for (ArchiveManagerListener listener : archiveManagerlisteners) { - listener.archiveDataTypeManagerChanged(archive); - } - }); - } - - public void fireArchiveStateChanged(final Archive archive) { - SystemUtilities.runSwingNow(() -> { - for (ArchiveManagerListener listener : archiveManagerlisteners) { - listener.archiveStateChanged(archive); - } - }); - } - - public boolean isInUse(File file) { - for (Archive archive : openArchives) { - if (archive instanceof FileArchive) { - if (file.equals(((FileArchive) archive).getFile().getFile(false))) { - return true; - } - } - } - return false; - } - - public List getAllFileOrProjectArchives() { - List archiveList = new ArrayList<>(); - for (Archive archive : openArchives) { - if (archive instanceof FileArchive || archive instanceof ProjectArchive) { - archiveList.add(archive); - } - } - return archiveList; - } - - public List getAllModifiedFileArchives() { - List archiveList = new ArrayList<>(); - for (Archive archive : openArchives) { - if (archive.isModifiable() && archive instanceof FileArchive) { - archiveList.add(archive); - } - } - return archiveList; - } - - /** - * Returns all favorite DataTypes in all archives. - * @return all favorite DataTypes in all archives. - */ - public List getFavoriteDataTypes() { - List list = new ArrayList<>(); - List allArchives = getAllArchives(); - for (Archive archive : allArchives) { - DataTypeManager dataTypeManager = archive.getDataTypeManager(); - list.addAll(dataTypeManager.getFavorites()); - } - - return list; - } - - private void initializeFavorites() { - - builtInDataTypesManager - .setFavorite(builtInDataTypesManager.resolve(PointerDataType.dataType, null), true); - - builtInDataTypesManager - .setFavorite(builtInDataTypesManager.resolve(CharDataType.dataType, null), true); - - builtInDataTypesManager - .setFavorite(builtInDataTypesManager.resolve(StringDataType.dataType, null), true); - builtInDataTypesManager.setFavorite( - builtInDataTypesManager.resolve(TerminatedStringDataType.dataType, null), true); - builtInDataTypesManager.setFavorite( - builtInDataTypesManager.resolve(TerminatedUnicodeDataType.dataType, null), true); - - builtInDataTypesManager - .setFavorite(builtInDataTypesManager.resolve(FloatDataType.dataType, null), true); - builtInDataTypesManager - .setFavorite(builtInDataTypesManager.resolve(DoubleDataType.dataType, null), true); - builtInDataTypesManager.setFavorite( - builtInDataTypesManager.resolve(LongDoubleDataType.dataType, null), true); - - builtInDataTypesManager - .setFavorite(builtInDataTypesManager.resolve(IntegerDataType.dataType, null), true); - builtInDataTypesManager - .setFavorite(builtInDataTypesManager.resolve(LongDataType.dataType, null), true); - builtInDataTypesManager.setFavorite( - builtInDataTypesManager.resolve(UnsignedIntegerDataType.dataType, null), true); - builtInDataTypesManager.setFavorite( - builtInDataTypesManager.resolve(UnsignedLongDataType.dataType, null), true); - - builtInDataTypesManager - .setFavorite(builtInDataTypesManager.resolve(ByteDataType.dataType, null), true); - builtInDataTypesManager - .setFavorite(builtInDataTypesManager.resolve(WordDataType.dataType, null), true); - builtInDataTypesManager - .setFavorite(builtInDataTypesManager.resolve(DWordDataType.dataType, null), true); - builtInDataTypesManager - .setFavorite(builtInDataTypesManager.resolve(QWordDataType.dataType, null), true); - - } - -//================================================================================================== -// Storage Methods -//================================================================================================== - - public void save(SaveState saveState) { - saveArchiveNames(saveState); - saveFavorites(saveState); - } - - public void restore(SaveState saveState) { - restoreArchiveNames(saveState); - restoreFavorites(saveState); - } - - private void saveArchiveNames(SaveState saveState) { - List openNameList = new ArrayList<>(); - if (knownOpenFileArchiveNames.isEmpty()) { - for (Archive archive : openArchives) { - String filePath = null; - if (archive instanceof FileArchive) { - FileArchive fileArchive = (FileArchive) archive; - ResourceFile file = fileArchive.getFile(); - if (file != null) { - filePath = file.getAbsolutePath(); - } - } - else if (archive instanceof ProjectArchive) { - filePath = getProjectPathname((ProjectArchive) archive, true); - } - if (filePath != null) { - openNameList.add(filePath); - } - } - } - else { - openNameList.addAll(knownOpenFileArchiveNames); - } - saveState.putStrings(ARCHIVE_NAMES, getSaveableArchiveNames(openNameList)); - - List recentMenuList = new ArrayList<>(); - Collection recentlyOpenedArchives = plugin.getRecentlyOpenedArchives(); - for (String file : recentlyOpenedArchives) { - recentMenuList.add(file); - } - saveState.putStrings(RECENT_NAMES, getSaveableArchiveNames(recentMenuList)); - - // update the initialArchives list so that future checks on that list do not trigger a - // state change - initiallyOpenedFileArchiveNames = getOpenFileArchiveNames(openArchives); - } - - private String[] getSaveableArchiveNames(List absoluteFilenameList) { - String[] saveableFilenames = new String[absoluteFilenameList.size()]; - for (int i = 0; i < absoluteFilenameList.size(); i++) { - saveableFilenames[i] = getSaveableArchive(absoluteFilenameList.get(i)); - } - return saveableFilenames; - } - - private String getSaveableArchive(String absoluteFilename) { - if (absoluteFilename.startsWith(PROJECT_NAME_DELIMETER)) { - return absoluteFilename; - } - Path path = new Path(absoluteFilename); - return path.getPathAsString(); - } - - private void restoreArchiveNames(SaveState saveState) { - String[] savedFilenames = saveState.getStrings(ARCHIVE_NAMES, new String[0]); -// for (int i = 0 ; i < savedFilenames.length ; ++i) { -// savedFilenames[i] = StringUtilities.fixupPathSeparator(savedFilenames[i]); -// } - String[] filenames = getAbsoluteArchiveNames(savedFilenames); - - openArchives(filenames); - - String[] recentFilenames = saveState.getStrings(RECENT_NAMES, null); - filenames = getAbsoluteArchiveNames(recentFilenames); - for (String filename : filenames) { - if (filename.startsWith(PROJECT_NAME_DELIMETER)) { - String[] projectPathname = parseProjectPathname(filename); - if (projectPathname != null) { - plugin.addRecentlyOpenedProjectArchive(projectPathname[0], projectPathname[1]); - } - } - else { - ResourceFile file = new ResourceFile(filename); - if (file.exists()) { - file = file.getCanonicalFile(); - plugin.addRecentlyOpenedArchiveFile(file); - } - } - } - - for (String filename : savedFilenames) { - initiallyOpenedFileArchiveNames.add(filename); - } - userOpenedFileArchiveNames = new HashSet<>(); - knownOpenFileArchiveNames = new HashSet<>(); - } - - /** - * Determine if we can remember the specified project archive using a simple project path - * (e.g., we can't remember specific versions). - * @param pa project archive - * @param activeProjectOnly if true pa must be contained within the - * active project to be remembered. - * @return return project path which can be remembered or null - */ - public String getProjectPathname(ProjectArchive pa, boolean activeProjectOnly) { - // Project archives are always opened by a user. - // Only remember it if it is the current version within the current project - DomainFile df = pa.getDomainObject().getDomainFile(); - ProjectLocator projectLocator = df.getProjectLocator(); - String projectName = projectLocator.getName(); - String dfProjectName = projectName; - boolean remember = df.isInWritableProject(); - if (!remember) { - // handle read-only case - Project project = tool.getProjectManager().getActiveProject(); - remember = (project != null && project.getName().equals(dfProjectName) && - df.getVersion() == DomainFile.DEFAULT_VERSION); - } - return remember ? getProjectPathname(projectName, df.getPathname()) : null; - } - - private static String[] getAbsoluteArchiveNames(String[] saveableFilenames) { - if (saveableFilenames == null) { - return new String[0]; - } - List absoluteFilenameList = new ArrayList<>(); - for (String filename : saveableFilenames) { - try { - filename = filename.startsWith(PROJECT_NAME_DELIMETER) ? filename - : getAbsoluteArchive(filename); - absoluteFilenameList.add(filename); - } - catch (FileNotFoundException e) { - Msg.error(DataTypeManagerHandler.class, e.getMessage()); - } - } - String[] absoluteFilenames = new String[absoluteFilenameList.size()]; - for (int i = 0; i < absoluteFilenames.length; i++) { - absoluteFilenames[i] = absoluteFilenameList.get(i); - } - return absoluteFilenames; - } - - private static String getAbsoluteArchive(String saveableFilename) throws FileNotFoundException { - if (saveableFilename.startsWith(RELATIVE_PATH_PREFIX)) { - ResourceFile file = DataTypeArchiveUtility.findArchiveFile(saveableFilename); - if (file == null) { - throw new FileNotFoundException("Archive not found: " + saveableFilename); - } - return file.getAbsolutePath(); - } - Path path = new Path(saveableFilename); - return path.getPath().getAbsolutePath(); - } - - void saveFavorites(SaveState saveState) { - List favoritesList = builtInDataTypesManager.getFavorites(); - String[] names = new String[favoritesList.size()]; - for (int i = 0; i < names.length; i++) { - DataType dataType = favoritesList.get(i); - names[i] = dataType.getPathName(); - } - - saveState.putStrings(FAVORITES, names); - } - - void restoreFavorites(SaveState saveState) { - String[] names = saveState.getStrings(FAVORITES, new String[0]); - if (names.length == 0) { - return; - } - Set favorites = new HashSet<>(); - for (String name : names) { - DataType dataType = builtInDataTypesManager.getDataType(name); - if (dataType != null) { - favorites.add(dataType); - } - } - - List currentFavoritesList = builtInDataTypesManager.getFavorites(); - for (DataType type : currentFavoritesList) { - if (favorites.contains(type)) { - favorites.remove(type); - } - else { - builtInDataTypesManager.setFavorite(type, false); - } - } - for (DataType dataType : favorites) { - builtInDataTypesManager.setFavorite(dataType, true); - } - } - - class RecentlyUsedDataType { - - private String dataTypeManagerName; - private CategoryPath path; - private String dataTypeName; - - RecentlyUsedDataType() { - // default constructor - } - - RecentlyUsedDataType(DataType dt) { - dataTypeName = dt.getName(); - path = dt.getCategoryPath(); - DataTypeManager dtMgr = dt.getDataTypeManager(); - dataTypeManagerName = dtMgr == null ? null : dtMgr.getName(); - - if (dataTypeManagerName == null && programArchive != null) { - DataTypeManager programDataTypeManager = programArchive.getDataTypeManager(); - dataTypeManagerName = programDataTypeManager.getName(); - } - } - - public DataType getDataType() { - if (dataTypeName == null) { - return null; - } - DataTypeManager dtMgr = findDataTypeManager(); - Category category = dtMgr.getCategory(path); - if (category != null) { - DataType dt = category.getDataType(dataTypeName); - if (dt != null) { - return dt; - } - } - return getBuiltInDataType(); - } - - private DataType getBuiltInDataType() { - DataTypeManager dtMgr = getBuiltInDataTypesManager(); - Category category = dtMgr.getCategory(path); - if (category != null) { - DataType dt = category.getDataType(dataTypeName); - if (dt != null) { - return dt; - } - } - return null; - } - - private DataTypeManager findDataTypeManager() { - for (Archive archive : openArchives) { - if (archive.getName().equals(dataTypeManagerName)) { - return archive.getDataTypeManager(); - } - } - return builtInDataTypesManager; - } - } - - public DataTypeManager[] getDataTypeManagers() { - DataTypeManager[] managers = new DataTypeManager[openArchives.size()]; - for (int i = 0; i < managers.length; i++) { - managers[i] = openArchives.get(i).getDataTypeManager(); - } - return managers; - } - - public void addDataTypeManagerChangeListener(DataTypeManagerChangeListener listener) { - dataTypeManagerListeners.add(listener); - } - - public void removeDataTypeManagerChangeListener(DataTypeManagerChangeListener listener) { - dataTypeManagerListeners.remove(listener); - } - - class DataTypeManagerListenerDelegate implements DataTypeManagerChangeListener { - - @Override - public void categoryAdded(DataTypeManager dtm, CategoryPath path) { - modCount++; - for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { - listener.categoryAdded(dtm, path); - } - } - - @Override - public void categoryMoved(DataTypeManager dtm, CategoryPath oldPath, CategoryPath newPath) { - modCount++; - for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { - listener.categoryMoved(dtm, oldPath, newPath); - } - } - - @Override - public void categoryRemoved(DataTypeManager dtm, CategoryPath path) { - modCount++; - for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { - listener.categoryRemoved(dtm, path); - } - } - - @Override - public void categoryRenamed(DataTypeManager dtm, CategoryPath oldPath, - CategoryPath newPath) { - modCount++; - for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { - listener.categoryRenamed(dtm, oldPath, newPath); - } - } - - @Override - public void dataTypeAdded(DataTypeManager dtm, DataTypePath path) { - modCount++; - for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { - listener.dataTypeAdded(dtm, path); - } - } - - @Override - public void dataTypeChanged(DataTypeManager dtm, DataTypePath path) { - modCount++; - for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { - listener.dataTypeChanged(dtm, path); - } - } - - @Override - public void dataTypeMoved(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) { - modCount++; - for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { - listener.dataTypeMoved(dtm, oldPath, newPath); - } - } - - @Override - public void dataTypeRemoved(DataTypeManager dtm, DataTypePath path) { - modCount++; - for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { - listener.dataTypeRemoved(dtm, path); - } - } - - @Override - public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath, - DataTypePath newPath) { - modCount++; - for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { - listener.dataTypeRenamed(dtm, oldPath, newPath); - } - } - - @Override - public void dataTypeReplaced(DataTypeManager dtm, DataTypePath oldPath, - DataTypePath newPath, DataType newDataType) { - modCount++; - for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { - listener.dataTypeReplaced(dtm, oldPath, newPath, newDataType); - } - } - - @Override - public void favoritesChanged(DataTypeManager dtm, DataTypePath path, boolean isFavorite) { - modCount++; - for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { - listener.favoritesChanged(dtm, path, isFavorite); - } - } - - @Override - public void sourceArchiveAdded(DataTypeManager dataTypeManager, - SourceArchive dataTypeSource) { - modCount++; - for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { - listener.sourceArchiveAdded(dataTypeManager, dataTypeSource); - } - } - - @Override - public void sourceArchiveChanged(DataTypeManager dataTypeManager, - SourceArchive dataTypeSource) { - modCount++; - for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { - listener.sourceArchiveChanged(dataTypeManager, dataTypeSource); - } - } - - @Override - public void programArchitectureChanged(DataTypeManager dataTypeManager) { - modCount++; - for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { - listener.programArchitectureChanged(dataTypeManager); - } - } - - @Override - public void restored(DataTypeManager dataTypeManager) { - modCount++; - for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { - listener.restored(dataTypeManager); - } - } - } - - /** - * Signals to this manager to save the knowledge of all currently opened archives and to mark - * the tool as dirty (changed) if the current open archives are not the same as those that - * were initially opened. - */ - public void updateKnownOpenArchives() { - knownOpenFileArchiveNames = getOpenFileArchiveNames(openArchives); - if (!knownOpenFileArchiveNames.equals(initiallyOpenedFileArchiveNames)) { - tool.setConfigChanged(true); - } - } - - private Set getOpenFileArchiveNames(List archives) { - Set newSet = new HashSet<>(); - for (Archive archive : archives) { - String filePath = null; - if (archive instanceof FileArchive) { - ResourceFile file = ((FileArchive) archive).getFile(); - if (file != null) { - filePath = getSaveableArchive(file.getAbsolutePath()); - } - } - else if (archive instanceof ProjectArchive) { - filePath = getProjectPathname((ProjectArchive) archive, true); - } - if (filePath != null && (initiallyOpenedFileArchiveNames.contains(filePath) || - userOpenedFileArchiveNames.contains(filePath))) { - newSet.add(filePath); - } - } - return newSet; - } - - public Set getPossibleEquateNames(long value) { - Set equateNames = new HashSet<>(); - for (Archive element : openArchives) { - DataTypeManager dtMgr = element.getDataTypeManager(); - dtMgr.findEnumValueNames(value, equateNames); - } - return equateNames; - - } - - public void save(DomainObject domainObject) { - - tool.prepareToSave(domainObject); - if (acquireSaveLock(domainObject)) { - - try { - DomainFileSaveTask task = - new DomainFileSaveTask(CONTENT_NAME, domainObject.getDomainFile(), tool); - new TaskLauncher(task, tool.getToolFrame()); - } - finally { - domainObject.unlock(); - } - } - } - - public void saveAs(DomainObject domainObject) { - if (!getSaveAsLock(domainObject)) { - return; - } - try { - DataTreeDialog dialog = getSaveDialog(); - treeDialogCancelled = true; - tool.showDialog(dialog); - if (!treeDialogCancelled) { - saveAs(domainObject, dialog.getDomainFolder(), dialog.getNameText()); - } - } - finally { - domainObject.unlock(); - } - } - - private void saveAs(DomainObject domainObject, DomainFolder folder, String name) { - DomainFile existingFile = folder.getFile(name); - if (existingFile == domainObject.getDomainFile()) { - save(domainObject); - return; - } - if (existingFile != null) { - String msg = "Program " + name + " already exists.\n" + "Do you want to overwrite it?"; - if (OptionDialog.showOptionDialog(tool.getToolFrame(), "Duplicate Name", msg, - "Overwrite", OptionDialog.QUESTION_MESSAGE) == OptionDialog.CANCEL_OPTION) { - return; - } - } - tool.prepareToSave(domainObject); - DomainObjectSaveAsTask task = new DomainObjectSaveAsTask(CONTENT_NAME, domainObject, folder, - name, existingFile != null); - new TaskLauncher(task, tool.getToolFrame()); - } - - private boolean acquireSaveLock(DomainObject domainObject) { - if (!domainObject.lock(null)) { - String title = "Save " + CONTENT_NAME + " (Busy)"; - StringBuilder buf = new StringBuilder(); - buf.append("The " + CONTENT_NAME + " is currently being modified by \n"); - buf.append("the following actions:\n "); - TransactionInfo t = domainObject.getCurrentTransactionInfo(); - List list = t.getOpenSubTransactions(); - for (String element : list) { - buf.append("\n "); - buf.append(element); - } - buf.append("\n \n"); - buf.append( - "WARNING! The above task(s) should be cancelled before attempting a Save.\n"); - buf.append("Only proceed if unable to cancel them.\n \n"); - buf.append( - "If you continue, all changes made by these tasks, as well as any other overlapping task,\n"); - buf.append( - "will be LOST and subsequent transaction errors may occur while these tasks remain active.\n \n"); - - int result = OptionDialog.showOptionDialog(tool.getToolFrame(), title, buf.toString(), - "Save Archive!", OptionDialog.WARNING_MESSAGE); - - if (result == OptionDialog.OPTION_ONE) { - domainObject.forceLock(true, "Save Archive"); - return true; - } - return false; - } - return true; - } - - private boolean getSaveAsLock(DomainObject domainObject) { - if (!domainObject.lock(null)) { - String title = "Save " + CONTENT_NAME + " As (Busy)"; - StringBuffer buf = new StringBuffer(); - buf.append("The " + CONTENT_NAME + - " is currently being modified by the following actions/tasks:\n \n"); - TransactionInfo t = domainObject.getCurrentTransactionInfo(); - List list = t.getOpenSubTransactions(); - for (String element : list) { - buf.append("\n "); - buf.append(element); - } - buf.append("\n \n"); - buf.append( - "WARNING! The above task(s) should be cancelled before attempting a Save As...\n"); - buf.append("Only proceed if unable to cancel them.\n \n"); - buf.append( - "If you click 'Save Archive As (Rollback)' {recommended}, all changes made\n"); - buf.append("by these tasks, as well as any other overlapping task, will be LOST!\n"); - buf.append( - "If you click 'Save As (As Is)', the archive will be saved in its current\n"); - buf.append("state which may contain some incomplete data.\n"); - buf.append("Any forced save may also result in subsequent transaction errors while\n"); - buf.append("the above tasks remain active.\n "); - - int result = OptionDialog.showOptionDialog(tool.getToolFrame(), title, buf.toString(), - "Save Archive As (Rollback)!", "Save Archive As (As Is)!", - OptionDialog.WARNING_MESSAGE); - - if (result == OptionDialog.OPTION_ONE) { - domainObject.forceLock(true, "Save Archive As"); - return true; - } - else if (result == OptionDialog.OPTION_TWO) { - domainObject.forceLock(false, "Save Archive As"); - return true; - } - return false; - } - return true; - } - - private DataTreeDialog getSaveDialog() { - DataTreeDialog dialog = new DataTreeDialog(null, "Save As", SAVE, archiveFileFilter); - - ActionListener listener = event -> { - DomainFolder folder = dialog.getDomainFolder(); - String newName = dialog.getNameText(); - if (newName.length() == 0) { - dialog.setStatusText("Please enter a name"); - return; - } - else if (folder == null) { - dialog.setStatusText("Please select a folder"); - return; - } - - DomainFile file = folder.getFile(newName); - if (file != null && file.isReadOnly()) { - dialog.setStatusText("Read Only. Choose new name/folder"); - } - else { - dialog.close(); - treeDialogCancelled = false; - } - }; - - dialog.addOkActionListener(listener); - dialog.setHelpLocation(new HelpLocation(HelpTopics.PROGRAM, "Save_As_File")); - return dialog; - } - - private CreateDataTypeArchiveDataTreeDialog getCreateDialog() { - - CreateDataTypeArchiveDataTreeDialog dialog = new CreateDataTypeArchiveDataTreeDialog(null, - "Create", CREATE, archiveFileFilter); - - ActionListener listener = event -> { - DomainFolder folder = dialog.getDomainFolder(); - String newName = dialog.getNameText(); - if (newName.length() == 0) { - dialog.setStatusText("Please enter a name"); - return; - } - else if (folder == null) { - dialog.setStatusText("Please select a folder"); - return; - } - - DomainFile file = folder.getFile(newName); - if (file != null) { - dialog.setStatusText("Choose a name that doesn't exist."); - return; - } - - if (!dialog.createNewDataTypeArchive()) { - return; - } - - // everything is OK - dialog.close(); - treeDialogCancelled = false; - }; - - dialog.addOkActionListener(listener); - dialog.setHelpLocation( - new HelpLocation(HelpTopics.DATA_MANAGER, "New_Project_Data_Type_Archive")); - - return dialog; - } - - public DataTypeManager getDataTypeManager(SourceArchive source) { - List allArchives = getAllArchives(); - for (Archive archive : allArchives) { - DataTypeManager dataTypeManager = archive.getDataTypeManager(); - UniversalID universalID = dataTypeManager.getUniversalID(); - if ((universalID != null) && universalID.equals(source.getSourceArchiveID())) { - return dataTypeManager; - } - } - return null; - } - -//================================================================================================== -// Inner Classes -//================================================================================================== - - private class CreateDataTypeArchiveDataTreeDialog extends DataTreeDialog { - - private DataTypeArchiveDB dataTypeArchiveDB; - - CreateDataTypeArchiveDataTreeDialog(Component parent, String title, DataTreeDialogType type, - DomainFileFilter filter) { - super(parent, title, type, filter); - } - - /* - * Callback method to create the DB and to show errors if they happen. - */ - boolean createNewDataTypeArchive() { - dataTypeArchiveDB = null; - DomainFolder domainFolder = getDomainFolder(); - String archiveName = getNameText(); - try { - // try to create the archive to make sure we don't get any exceptions - dataTypeArchiveDB = new DataTypeArchiveDB(domainFolder, archiveName, tool); - return true; - } - catch (DuplicateNameException e) { - setStatusText("Duplicate Name: " + e.getMessage()); - } - catch (InvalidNameException e) { - setStatusText("Invalid Name: " + e.getMessage()); - } - catch (IOException e) { - setStatusText("Unexpected IOException!"); - Msg.showError(null, getComponent(), "Unexpected Exception", e.getMessage(), e); - } - - return false; - } - - DataTypeArchiveDB getNewDataTypeArchiveDB() { - return dataTypeArchiveDB; - } - } - - static class DomainFileSaveTask extends Task { - - private String domainObjectType; - private DomainFile domainFile; - private PluginTool pluginTool; - - DomainFileSaveTask(String domainObjectType, DomainFile df, PluginTool tool) { - super("Save " + domainObjectType, true, true, true); - this.domainObjectType = domainObjectType; - this.domainFile = df; - this.pluginTool = tool; - } - - @Override - public void run(TaskMonitor monitor) { - monitor.setMessage("Saving " + domainObjectType + "..."); - try { - domainFile.save(monitor); - } - catch (CancelledException e) { - // O.K., expected - } - catch (NotConnectedException e) { - ClientUtil.promptForReconnect(pluginTool.getProject().getRepository(), - pluginTool.getToolFrame()); - } - catch (ConnectException e) { - ClientUtil.promptForReconnect(pluginTool.getProject().getRepository(), - pluginTool.getToolFrame()); - } - catch (IOException e) { - ClientUtil.handleException(pluginTool.getProject().getRepository(), e, "Save File", - pluginTool.getToolFrame()); - } - } - } - - static class DomainObjectSaveAsTask extends Task { - - private String domainObjectType; - private DomainObject domainObject; - private DomainFolder parentFolder; - private String newName; - private boolean doOverwrite; - - DomainObjectSaveAsTask(String domainObjectType, DomainObject domainObject, - DomainFolder folder, String newName, boolean doOverwrite) { - - super("Save " + domainObjectType + " As", true, true, true); - this.domainObjectType = domainObjectType; - this.domainObject = domainObject; - this.parentFolder = folder; - this.newName = newName; - this.doOverwrite = doOverwrite; - } - - @Override - public void run(TaskMonitor monitor) { - monitor.setMessage("Saving " + domainObjectType + "..."); - try { - if (doOverwrite) { - DomainFile df = parentFolder.getFile(newName); - df.delete(); - } - parentFolder.createFile(newName, domainObject, monitor); - } - catch (IOException e) { - Msg.showError(this, null, "Error Overwriting " + domainObjectType, e.getMessage(), - e); - } - catch (InvalidNameException e) { - Msg.showError(this, null, "Invalid Name", e.getMessage(), e); - } - catch (Throwable e) { - Msg.showError(this, null, "Error", e.getMessage(), e); - } - } - } - - private class MyFolderListener extends DomainFolderListenerAdapter { - - @Override - public void domainFileStatusChanged(DomainFile file, boolean fileIDset) { - if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE - .equals(file.getContentType())) { - return; - } - for (Archive archive : openArchives) { - if (archive instanceof ProjectArchive) { - ProjectArchive projectArchive = (ProjectArchive) archive; - DomainFile domainFile = projectArchive.getDomainFile(); - if (file.equals(domainFile) && !projectArchive.isModifiable() && - file.isCheckedOut()) { - replaceArchiveWithFile(projectArchive, file); - return; - } - } - } - } - - @Override - public void domainFileRemoved(DomainFolder parentFolder, String name, String fileID) { - // ignore - } - - @Override - public void domainFileRenamed(DomainFile file, String oldName) { - if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE - .equals(file.getContentType())) { - return; - } - String newName = file.getName(); - String fileID = file.getFileID(); - for (Archive archive : openArchives) { - if (!archive.isModifiable()) { - continue; - } - DataTypeManager dataTypeManager = archive.getDataTypeManager(); - updateSourceArchiveName(dataTypeManager, fileID, newName); - } - } - - private void replaceArchiveWithFile(ProjectArchive projectArchive, - DomainFile newDomainFile) { - DomainFile archiveDomainFile = projectArchive.getDomainFile(); - DomainObject archiveDomainObject = projectArchive.getDomainObject(); - DomainFile objectDomainFile = archiveDomainObject.getDomainFile(); - if (archiveDomainFile == objectDomainFile) { - return; - } - - closeArchive(projectArchive); - - String contentType = null; - try { - contentType = newDomainFile.getContentType(); - try { - openArchive(newDomainFile, false, false, TaskMonitor.DUMMY); - } - catch (VersionException e) { - // should never happen following check-in (i.e., DomainObjectReplaced) - if (VersionExceptionHandler.isUpgradeOK(null, newDomainFile, "Re-open", e)) { - openArchive(newDomainFile, true, false, TaskMonitor.DUMMY); - } - } - } - catch (VersionException e) { - VersionExceptionHandler.showVersionError(null, newDomainFile.getName(), contentType, - "Re-open", false, e); - } - catch (CancelledException e) { - throw new AssertException(e); - } - catch (Exception e) { - if (newDomainFile.isInWritableProject() && (e instanceof IOException)) { - RepositoryAdapter repo = - newDomainFile.getParent().getProjectData().getRepository(); - ClientUtil.handleException(repo, e, "Re-open File", null); - } - else { - Msg.showError(this, null, "Error Opening " + newDomainFile.getName(), - "Opening data type archive failed.\n" + e.getMessage()); - } - } - } - } - - //================================================================================================== - // Static Methods - //================================================================================================== - - /** - * Provides an exception handler for a failed attempt to open an datatype archive file. - * This method will display exception information to the user and/or log. - * @param plugin datatype manager plugin - * @param archiveFile archive file resource being opened - * @param t throwable - */ - public static void handleArchiveFileException(DataTypeManagerPlugin plugin, - ResourceFile archiveFile, Throwable t) { - if (t instanceof FileNotFoundException) { - Msg.showError(plugin, plugin.getProvider().getComponent(), "File Not Found", - archiveFile.getAbsolutePath() + " not found!"); - } - else if (t instanceof IOException) { - Throwable cause = t.getCause(); - if (cause instanceof VersionException) { - VersionExceptionHandler.showVersionError(null, archiveFile.getName(), "Archive", - "open", false, (VersionException) cause); - } - else { - Msg.showError(plugin, plugin.getProvider().getComponent(), "Open Archive Failed", - t.getMessage() + ": " + archiveFile.getName()); - } - } - else if (t instanceof DuplicateIdException) { - DuplicateIdException dupIdExc = (DuplicateIdException) t; - Msg.showError(plugin, plugin.getProvider().getComponent(), "Duplicate Archive ID Error", - "Attempted to open a datatype archive with the same ID as datatype archive that is " + - "already open. " + dupIdExc.getNewArchiveName() + " has same id as " + - dupIdExc.getExistingArchiveName()); - } - else { - Msg.showError(plugin, plugin.getProvider().getComponent(), "Open Archive Failed", - "Unexpected exception opening archive: " + archiveFile.getName(), t); - } - } - - /** - * Create project archive path string for recently used project archive - * @param projectName the project name - * @param pathname the pathname used to create the final path - * @return recently used project pathname string - */ - public static String getProjectPathname(String projectName, String pathname) { - if (pathname.length() < 2 || !pathname.startsWith(FileSystem.SEPARATOR)) { - throw new IllegalArgumentException("Absolute project pathname required"); - } - return PROJECT_NAME_DELIMETER + projectName + PROJECT_NAME_DELIMETER + pathname; - } - - /** - * Parse a recently used project pathname string - * @param projectFilePath project pathname string - * @return 2-element String array containing project name and pathname of project archive, or null if path is invalid - */ - public static String[] parseProjectPathname(String projectFilePath) { - if (projectFilePath.startsWith(PROJECT_NAME_DELIMETER)) { - int index = projectFilePath.indexOf(PROJECT_NAME_DELIMETER, 1); - if (index > 0) { - String projectName = projectFilePath.substring(1, index); - String pathname = projectFilePath.substring(index + 1); - if (pathname.length() > 1 && pathname.startsWith(FileSystem.SEPARATOR)) { - return new String[] { projectName, pathname }; - } - } - } - return null; - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DefaultDataTypeArchiveService.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DefaultDataTypeArchiveService.java deleted file mode 100644 index e0a58b78f7..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DefaultDataTypeArchiveService.java +++ /dev/null @@ -1,236 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.datamgr.archive; - -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.stream.Collectors; - -import generic.jar.ResourceFile; -import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility; -import ghidra.app.services.DataTypeArchiveService; -import ghidra.framework.model.DomainFile; -import ghidra.framework.model.DomainObject; -import ghidra.program.database.data.ProgramDataTypeManager; -import ghidra.program.model.data.*; -import ghidra.program.model.listing.DataTypeArchive; -import ghidra.util.Msg; -import ghidra.util.UniversalID; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - -/** - * Simple, non-ui implementation of the {@link DataTypeArchiveService} interface. - */ -public class DefaultDataTypeArchiveService implements DataTypeArchiveService { - protected record DataTypeManagerInfo(ResourceFile file, DomainObject domainObject, String name, - DataTypeManager dtm) { - public boolean isClosed() { - return dtm instanceof FileDataTypeManager fdtm - ? fdtm.isClosed() - : false; - } - } - - protected Map openDTMs = new HashMap<>(); - protected BuiltInDataTypeManager builtInDataTypesManager; - - public DefaultDataTypeArchiveService() { - this.builtInDataTypesManager = BuiltInDataTypeManager.getDataTypeManager(); - } - - public synchronized void dispose() { - for (DataTypeManagerInfo dtmInfo : new ArrayList<>(openDTMs.values())) { - closeDTM(dtmInfo); // mutates openDTMs map - } - } - - @Override - public DataTypeManager getBuiltInDataTypesManager() { - return builtInDataTypesManager; - } - - @Override - public DataTypeManager[] getDataTypeManagers() { - List dtmList = openDTMs.values() - .stream() - .filter(dtmInfo -> !dtmInfo.isClosed()) - .map(dtmInfo -> dtmInfo.dtm()) - .collect(Collectors.toList()); - return dtmList.toArray(DataTypeManager[]::new); - } - - @Override - public synchronized void closeArchive(DataTypeManager dtm) { - if (dtm instanceof BuiltInDataTypeManager) { - Msg.info(this, "Cannot close the built-in Data Type Manager"); - return; - } - - if (dtm instanceof ProgramDataTypeManager) { - Msg.info(this, "Cannot close the Program's Data Type Manager"); - return; - } - - DataTypeManagerInfo dtmInfo = openDTMs.get(dtm.getUniversalID()); - if (dtmInfo == null) { - Msg.info(this, "Unable close archive; archive not open: '%s'".formatted(dtm.getName())); - return; - } - - beforeCloseDataTypeManager(dtmInfo); - openDTMs.remove(dtm.getUniversalID()); - - if (dtmInfo.domainObject != null) { - dtmInfo.domainObject.release(this); - } - dtmInfo.dtm.close(); - - afterCloseDataTypeManager(dtmInfo); - } - - @Override - public Archive openArchive(DataTypeArchive dataTypeArchive) { - throw new UnsupportedOperationException(); - } - - @Override - public Archive openArchive(File file, boolean acquireWriteLock) - throws IOException, DuplicateIdException { - throw new UnsupportedOperationException(); - } - - @Override - public DataTypeManager openDataTypeArchive(String archiveName) - throws IOException, DuplicateIdException { - ResourceFile file = DataTypeArchiveUtility.findArchiveFile(archiveName); - if (file != null) { - return openArchive(file, false); - } - return null; - } - - @Override - public synchronized DataTypeManager openArchive(DomainFile domainFile, TaskMonitor monitor) - throws VersionException, CancelledException, IOException, DuplicateIdException { - - if (!DataTypeArchive.class.isAssignableFrom(domainFile.getDomainObjectClass())) { - throw new IOException("Unable to open domain file: '%s', not a data type archive" - .formatted(domainFile.getName())); - } - - DataTypeManagerInfo dtmInfo = getOpenDTMInfo(domainFile); - if (dtmInfo == null) { - DataTypeArchive dta = openDomainFile(domainFile, monitor); - DataTypeManager dtm = dta.getDataTypeManager(); - dtmInfo = addDTM(new DataTypeManagerInfo(null, dta, domainFile.getPathname(), dtm)); - } - - return dtmInfo.dtm; - } - - protected DataTypeArchive openDomainFile(DomainFile domainFile, TaskMonitor monitor) - throws VersionException, CancelledException, IOException { - DataTypeArchive dta = - (DataTypeArchive) domainFile.getDomainObject(this, false, false, monitor); - return dta; - } - - @Override - public synchronized DataTypeManager openArchive(ResourceFile file, boolean acquireWriteLock) - throws IOException, DuplicateIdException { - - file = file.getCanonicalFile(); - DataTypeManagerInfo dtmInfo = getOpenDTMInfo(file); - if (dtmInfo == null) { - FileDataTypeManager fileDTM = - FileDataTypeManager.openFileArchive(file, acquireWriteLock); - - dtmInfo = addDTM(new DataTypeManagerInfo(file, null, file.getName(), fileDTM)); - } - return dtmInfo.dtm; - } - - protected DataTypeManagerInfo addDTM(DataTypeManagerInfo dtmInfo) throws DuplicateIdException { - DataTypeManagerInfo existingDTM = openDTMs.get(dtmInfo.dtm.getUniversalID()); - if (existingDTM != null) { - if (existingDTM.isClosed()) { - openDTMs.remove(dtmInfo.dtm.getUniversalID()); - } - else { - dtmInfo.dtm.close(); - throw new DuplicateIdException(dtmInfo.name(), existingDTM.name()); - } - } - openDTMs.put(dtmInfo.dtm.getUniversalID(), dtmInfo); - afterAddDataTypeManager(dtmInfo); - return dtmInfo; - } - - protected void closeDTM(DataTypeManagerInfo dtmInfo) { - beforeCloseDataTypeManager(dtmInfo); - openDTMs.remove(dtmInfo.dtm.getUniversalID()); - - if (dtmInfo.domainObject != null) { - dtmInfo.domainObject.release(this); - } - dtmInfo.dtm.close(); - - afterCloseDataTypeManager(dtmInfo); - } - - private DataTypeManagerInfo getOpenDTMInfo(ResourceFile file) { - for (DataTypeManagerInfo dtmInfo : openDTMs.values()) { - if (dtmInfo.file != null && dtmInfo.file.equals(file)) { - if (dtmInfo.isClosed()) { - openDTMs.remove(dtmInfo.dtm.getUniversalID()); - return null; - } - return dtmInfo; - } - } - return null; - } - - private DataTypeManagerInfo getOpenDTMInfo(DomainFile projectFile) { - for (DataTypeManagerInfo dtmInfo : openDTMs.values()) { - if (dtmInfo.domainObject != null && - dtmInfo.domainObject.getDomainFile().equals(projectFile)) { - if (dtmInfo.isClosed()) { - openDTMs.remove(dtmInfo.dtm.getUniversalID()); - return null; - } - return dtmInfo; - } - } - return null; - } - - protected void afterAddDataTypeManager(DataTypeManagerInfo dtmInfo) { - // override as needed - } - - protected void afterCloseDataTypeManager(DataTypeManagerInfo dtmInfo) { - // override as needed - } - - protected void beforeCloseDataTypeManager(DataTypeManagerInfo dtmInfo) { - // override as needed - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/FileArchive.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/FileArchive.java deleted file mode 100644 index 8ea1db5e57..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/FileArchive.java +++ /dev/null @@ -1,330 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.datamgr.archive; - -import java.awt.Component; -import java.io.File; -import java.io.IOException; -import java.util.Objects; - -import javax.swing.Icon; - -import generic.jar.ResourceFile; -import generic.theme.GIcon; -import ghidra.framework.store.LockException; -import ghidra.program.model.data.*; -import ghidra.util.exception.DuplicateFileException; - -/** - * Manages a DataTypeFileManager and relative state. For example, whether the manager is writable - * or whether changes have been made. - */ -public class FileArchive implements Archive { - - private static Icon CLOSED_ICON = new GIcon("icon.plugin.datatypes.archive.file.closed"); - private static Icon OPEN_ICON = new GIcon("icon.plugin.datatypes.archive.file.open"); - private ResourceFile archiveFile; - private boolean hasWriteLock; - private boolean changed; - private DataTypeManagerHandler archiveManager; - private FileDataTypeManager fileDataTypeManager; - DataTypeManagerChangeListener categoryListener; // hold on to since it is stored in a weak set - private String name; - - FileArchive(DataTypeManagerHandler archiveManager, File file) throws IOException { - this(archiveManager, FileDataTypeManager.createFileArchive(file)); - archiveFile = new ResourceFile(file.getCanonicalFile()); - hasWriteLock = true; - } - - FileArchive(DataTypeManagerHandler archiveManager, ResourceFile file, boolean acquireWriteLock) - throws IOException { - this(archiveManager, FileDataTypeManager.openFileArchive(file, acquireWriteLock)); - this.archiveFile = file.getCanonicalFile(); - this.hasWriteLock = acquireWriteLock; - } - - private FileArchive(DataTypeManagerHandler archiveManager, FileDataTypeManager manager) { - this.archiveManager = archiveManager; - this.fileDataTypeManager = manager; - categoryListener = new ArchiveCategoryChangeListener(); - manager.addDataTypeManagerListener(categoryListener); - name = fileDataTypeManager.getName(); - changed = fileDataTypeManager.isChanged(); // may have immediately changed due to upgrade - } - - @Override - public String getName() { - return name; - } - - @Override - public int compareTo(Archive archive) { - if ((archive instanceof BuiltInArchive) || (archive instanceof ProgramArchive) || - (archive instanceof ProjectArchive)) { - return 1; - } - if (archive instanceof FileArchive) { - return getName().compareToIgnoreCase(archive.getName()); - } - return -1; // File Archives appear between ProjectArchives and InvalidFileArchives. - } - - @Override - public void close() { - fileDataTypeManager.removeDataTypeManagerListener(categoryListener); - fileDataTypeManager.close(); - archiveManager.archiveClosed(this); - fileDataTypeManager = null; - } - - public void delete() throws IOException { - fileDataTypeManager.removeDataTypeManagerListener(categoryListener); - fileDataTypeManager.delete(); - archiveManager.archiveClosed(this); - fileDataTypeManager = null; - } - - public void acquireWriteLock() throws LockException, IOException { - refreshArchive(true); - hasWriteLock = true; - fireStateChanged(); - } - - public void releaseWriteLock() throws IOException { - try { - refreshArchive(false); - } - catch (LockException e) { - // we know this can't happen, since we are already have the write lock - } - hasWriteLock = false; - setChanged(false); - } - - public void saveAs(File file) throws DuplicateFileException, IOException { - File newFile = FileDataTypeManager.convertFilename(file); - fileDataTypeManager.saveAs(newFile); - archiveFile = new ResourceFile(newFile); - hasWriteLock = true; - setChanged(false); - } - - @Override - public void save() throws IOException { - fileDataTypeManager.save(); - setChanged(false); - } - - public ResourceFile getFile() { - return archiveFile; - } - - public boolean hasWriteLock() { - return hasWriteLock; - } - - @Override - public boolean isChanged() { - if (isClosed()) { - return false; - } - return changed || fileDataTypeManager.isChanged(); - } - - @Override - public FileDataTypeManager getDataTypeManager() { - return fileDataTypeManager; - } - - boolean isClosed() { - return fileDataTypeManager == null; - } - - @Override - public boolean isSavable() { - return (archiveFile != null); - } - - @Override - public boolean isModifiable() { - return hasWriteLock(); - } - - public DataTypeManagerHandler getArchiveManager() { - return archiveManager; - } - - @Override - public int hashCode() { - return Objects.hash(archiveFile); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - FileArchive other = (FileArchive) obj; - return Objects.equals(archiveFile, other.archiveFile); - } - - private void fireStateChanged() { - archiveManager.fireArchiveStateChanged(this); - } - - /** - * Re-read the archive file. - * @param forUpdate true indicates to open the database for update, false for read-only. - * @throws LockException if the open if for update and a lock could not - * be obtained - * @throws IOException if the archive file can't be opened - */ - private void refreshArchive(boolean forUpdate) throws LockException, IOException { - - // re-read archive file if it isn't null - if (archiveFile != null) { - FileDataTypeManager newManager = - FileDataTypeManager.openFileArchive(archiveFile, forUpdate); - - FileDataTypeManager oldManager = fileDataTypeManager; - oldManager.removeDataTypeManagerListener(categoryListener); - oldManager.close(); - - fileDataTypeManager = newManager; - fileDataTypeManager.addDataTypeManagerListener(categoryListener); - - archiveManager.dataTypeManagerChanged(this, oldManager, newManager); - } - } - - private void setChanged(boolean change) { - if (changed == change) { - return; - } - changed = change; - fireStateChanged(); - } - - class ArchiveCategoryChangeListener implements DataTypeManagerChangeListener { - @Override - public void categoryAdded(DataTypeManager dtm, CategoryPath path) { - setChanged(true); - } - - @Override - public void categoryMoved(DataTypeManager dtm, CategoryPath oldPath, CategoryPath newPath) { - setChanged(true); - } - - @Override - public void categoryRemoved(DataTypeManager dtm, CategoryPath path) { - setChanged(true); - } - - @Override - public void categoryRenamed(DataTypeManager dtm, CategoryPath oldPath, - CategoryPath newPath) { - if (!oldPath.equals(newPath)) { - setChanged(true); - } - } - - @Override - public void dataTypeAdded(DataTypeManager dtm, DataTypePath path) { - setChanged(true); - } - - @Override - public void dataTypeChanged(DataTypeManager dtm, DataTypePath path) { - setChanged(true); - } - - @Override - public void dataTypeMoved(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) { - setChanged(true); - } - - @Override - public void dataTypeRemoved(DataTypeManager dtm, DataTypePath path) { - setChanged(true); - } - - @Override - public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath, - DataTypePath newPath) { - setChanged(true); - } - - @Override - public void dataTypeReplaced(DataTypeManager dtm, DataTypePath oldPath, - DataTypePath newPath, DataType newDataType) { - setChanged(true); - } - - @Override - public void favoritesChanged(DataTypeManager dtm, DataTypePath path, boolean isFavorite) { - // don't care - } - - @Override - public void sourceArchiveAdded(DataTypeManager dtm, SourceArchive dataTypeSource) { - setChanged(true); - } - - @Override - public void sourceArchiveChanged(DataTypeManager dtm, SourceArchive dataTypeSource) { - setChanged(true); - } - - @Override - public void programArchitectureChanged(DataTypeManager dataTypeManager) { - setChanged(true); - } - - @Override - public void restored(DataTypeManager dataTypeManager) { - archiveManager.dataTypeManagerChanged(FileArchive.this, dataTypeManager, - dataTypeManager); - } - } - - @Override - public void saveAs(Component component) throws IOException { - File saveAsFile = ArchiveUtils.getFile(component, this); - if (saveAsFile == null) { - return; - } - - if (saveAsFile.equals(archiveFile.getFile(false))) { - save(); - } - else { - saveAs(saveAsFile); - } - } - - @Override - public Icon getIcon(boolean expanded) { - return expanded ? OPEN_ICON : CLOSED_ICON; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DomainFileArchive.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/InvalidArchive.java similarity index 62% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DomainFileArchive.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/InvalidArchive.java index 2660379a8a..2f9ae893bf 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DomainFileArchive.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/InvalidArchive.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,14 +15,13 @@ */ package ghidra.app.plugin.core.datamgr.archive; -import ghidra.framework.model.DomainFile; -import ghidra.framework.model.DomainObject; +import ghidra.program.model.data.ArchiveType; +import ghidra.util.UniversalID; -public interface DomainFileArchive extends Archive { +/** + * Record for storing info about a SourceArchive from a program that couldn't be found and opened. + * It provides a handle for actions to remove the datatype references to a missing archive. + */ +public record InvalidArchive(String name, UniversalID id, ArchiveType type, String domainFileID) { - public abstract DomainFile getDomainFile(); - - public abstract DomainObject getDomainObject(); - - public abstract boolean hasExclusiveAccess(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/InvalidFileArchive.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/InvalidFileArchive.java deleted file mode 100644 index 526fc35932..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/InvalidFileArchive.java +++ /dev/null @@ -1,110 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.datamgr.archive; - -import java.awt.Component; -import java.io.IOException; - -import javax.swing.Icon; - -import generic.theme.GIcon; -import ghidra.program.model.data.*; -import ghidra.util.UniversalID; -import ghidra.util.exception.DuplicateFileException; - -public class InvalidFileArchive implements Archive { - - private static final Icon INVALID_ARCHIVE_ICON = - new GIcon("icon.plugin.datatypes.archive.invalid"); - private DataTypeManagerHandler archiveManager; - private UniversalID universalID; - private ArchiveType archiveType; - private String name; - private String domainFileID; - - InvalidFileArchive(DataTypeManagerHandler archiveManager, SourceArchive sourceArchive) { - this.archiveManager = archiveManager; - this.universalID = sourceArchive.getSourceArchiveID(); - this.archiveType = sourceArchive.getArchiveType(); - this.name = sourceArchive.getName(); - this.domainFileID = sourceArchive.getDomainFileID(); - } - - @Override - public DataTypeManager getDataTypeManager() { - return null; - } - - @Override - public boolean isModifiable() { - return false; - } - - @Override - public String getName() { - return name; - } - - @Override - public int compareTo(Archive archive) { - if (archive instanceof InvalidFileArchive) { - return getName().compareToIgnoreCase(archive.getName()); - } - return 1; - } - - public ArchiveType getArchiveType() { - return archiveType; - } - - public String getDomainFileID() { - return domainFileID; - } - - @Override - public void close() { - archiveManager.archiveClosed(this); - } - - @Override - public boolean isChanged() { - return false; - } - - @Override - public boolean isSavable() { - return false; - } - - @Override - public void save() throws DuplicateFileException, IOException { - // Can't "Save" so do nothing. - } - - @Override - public void saveAs(Component component) throws IOException { - // Can't "Save As" so do nothing. - } - - public UniversalID getUniversalID() { - return universalID; - } - - @Override - public Icon getIcon(boolean expanded) { - return INVALID_ARCHIVE_ICON; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProgramArchive.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProgramArchive.java deleted file mode 100644 index bbfbd94ee4..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProgramArchive.java +++ /dev/null @@ -1,135 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.datamgr.archive; - -import java.awt.Component; -import java.io.IOException; -import java.util.Objects; - -import javax.swing.Icon; - -import generic.theme.GIcon; -import ghidra.framework.model.DomainFile; -import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.DataTypeManagerChangeListener; -import ghidra.program.model.listing.Program; -import ghidra.util.exception.DuplicateFileException; - -public class ProgramArchive implements DomainFileArchive { - - private static Icon CLOSED_ICON = new GIcon("icon.plugin.datatypes.archive.program.closed"); - private static Icon OPEN_ICON = new GIcon("icon.plugin.datatypes.archive.program.open"); - private final Program program; - DataTypeManagerChangeListener categoryListener; // hold on to since it is stored in a weak set - private DataTypeManager dataTypeManager; - - ProgramArchive(Program program) { - this.program = program; - this.dataTypeManager = program.getDataTypeManager(); - } - - public Program getProgram() { - return program; - } - - @Override - public DataTypeManager getDataTypeManager() { - return dataTypeManager; - } - - @Override - public String getName() { - return dataTypeManager.getName(); - } - - @Override - public int hashCode() { - return program.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - ProgramArchive other = (ProgramArchive) obj; - return Objects.equals(program, other.program); - } - - @Override - public int compareTo(Archive archive) { - if (archive instanceof BuiltInArchive) { - return 1; - } - return -1; // Programs are always before everything else except for built-ins - } - - @Override - public boolean isModifiable() { - return true; - } - - @Override - public void close() { - // Can't directly close the program archive. Instead you must close the Program. - } - - @Override - public boolean hasExclusiveAccess() { - return program.hasExclusiveAccess(); - } - - @Override - public boolean isChanged() { - return false; - } - - @Override - public boolean isSavable() { - return false; - } - - @Override - public void save() throws DuplicateFileException, IOException { - // Can't "Save" so do nothing. - } - - @Override - public void saveAs(Component component) throws IOException { - // Can't "Save As" so do nothing. - } - - @Override - public DomainFile getDomainFile() { - return program.getDomainFile(); - } - - @Override - public Program getDomainObject() { - return program; - } - - @Override - public Icon getIcon(boolean expanded) { - return expanded ? OPEN_ICON : CLOSED_ICON; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProjectArchive.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProjectArchive.java deleted file mode 100644 index 3cb1adda5f..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProjectArchive.java +++ /dev/null @@ -1,231 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.datamgr.archive; - -import java.awt.Component; -import java.io.IOException; - -import javax.swing.Icon; - -import generic.theme.GIcon; -import ghidra.framework.model.DomainFile; -import ghidra.program.model.data.*; -import ghidra.program.model.listing.DataTypeArchive; -import ghidra.util.exception.ClosedException; - -public class ProjectArchive implements DomainFileArchive { - - private static Icon CLOSED_ICON = new GIcon("icon.plugin.datatypes.archive.project.closed"); - private static Icon OPEN_ICON = new GIcon("icon.plugin.datatypes.archive.project.open"); - - private DataTypeArchive dataTypeArchive; - private DomainFile sourceDomainFile; - private DataTypeManagerChangeListener categoryListener; // hold on to since it is stored in a weak set - private DataTypeManagerHandler archiveManager; - private StandAloneDataTypeManager dataTypeManager; - - ProjectArchive(DataTypeManagerHandler archiveManager, DataTypeArchive dataTypeArchive, - DomainFile sourceDomainFile) { - this.archiveManager = archiveManager; - this.dataTypeArchive = dataTypeArchive; - this.dataTypeManager = dataTypeArchive.getDataTypeManager(); - this.sourceDomainFile = sourceDomainFile; - categoryListener = new ArchiveCategoryChangeListener(); - dataTypeManager.addDataTypeManagerListener(categoryListener); - } - - @Override - public DataTypeManager getDataTypeManager() { - return dataTypeManager; - } - - @Override - public String getName() { - if (dataTypeManager == null) { - return null; - } - return dataTypeManager.getName(); - } - - @Override - public int compareTo(Archive archive) { - if ((archive instanceof BuiltInArchive) || (archive instanceof ProgramArchive)) { - return 1; - } - if (archive instanceof ProjectArchive) { - return getName().compareToIgnoreCase(archive.getName()); - } - return -1; // Project Archives appear between the ProgramArchive and FileArchives. - } - - @Override - public boolean hasExclusiveAccess() { - return dataTypeArchive.hasExclusiveAccess(); - } - - @Override - public boolean isModifiable() { - if (dataTypeManager == null) { - return false; - } - DomainFile df = getDomainObject().getDomainFile(); - return df.canSave(); - } - - @Override - public DomainFile getDomainFile() { - return sourceDomainFile; - } - - @Override - public DataTypeArchive getDomainObject() { - return dataTypeArchive; - } - - @Override - public boolean isChanged() { - if (dataTypeManager == null) { - return false; - } - DomainFile df = dataTypeArchive.getDomainFile(); - long lastModifiedTime = df.getLastModifiedTime(); - return (lastModifiedTime == 0) || dataTypeArchive.isChanged(); - } - - @Override - public boolean isSavable() { - return dataTypeManager != null && !dataTypeArchive.getDomainFile().isReadOnly() && - dataTypeArchive.isChangeable(); - } - - @Override - public void save() throws IOException { - if (dataTypeManager == null) { - throw new ClosedException(); - } - archiveManager.save(getDomainObject()); - } - - @Override - public void close() { - if (dataTypeManager != null) { - dataTypeManager.close(); - archiveManager.archiveClosed(this); - dataTypeManager = null; - } - } - - @Override - public void saveAs(Component component) throws IOException { - if (dataTypeManager == null) { - throw new ClosedException(); - } - archiveManager.saveAs(dataTypeArchive); - sourceDomainFile = dataTypeArchive.getDomainFile(); // update with new domain file - dataTypeManager.updateID(); - } - - @Override - public Icon getIcon(boolean expanded) { - return expanded ? OPEN_ICON : CLOSED_ICON; - } - - private void fireStateChanged() { - archiveManager.fireArchiveStateChanged(this); - } - - class ArchiveCategoryChangeListener implements DataTypeManagerChangeListener { - @Override - public void categoryAdded(DataTypeManager dtm, CategoryPath path) { - fireStateChanged(); - } - - @Override - public void categoryMoved(DataTypeManager dtm, CategoryPath oldPath, CategoryPath newPath) { - fireStateChanged(); - } - - @Override - public void categoryRemoved(DataTypeManager dtm, CategoryPath path) { - fireStateChanged(); - } - - @Override - public void categoryRenamed(DataTypeManager dtm, CategoryPath oldPath, - CategoryPath newPath) { - if (!oldPath.equals(newPath)) { - fireStateChanged(); - } - } - - @Override - public void dataTypeAdded(DataTypeManager dtm, DataTypePath path) { - fireStateChanged(); - } - - @Override - public void dataTypeChanged(DataTypeManager dtm, DataTypePath path) { - fireStateChanged(); - } - - @Override - public void dataTypeMoved(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) { - fireStateChanged(); - } - - @Override - public void dataTypeRemoved(DataTypeManager dtm, DataTypePath path) { - fireStateChanged(); - } - - @Override - public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath, - DataTypePath newPath) { - fireStateChanged(); - } - - @Override - public void dataTypeReplaced(DataTypeManager dtm, DataTypePath oldPath, - DataTypePath newPath, DataType newDataType) { - fireStateChanged(); - } - - @Override - public void favoritesChanged(DataTypeManager dtm, DataTypePath path, boolean isFavorite) { - // don't care - } - - @Override - public void sourceArchiveAdded(DataTypeManager dtm, SourceArchive dataTypeSource) { - fireStateChanged(); - } - - @Override - public void sourceArchiveChanged(DataTypeManager dtm, SourceArchive dataTypeSource) { - fireStateChanged(); - } - - @Override - public void programArchitectureChanged(DataTypeManager dtm) { - fireStateChanged(); - } - - @Override - public void restored(DataTypeManager dtm) { - fireStateChanged(); - } - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java index 6fa0f74901..532bfcbba4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java @@ -24,11 +24,11 @@ import ghidra.app.plugin.core.compositeeditor.*; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import ghidra.app.plugin.core.datamgr.actions.AbstractFindReferencesToFieldAction; import ghidra.app.plugin.core.function.AbstractEditFunctionSignatureDialog; -import ghidra.framework.model.DomainObject; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; -import ghidra.program.model.listing.*; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.FunctionSignature; import ghidra.util.*; import ghidra.util.exception.*; @@ -458,21 +458,6 @@ public class DataTypeEditorManager implements EditorListener { dismissEditors(null); } - /** - * Determines whether the indicated domain object can be closed. The user will be prompted - * to save any editor changes. If the user cancels when prompted to save then the domain - * object should not close. - * @param dObj the domain object - * @return true if it can close. - */ - protected boolean canCloseDomainObject(DomainObject dObj) { - if (dObj instanceof Program) { - Program p = (Program) dObj; - return checkEditors(p.getListing().getDataTypeManager(), true); - } - return true; - } - public boolean showStructureNumbersInHex() { return editorOptionMgr.showStructureNumbersInHex(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/framework/main/datatree/ArchiveProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveComparator.java similarity index 55% rename from Ghidra/Features/Base/src/main/java/ghidra/framework/main/datatree/ArchiveProvider.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveComparator.java index 28dea35b52..de7c452faf 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/framework/main/datatree/ArchiveProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveComparator.java @@ -1,30 +1,33 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.framework.main.datatree; +package ghidra.app.plugin.core.datamgr.tree; -import ghidra.app.plugin.core.datamgr.archive.Archive; +import java.util.Comparator; -import java.util.List; +import ghidra.program.model.dtarchive.DataTypeStore; -/** - * An interface to be implemented by any class that can return a list of Archives. - * For example, the tool's data type manager can return a list of archives within the project. - */ -public interface ArchiveProvider { +public class ArchiveComparator implements Comparator { + + @Override + public int compare(DataTypeStore a1, DataTypeStore a2) { + int result = Integer.compare(a1.getArchiveType().sortOrder, a2.getArchiveType().sortOrder); + if (result == 0) { + result = a1.getName().compareTo(a2.getName()); + } + return result; + } - public List getArchives(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveNode.java index 7858666e85..2074df0a62 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveNode.java @@ -15,53 +15,34 @@ */ package ghidra.app.plugin.core.datamgr.tree; -import java.util.List; - -import javax.swing.Icon; - -import docking.widgets.tree.GTree; -import docking.widgets.tree.GTreeNode; import generic.theme.GColor; import generic.theme.GThemeDefaults.Colors.Messages; -import ghidra.app.plugin.core.datamgr.archive.Archive; -import ghidra.program.model.data.*; -import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; -import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarningLevel; +import ghidra.program.model.dtarchive.ArchiveWarning; +import ghidra.program.model.dtarchive.ArchiveWarning.ArchiveWarningLevel; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; import ghidra.util.HTMLUtilities; -import ghidra.util.task.SwingUpdateManager; -public class ArchiveNode extends CategoryNode { +/** + * Nodes that represent a file or project datatype archive + */ +public class ArchiveNode extends DataTypeStoreNode { - protected static final String DEFAULT_DATA_ORG_DESCRIPTION = - "[Using Default Data Organization]"; + protected PersistentDataTypeArchive archive; - protected Archive archive; - protected ArchiveNodeCategoryChangeListener listener; - private DataTypeManager dataTypeManager; // may be null - - public ArchiveNode(Archive archive, DtFilterState filterState) { - this(archive, archive.getDataTypeManager() == null ? null - : archive.getDataTypeManager().getRootCategory(), - filterState); - this.dataTypeManager = archive.getDataTypeManager(); - updateDataTypeManager(); - } - - protected ArchiveNode(Archive archive, Category rootCategory, - DtFilterState filterState) { - super(rootCategory, filterState); + protected ArchiveNode(PersistentDataTypeArchive archive, DtFilterState filterState) { + super(archive, filterState); this.archive = archive; } - protected String buildTooltip(String path) { - DataTypeManager dtm = archive.getDataTypeManager(); - if (dtm == null) { - return null; - } - StringBuilder buf = new StringBuilder(HTMLUtilities.HTML); - buf.append(HTMLUtilities.escapeHTML(path)); - buf.append(HTMLUtilities.BR); - String programArchSummary = dtm.getProgramArchitectureSummary(); + public PersistentDataTypeArchive getArchive() { + return archive; + } + + protected String getArchitectureDetails() { + + StringBuilder buf = new StringBuilder(); + + String programArchSummary = dataTypeStore.getProgramArchitectureSummary(); if (programArchSummary != null) { buf.append(HTMLUtilities.HTML_SPACE); buf.append(HTMLUtilities.HTML_SPACE); @@ -70,463 +51,22 @@ public class ArchiveNode extends CategoryNode { else { buf.append(DEFAULT_DATA_ORG_DESCRIPTION); } - addArchiveWarnings(dtm, buf); + + ArchiveWarning warning = archive.getWarning(); + if (warning != ArchiveWarning.NONE) { + GColor c = Messages.NORMAL; + ArchiveWarningLevel level = warning.level(); + if (level == ArchiveWarningLevel.ERROR) { + c = Messages.ERROR; + } + else if (level == ArchiveWarningLevel.WARN) { + c = Messages.WARNING; + } + String msg = archive.getWarningMessage(false); + buf.append(HTMLUtilities.BR); + buf.append("** " + msg + " **"); + } return buf.toString(); } - private void addArchiveWarnings(DataTypeManager dtm, StringBuilder buf) { - if (dtm instanceof StandAloneDataTypeManager archiveDtm) { - ArchiveWarning warning = archiveDtm.getWarning(); - if (warning != ArchiveWarning.NONE) { - GColor c = Messages.NORMAL; - ArchiveWarningLevel level = warning.level(); - if (level == ArchiveWarningLevel.ERROR) { - c = Messages.ERROR; - } - else if (level == ArchiveWarningLevel.WARN) { - c = Messages.WARNING; - } - String msg = archiveDtm.getWarningMessage(false); - buf.append(HTMLUtilities.BR); - buf.append("** " + msg + " **"); - } - } - } - - protected void archiveStateChanged() { - nodeChanged(); - } - - protected void dataTypeManagerChanged() { - updateDataTypeManager(); - - // old children are no longer valid--clear the cache and fire a node structure changed event - structureChanged(); // notify children have been refreshed; tree cache needs to be cleared - nodeChanged(); // notify that this nodes display data has changed - } - - protected void installDataTypeManagerListener() { - if (dataTypeManager == null) { - return; // some nodes do not have DataTypeManagers, like InvalidFileArchives - } - listener = new ArchiveNodeCategoryChangeListener(); - dataTypeManager.addDataTypeManagerListener(listener); - } - - protected void updateDataTypeManager() { - if (dataTypeManager == null) { - return; // some nodes do not have DataTypeManagers, like InvalidFileArchives - } - - if (listener != null) { - dataTypeManager.removeDataTypeManagerListener(listener); - listener.dispose(); - } - - dataTypeManager = archive.getDataTypeManager(); - installDataTypeManagerListener(); - setCategory(dataTypeManager.getRootCategory()); - } - - @Override - public void dispose() { - if (dataTypeManager != null) { - dataTypeManager.removeDataTypeManagerListener(listener); - } - - if (listener != null) { - listener.dispose(); - } - - super.dispose(); - } - - @Override - public Icon getIcon(boolean expanded) { - return archive.getIcon(expanded); - } - - @Override - public String getName() { - return archive.getName(); - } - - @Override - public String getToolTip() { - return null; - } - - @Override - public boolean isLeaf() { - return false; - } - - @Override - public boolean isEditable() { - return false; - } - - public Archive getArchive() { - return archive; - } - - public void structureChanged() { - setChildren(null); - } - - public void nodeChanged() { - fireNodeChanged(); - - GTree tree = getTree(); - if (tree != null) { - tree.repaint(); - } - } - - @Override - /** - * Overridden since you cannot cut an archive node. - */ - public boolean canCut() { - return false; - } - - @Override - public boolean isCut() { - return false; - } - - @Override - /** - * The equals must not be based on the name since it can change based upon the underlying - * archive. This must be consistent with the hashCode method implementation. - */ - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (getClass() != o.getClass()) { - return false; - } - ArchiveNode otherNode = (ArchiveNode) o; - return getArchive().equals(otherNode.getArchive()); - } - - @Override - public int compareTo(GTreeNode node) { - if (node instanceof ArchiveNode) { - return archive.compareTo(((ArchiveNode) node).archive); - } - return -1; // All ArchiveNodes are before any other types of nodes - } - - /** - * The hashcode must not be based on the name since it can change based upon the underlying - * archive. This must be consistent with the equals method implementation. - */ - @Override - public int hashCode() { - return getArchive().hashCode(); - } - - @Override - public ArchiveNode getArchiveNode() { - return this; - } - - @Override - public boolean isModifiable() { - return archive.isModifiable(); - } - - /** - * Finds the node that represents the given category. - * - *

- * Children will not be loaded when searching for the node. This allows clients to search - * for data types of interest, only updating the tree when the nodes are loaded. - * - * @param localCategory the category of interest - * @return the node if loaded; null if not loaded - */ - public CategoryNode findCategoryNode(Category localCategory) { - return findCategoryNode(localCategory, false); - } - - /** - * Finds the node that represents the given category. - * - * @param localCategory the category of interest - * @param loadChildren true will load child nodes when searching; false will not load children - * @return the node - */ - public CategoryNode findCategoryNode(Category localCategory, boolean loadChildren) { - - // if we don't have to loadChildren and we are not loaded get out. - if (!loadChildren && !isLoaded()) { - return null; - } - - if (localCategory == null) { - return null; - } - - if (getCategory() == localCategory) { - return ArchiveNode.this; - } - - Category parentCategory = localCategory.getParent(); - if (getParent() == null) { - return null; - } - - CategoryNode node = findCategoryNode(parentCategory, loadChildren); - if (node == null) { - return null; - } - - List children = node.getChildren(); - for (GTreeNode child : children) { - if (!(child instanceof CategoryNode)) { - continue; - } - - CategoryNode categoryNode = (CategoryNode) child; - if (categoryNode.getCategory() == localCategory) { - return categoryNode; - } - } - return null; - } - -//================================================================================================== -// Inner Classes -//================================================================================================== - - private class ArchiveNodeCategoryChangeListener implements DataTypeManagerChangeListener { - - private SwingUpdateManager nodeChangedUpdater = new SwingUpdateManager(() -> nodeChanged()); - - @Override - public void categoryAdded(DataTypeManager dtm, CategoryPath path) { - if (!isLoaded()) { - return; - } - - Category newCategory = dtm.getCategory(path); - if (newCategory == null) { - return; - } - Category category = newCategory.getParent(); - CategoryNode categoryNode = findCategoryNode(category); - if (categoryNode != null) { - categoryNode.categoryAdded(newCategory); - } - } - - void dispose() { - nodeChangedUpdater.dispose(); - } - - @Override - public void categoryMoved(DataTypeManager dtm, CategoryPath oldPath, CategoryPath newPath) { - if (!isLoaded()) { - return; - } - - Category newCategory = dtm.getCategory(newPath); - Category oldParent = dtm.getCategory(oldPath.getParent()); - CategoryNode categoryNode = findCategoryNode(oldParent); - if (categoryNode != null) { - categoryNode.categoryRemoved(oldPath.getName()); - } - categoryNode = findCategoryNode(newCategory.getParent()); - if (categoryNode != null) { - categoryNode.categoryAdded(newCategory); - } - } - - @Override - public void categoryRemoved(DataTypeManager dtm, CategoryPath path) { - if (!isLoaded()) { - return; - } - - Category parentCategory = dtm.getCategory(path.getParent()); - CategoryNode categoryNode = findCategoryNode(parentCategory); - if (categoryNode != null) { - categoryNode.categoryRemoved(path.getName()); - } - } - - @Override - public void categoryRenamed(DataTypeManager dtm, CategoryPath oldPath, - CategoryPath newPath) { - if (!isLoaded()) { - return; - } - - if (oldPath.getParent() == null) { // root has no parent - ArchiveNode.this.fireNodeChanged(); // fire that the root changed - return; - } - Category parentCategory = dtm.getCategory(oldPath.getParent()); - CategoryNode categoryNode = findCategoryNode(parentCategory); - if (categoryNode != null) { - categoryNode.categoryRemoved(oldPath.getName()); - Category newCategory = dtm.getCategory(newPath); - categoryNode.categoryAdded(newCategory); - } - } - - @Override - public void dataTypeAdded(DataTypeManager dtm, DataTypePath path) { - if (!isLoaded()) { - return; - } - - Category parentCategory = dtm.getCategory(path.getCategoryPath()); - CategoryNode categoryNode = findCategoryNode(parentCategory); - if (categoryNode != null) { - DataType dataType = dtm.getDataType(path); - - // the data type can be changed before we get this call, since it's asynchronous - if (dataType != null) { - categoryNode.dataTypeAdded(dataType); - } - } - } - - @Override - public void favoritesChanged(DataTypeManager dtm, DataTypePath path, boolean isFavorite) { - if (!isLoaded()) { - return; - } - - DataType dataType = dtm.getDataType(path); - Category category = dtm.getCategory(path.getCategoryPath()); - CategoryNode categoryNode = findCategoryNode(category); - if (categoryNode == null) { - return; - } - List children = categoryNode.getChildren(); - for (GTreeNode node : children) { - if (node instanceof DataTypeNode) { - DataTypeNode dataTypeNode = (DataTypeNode) node; - if (dataTypeNode.getDataType() == dataType) { - dataTypeNode.dataTypeStatusChanged(); - return; - } - } - } - } - - @Override - public void dataTypeChanged(DataTypeManager dtm, DataTypePath path) { - if (!isLoaded()) { - return; - } - - Category dtmCategory = dtm.getCategory(path.getCategoryPath()); - CategoryNode categoryNode = findCategoryNode(dtmCategory); - if (categoryNode != null) { - DataType dataType = dtm.getDataType(path); - - // the data type can be changed before we get this call, since it's asynchronous - if (dataType != null) { - categoryNode.dataTypeChanged(dataType); - } - } - } - - @Override - public void dataTypeMoved(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) { - if (!isLoaded()) { - return; - } - - Category oldParent = dtm.getCategory(oldPath.getCategoryPath()); - CategoryNode categoryNode = findCategoryNode(oldParent); - if (categoryNode != null) { - categoryNode.dataTypeRemoved(oldPath.getDataTypeName()); - } - - Category newParent = dtm.getCategory(newPath.getCategoryPath()); - categoryNode = findCategoryNode(newParent); - if (categoryNode != null) { - DataType dataType = dtm.getDataType(newPath); - - // the data type can be changed before we get this call, since it's asynchronous - if (dataType != null) { - categoryNode.dataTypeAdded(dataType); - } - } - } - - @Override - public void dataTypeRemoved(DataTypeManager dtm, DataTypePath path) { - if (!isLoaded()) { - return; - } - - Category oldParent = dtm.getCategory(path.getCategoryPath()); - CategoryNode categoryNode = findCategoryNode(oldParent); - if (categoryNode != null) { - categoryNode.dataTypeRemoved(path.getDataTypeName()); - } - } - - @Override - public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath, - DataTypePath newPath) { - if (!isLoaded()) { - return; - } - - Category dtmCategory = dtm.getCategory(newPath.getCategoryPath()); - CategoryNode categoryNode = findCategoryNode(dtmCategory); - if (categoryNode != null) { - categoryNode.dataTypeRemoved(oldPath.getDataTypeName()); - DataType dataType = dtm.getDataType(newPath); - - // the data type can be changed before we get this call, since it's asynchronous - if (dataType != null) { - categoryNode.dataTypeAdded(dataType); - } - } - } - - @Override - public void dataTypeReplaced(DataTypeManager dtm, DataTypePath oldPath, - DataTypePath newPath, DataType newDataType) { - // Note: the replacement has already been added with its own event - dataTypeRemoved(dtm, oldPath); - } - - @Override - public void sourceArchiveAdded(DataTypeManager manager, SourceArchive sourceArchive) { - // DT should these do anything? - } - - @Override - public void sourceArchiveChanged(DataTypeManager manager, SourceArchive sourceArchive) { - nodeChangedUpdater.update(); - } - - @Override - public void programArchitectureChanged(DataTypeManager manager) { - // need to force all cached datatype tooltips to be cleared - // due to change in data organization - unloadChildren(); - nodeChangedUpdater.update(); - } - - @Override - public void restored(DataTypeManager manager) { - // need to force all cached datatype tooltips to be cleared - // due to potential changes (e.g., undo/redo) - unloadChildren(); - nodeChangedUpdater.update(); - } - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveRootNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveRootNode.java index 8b1e8d777a..de9b411fcd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveRootNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveRootNode.java @@ -20,16 +20,20 @@ import java.util.*; import javax.swing.Icon; import docking.widgets.tree.GTreeNode; -import ghidra.app.plugin.core.datamgr.archive.*; +import ghidra.app.plugin.core.datamgr.ArchiveManager; +import ghidra.app.plugin.core.datamgr.ArchiveManagerListener; +import ghidra.app.plugin.core.datamgr.archive.InvalidArchive; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; -import ghidra.program.model.data.Category; -import ghidra.program.model.data.DataTypeManager; +import ghidra.program.database.dtarchive.FileDtArchiveDB; +import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.*; +import ghidra.program.model.listing.Program; import ghidra.util.exception.AssertException; public class ArchiveRootNode extends DataTypeTreeNode { private static final String NAME = "Data Types"; - private DataTypeManagerHandler archiveManager; + private ArchiveManager archiveManager; private RootNodeListener archiveListener; private boolean programDtmOnly; @@ -37,11 +41,11 @@ public class ArchiveRootNode extends DataTypeTreeNode { private DtFilterState dtFilterState = new DtFilterState(); - public ArchiveRootNode(DataTypeManagerHandler archiveManager) { + public ArchiveRootNode(ArchiveManager archiveManager) { this(archiveManager, false); } - public ArchiveRootNode(DataTypeManagerHandler archiveManager, boolean programDtmOnly) { + public ArchiveRootNode(ArchiveManager archiveManager, boolean programDtmOnly) { this.archiveManager = archiveManager; this.programDtmOnly = programDtmOnly; init(); @@ -60,10 +64,6 @@ public class ArchiveRootNode extends DataTypeTreeNode { this.listener = listener; } - public DataTypeManagerHandler getArchiveHandler() { - return archiveManager; - } - /** * Returns the modification count for any changes to any category or datatype in any * open archive including the program. @@ -109,30 +109,20 @@ public class ArchiveRootNode extends DataTypeTreeNode { } // a factory method to isolate non-OO inheritance checks - private final ArchiveNode createArchiveNode(Archive archive, DtFilterState filterState) { + private final ArchiveNode createArchiveNode(PersistentDataTypeArchive archive, + DtFilterState filterState) { if (programDtmOnly) { - if (archive instanceof ProgramArchive) { - return new ProgramArchiveNode((ProgramArchive) archive, filterState); - } return null; } - if (archive instanceof FileArchive) { - return new FileArchiveNode((FileArchive) archive, filterState); + if (archive instanceof FileDtArchiveDB fileArchive) { + return new FileArchiveNode(fileArchive, filterState); } - else if (archive instanceof ProjectArchive) { - return new ProjectArchiveNode((ProjectArchive) archive, filterState); - } - else if (archive instanceof InvalidFileArchive) { - return new InvalidArchiveNode((InvalidFileArchive) archive); - } - else if (archive instanceof ProgramArchive) { - return new ProgramArchiveNode((ProgramArchive) archive, filterState); - } - else if (archive instanceof BuiltInArchive) { - return new BuiltInArchiveNode((BuiltInArchive) archive, filterState); + else if (archive instanceof ProjectDataTypeArchive projectArchive) { + return new ProjectArchiveNode(projectArchive, filterState); } + return null; } @@ -166,7 +156,7 @@ public class ArchiveRootNode extends DataTypeTreeNode { * @see ghidra.app.plugin.core.datamgr.tree.DataTypeTreeNode#getArchiveNode() */ @Override - public ArchiveNode getArchiveNode() { + public DataTypeStoreNode getArchiveNode() { return null; } @@ -177,7 +167,7 @@ public class ArchiveRootNode extends DataTypeTreeNode { public CategoryNode findCategoryNode(Category category) { for (GTreeNode node : getChildren()) { - ArchiveNode archiveNode = (ArchiveNode) node; + DataTypeStoreNode archiveNode = (DataTypeStoreNode) node; CategoryNode categoryNode = archiveNode.findCategoryNode(category); if (categoryNode != null) { return categoryNode; @@ -186,13 +176,14 @@ public class ArchiveRootNode extends DataTypeTreeNode { return null; } - public ArchiveNode getNodeForManager(DataTypeManager dtm) { + public DataTypeStoreNode getNodeForManager(DataTypeManager dtm) { for (GTreeNode node : getChildren()) { - ArchiveNode archiveNode = (ArchiveNode) node; - Archive archive = archiveNode.getArchive(); - DataTypeManager manager = archive.getDataTypeManager(); - if (manager.equals(dtm)) { - return archiveNode; + if (node instanceof DataTypeStoreNode storeNode) { + DataTypeStore archive = storeNode.getDataTypeStore(); + DataTypeManager manager = archive.getDataTypeManager(); + if (manager.equals(dtm)) { + return storeNode; + } } } return null; @@ -201,22 +192,54 @@ public class ArchiveRootNode extends DataTypeTreeNode { @Override public List generateChildren() { List list = new ArrayList<>(); - for (Archive element : archiveManager.getAllArchives()) { - GTreeNode node = createArchiveNode(element, dtFilterState); - if (node != null) { - list.add(node); - } - } + addBuiltInNode(list); + addProgramNode(list); + addArchiveNodes(list); + addInvalidNodes(list); Collections.sort(list); return list; } - private ArchiveNode getArchiveNode(Archive archive) { + private void addBuiltInNode(List list) { + if (programDtmOnly) { + return; + } + DataTypeManager builtInDtm = BuiltInDataTypeManager.getDataTypeManager(); + DataTypeStore dataStore = builtInDtm.getDataStore(); + list.add(new BuiltInArchiveNode(dataStore, dtFilterState)); + } + + private void addProgramNode(List list) { + Program program = archiveManager.getProgram(); + if (program != null) { + list.add(new ProgramArchiveNode(program, dtFilterState)); + } + } + + private void addArchiveNodes(List list) { + + for (PersistentDataTypeArchive archive : archiveManager.getOpenArchives()) { + GTreeNode node = createArchiveNode(archive, dtFilterState); + if (node != null) { + list.add(node); + } + } + } + + private void addInvalidNodes(List list) { + List invalidArchives = archiveManager.getInvalidArchives(); + for (InvalidArchive invalidArchive : invalidArchives) { + InvalidArchiveNode node = new InvalidArchiveNode(invalidArchive); + list.add(node); + } + } + + private DataTypeStoreNode getArchiveNode(DataTypeStore store) { List allChildrenList = getChildren(); for (GTreeNode node : allChildrenList) { - if (node instanceof ArchiveNode) { - ArchiveNode archiveNode = (ArchiveNode) node; - if (archiveNode.archive == archive) { + if (node instanceof DataTypeStoreNode) { + DataTypeStoreNode archiveNode = (DataTypeStoreNode) node; + if (archiveNode.dataTypeStore == store) { return archiveNode; } } @@ -229,36 +252,67 @@ public class ArchiveRootNode extends DataTypeTreeNode { //================================================================================================== private class RootNodeListener implements ArchiveManagerListener { - @Override - public void archiveClosed(Archive archive) { + public void programClosed(Program program) { if (!isLoaded()) { return; } List allChildrenList = getChildren(); for (GTreeNode node : allChildrenList) { - ArchiveNode archiveNode = (ArchiveNode) node; - if (archive == archiveNode.getArchive()) { - listener.archiveNodeRemoved(archiveNode); - removeNode(archiveNode); - archiveNode.dispose(); - return; + if (node instanceof ProgramArchiveNode programNode) { + if (program == programNode.getProgram()) { + listener.archiveNodeRemoved(programNode); + removeNode(programNode); + programNode.dispose(); + return; + } } } } @Override - public void archiveOpened(Archive archive) { + public void archiveClosed(PersistentDataTypeArchive archive) { + if (!isLoaded()) { + return; + } + + List allChildrenList = getChildren(); + for (GTreeNode node : allChildrenList) { + if (node instanceof ArchiveNode archiveNode) { + if (archive == archiveNode.getArchive()) { + listener.archiveNodeRemoved(archiveNode); + removeNode(archiveNode); + archiveNode.dispose(); + return; + } + } + } + } + + @Override + public void programOpened(Program program) { + if (!isLoaded()) { + return; + } + ProgramArchiveNode programNode = new ProgramArchiveNode(program, dtFilterState); + + addDataStoreNode(programNode); + } + + @Override + public void archiveOpened(PersistentDataTypeArchive archive) { if (!isLoaded()) { return; } ArchiveNode node = createArchiveNode(archive, dtFilterState); - if (node == null) { - return; + if (node != null) { + addDataStoreNode(node); } + } + private void addDataStoreNode(DataTypeStoreNode node) { List allChildrenList = getChildren(); int index = Collections.binarySearch(allChildrenList, node); if (index < 0) { @@ -270,18 +324,35 @@ public class ArchiveRootNode extends DataTypeTreeNode { } @Override - public void archiveDataTypeManagerChanged(Archive archive) { - ArchiveNode archiveNode = getArchiveNode(archive); + public void stateChanged(DataTypeStore dataTypeStore) { + DataTypeStoreNode archiveNode = getArchiveNode(dataTypeStore); if (archiveNode != null) { - archiveNode.dataTypeManagerChanged(); + archiveNode.nodeChanged(); } } @Override - public void archiveStateChanged(Archive archive) { - ArchiveNode archiveNode = getArchiveNode(archive); - if (archiveNode != null) { - archiveNode.archiveStateChanged(); + public void invalidArchiveAdded(InvalidArchive invalidArchive) { + if (!isLoaded()) { + return; + } + addNode(new InvalidArchiveNode(invalidArchive)); + } + + @Override + public void invalidArchiveRemoved(InvalidArchive invalidArchive) { + if (!isLoaded()) { + return; + } + List allChildrenList = getChildren(); + for (GTreeNode node : allChildrenList) { + if (node instanceof InvalidArchiveNode invalidNode) { + if (invalidArchive.equals(invalidNode.getInvalidArchive())) { + removeNode(node); + invalidNode.dispose(); + return; + } + } } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveRootNodeListener.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveRootNodeListener.java index c0f9448641..5cf2d6440d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveRootNodeListener.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveRootNodeListener.java @@ -24,11 +24,11 @@ public interface ArchiveRootNodeListener { * Called when a node has been added to the root node * @param node the added node */ - public void archiveNodeAdded(ArchiveNode node); + public void archiveNodeAdded(DataTypeStoreNode node); /** * Called when a node is about to be removed from the root node * @param node the node that is about to be removed */ - public void archiveNodeRemoved(ArchiveNode node); + public void archiveNodeRemoved(DataTypeStoreNode node); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/BuiltInArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/BuiltInArchiveNode.java index 85a120cd3b..0667efb78f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/BuiltInArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/BuiltInArchiveNode.java @@ -17,19 +17,25 @@ package ghidra.app.plugin.core.datamgr.tree; import javax.swing.Icon; -import ghidra.app.plugin.core.datamgr.archive.BuiltInArchive; +import generic.theme.GIcon; +import ghidra.program.model.dtarchive.DataTypeStore; import ghidra.util.HTMLUtilities; import resources.MultiIcon; -public class BuiltInArchiveNode extends ArchiveNode { +/** + * Node that represents the Built-in datatype archive. + */ +public class BuiltInArchiveNode extends DataTypeStoreNode { + private static Icon CLOSED_ICON = new GIcon("icon.plugin.datatypes.archive.built.in.closed"); + private static Icon OPEN_ICON = new GIcon("icon.plugin.datatypes.archive.built.in.open"); - public BuiltInArchiveNode(BuiltInArchive archive, DtFilterState filterState) { - super(archive, filterState); + public BuiltInArchiveNode(DataTypeStore store, DtFilterState filterState) { + super(store, filterState); } @Override public Icon getIcon(boolean expanded) { - Icon baseIcon = archive.getIcon(expanded); + Icon baseIcon = expanded ? OPEN_ICON : CLOSED_ICON; MultiIcon multiIcon = new MultiIcon(baseIcon); return multiIcon; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/CategoryNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/CategoryNode.java index 9420af33da..65852d53de 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/CategoryNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/CategoryNode.java @@ -34,7 +34,7 @@ public class CategoryNode extends DataTypeTreeNode { private boolean isCut; private DtFilterState filterState; - public CategoryNode(Category category, DtFilterState filterState) { + protected CategoryNode(Category category, DtFilterState filterState) { this.filterState = filterState; setCategory(category); } @@ -311,7 +311,7 @@ public class CategoryNode extends DataTypeTreeNode { } @Override - public ArchiveNode getArchiveNode() { + public DataTypeStoreNode getArchiveNode() { GTreeNode parent = getParent(); if (parent == null) { return null; // could happen during tree mutations @@ -322,7 +322,7 @@ public class CategoryNode extends DataTypeTreeNode { @Override public boolean isModifiable() { - ArchiveNode archiveNode = getArchiveNode(); + DataTypeStoreNode archiveNode = getArchiveNode(); if (archiveNode == null) { return false; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeArchiveGTree.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeArchiveGTree.java index 433ca1b593..003186537e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeArchiveGTree.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeArchiveGTree.java @@ -33,10 +33,9 @@ import docking.widgets.tree.internal.DefaultGTreeDataTransformer; import docking.widgets.tree.support.GTreeRenderer; import generic.theme.GIcon; import ghidra.app.plugin.core.datamgr.*; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; -import ghidra.app.plugin.core.datamgr.archive.FileArchive; import ghidra.framework.model.*; import ghidra.framework.plugintool.PluginTool; +import ghidra.program.database.dtarchive.FileDtArchiveDB; import ghidra.program.model.data.*; import ghidra.program.model.data.Composite; import ghidra.program.model.data.Enum; @@ -58,7 +57,7 @@ public class DataTypeArchiveGTree extends GTree { private DataTypeTreeExpansionListener cleanupListener = new DataTypeTreeExpansionListener(); public DataTypeArchiveGTree(DataTypeManagerPlugin dataTypeManagerPlugin) { - super(new ArchiveRootNode(dataTypeManagerPlugin.getDataTypeManagerHandler())); + super(new ArchiveRootNode(dataTypeManagerPlugin.getArchiveManager())); this.plugin = dataTypeManagerPlugin; setDragNDropHandler(new DataTypeDragNDropHandler(plugin, this)); @@ -274,13 +273,13 @@ public class DataTypeArchiveGTree extends GTree { return null; } - DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler(); - DataTypeSyncState status = DataTypeSynchronizer.getSyncStatus(handler, dataType); + ArchiveManager archiveManager = plugin.getArchiveManager(); + DataTypeSyncState status = DataTypeSynchronizer.getSyncStatus(archiveManager, dataType); switch (status) { case CONFLICT: case UPDATE: case COMMIT: - return DataTypeSynchronizer.getDiffToolTip(handler, dataType); + return DataTypeSynchronizer.getDiffToolTip(archiveManager, dataType); case ORPHAN: case UNKNOWN: case IN_SYNC: @@ -430,9 +429,8 @@ public class DataTypeArchiveGTree extends GTree { String displayText = ((DataTypeNode) value).getDisplayText(); label.setText(displayText); } - else if (value instanceof DomainFileArchiveNode) { - DomainFileArchiveNode node = (DomainFileArchiveNode) value; - String info = node.getDomainObjectInfo(); + else if (value instanceof VersionedNode versionedNode) { + String info = versionedNode.getDomainObjectInfo(); if (info.length() > 0) { label.setText(label.getText() + info); } @@ -440,8 +438,8 @@ public class DataTypeArchiveGTree extends GTree { decorateWithArchiveCharacteristics(value, label, multiIcon); - if (value instanceof ArchiveNode) { - updateIconForChangeIndicator((ArchiveNode) value, multiIcon); + if (value instanceof DataTypeStoreNode) { + updateIconForChangeIndicator((DataTypeStoreNode) value, multiIcon); } setIcon(multiIcon); @@ -453,7 +451,7 @@ public class DataTypeArchiveGTree extends GTree { if (value instanceof FileArchiveNode) { FileArchiveNode archiveNode = (FileArchiveNode) value; - FileArchive archive = (FileArchive) archiveNode.getArchive(); + FileDtArchiveDB archive = (FileDtArchiveDB) archiveNode.getArchive(); if (archive.isChanged()) { label.setText(label.getText() + " *"); } @@ -473,8 +471,8 @@ public class DataTypeArchiveGTree extends GTree { return; } - DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler(); - DataTypeSyncState status = DataTypeSynchronizer.getSyncStatus(handler, dataType); + ArchiveManager archiveManager = plugin.getArchiveManager(); + DataTypeSyncState status = DataTypeSynchronizer.getSyncStatus(archiveManager, dataType); switch (status) { case CONFLICT: multiIcon.addIcon(new TranslateIcon(CONFLICT_ICON, 10, 5)); @@ -494,8 +492,8 @@ public class DataTypeArchiveGTree extends GTree { } } - private void updateIconForChangeIndicator(ArchiveNode node, MultiIcon multiIcon) { - DataTypeManager dtm = node.getArchive().getDataTypeManager(); + private void updateIconForChangeIndicator(DataTypeStoreNode node, MultiIcon multiIcon) { + DataTypeManager dtm = node.getDataTypeManager(); if (dtm == null) { return; // for InvalidArchiveNodes } @@ -521,7 +519,7 @@ public class DataTypeArchiveGTree extends GTree { private boolean checkforUpdates(List sourceArchives) { for (SourceArchive sourceArchive : sourceArchives) { DataTypeManager sourceDTM = - plugin.getDataTypeManagerHandler().getDataTypeManager(sourceArchive); + plugin.getArchiveManager().getDataTypeManager(sourceArchive); if (sourceDTM != null && sourceArchive.getLastSyncTime() != sourceDTM.getLastChangeTimeForMyManager()) { return true; @@ -547,9 +545,8 @@ public class DataTypeArchiveGTree extends GTree { public void domainFileStatusChanged(DomainFile file, boolean fileIDset) { List archiveNodes = getModelRoot().getChildren(); for (GTreeNode treeNode : archiveNodes) { - if (treeNode instanceof ProjectArchiveNode) { - ProjectArchiveNode projectArchiveNode = (ProjectArchiveNode) treeNode; - DomainFile nodesDomainFile = projectArchiveNode.getDomainFile(); + if (treeNode instanceof ProjectArchiveNode projectArchiveNode) { + DomainFile nodesDomainFile = projectArchiveNode.getOriginalDomainFile(); if (file.equals(nodesDomainFile)) { projectArchiveNode.nodeChanged(); return; @@ -559,8 +556,18 @@ public class DataTypeArchiveGTree extends GTree { } @Override - public void domainFileRemoved(DomainFolder parentFolder, String name, String fileID) { - // DT What if anything needs to be done here? + public void domainFileObjectOpenedForUpdate(DomainFile file, DomainObject doa) { + // When a saveAs is performed the existing becomes associated with a new DomainFile. + // If this corresponds to an archive in the tree we need to invoke nodeChanged on it + List archiveNodes = getModelRoot().getChildren(); + for (GTreeNode treeNode : archiveNodes) { + if (treeNode instanceof ProjectArchiveNode projectArchiveNode) { + if (projectArchiveNode.getDomainObject() == doa) { + projectArchiveNode.nodeChanged(); + return; + } + } + } } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeDragNDropHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeDragNDropHandler.java index 4d76378819..a0057fbf71 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeDragNDropHandler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeDragNDropHandler.java @@ -27,9 +27,9 @@ import docking.widgets.tree.GTreeNode; import docking.widgets.tree.support.GTreeDragNDropHandler; import generic.jar.ResourceFile; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.archive.FileArchive; import ghidra.app.plugin.core.datamgr.util.DataTypeTreeCopyMoveTask; import ghidra.app.plugin.core.datamgr.util.DataTypeTreeCopyMoveTask.ActionType; +import ghidra.program.database.dtarchive.FileDtArchiveDB; import ghidra.program.model.data.*; import ghidra.util.Msg; import ghidra.util.task.Task; @@ -118,7 +118,7 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler { } // we don't support dragging archives in their entirety - if (node instanceof ArchiveNode) { + if (node instanceof DataTypeStoreNode) { return new DataFlavor[] {}; } } @@ -144,14 +144,14 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler { return dragUserData; } else if (flavor.equals(DataFlavor.javaFileListFlavor)) { - List nodeList = dragUserData; + List nodeList = dragUserData; ArrayList fileList = new ArrayList(); for (Object node : nodeList) { - - ArchiveNode archiveNode = (ArchiveNode) node; - FileArchive archive = (FileArchive) archiveNode.getArchive(); - ResourceFile file = archive.getFile(); - fileList.add(file); + if (node instanceof FileArchiveNode fileArchiveNode) { + FileDtArchiveDB archive = (FileDtArchiveDB) fileArchiveNode.getArchive(); + ResourceFile file = archive.getFile(); + fileList.add(file); + } } return fileList; } @@ -199,7 +199,7 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler { // destination node must belong to either a modifiable archive or a program archive. // i.e. it must be writable. - ArchiveNode archiveNode = ((DataTypeTreeNode) destinationNode).getArchiveNode(); + DataTypeStoreNode archiveNode = ((DataTypeTreeNode) destinationNode).getArchiveNode(); if (archiveNode == null || !archiveNode.isModifiable()) { return false; } @@ -221,7 +221,7 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler { return true; } CategoryNode categoryNode = (CategoryNode) destinationNode; - return (categoryNode instanceof ArchiveNode); + return (categoryNode instanceof DataTypeStoreNode); } private boolean containsFlavor(DataFlavor[] flavors, DataFlavor flavor) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeNode.java index b6ceb9564a..c7e1a2c770 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeNode.java @@ -231,7 +231,7 @@ public class DataTypeNode extends DataTypeTreeNode { } @Override - public ArchiveNode getArchiveNode() { + public DataTypeStoreNode getArchiveNode() { GTreeNode parent = getParent(); if (parent == null) { return null; // could happen during tree mutations @@ -242,7 +242,7 @@ public class DataTypeNode extends DataTypeTreeNode { @Override public boolean isModifiable() { - ArchiveNode archiveNode = getArchiveNode(); + DataTypeStoreNode archiveNode = getArchiveNode(); return archiveNode != null && archiveNode.isModifiable(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeStoreNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeStoreNode.java new file mode 100644 index 0000000000..a78dc7191d --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeStoreNode.java @@ -0,0 +1,447 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.datamgr.tree; + +import java.util.Comparator; +import java.util.List; + +import docking.widgets.tree.GTree; +import docking.widgets.tree.GTreeNode; +import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.DataTypeStore; +import ghidra.util.task.SwingUpdateManager; + +/** + * That have a valid DataTypeManager (program, file archive, project archive, built-in archive) + */ +public abstract class DataTypeStoreNode extends CategoryNode { + + protected static final String DEFAULT_DATA_ORG_DESCRIPTION = + "[Using Default Data Organization]"; + + protected DataTypeStore dataTypeStore; + protected ArchiveNodeCategoryChangeListener listener; + private DataTypeManager dataTypeManager; // may be null + private Comparator archiveComparator = new ArchiveComparator(); + + protected DataTypeStoreNode(DataTypeStore archive, DtFilterState filterState) { + super(archive.getDataTypeManager().getRootCategory(), filterState); + this.dataTypeStore = archive; + dataTypeManager = archive.getDataTypeManager(); + installDataTypeManagerListener(); + } + + public DataTypeManager getDataTypeManager() { + return dataTypeStore.getDataTypeManager(); + } + + protected void stateChanged() { + nodeChanged(); // notify that this nodes display data has changed + } + + protected void installDataTypeManagerListener() { + listener = new ArchiveNodeCategoryChangeListener(); + dataTypeManager.addDataTypeManagerListener(listener); + } + + @Override + public void dispose() { + dataTypeManager.removeDataTypeManagerListener(listener); + listener.dispose(); + super.dispose(); + } + + @Override + public String getName() { + return dataTypeStore.getName(); + } + + @Override + public String getToolTip() { + return null; + } + + @Override + public boolean isLeaf() { + return false; + } + + @Override + public boolean isEditable() { + return false; + } + + public DataTypeStore getDataTypeStore() { + return dataTypeStore; + } + + public void structureChanged() { + setChildren(null); + } + + public void nodeChanged() { + fireNodeChanged(); + + GTree tree = getTree(); + if (tree != null) { + tree.repaint(); + } + } + + @Override + public boolean canCut() { + return false; + } + + @Override + public boolean isCut() { + return false; + } + + /** + * The equals must not be based on the name since it can change based upon the underlying + * archive. This must be consistent with the hashCode method implementation. + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (getClass() != o.getClass()) { + return false; + } + DataTypeStoreNode otherNode = (DataTypeStoreNode) o; + return dataTypeStore == otherNode.dataTypeStore; + } + + @Override + public int compareTo(GTreeNode node) { + if (node instanceof DataTypeStoreNode archiveNode) { + return archiveComparator.compare(dataTypeStore, archiveNode.dataTypeStore); + } + return -1; // All ArchiveNodes are before any other types of nodes + } + + /** + * The hash code must not be based on the name since it can change based upon the underlying + * archive. This must be consistent with the equals method implementation. + */ + @Override + public int hashCode() { + return dataTypeStore.hashCode(); + } + + @Override + public DataTypeStoreNode getArchiveNode() { + return this; + } + + @Override + public boolean isModifiable() { + return dataTypeStore.isChangeable(); + } + + /** + * Finds the node that represents the given category. + * + *

+ * Children will not be loaded when searching for the node. This allows clients to search + * for data types of interest, only updating the tree when the nodes are loaded. + * + * @param localCategory the category of interest + * @return the node if loaded; null if not loaded + */ + public CategoryNode findCategoryNode(Category localCategory) { + return findCategoryNode(localCategory, false); + } + + /** + * Finds the node that represents the given category. + * + * @param localCategory the category of interest + * @param loadChildren true will load child nodes when searching; false will not load children + * @return the node + */ + public CategoryNode findCategoryNode(Category localCategory, boolean loadChildren) { + + // if we don't have to loadChildren and we are not loaded get out. + if (!loadChildren && !isLoaded()) { + return null; + } + + if (localCategory == null) { + return null; + } + + if (getCategory() == localCategory) { + return DataTypeStoreNode.this; + } + + Category parentCategory = localCategory.getParent(); + if (getParent() == null) { + return null; + } + + CategoryNode node = findCategoryNode(parentCategory, loadChildren); + if (node == null) { + return null; + } + + List children = node.getChildren(); + for (GTreeNode child : children) { + if (!(child instanceof CategoryNode)) { + continue; + } + + CategoryNode categoryNode = (CategoryNode) child; + if (categoryNode.getCategory() == localCategory) { + return categoryNode; + } + } + return null; + } + +//================================================================================================== +// Inner Classes +//================================================================================================== + + private class ArchiveNodeCategoryChangeListener implements DataTypeManagerChangeListener { + + private SwingUpdateManager nodeChangedUpdater = new SwingUpdateManager(() -> nodeChanged()); + + @Override + public void categoryAdded(DataTypeManager dtm, CategoryPath path) { + if (!isLoaded()) { + return; + } + + Category newCategory = dtm.getCategory(path); + if (newCategory == null) { + return; + } + Category category = newCategory.getParent(); + CategoryNode categoryNode = findCategoryNode(category); + if (categoryNode != null) { + categoryNode.categoryAdded(newCategory); + } + } + + void dispose() { + nodeChangedUpdater.dispose(); + } + + @Override + public void categoryMoved(DataTypeManager dtm, CategoryPath oldPath, CategoryPath newPath) { + if (!isLoaded()) { + return; + } + + Category newCategory = dtm.getCategory(newPath); + Category oldParent = dtm.getCategory(oldPath.getParent()); + CategoryNode categoryNode = findCategoryNode(oldParent); + if (categoryNode != null) { + categoryNode.categoryRemoved(oldPath.getName()); + } + categoryNode = findCategoryNode(newCategory.getParent()); + if (categoryNode != null) { + categoryNode.categoryAdded(newCategory); + } + } + + @Override + public void categoryRemoved(DataTypeManager dtm, CategoryPath path) { + if (!isLoaded()) { + return; + } + + Category parentCategory = dtm.getCategory(path.getParent()); + CategoryNode categoryNode = findCategoryNode(parentCategory); + if (categoryNode != null) { + categoryNode.categoryRemoved(path.getName()); + } + } + + @Override + public void categoryRenamed(DataTypeManager dtm, CategoryPath oldPath, + CategoryPath newPath) { + if (!isLoaded()) { + return; + } + + if (oldPath.getParent() == null) { // root has no parent + DataTypeStoreNode.this.fireNodeChanged(); // fire that the root changed + return; + } + Category parentCategory = dtm.getCategory(oldPath.getParent()); + CategoryNode categoryNode = findCategoryNode(parentCategory); + if (categoryNode != null) { + categoryNode.categoryRemoved(oldPath.getName()); + Category newCategory = dtm.getCategory(newPath); + categoryNode.categoryAdded(newCategory); + } + } + + @Override + public void dataTypeAdded(DataTypeManager dtm, DataTypePath path) { + if (!isLoaded()) { + return; + } + + Category parentCategory = dtm.getCategory(path.getCategoryPath()); + CategoryNode categoryNode = findCategoryNode(parentCategory); + if (categoryNode != null) { + DataType dataType = dtm.getDataType(path); + + // the data type can be changed before we get this call, since it's asynchronous + if (dataType != null) { + categoryNode.dataTypeAdded(dataType); + } + } + } + + @Override + public void favoritesChanged(DataTypeManager dtm, DataTypePath path, boolean isFavorite) { + if (!isLoaded()) { + return; + } + + DataType dataType = dtm.getDataType(path); + Category category = dtm.getCategory(path.getCategoryPath()); + CategoryNode categoryNode = findCategoryNode(category); + if (categoryNode == null) { + return; + } + List children = categoryNode.getChildren(); + for (GTreeNode node : children) { + if (node instanceof DataTypeNode) { + DataTypeNode dataTypeNode = (DataTypeNode) node; + if (dataTypeNode.getDataType() == dataType) { + dataTypeNode.dataTypeStatusChanged(); + return; + } + } + } + } + + @Override + public void dataTypeChanged(DataTypeManager dtm, DataTypePath path) { + if (!isLoaded()) { + return; + } + + Category dtmCategory = dtm.getCategory(path.getCategoryPath()); + CategoryNode categoryNode = findCategoryNode(dtmCategory); + if (categoryNode != null) { + DataType dataType = dtm.getDataType(path); + + // the data type can be changed before we get this call, since it's asynchronous + if (dataType != null) { + categoryNode.dataTypeChanged(dataType); + } + } + } + + @Override + public void dataTypeMoved(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) { + if (!isLoaded()) { + return; + } + + Category oldParent = dtm.getCategory(oldPath.getCategoryPath()); + CategoryNode categoryNode = findCategoryNode(oldParent); + if (categoryNode != null) { + categoryNode.dataTypeRemoved(oldPath.getDataTypeName()); + } + + Category newParent = dtm.getCategory(newPath.getCategoryPath()); + categoryNode = findCategoryNode(newParent); + if (categoryNode != null) { + DataType dataType = dtm.getDataType(newPath); + + // the data type can be changed before we get this call, since it's asynchronous + if (dataType != null) { + categoryNode.dataTypeAdded(dataType); + } + } + } + + @Override + public void dataTypeRemoved(DataTypeManager dtm, DataTypePath path) { + if (!isLoaded()) { + return; + } + + Category oldParent = dtm.getCategory(path.getCategoryPath()); + CategoryNode categoryNode = findCategoryNode(oldParent); + if (categoryNode != null) { + categoryNode.dataTypeRemoved(path.getDataTypeName()); + } + } + + @Override + public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath, + DataTypePath newPath) { + if (!isLoaded()) { + return; + } + + Category dtmCategory = dtm.getCategory(newPath.getCategoryPath()); + CategoryNode categoryNode = findCategoryNode(dtmCategory); + if (categoryNode != null) { + categoryNode.dataTypeRemoved(oldPath.getDataTypeName()); + DataType dataType = dtm.getDataType(newPath); + + // the data type can be changed before we get this call, since it's asynchronous + if (dataType != null) { + categoryNode.dataTypeAdded(dataType); + } + } + } + + @Override + public void dataTypeReplaced(DataTypeManager dtm, DataTypePath oldPath, + DataTypePath newPath, DataType newDataType) { + // Note: the replacement has already been added with its own event + dataTypeRemoved(dtm, oldPath); + } + + @Override + public void sourceArchiveAdded(DataTypeManager manager, SourceArchive sourceArchive) { + // DT should these do anything? + } + + @Override + public void sourceArchiveChanged(DataTypeManager manager, SourceArchive sourceArchive) { + nodeChangedUpdater.update(); + } + + @Override + public void programArchitectureChanged(DataTypeManager manager) { + // need to force all cached datatype tooltips to be cleared + // due to change in data organization + unloadChildren(); + nodeChangedUpdater.update(); + } + + @Override + public void restored(DataTypeManager manager) { + // need to force all cached datatype tooltips to be cleared + // due to potential changes (e.g., undo/redo) + unloadChildren(); + nodeChangedUpdater.update(); + } + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeTreeNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeTreeNode.java index 4f15b1e1bc..9ce1810511 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeTreeNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeTreeNode.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -56,7 +56,7 @@ public abstract class DataTypeTreeNode extends GTreeLazyNode { * Returns the ArchiveNode for this tree node. * @return the ArchiveNode for this tree node. */ - public abstract ArchiveNode getArchiveNode(); + public abstract DataTypeStoreNode getArchiveNode(); /** * Returns true if this node is from an archive that can be modified. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/FileArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/FileArchiveNode.java index b01983f62f..cc3fd473ca 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/FileArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/FileArchiveNode.java @@ -19,45 +19,53 @@ import javax.swing.Icon; import generic.jar.ResourceFile; import generic.theme.GIcon; -import ghidra.app.plugin.core.datamgr.archive.FileArchive; +import ghidra.program.model.dtarchive.FileDataTypeArchive; +import ghidra.util.HTMLUtilities; import resources.MultiIcon; import resources.icons.TranslateIcon; +/** + * Nodes that represent FileDataTypeArchives + */ public class FileArchiveNode extends ArchiveNode { + private static Icon CLOSED_ICON = new GIcon("icon.plugin.datatypes.archive.file.closed"); + private static Icon OPEN_ICON = new GIcon("icon.plugin.datatypes.archive.file.open"); private static Icon CHECKED_OUT_EXCLUSIVE_ICON = new GIcon("icon.plugin.datatypes.tree.node.archive.file.checked.out.exclusive"); - FileArchive fileArchive; // casted reference for easy access - - public FileArchiveNode(FileArchive archive, DtFilterState filterState) { + public FileArchiveNode(FileDataTypeArchive archive, DtFilterState filterState) { super(archive, filterState); - this.fileArchive = archive; } @Override public Icon getIcon(boolean expanded) { DtBackgroundIcon bgIcon = new DtBackgroundIcon(); MultiIcon multiIcon = new MultiIcon(bgIcon); - boolean hasWriteLock = fileArchive.hasWriteLock(); - Icon baseIcon = fileArchive.getIcon(expanded); + Icon baseIcon = expanded ? OPEN_ICON : CLOSED_ICON; multiIcon.addIcon(baseIcon); - if (hasWriteLock) { + if (archive.isChangeable()) { multiIcon.addIcon(new TranslateIcon(CHECKED_OUT_EXCLUSIVE_ICON, 8, -4)); } - // TODO: add program architecture state - return multiIcon; } @Override public String getToolTip() { - ResourceFile file = fileArchive.getFile(); - return buildTooltip(file != null ? file.getAbsolutePath() : "[Unsaved New Archive]"); + ResourceFile file = ((FileDataTypeArchive) archive).getFile(); + String path = file != null ? file.getAbsolutePath() : "[Unsaved New Archive]"; + + StringBuilder buf = new StringBuilder(HTMLUtilities.HTML); + buf.append(HTMLUtilities.escapeHTML(path)); + buf.append(HTMLUtilities.BR); + buf.append(getArchitectureDetails()); + buf.append(HTMLUtilities.HTML_CLOSE); + return buf.toString(); } - public boolean hasWriteLock() { - return fileArchive.hasWriteLock(); + @Override + public FileDataTypeArchive getArchive() { + return (FileDataTypeArchive) super.getArchive(); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/InvalidArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/InvalidArchiveNode.java index 88772e9259..6b2cf51b9a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/InvalidArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/InvalidArchiveNode.java @@ -15,18 +15,26 @@ */ package ghidra.app.plugin.core.datamgr.tree; +import java.util.Collections; import java.util.List; +import javax.swing.Icon; + import docking.widgets.tree.GTreeNode; -import ghidra.app.plugin.core.datamgr.archive.Archive; -import ghidra.app.plugin.core.datamgr.archive.InvalidFileArchive; -import ghidra.program.model.data.ArchiveType; +import generic.theme.GIcon; +import ghidra.app.plugin.core.datamgr.archive.InvalidArchive; import ghidra.util.HTMLUtilities; -public class InvalidArchiveNode extends ArchiveNode { +/** + * Archive node for an invalid file data type archive. + */ +public class InvalidArchiveNode extends DataTypeTreeNode { + private static final Icon INVALID_ARCHIVE_ICON = + new GIcon("icon.plugin.datatypes.archive.invalid"); + private InvalidArchive archive; - public InvalidArchiveNode(InvalidFileArchive archive) { - super(archive, new DtFilterState()); + public InvalidArchiveNode(InvalidArchive archive) { + this.archive = archive; } @Override @@ -41,15 +49,13 @@ public class InvalidArchiveNode extends ArchiveNode { @Override public String getToolTip() { - ArchiveType archiveType = ((InvalidFileArchive) archive).getArchiveType(); - String type = archiveType == ArchiveType.FILE ? "File" : "Project"; - return "Unable to locate " + type + " data type archive: " + - HTMLUtilities.escapeHTML(archive.getName()); + return "Unable to locate file data type archive: " + + HTMLUtilities.escapeHTML(archive.name()); } @Override public String getName() { - return archive.getName(); + return archive.name(); } @Override @@ -63,7 +69,7 @@ public class InvalidArchiveNode extends ArchiveNode { } @Override - public ArchiveNode getArchiveNode() { + public FileArchiveNode getArchiveNode() { return null; } @@ -79,11 +85,20 @@ public class InvalidArchiveNode extends ArchiveNode { @Override public void setNodeCut(boolean isCut) { + // do nothing } @Override - public Archive getArchive() { + public Icon getIcon(boolean expanded) { + return INVALID_ARCHIVE_ICON; + } + + public InvalidArchive getInvalidArchive() { return archive; } + @Override + protected List generateChildren() { + return Collections.emptyList(); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProgramArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProgramArchiveNode.java index e49c290d87..4d4e3ff915 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProgramArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProgramArchiveNode.java @@ -15,32 +15,82 @@ */ package ghidra.app.plugin.core.datamgr.tree; -import ghidra.app.plugin.core.datamgr.archive.ProgramArchive; +import javax.swing.Icon; + +import generic.theme.GIcon; +import ghidra.framework.data.DomainFileProxy; import ghidra.framework.model.DomainFile; -import ghidra.program.model.data.DataTypeManager; +import ghidra.framework.model.DomainObject; +import ghidra.program.model.listing.Program; import ghidra.util.HTMLUtilities; -public class ProgramArchiveNode extends DomainFileArchiveNode { +/** + * Tree node representing datatypes from the currently active program + */ +public class ProgramArchiveNode extends DataTypeStoreNode implements VersionedNode { + private static Icon CLOSED_ICON = new GIcon("icon.plugin.datatypes.archive.program.closed"); + private static Icon OPEN_ICON = new GIcon("icon.plugin.datatypes.archive.program.open"); + private VersionState versionState; - public ProgramArchiveNode(ProgramArchive archive, DtFilterState filterState) { - super(archive, filterState); + public ProgramArchiveNode(Program program, DtFilterState filterState) { + super(program, filterState); + versionState = new VersionState(program); + } + + public Program getProgram() { + return (Program) dataTypeStore; } @Override public String getToolTip() { - DataTypeManager dtm = archive.getDataTypeManager(); - DomainFile file = ((ProgramArchive) archive).getDomainFile(); + DomainFile file = getDomainObject().getDomainFile(); + DomainFile originalFile = file; + if (file instanceof DomainFileProxy proxy) { + originalFile = proxy.getOriginalDomainFile(); + } StringBuilder buf = new StringBuilder(HTMLUtilities.HTML); - if (file != null) { - buf.append(HTMLUtilities.escapeHTML(file.toString())); + if (originalFile != null) { + buf.append(HTMLUtilities.escapeHTML(originalFile.toString())); } else { - buf.append("[Unsaved New Program Archive]"); + buf.append("[Unsaved Program]"); } buf.append(HTMLUtilities.BR); buf.append(HTMLUtilities.HTML_SPACE); buf.append(HTMLUtilities.HTML_SPACE); - buf.append(HTMLUtilities.escapeHTML(dtm.getProgramArchitectureSummary())); + buf.append(HTMLUtilities.escapeHTML(dataTypeStore.getProgramArchitectureSummary())); return buf.toString(); } + + @Override + public Icon getIcon(boolean expanded) { + Icon baseIcon = expanded ? OPEN_ICON : CLOSED_ICON; + return versionState.getIcon(baseIcon); + } + + @Override + public DomainObject getDomainObject() { + return dataTypeStore; + } + + @Override + public String getDomainObjectInfo() { + return versionState.getDomainObjectInfo(); + } + + @Override + public void nodeChanged() { + super.nodeChanged(); + versionState.updateDomainFileInfo(); + } + + @Override + public DomainFile getOriginalDomainFile() { + return versionState.getOriginalDomainFile(); + } + + @Override + public DomainFile getDomainFile() { + return dataTypeStore.getDomainFile(); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProjectArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProjectArchiveNode.java index 53c3054a73..6cdf1b2352 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProjectArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProjectArchiveNode.java @@ -15,53 +15,86 @@ */ package ghidra.app.plugin.core.datamgr.tree; -import ghidra.app.plugin.core.datamgr.archive.ProjectArchive; +import javax.swing.Icon; + +import generic.theme.GIcon; +import ghidra.framework.data.DomainFileProxy; import ghidra.framework.model.DomainFile; +import ghidra.framework.model.DomainObject; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; +import ghidra.util.HTMLUtilities; -public class ProjectArchiveNode extends DomainFileArchiveNode { +/** + * Tree node for representing open project datatype archives. + */ +public class ProjectArchiveNode extends ArchiveNode implements VersionedNode { + private static Icon CLOSED_ICON = new GIcon("icon.plugin.datatypes.archive.project.closed"); + private static Icon OPEN_ICON = new GIcon("icon.plugin.datatypes.archive.project.open"); + private VersionState versionState; - public ProjectArchiveNode(ProjectArchive archive, DtFilterState filterState) { + public ProjectArchiveNode(ProjectDataTypeArchive archive, DtFilterState filterState) { super(archive, filterState); - } - - @Override - protected void dataTypeManagerChanged() { - setChildren(null); // old children are no longer valid. - installDataTypeManagerListener(); - nodeChanged(); + versionState = new VersionState(archive); } @Override public String getToolTip() { - DomainFile file = ((ProjectArchive) archive).getDomainFile(); - return buildTooltip(file != null ? file.getPathname() : "[Unsaved New Project Archive]"); + DomainFile file = getDomainObject().getDomainFile(); + DomainFile originalFile = file; + if (file instanceof DomainFileProxy proxy) { + originalFile = proxy.getOriginalDomainFile(); + } + StringBuilder buf = new StringBuilder(HTMLUtilities.HTML); + if (originalFile != null) { + buf.append(HTMLUtilities.escapeHTML(originalFile.toString())); + } + else { + buf.append("[Unsaved Project Archive]"); + } + buf.append(HTMLUtilities.BR); + buf.append(getArchitectureDetails()); + buf.append(HTMLUtilities.HTML_CLOSE); + return buf.toString(); } public boolean hasWriteLock() { return false; } - /** - * Overridden to avoid path conflicts that arise in CategoryNode.equals() - * - * @see java.lang.Object#equals(java.lang.Object) - */ @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (getClass() != o.getClass()) { - return false; - } - - if (super.equals(o)) { - DomainFile myFile = ((ProjectArchive) archive).getDomainFile(); - DomainFile otherFile = - ((ProjectArchive) ((ProjectArchiveNode) o).archive).getDomainFile(); - return myFile.equals(otherFile); - } - return false; + public void nodeChanged() { + super.nodeChanged(); + versionState.updateDomainFileInfo(); } + @Override + public DomainObject getDomainObject() { + return getArchive(); + } + + @Override + public Icon getIcon(boolean expanded) { + Icon baseIcon = expanded ? OPEN_ICON : CLOSED_ICON; + return versionState.getIcon(baseIcon); + } + + @Override + public ProjectDataTypeArchive getArchive() { + return (ProjectDataTypeArchive) super.getArchive(); + } + + @Override + public String getDomainObjectInfo() { + return versionState.getDomainObjectInfo(); + } + + @Override + public DomainFile getOriginalDomainFile() { + return versionState.getOriginalDomainFile(); + } + + @Override + public DomainFile getDomainFile() { + return archive.getDomainFile(); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DomainFileArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/VersionState.java similarity index 58% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DomainFileArchiveNode.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/VersionState.java index 1d2deec4cb..f7c453c47a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DomainFileArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/VersionState.java @@ -18,15 +18,18 @@ package ghidra.app.plugin.core.datamgr.tree; import javax.swing.Icon; import generic.theme.GIcon; -import ghidra.app.plugin.core.datamgr.archive.DomainFileArchive; +import ghidra.framework.data.DomainFileProxy; import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainObject; import ghidra.program.model.listing.Program; import resources.MultiIcon; import resources.icons.TranslateIcon; -public abstract class DomainFileArchiveNode extends ArchiveNode { - +/** + * Class to keep track of version information. Used by the {@link ProgramArchiveNode} and the + * {@link ProjectArchiveNode} + */ +public class VersionState { //@formatter:off private static Icon CHECKED_OUT_ICON = new GIcon("icon.plugin.datatypes.tree.node.archive.file.checked.out"); private static Icon CHECKED_OUT_EXCLUSIVE_ICON = new GIcon("icon.plugin.datatypes.tree.node.archive.file.checked.out.exclusive"); @@ -43,71 +46,61 @@ public abstract class DomainFileArchiveNode extends ArchiveNode { private boolean isVersioned; private int version; private int latestVersion; - + private DomainObject domainObject; private String domainFileInfoString; - public DomainFileArchiveNode(DomainFileArchive archive, DtFilterState filterState) { - super(archive, filterState); - + VersionState(DomainObject domainObject) { + this.domainObject = domainObject; updateDomainFileInfo(); } - private void updateDomainFileInfo() { - DomainObject domainObject = ((DomainFileArchive) archive).getDomainObject(); - DomainFile domainFile = ((DomainFileArchive) archive).getDomainFile(); + void updateDomainFileInfo() { + DomainFile domainFile = domainObject.getDomainFile(); + DomainFile originalDomainFile = getOriginalDomainFile(); isChanged = domainObject.isChanged(); - isReadOnly = domainFile.isReadOnly() || !domainFile.isInWritableProject(); - isHijacked = domainFile.isHijacked(); - isVersioned = domainFile.isVersioned(); - version = (isVersioned || !domainFile.canSave()) ? domainFile.getVersion() - : DomainFile.DEFAULT_VERSION; - isCheckedOutExclusive = - (!isVersioned && domainObject.hasExclusiveAccess() && !isReadOnly) || - (isVersioned && domainFile.isCheckedOutExclusive()); - isCheckedOut = isCheckedOutExclusive || domainFile.isCheckedOut(); - latestVersion = domainFile.getLatestVersion(); + isReadOnly = domainFile.isReadOnly(); + + isVersioned = originalDomainFile.isVersioned(); + + // NOTE: DomainFileProxy may indicate version as DEFAULT_VERSION (-1) if latest version + // was open read-only. A specific version is only provided when that version was + // explicitly opened read-only. + latestVersion = originalDomainFile.getLatestVersion(); + version = domainFile.getVersion(); + if (version == DomainFile.DEFAULT_VERSION && isVersioned && latestVersion > 0) { + version = latestVersion; // default version tracks latest version + } + + isHijacked = !isReadOnly && originalDomainFile.isHijacked(); + isCheckedOutExclusive = !isReadOnly && originalDomainFile.isCheckedOutExclusive(); + isCheckedOut = !isReadOnly && originalDomainFile.isCheckedOut(); domainFileInfoString = createDomainFileInfoString(); } - private String createDomainFileInfoString() { - DomainObject domainObject = ((DomainFileArchive) archive).getDomainObject(); - String name = ""; - if (isHijacked) { - name += " (hijacked)"; - } - else if (isVersioned) { - if (version == latestVersion && !isCheckedOut) { - name += " (" + version + ")"; - } - else { - name += " (" + version + " of " + latestVersion + ")"; + /** + * {@return the original domain file associated with this archive node or + * the same as {@link #getDomainFile()} if not opened from project storage} + */ + public DomainFile getOriginalDomainFile() { + DomainFile domainFile = domainObject.getDomainFile(); + if (domainFile instanceof DomainFileProxy proxy) { + DomainFile originalDomainFile = proxy.getOriginalDomainFile(); + if (originalDomainFile != null) { + return originalDomainFile; } } - else if (version != DomainFile.DEFAULT_VERSION) { - name += " @ " + version; - } - if (!(domainObject instanceof Program) && isChanged) { - name += " *"; - } - - return name; + return domainFile; } - @Override - public abstract String getToolTip(); - - @Override - public boolean canDelete() { - return false; - } - - @Override - public Icon getIcon(boolean expanded) { - - Icon baseIcon = archive.getIcon(expanded); + /** + * Returns an icon that overlays a given base icon with version state information. + * @param baseIcon the base icon to be decorated + * @return a new Icon that decorates a base icon with version state information + */ + public Icon getIcon(Icon baseIcon) { DtBackgroundIcon bgIcon = new DtBackgroundIcon(isVersioned); MultiIcon multiIcon = new MultiIcon(bgIcon); multiIcon.addIcon(baseIcon); @@ -130,22 +123,31 @@ public abstract class DomainFileArchiveNode extends ArchiveNode { } } - // TODO: add program architecture state - return multiIcon; } - protected String getDomainObjectInfo() { + private String createDomainFileInfoString() { + String name = ""; + if (isHijacked) { + name += " (hijacked)"; + } + if (isVersioned && version != DomainFile.DEFAULT_VERSION) { + if (version == latestVersion) { + name += " (" + version + ")"; + } + else { + name += " (" + version + " of " + latestVersion + ")"; + } + } + if (!(domainObject instanceof Program) && isChanged) { + name += " *"; + } + + return name; + } + + public String getDomainObjectInfo() { return domainFileInfoString; } - public DomainFile getDomainFile() { - return ((DomainFileArchive) archive).getDomainFile(); - } - - @Override - public void nodeChanged() { - super.nodeChanged(); - updateDomainFileInfo(); - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/VersionedNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/VersionedNode.java new file mode 100644 index 0000000000..812e68b90f --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/VersionedNode.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.datamgr.tree; + +import ghidra.framework.model.DomainFile; +import ghidra.framework.model.DomainObject; + +/** + * Common interface for DataTypeStoreNodes that support versioning. + */ +public interface VersionedNode { + /** + * {@return the domainObject (this will be the program or archive)} + */ + public DomainObject getDomainObject(); + + /** + * {@return the actual project DomainFile that was used to open this DataTypeStore} + */ + public DomainFile getOriginalDomainFile(); + + /** + * {@return the current DomainFile associated with this DataTypeStore. This may be a proxy} + * if the program or archive was opened read-only + */ + public DomainFile getDomainFile(); + + /** + * {@return a string that describes the current version state of this program or archive} + */ + public String getDomainObjectInfo(); +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeArchiveUtility.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeArchiveUtility.java index 98d23a5cd2..571492328f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeArchiveUtility.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeArchiveUtility.java @@ -18,10 +18,10 @@ package ghidra.app.plugin.core.datamgr.util; import java.util.*; import generic.jar.ResourceFile; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.util.opinion.*; import ghidra.framework.Application; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.database.dtarchive.FileDtArchiveDB; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.program.model.listing.Program; import ghidra.util.Msg; @@ -40,14 +40,14 @@ public class DataTypeArchiveUtility { new HashMap(); static { for (ResourceFile file : Application - .findFilesByExtensionInApplication(FileDataTypeManager.SUFFIX)) { + .findFilesByExtensionInApplication(FileDtArchiveDB.SUFFIX)) { String name = file.getName(); ResourceFile resourceFile = GHIDRA_ARCHIVES.get(name); if (resourceFile == null) { GHIDRA_ARCHIVES.put(file.getName(), file); } else { - Msg.showError(DataTypeManagerHandler.class, null, "Duplicate Archive Name Error", + Msg.showError(DataTypeArchiveUtility.class, null, "Duplicate Archive Name Error", "Duplicate datatype archive name detected and is not supported:\n " + resourceFile.getAbsolutePath() + "\n " + file.getAbsolutePath()); } @@ -76,8 +76,8 @@ public class DataTypeArchiveUtility { * @return existing resource file or null if not found */ public static ResourceFile findArchiveFile(String archiveName) { - if (!archiveName.endsWith(FileDataTypeManager.SUFFIX)) { - archiveName = archiveName + FileDataTypeManager.SUFFIX; + if (!archiveName.endsWith(FileDataTypeArchive.SUFFIX)) { + archiveName = archiveName + FileDataTypeArchive.SUFFIX; } archiveName = archiveName.replace('\\', '/'); if (archiveName.indexOf(':') >= 0 || archiveName.charAt(0) == '/') { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeChooserDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeChooserDialog.java index 873a022744..1325ee6789 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeChooserDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeChooserDialog.java @@ -33,11 +33,12 @@ import docking.widgets.filter.FilterOptions; import docking.widgets.filter.TextFilterStrategy; import docking.widgets.label.GDLabel; import docking.widgets.tree.*; +import ghidra.app.plugin.core.datamgr.ArchiveManager; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.plugin.core.datamgr.tree.*; import ghidra.app.util.datatype.DataTypeSelectionDialog; import ghidra.program.model.data.*; +import ghidra.program.model.listing.Program; import ghidra.util.HelpLocation; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -124,17 +125,17 @@ public class DataTypeChooserDialog extends DialogComponentProvider { } public void setShowProgramArchiveOnly(boolean programOnly) { - DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler(); + ArchiveManager archiveManager = plugin.getArchiveManager(); if (programOnly) { - DataTypeManager programDtm = handler.getProgramDataTypeManager(); - if (programDtm != null) { - ArchiveRootNode root = new ArchiveRootNode(handler, true); + Program program = archiveManager.getProgram(); + if (program != null) { + ArchiveRootNode root = new ArchiveRootNode(archiveManager, true); tree.setRootNode(root); return; } } - ArchiveRootNode root = new ArchiveRootNode(handler); + ArchiveRootNode root = new ArchiveRootNode(archiveManager); tree.setRootNode(root); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java index 3d86e8836f..8c95a3b93f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java @@ -22,10 +22,10 @@ import java.util.regex.Pattern; import docking.widgets.OptionDialog; import docking.widgets.tree.GTreeNode; import docking.widgets.tree.GTreeState; -import ghidra.app.plugin.core.datamgr.archive.Archive; -import ghidra.app.plugin.core.datamgr.archive.ProgramArchive; import ghidra.app.plugin.core.datamgr.tree.*; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.DataTypeStore; +import ghidra.program.model.listing.Program; import ghidra.util.*; import ghidra.util.exception.CancelledException; import ghidra.util.exception.DuplicateNameException; @@ -51,8 +51,8 @@ public class DataTypeTreeCopyMoveTask extends Task { private DataTypeArchiveGTree gTree; private Category destinationCategory; private List copyMoveNodes; - private Archive sourceArchive; - private Archive destinationArchive; + private DataTypeStore sourceDataTypeStore; + private DataTypeStore destinationDataTypeStore; private boolean promptToAssociateTypes = true; private ActionType actionType; private DataTypeConflictHandler conflictHandler; @@ -66,11 +66,11 @@ public class DataTypeTreeCopyMoveTask extends Task { public DataTypeTreeCopyMoveTask(CategoryNode destinationNode, List droppedNodeList, ActionType actionType, DataTypeArchiveGTree gTree, DataTypeConflictHandler conflictHandler) { - this(findArchive(destinationNode), destinationNode.getCategory(), droppedNodeList, + this(findDataStore(destinationNode), destinationNode.getCategory(), droppedNodeList, actionType, gTree, conflictHandler); } - public DataTypeTreeCopyMoveTask(Archive destinationArchive, Category destinationCategory, + public DataTypeTreeCopyMoveTask(DataTypeStore destination, Category destinationCategory, List droppedNodeList, ActionType actionType, DataTypeArchiveGTree gTree, DataTypeConflictHandler conflictHandler) { super("Drag/Drop", true, true, true); @@ -79,16 +79,16 @@ public class DataTypeTreeCopyMoveTask extends Task { this.actionType = actionType; this.gTree = gTree; this.conflictHandler = conflictHandler; - this.destinationArchive = destinationArchive; + this.destinationDataTypeStore = destination; GTreeNode firstNode = copyMoveNodes.get(0); - this.sourceArchive = findArchive(firstNode); + this.sourceDataTypeStore = findDataStore(firstNode); } - private static Archive findArchive(GTreeNode node) { + private static DataTypeStore findDataStore(GTreeNode node) { while (node != null) { - if (node instanceof ArchiveNode) { - return ((ArchiveNode) node).getArchive(); + if (node instanceof DataTypeStoreNode archiveNode) { + return archiveNode.getDataTypeStore(); } node = node.getParent(); } @@ -178,7 +178,7 @@ public class DataTypeTreeCopyMoveTask extends Task { private boolean checkForDifferentSourceArchives() { for (GTreeNode node : copyMoveNodes) { - if (sourceArchive != findArchive(node)) { + if (sourceDataTypeStore != findDataStore(node)) { Msg.showError(this, gTree, "Copy Failed", "All data types must be from the same archive!"); return true; @@ -189,7 +189,7 @@ public class DataTypeTreeCopyMoveTask extends Task { } private void doCopy(TaskMonitor monitor) { - DataTypeManager dtm = destinationArchive.getDataTypeManager(); + DataTypeManager dtm = destinationDataTypeStore.getDataTypeManager(); int txId = dtm.startTransaction("Copy/Move Category/DataType"); try { copyOrMoveNodesToCategory(monitor); @@ -202,9 +202,9 @@ public class DataTypeTreeCopyMoveTask extends Task { private boolean needToCreateAssociation() { // copying from the program archive into another archive - return sourceArchive != destinationArchive && - !(destinationArchive instanceof ProgramArchive) && - (sourceArchive instanceof ProgramArchive); + return sourceDataTypeStore != destinationDataTypeStore && + !(destinationDataTypeStore instanceof Program) && + (sourceDataTypeStore instanceof Program); } private void collapseArchives() { @@ -223,8 +223,9 @@ public class DataTypeTreeCopyMoveTask extends Task { monitor.initialize(copyMoveNodes.size()); - SourceArchive destination = destinationArchive.getDataTypeManager().getLocalSourceArchive(); - DataTypeManager dtm = sourceArchive.getDataTypeManager(); + SourceArchive destination = + destinationDataTypeStore.getDataTypeManager().getLocalSourceArchive(); + DataTypeManager dtm = sourceDataTypeStore.getDataTypeManager(); int txId = dtm.startTransaction("Associate Data Types"); try { for (GTreeNode node : copyMoveNodes) { @@ -348,7 +349,7 @@ public class DataTypeTreeCopyMoveTask extends Task { monitor.setMessage("Adding " + node.getName()); // COPY is only allowed action if the source and destination archives are different. - if (actionType == ActionType.COPY || sourceArchive != destinationArchive) { + if (actionType == ActionType.COPY || sourceDataTypeStore != destinationDataTypeStore) { copyNode(toCategory, node, monitor); } else { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeDeleteTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeDeleteTask.java index f9441689ad..eebab1cea8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeDeleteTask.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeDeleteTask.java @@ -22,7 +22,6 @@ import docking.widgets.tree.GTreeNode; import docking.widgets.tree.GTreeState; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import ghidra.app.plugin.core.datamgr.DataTypesProvider; -import ghidra.app.plugin.core.datamgr.archive.Archive; import ghidra.app.plugin.core.datamgr.tree.*; import ghidra.program.model.data.*; import ghidra.program.model.listing.Program; @@ -36,7 +35,7 @@ public class DataTypeTreeDeleteTask extends Task { // if the total number of nodes is small, we won't need to collapse the tree before deleting // the nodes to avoid excess tree events private static final int NODE_COUNT_FOR_COLLAPSING_TREE = 100; - private Map> nodesByArchive; + private Map> nodesByArchive; private DataTypeManagerPlugin plugin; private int nodeCount; @@ -51,11 +50,11 @@ public class DataTypeTreeDeleteTask extends Task { nodesByArchive = groupNodeByArchive(nodes); } - private Map> groupNodeByArchive(List nodes) { + private Map> groupNodeByArchive(List nodes) { - Map> archiveNodeMap = new HashMap<>(); + Map> archiveNodeMap = new HashMap<>(); for (GTreeNode node : nodes) { - ArchiveNode archiveNode = ((DataTypeTreeNode) node).getArchiveNode(); + DataTypeStoreNode archiveNode = ((DataTypeTreeNode) node).getArchiveNode(); List archiveNodeList = archiveNodeMap.computeIfAbsent(archiveNode, n -> new ArrayList<>()); @@ -128,10 +127,10 @@ public class DataTypeTreeDeleteTask extends Task { collapseArchives(tree); } - Set>> entries = nodesByArchive.entrySet(); - for (Entry> entry : entries) { + Set>> entries = nodesByArchive.entrySet(); + for (Entry> entry : entries) { List list = entry.getValue(); - ArchiveNode node = entry.getKey(); + DataTypeStoreNode node = entry.getKey(); deleteNodes(node, list, monitor); } } @@ -160,11 +159,10 @@ public class DataTypeTreeDeleteTask extends Task { } } - private void deleteNodes(ArchiveNode archiveNode, List list, TaskMonitor monitor) + private void deleteNodes(DataTypeStoreNode archiveNode, List list, + TaskMonitor monitor) throws CancelledException { - - Archive archive = archiveNode.getArchive(); - DataTypeManager dtm = archive.getDataTypeManager(); + DataTypeManager dtm = archiveNode.getDataTypeManager(); dtm.withTransaction("Delete Category/DataType", () -> { for (GTreeNode node : list) { monitor.checkCancelled(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtils.java index 3f5af5ec9f..8a02d5b13b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtils.java @@ -28,6 +28,7 @@ import ghidra.program.model.address.Address; import ghidra.program.model.data.*; import ghidra.program.model.data.Composite; import ghidra.program.model.data.Enum; +import ghidra.program.model.dtarchive.*; import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Program; import ghidra.util.Msg; @@ -433,31 +434,28 @@ public class DataTypeUtils { public static void showUnmodifiableArchiveErrorMessage(Component parent, String title, DataTypeManager dtm) { - String msg; - if (dtm instanceof ProgramBasedDataTypeManager) { - msg = "The Program is not modifiable!\n"; - } - else if (dtm instanceof FileArchiveBasedDataTypeManager) { - msg = "The archive file is not modifiable!\nYou must open the archive for editing\n" + - "before performing this operation.\n" + dtm.getName(); - } - else if (dtm instanceof ProjectArchiveBasedDataTypeManager) { - ProjectArchiveBasedDataTypeManager projectDtm = - (ProjectArchiveBasedDataTypeManager) dtm; - if (!projectDtm.isUpdatable() && !projectDtm.getDomainFile().canCheckout()) { - msg = "The project archive is not modifiable!\n" + dtm.getName(); - } - else { - msg = "The project archive is not modifiable!\nYou must check out the archive\n" + - "before performing this operation.\n" + dtm.getName(); - } - } - else { - msg = "The Archive is not modifiable!\n"; - } + String msg = getUnmodifiableArchiveMessage(dtm.getDataStore()); Msg.showInfo(DataTypeUtils.class, parent, title, msg); } + private static String getUnmodifiableArchiveMessage(DataTypeStore dataStore) { + if (dataStore instanceof Program) { + return "The Program is not modifiable!\n"; + } + else if (dataStore instanceof FileDataTypeArchive) { + return "The archive file is not modifiable!\nYou must open the archive for editing\n" + + "before performing this operation.\n" + dataStore.getName(); + } + else if (dataStore instanceof ProjectDataTypeArchive projectArchive) { + if (!projectArchive.isUpdatable() && !projectArchive.getDomainFile().canCheckout()) { + return "The project archive is not modifiable!\n" + projectArchive.getName(); + } + return "The project archive is not modifiable!\nYou must check out the archive\n" + + "before performing this operation.\n" + projectArchive.getName(); + } + return "The Archive is not modifiable!\n"; + } + public static int binarySearchWithDuplicates(List data, String searchItem, Comparator comparator) { int index = Collections.binarySearch(data, searchItem, comparator); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java index ffc179a131..7ebebfb25f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -38,6 +38,7 @@ import ghidra.app.util.datatype.DataTypeSelectionDialog; import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.util.PluginStatus; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; import ghidra.program.model.listing.Program; @@ -705,7 +706,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin { DataOrganization dataOrg = (activeProgram != null) ? activeProgram.getCompilerSpec().getDataOrganization() : DataOrganizationImpl.getDefaultOrganization(); - return new StandAloneDataTypeManager(ROOT_NAME, dataOrg); + return new TransientDataTypeManager(ROOT_NAME, dataOrg); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorModel.java index 7d9585ca1e..ff787fb606 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorModel.java @@ -125,7 +125,7 @@ public class StackEditorModel extends CompositeEditorModel { } // Establish editor's datatype manager which will manage datatype dependencies. - viewDTM = new CompositeViewerDataTypeManager<>(originalDTM.getName(), originalDTM); + viewDTM = CompositeViewerDataTypeManager.createNonUndoableInstance(originalDTM); // Create a copy of the original stack frame datatype and force the resolving of its // datatype dependencies. A round-about approach is used since the StackFrameDataType diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataTypeArchiveService.java b/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataTypeArchiveService.java index f6969e1b02..6cfca0a290 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataTypeArchiveService.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataTypeArchiveService.java @@ -15,17 +15,16 @@ */ package ghidra.app.services; -import java.io.File; import java.io.IOException; +import java.util.List; import generic.jar.ResourceFile; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.archive.Archive; import ghidra.app.plugin.core.datamgr.archive.DuplicateIdException; import ghidra.framework.model.DomainFile; import ghidra.framework.plugintool.ServiceInfo; import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.listing.DataTypeArchive; +import ghidra.program.model.dtarchive.*; import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; @@ -48,18 +47,102 @@ public interface DataTypeArchiveService { public DataTypeManager getBuiltInDataTypesManager(); /** - * Gets the open data type managers. + * Gets the open data type managers. (Includes DataTypeManagers from the built-in, program and + * all open archives in the tool.) * * @return the open data type managers. */ public DataTypeManager[] getDataTypeManagers(); /** - * Closes the archive for the given {@link DataTypeManager}. This will ignore request to - * close the open Program's manager and the built-in manager. + * {@return a list of all open ProjectDataTypeArchives or FileDataTypeArchives open in the tool} + */ + public List getDataTypeArchives(); + + /** + * Close the given archive in the tool + * @param archive The DataTypeArchive to close in the tool + */ + public void closeArchive(PersistentDataTypeArchive archive); + + /** + * Opens a file data type archive that was included in the distribution + *

+ * NOTE: This is predicated upon all archive files having a unique name within the installation. + *

+ * Any path prefix specified may prevent the file from opening (or reopening) correctly. + * + * @param archiveName archive file name (i.e., "generic_C_lib") + * @param monitor the TaskMonitor that can be used to cancel the open operation + * @return the data type archive or null if an archive with the specified name + * can not be found. + * @throws IOException if an i/o error occurs opening the data type archive + * @throws VersionException if there is a version exception that can't be upgraded (or the + * user chose not to upgrade to the latest version) + * @throws DuplicateIdException if another archive with the same ID is already open + * @throws CancelledException if the operation was cancelled via the TaskMonitor + */ + public FileDataTypeArchive openFileArchive(String archiveName, TaskMonitor monitor) + throws IOException, VersionException, DuplicateIdException, CancelledException; + + /** + * Opens the specified file data type archive (gdt). + * + * @param file archive file + * @param openForUpdate true if open for update, false for read-only + * @param upgradeStrategy Determines how to deal with a file that is not up to current version. + * if YES, then the archive will be upgraded to the latest version. If No then the file + * will NOT be upgraded to the current version and will throw a VersionException if the file + * is not at the current version. ASK will cause a dialog to popup for the user to choose. In + * headless mode, ASK will be treated as a NO. This option only applies if the openForUpdate + * is true. + * @param monitor the TaskMonitor that can be used to cancel the open operation + * @return the file data type archive + * @throws IOException if an i/o error occurs opening the data type archive + * @throws VersionException if there is a version exception that can't be upgraded (or the + * user chose not to upgrade to the latest version) + * @throws DuplicateIdException if another archive with the same ID is already open + * @throws CancelledException if the operation was cancelled via the TaskMonitor + */ + public FileDataTypeArchive openFileArchive(ResourceFile file, boolean openForUpdate, + Upgrade upgradeStrategy, TaskMonitor monitor) + throws IOException, VersionException, DuplicateIdException, CancelledException; + + /** + * Opens the specified project-located data type archive. + * + * @param domainFile archive file located in the current project + * @param upgradeStrategy Determines how to deal with a file that is not up to current version. + * if YES, then the archive will be upgraded to the latest version. If No then the file + * will NOT be upgraded to the current version and will throw a VersionException if the file + * is not at the current version. ASK will cause a dialog to popup for the user to choose. In + * headless mode, ASK will be treated as a NO. This option only applies if the openForUpdate + * is true. + * @param recoverStrategy Determines how to deal with a file that has crash data (changes that + * were not saved because Ghidra crashed (or was externally terminated). + * if YES, then the recover crash data will be applied. If No then the crash data will be + * ignored. ASK will cause a dialog to popup for the user to choose. In + * headless mode, ASK will be treated as a NO. + * @param monitor the TaskMonitor that can be used to cancel the open operation + * @return the data type archive + * @throws IOException if an i/o error occurs opening the data type archive + * @throws DuplicateIdException if another archive with the same ID is already open + * @throws VersionException if there is a version exception that can't be upgraded (or the + * user chose not to upgrade to the latest version) + * @throws CancelledException if the user cancels + */ + public ProjectDataTypeArchive openProjectArchive(DomainFile domainFile, Upgrade upgradeStrategy, + Recover recoverStrategy, TaskMonitor monitor) + throws VersionException, CancelledException, IOException, DuplicateIdException; + + /** + * Closes the archive for the given {@link DataTypeManager}. This will ignore any request to + * close the open Program's manager or the built-in manager. * * @param dtm the data type manager of the archive to close + * @deprecated use {@link #closeArchive(PersistentDataTypeArchive)} instead */ + @Deprecated(forRemoval = true, since = "12.2") public void closeArchive(DataTypeManager dtm); /** @@ -70,61 +153,73 @@ public interface DataTypeArchiveService { * Any path prefix specified may prevent the file from opening (or reopening) correctly. * * @param archiveName archive file name (i.e., "generic_C_lib") - * @return the data type archive or null if an archive with the specified name + * @return the data type manager or null if an archive with the specified name * can not be found. * @throws IOException if an i/o error occurs opening the data type archive * @throws DuplicateIdException if another archive with the same ID is already open + * @deprecated use {@link #openFileArchive(String, TaskMonitor)} instead */ - public DataTypeManager openDataTypeArchive(String archiveName) - throws IOException, DuplicateIdException; + @Deprecated(forRemoval = true, since = "12.2") + public default DataTypeManager openDataTypeArchive(String archiveName) + throws IOException, DuplicateIdException { + FileDataTypeArchive archive; + try { + archive = openFileArchive(archiveName, TaskMonitor.DUMMY); + return archive == null ? null : archive.getDataTypeManager(); + } + catch (CancelledException e) { + // can't happen because using dummy monitor + } + catch (VersionException e) { + throw new IOException(e); // legacy didn't declare version exception + } + return null; + } /** * Opens the specified gdt (file based) data type archive. * - * @param file gdt file - * @param acquireWriteLock true if write lock should be acquired (i.e., open for update) + * @param file archive file + * @param openForUpdate true if open for update, false for read-only * @return the data type archive * @throws IOException if an i/o error occurs opening the data type archive * @throws DuplicateIdException if another archive with the same ID is already open + * @deprecated use {@link #openFileArchive(ResourceFile, boolean, Upgrade, TaskMonitor)} instead */ - public DataTypeManager openArchive(ResourceFile file, boolean acquireWriteLock) - throws IOException, DuplicateIdException; + @Deprecated(forRemoval = true, since = "12.2") + public default DataTypeManager openArchive(ResourceFile file, boolean openForUpdate) + throws IOException, DuplicateIdException { + FileDataTypeArchive archive; + try { + archive = openFileArchive(file, openForUpdate, Upgrade.NO, TaskMonitor.DUMMY); + return archive == null ? null : archive.getDataTypeManager(); + } + catch (CancelledException e) { + // can't happen because using dummy monitor + } + catch (VersionException e) { + throw new IOException(e); // legacy didn't declare version exception + } + return null; + } /** - * Opens the specified project-located data type archive. - * - * @param domainFile archive file located in the current project - * @param monitor {@link TaskMonitor} to display progress during the opening - * @return the data type archive - * @throws IOException if an i/o error occurs opening the data type archive - * @throws DuplicateIdException if another archive with the same ID is already open - * @throws VersionException if there is a version exception - * @throws CancelledException if the user cancels - */ - public DataTypeManager openArchive(DomainFile domainFile, TaskMonitor monitor) - throws VersionException, CancelledException, IOException, DuplicateIdException; - - /** - * A method to open an Archive for the given, pre-existing DataTypeArchive (like one that - * was opened during the import process. - * - * @param dataTypeArchive the archive from which to create an Archive - * @return an Archive based upon the given DataTypeArchive - */ - @Deprecated - public Archive openArchive(DataTypeArchive dataTypeArchive); - - /** - * A method to open an Archive for the given, pre-existing archive file (*.gdt) - * - * @param file data type archive file - * @param acquireWriteLock true if write lock should be acquired (i.e., open for update) - * @return an Archive based upon the given archive files - * @throws IOException if an i/o error occurs opening the data type archive - * @throws DuplicateIdException if another archive with the same ID is already open - */ - @Deprecated - public Archive openArchive(File file, boolean acquireWriteLock) - throws IOException, DuplicateIdException; - + * Opens the specified project-located data type archive. + * + * @param domainFile archive file located in the current project + * @param monitor {@link TaskMonitor} to display progress during the opening + * @return the data type archive + * @throws IOException if an i/o error occurs opening the data type archive + * @throws DuplicateIdException if another archive with the same ID is already open + * @throws VersionException if there is a version exception + * @throws CancelledException if the user cancels + * @deprecated use {@link #openProjectArchive(DomainFile, Upgrade, Recover, TaskMonitor)} instead + */ + @Deprecated(forRemoval = true, since = "12.2") + public default DataTypeManager openArchive(DomainFile domainFile, TaskMonitor monitor) + throws VersionException, CancelledException, IOException, DuplicateIdException { + ProjectDataTypeArchive archive = + openProjectArchive(domainFile, Upgrade.NO, Recover.NO, monitor); + return archive == null ? null : archive.getDataTypeManager(); + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileArchiveBasedDataTypeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/services/Recover.java similarity index 55% rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileArchiveBasedDataTypeManager.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/services/Recover.java index 11ab160b4d..284334934b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileArchiveBasedDataTypeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/services/Recover.java @@ -1,24 +1,26 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.program.model.data; +package ghidra.app.services; /** - * Extends DataTypeManager to provide methods specific to file data type archives (.gdt). + * Enum for the DataTypeArchiveService to specify that when opening a ProjectDataTypeArchive if the + * archive should recover any unsaved changes from a crashed session. */ -public interface FileArchiveBasedDataTypeManager extends FileBasedDataTypeManager { - // TODO +public enum Recover { + YES, // The open archive operation will automatically recover any crash data + NO, // The open archive operation will ignore any recovery crash data + ASK // The open archive operation will prompt the user if recovery crash data exists } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/services/Upgrade.java b/Ghidra/Features/Base/src/main/java/ghidra/app/services/Upgrade.java new file mode 100644 index 0000000000..9b3d0bbfc0 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/services/Upgrade.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.services; + +/** + * Enum for the DataTypeArchiveService to specify that when opening a ProjectDataTypeArchive if the + * archive should upgrade to the latest version (Only relevant if opening for update) + */ +public enum Upgrade { + YES, // will try to upgrade to the latest version if opened for update and not at latest version + NO, // will not upgrade to the latest version and will fail instead if not at latest version + ASK // will prompt the user if the archive can be upgraded to the latest version +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java index 15ed684f15..90c9d7c8c1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,10 +22,14 @@ import generic.jar.ResourceFile; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.MemoryByteProvider; import ghidra.app.util.bin.format.dwarf.DWARFDataTypeConflictHandler; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.program.model.listing.Program; import ghidra.util.DataConverter; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; /** @@ -72,6 +76,7 @@ import ghidra.util.task.TaskMonitor; public class DataTypeMapper implements AutoCloseable { protected Program program; protected DataTypeManager programDTM; + protected FileDataTypeArchive archive; protected DataTypeManager archiveDTM; protected List programSearchCPs = new ArrayList<>(); protected List archiveSearchCPs = new ArrayList<>(); @@ -88,15 +93,26 @@ public class DataTypeMapper implements AutoCloseable { protected DataTypeMapper(Program program, ResourceFile archiveGDT) throws IOException { this.program = program; this.programDTM = program.getDataTypeManager(); - this.archiveDTM = - archiveGDT != null ? FileDataTypeManager.openFileArchive(archiveGDT, false) : null; + try { + this.archive = + archiveGDT != null + ? DataTypeArchiveFactory.openReadOnly(archiveGDT, this, TaskMonitor.DUMMY) + : null; + } + catch (CancelledException e) { + // can't happen since we are using a DUMMY + } + catch (VersionException e) { + throw new IOException(e); + } + archiveDTM = archive != null ? archive.getDataTypeManager() : null; } @Override public void close() { - if (archiveDTM != null) { - archiveDTM.close(); - archiveDTM = null; + if (archive != null) { + archive.release(this); + archive = null; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CParserUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CParserUtils.java index 38ab475ae8..cd2861fc5c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CParserUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CParserUtils.java @@ -24,9 +24,13 @@ import ghidra.app.services.DataTypeManagerService; import ghidra.app.util.cparser.CPP.PreProcessor; import ghidra.framework.Application; import ghidra.framework.plugintool.ServiceProvider; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.DataTypeStore; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.program.model.listing.Program; import ghidra.util.*; +import ghidra.util.exception.CancelledException; import ghidra.util.exception.DuplicateNameException; import ghidra.util.task.TaskMonitor; @@ -240,7 +244,48 @@ public class CParserUtils { /** * Parse a set of C Header files and associated parsing arguments, returning a new File - * Data TypeManager with in the provided dataFileName. The resulting archive + * Data TypeManager with the provided dataFileName. The resulting archive + * will not be associated with a specific language/compiler and will use the default + * data organization. + * + * Note: Using another open archive while parsing will cause: + * - a dependence on the other archive + * - any missing data types while parsing are supplied if present from an openDTMgr + * - after parsing all data types parsed with an equivalent data type in any openDTMgr + * replaced by the data type from the openDTMgr + * + * NOTE: This will only occur if the data type from the openDTMgr's is equivalent. + * + * @param openDTMgrs array of datatypes managers to use for undefined data types + * + * @param filenames names of files in order to parse, could include strings with + * "#" at start, which are ignored as comments + * @param args arguments for parsing, {@code -D=, -I} + * + * @param dataFileName name of data type archive file (include the .gdt extension) + * + * @param monitor used to cancel or provide results + * + * @return the data types in the ghidra .gdt archive file + * + * @throws ghidra.app.util.cparser.C.ParseException for catastrophic errors in C parsing + * @throws ghidra.app.util.cparser.CPP.ParseException for catastrophic errors in Preprocessor macro parsing + * @throws IOException if there io are errors saving the archive + * + * @deprecated Use {@link #parseHeaderFiles(String[], String[], String[],String, DataTypeManager[], TaskMonitor)} instead. + */ + @Deprecated(since = "12.2", forRemoval = true) + public static FileDataTypeManager parseHeaderFiles(DataTypeManager[] openDTMgrs, + String[] filenames, String[] args, String dataFileName, + TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, + ghidra.app.util.cparser.CPP.ParseException, IOException { + + return parseHeaderFiles(openDTMgrs, filenames, null, args, dataFileName, monitor); + } + + /** + * Parse a set of C Header files and associated parsing arguments, returning a new + * {@link FileDataTypeArchive} with the provided dataFileName. The resulting archive * will not be associated with a specific language/compiler and will use the default * data organization. * @@ -269,18 +314,61 @@ public class CParserUtils { * @throws IOException if there io are errors saving the archive * */ - - public static FileDataTypeManager parseHeaderFiles(DataTypeManager[] openDTMgrs, - String[] filenames, String[] args, String dataFileName, - TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, + public static FileDataTypeArchive parseHeaderFiles(String[] filenames, String[] args, + String dataFileName, DataTypeManager[] openDTMgrs, TaskMonitor monitor) + throws ghidra.app.util.cparser.C.ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { - return parseHeaderFiles(openDTMgrs, filenames, null, args, dataFileName, monitor); + return parseHeaderFiles(filenames, null, args, dataFileName, openDTMgrs, monitor); } /** * Parse a set of C Header files and associated parsing arguments, returning a new - * File Data TypeManager with in the provided dataFileName. The resulting archive + * FileDataTypeManager with the provided dataFileName. The resulting archive + * will not be associated with a specific language/compiler and will use the default + * data organization. + * + * Note: Using another open archive while parsing will cause: + * - a dependence on the other archive + * - any missing data types while parsing are supplied if present from an openDTMgr + * - after parsing all data types parsed with an equivalent data type in any openDTMgr + * replaced by the data type from the openDTMgr + * + * NOTE: This will only occur if the data type from the openDTMgr's is equivalent. + * + * @param openDTMgrs array of datatypes managers to use for undefined data types + * + * @param filenames names of files in order to parse, could include strings with + * "#" at start, which are ignored as comments + * @param includePaths paths to include files, instead of using {@code -I} in args + * @param args arguments for parsing, {@code -D=}, ( {@code -I} use + * includePaths parm instead) + * + * @param dataFileName name of data type archive file (include the .gdt extension) + * + * @param monitor used to cancel or provide results + * + * @return the data types in the ghidra .gdt archive file + * + * @throws ghidra.app.util.cparser.C.ParseException for catastrophic errors in C parsing + * @throws ghidra.app.util.cparser.CPP.ParseException for catastrophic errors in Preprocessor macro parsing + * @throws IOException if there io are errors saving the archive + * + * @deprecated Use {@link #parseHeaderFiles(String[], String[], String[],String, DataTypeManager[], TaskMonitor)} instead. + */ + @Deprecated(since = "12.2", forRemoval = true) + public static FileDataTypeManager parseHeaderFiles(DataTypeManager[] openDTMgrs, + String[] filenames, String[] includePaths, String[] args, String dataFileName, + TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, + ghidra.app.util.cparser.CPP.ParseException, IOException { + + return parseHeaderFiles(openDTMgrs, filenames, includePaths, args, dataFileName, null, null, + monitor); + } + + /** + * Parse a set of C Header files and associated parsing arguments, returning a new + * {@link FileDataTypeArchive} with the provided dataFileName. The resulting archive * will not be associated with a specific language/compiler and will use the default * data organization. * @@ -311,14 +399,12 @@ public class CParserUtils { * @throws IOException if there io are errors saving the archive * */ - - public static FileDataTypeManager parseHeaderFiles(DataTypeManager[] openDTMgrs, - String[] filenames, String[] includePaths, String[] args, String dataFileName, - TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, + public static FileDataTypeArchive parseHeaderFiles(String[] filenames, String[] includePaths, + String[] args, String dataFileName, DataTypeManager[] openDTMgrs, TaskMonitor monitor) + throws ghidra.app.util.cparser.C.ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { - - return parseHeaderFiles(openDTMgrs, filenames, includePaths, args, dataFileName, null, null, - monitor); + return parseHeaderFiles(filenames, includePaths, args, dataFileName, null, null, openDTMgrs, + new Object(), monitor); } /** @@ -357,17 +443,71 @@ public class CParserUtils { * @throws ghidra.app.util.cparser.C.ParseException for catastrophic errors in C parsing * @throws ghidra.app.util.cparser.CPP.ParseException for catastrophic errors in Preprocessor macro parsing * @throws IOException if there io are errors saving the archive - * + * @deprecated Use {@link #parseHeaderFiles(String[], String[], String[], String, String, String, DataTypeManager[], Object,TaskMonitor)} instead. */ + @Deprecated(since = "12.2", forRemoval = true) public static FileDataTypeManager parseHeaderFiles(DataTypeManager[] openDTMgrs, String[] filenames, String[] includePaths, String[] args, String dataFileName, String languageId, String compileSpecId, TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { + FileDataTypeArchive archive = + parseHeaderFiles(filenames, includePaths, args, dataFileName, languageId, compileSpecId, + openDTMgrs, new Object(), monitor); + return (FileDataTypeManager) archive.getDataTypeManager(); + } + + /** + * Parse a set of C Header files and associated parsing arguments, returning a new + * {@link FileDataTypeArchive} with the provided dataFileName. + * + * When parsing is complete any parser messages will be logged. + * + * Note: Using another open archive while parsing will cause: + * - a dependence on the other archive + * - any missing data types while parsing are supplied if present from an openDTMgr + * - after parsing all data types parsed with an equivalent data type in any openDTMgr + * replaced by the data type from the openDTMgr + * + * NOTE: This will only occur if the data type from the openDTMgr's is equivalent. + * + * NOTE: Providing the correct languageId and compileSpecId is very important for header files that might use sizeof() + * + * + * @param filenames names of files in order to parse, could include strings with + * "#" at start, which are ignored as comments + * @param includePaths path to include files, could also be in args with {@code -I} + * @param args arguments for parsing, {@code -D=, -I} + * + * @param dataFileName name of data type archive file (include the .gdt extension) + * + * @param languageId language identification to use for data type organization definitions (int, long, ptr size) + * @param compileSpecId compiler specification to use for parsing + * + * @param openDTMgrs array of datatypes managers to use for undefined data types + * @param consumer the user of the archive. The archive will be closed when all consumers are + * released + * @param monitor used to cancel or provide results + * + * @return the FileDataTypeManager corresponding to the Ghidra .gdt archive file. + * The caller is responsible for closing the instance. + * + * @throws ghidra.app.util.cparser.C.ParseException for catastrophic errors in C parsing + * @throws ghidra.app.util.cparser.CPP.ParseException for catastrophic errors in Preprocessor macro parsing + * @throws IOException if there io are errors saving the archive + * + */ + public static FileDataTypeArchive parseHeaderFiles(String[] filenames, String[] includePaths, + String[] args, String dataFileName, String languageId, String compileSpecId, + DataTypeManager[] openDTMgrs, Object consumer, TaskMonitor monitor) + throws ghidra.app.util.cparser.C.ParseException, + ghidra.app.util.cparser.CPP.ParseException, IOException { + File file = new File(dataFileName); - FileDataTypeManager dtMgr = - FileDataTypeManager.createFileArchive(file, languageId, compileSpecId); + FileDataTypeArchive archive = + DataTypeArchiveFactory.createFileArchive(file, languageId, compileSpecId, consumer); + DataTypeManager dtMgr = archive.getDataTypeManager(); boolean success = false; try { CParseResults results = @@ -376,10 +516,14 @@ public class CParserUtils { String messages = results.getFormattedParseMessage(null); Msg.info(CParserUtils.class, messages); - dtMgr.save(); - + try { + archive.save(null, TaskMonitor.DUMMY); + } + catch (CancelledException e) { + // can't happen since we used a dummy + } success = true; - return dtMgr; + return archive; } finally { if (!success) { @@ -478,8 +622,9 @@ public class CParserUtils { // make a path to tmpdir with name of data type manager String path = new File(Application.getUserTempDirectory(), fName).getAbsolutePath(); // if file data type manager, use path to .gdt file - if (dtMgr instanceof FileDataTypeManager) { - path = ((FileDataTypeManager) dtMgr).getPath(); + DataTypeStore archive = dtMgr.getDataStore(); + if (archive instanceof FileDataTypeArchive fileArchive) { + path = fileArchive.getPath(); } path = path + "_CParser.out"; @@ -567,7 +712,7 @@ public class CParserUtils { parserMessages = cParser.getParseMessages(); } } - + return new CParseResults(cpp, cppMessages, parserMessages, cparseSucceeded); } @@ -750,13 +895,15 @@ public class CParserUtils { File iFile = null; iFile = new File(parent + File.separator + filename); - if (iFile.exists()) + if (iFile.exists()) { return iFile; + } // try just in this directory File sameiFile = new File(parent + File.separator + (new File(filename)).getName()); - if (sameiFile.exists()) + if (sameiFile.exists()) { return sameiFile; + } // try all files in this directory doing to-lower on both input file and output file // if match return it diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/GdtExporter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/GdtExporter.java index 1246f6d6d5..d4c9dc9643 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/GdtExporter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/GdtExporter.java @@ -23,8 +23,8 @@ import ghidra.app.util.DomainObjectService; import ghidra.app.util.Option; import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainObject; -import ghidra.program.database.DataTypeArchiveDB; import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; import ghidra.util.HelpLocation; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -42,7 +42,7 @@ public class GdtExporter extends Exporter { @Override public boolean canExportDomainObject(Class domainObjectClass) { - return DataTypeArchiveDB.class.isAssignableFrom(domainObjectClass); + return ProjectDataTypeArchive.class.isAssignableFrom(domainObjectClass); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/GdtLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/GdtLoader.java index d2892acb97..c66fb1b2cd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/GdtLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/GdtLoader.java @@ -20,19 +20,14 @@ import java.util.*; import org.apache.commons.io.FilenameUtils; -import db.DBHandle; import ghidra.app.util.Option; import ghidra.app.util.bin.ByteProvider; import ghidra.framework.Application; -import ghidra.framework.data.OpenMode; import ghidra.framework.model.DomainObject; -import ghidra.framework.store.db.PackedDatabase; import ghidra.framework.store.local.ItemSerializer; -import ghidra.program.database.DataTypeArchiveContentHandler; -import ghidra.program.database.DataTypeArchiveDB; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.database.dtarchive.*; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; import ghidra.program.model.lang.LanguageNotFoundException; -import ghidra.program.model.listing.DataTypeArchive; import ghidra.program.model.listing.Program; import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; @@ -54,15 +49,16 @@ public class GdtLoader implements Loader { public LoadResults load(ImporterSettings settings) throws IOException, CancelledException, VersionException { - DataTypeArchive dtArchive = loadPackedProgramDatabase(settings.provider(), + ProjectDataTypeArchive dtArchive = loadPackedDtArchiveDatabase(settings.provider(), settings.importName(), settings.consumer(), settings.monitor()); return new LoadResults<>(new Loaded<>(dtArchive, settings)); } - private DataTypeArchive loadPackedProgramDatabase(ByteProvider provider, String programName, + private ProjectDataTypeArchive loadPackedDtArchiveDatabase(ByteProvider provider, + String archiveName, Object consumer, TaskMonitor monitor) throws IOException, CancelledException, VersionException, LanguageNotFoundException { - DataTypeArchive dtArchive; + ProjectDtArchiveDB dtArchive; File file = provider.getFile(); File tmpFile = null; if (file == null) { @@ -70,32 +66,8 @@ public class GdtLoader implements Loader { } try { - PackedDatabase packedDatabase = PackedDatabase.getPackedDatabase(file, true, monitor); - boolean success = false; - DBHandle dbh = null; - try { - if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE - .equals(packedDatabase.getContentType())) { - throw new IOException("File imported is not a Program: " + programName); - } - - monitor.setMessage("Restoring " + provider.getName()); - - dbh = packedDatabase.open(monitor); - dtArchive = new DataTypeArchiveDB(dbh, OpenMode.UPGRADE, monitor, consumer); - success = true; - } - finally { - if (!success) { - if (dbh != null) { - dbh.close(); // also disposes packed database object - } - else { - packedDatabase.dispose(); - } - } - } - return dtArchive; + return DataTypeArchiveFactory.importProjectArchive(file, archiveName, consumer, + monitor); } finally { if (tmpFile != null) { @@ -144,7 +116,7 @@ public class GdtLoader implements Loader { } private static boolean isGDTFile(ByteProvider provider) { - if (!provider.getName().toLowerCase().endsWith(FileDataTypeManager.SUFFIX)) { + if (!provider.getName().toLowerCase().endsWith(FileDtArchiveDB.SUFFIX)) { return false; } boolean isGDT = false; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/framework/main/OpenVersionedFileDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/framework/main/OpenVersionedFileDialog.java index bfef36d641..59dc0ca585 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/framework/main/OpenVersionedFileDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/framework/main/OpenVersionedFileDialog.java @@ -177,6 +177,10 @@ public class OpenVersionedFileDialog extends AbstractDat return super.getDomainFolder(); } + public void showHistory() { + showHistoryPanel(true); + } + protected JPanel buildMainPanel() { historyButton = new JButton("History>>"); historyButton.addActionListener(e -> showHistoryPanel(!historyIsShowing)); @@ -448,4 +452,5 @@ public class OpenVersionedFileDialog extends AbstractDat } } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/database/GhidraDataTypeArchiveMergeManagerFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/program/database/GhidraDataTypeArchiveMergeManagerFactory.java index 415da3f427..6b46a1d314 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/database/GhidraDataTypeArchiveMergeManagerFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/database/GhidraDataTypeArchiveMergeManagerFactory.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,23 +15,19 @@ */ package ghidra.program.database; -import ghidra.app.merge.*; -import ghidra.framework.data.*; -import ghidra.framework.model.*; -import ghidra.program.model.data.*; -import ghidra.program.model.listing.*; +import ghidra.app.merge.DataTypeArchiveMergeManager; +import ghidra.framework.data.DomainObjectMergeManager; +import ghidra.program.database.data.DataTypeArchiveMergeManagerFactory; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; public class GhidraDataTypeArchiveMergeManagerFactory extends DataTypeArchiveMergeManagerFactory { - @Override - protected DomainObjectMergeManager doGetMergeManager( - DomainObject resultsObj, DomainObject sourceObj, - DomainObject originalObj, DomainObject latestObj) { - return new DataTypeArchiveMergeManager( - (DataTypeManagerDomainObject)resultsObj, - (DataTypeManagerDomainObject)sourceObj, - (DataTypeManagerDomainObject)originalObj, - (DataTypeManagerDomainObject)latestObj, - ((DataTypeArchive)latestObj).getChanges(), - ((DataTypeArchive)sourceObj).getChanges()); - } + @Override + protected DomainObjectMergeManager doGetMergeManager( + ProjectDataTypeArchive resultsObj, ProjectDataTypeArchive sourceObj, + ProjectDataTypeArchive originalObj, ProjectDataTypeArchive latestObj) { + + return new DataTypeArchiveMergeManager( + resultsObj, sourceObj, originalObj, latestObj, latestObj.getChanges(), + sourceObj.getChanges()); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/database/GhidraProgramMultiUserMergeManagerFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/program/database/GhidraProgramMultiUserMergeManagerFactory.java index 28f66668af..e569c8aecf 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/database/GhidraProgramMultiUserMergeManagerFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/database/GhidraProgramMultiUserMergeManagerFactory.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,22 +15,16 @@ */ package ghidra.program.database; -import ghidra.app.merge.*; -import ghidra.framework.data.*; -import ghidra.framework.model.*; -import ghidra.program.model.listing.*; +import ghidra.app.merge.ProgramMultiUserMergeManager; +import ghidra.program.model.listing.Program; public class GhidraProgramMultiUserMergeManagerFactory extends ProgramMultiUserMergeManagerFactory { @Override - protected DomainObjectMergeManager doGetMergeManager( - DomainObject resultsObj, DomainObject sourceObj, - DomainObject originalObj, DomainObject latestObj) { - return new ProgramMultiUserMergeManager((Program)resultsObj, - (Program)sourceObj, - (Program)originalObj, - (Program)latestObj, - ((ProgramDB)latestObj).getChanges(), - ((ProgramDB)sourceObj).getChanges()); + protected ProgramMultiUserMergeManager doGetMergeManager( + Program resultsObj, Program sourceObj, + Program originalObj, Program latestObj) { + return new ProgramMultiUserMergeManager(resultsObj, sourceObj, originalObj, latestObj, + latestObj.getChanges(), sourceObj.getChanges()); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/flatapi/FlatProgramAPI.java b/Ghidra/Features/Base/src/main/java/ghidra/program/flatapi/FlatProgramAPI.java index 7a97a642ec..b2f728ecd8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/flatapi/FlatProgramAPI.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/flatapi/FlatProgramAPI.java @@ -41,8 +41,11 @@ import ghidra.features.base.memsearch.searcher.MemorySearcher; import ghidra.framework.main.AppInfo; import ghidra.framework.model.*; import ghidra.framework.plugintool.PluginTool; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.address.*; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.Language; import ghidra.program.model.listing.*; @@ -2547,12 +2550,41 @@ public class FlatProgramAPI { * @param readOnly should file be opened read only * @return the data type manager * @throws Exception if there is any exception + * @deprecated Use {@link #openFileDataTypeArchive(File, boolean)} instead. Then use + * {@link PersistentDataTypeArchive#getDataTypeManager()} */ + @Deprecated(since = "12.2", forRemoval = true) public final FileDataTypeManager openDataTypeArchive(File archiveFile, boolean readOnly) throws Exception { return FileDataTypeManager.openFileArchive(archiveFile, !readOnly); } + /** + * Opens an existing File Data Type Archive. When done using this archive, the script must call + * archive.release(consumer) where consumer is the script that called this method (so typically, + * use "this" (i.e., achive.release(this)) + *

+ * NOTE: If archive has an assigned architecture, issues may arise due to a revised or + * missing {@link Language}/{@link CompilerSpec} which will result in a warning but not + * prevent the archive from being opened. Such a warning condition will be logged and may + * result in missing or stale information for existing datatypes which have architecture related + * data. In some case it may be appropriate to + * {@link FileDataTypeArchive#getWarning() check for warnings} on the returned archive + * object prior to its use. + * + * @param archiveFile the archive file to open + * @param readOnly should file be opened read only + * @return the The file datatype archive + * @throws Exception if there is any exception + */ + public final FileDataTypeArchive openFileDataTypeArchive(File archiveFile, boolean readOnly) + throws Exception { + if (readOnly) { + return DataTypeArchiveFactory.openReadOnly(archiveFile, this, TaskMonitor.DUMMY); + } + return DataTypeArchiveFactory.openForUpdate(archiveFile, true, this, TaskMonitor.DUMMY); + } + /** * Saves the changes to the specified program. * If the program does not already exist in the current project diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/DataTypeCleaner.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/DataTypeCleaner.java index 161f837e71..99f8e2bdff 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/DataTypeCleaner.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/DataTypeCleaner.java @@ -19,8 +19,9 @@ import java.io.Closeable; import java.util.Iterator; import ghidra.program.database.data.DataTypeUtilities; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.program.model.data.*; -import ghidra.program.model.data.StandAloneDataTypeManager.LanguageUpdateOption; +import ghidra.program.model.dtarchive.DataTypeArchive.LanguageUpdateOption; import ghidra.program.model.lang.ProgramArchitecture; import ghidra.util.task.TaskMonitor; @@ -40,7 +41,7 @@ public class DataTypeCleaner implements Closeable { private final DataTypeManager targetDtm; private final boolean retainExistingComposites; - private final StandAloneDataTypeManager cleanerDtm; + private final TransientDataTypeManager cleanerDtm; private int txId; @@ -56,7 +57,7 @@ public class DataTypeCleaner implements Closeable { public DataTypeCleaner(DataTypeManager targetDtm, boolean retainExistingComposites) { this.targetDtm = targetDtm; this.retainExistingComposites = retainExistingComposites; - this.cleanerDtm = new StandAloneDataTypeManager("CleanerDTM"); + cleanerDtm = new TransientDataTypeManager("CleanerDTM"); txId = cleanerDtm.startTransaction("Clean Datatypes"); ProgramArchitecture arch = targetDtm.getProgramArchitecture(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java b/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java index 8099332624..acf7f336b1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java @@ -52,7 +52,7 @@ import ghidra.framework.project.DefaultProjectManager; import ghidra.framework.protocol.ghidra.GhidraURL; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.program.model.lang.*; import ghidra.program.model.listing.Program; import ghidra.program.util.DefaultLanguageService; @@ -689,7 +689,7 @@ public class TestEnv { public static ResourceFile findProvidedDataTypeArchive(String relativePathName) { relativePathName = relativePathName.replace('\\', '/'); - String suffix = FileDataTypeManager.SUFFIX; + String suffix = FileDataTypeArchive.SUFFIX; if (!relativePathName.endsWith(suffix)) { relativePathName = relativePathName + suffix; } @@ -729,7 +729,7 @@ public class TestEnv { } String baseName = relativePathName; - String suffix = FileDataTypeManager.SUFFIX; + String suffix = FileDataTypeArchive.SUFFIX; if (relativePathName.toLowerCase().endsWith(suffix)) { baseName = baseName.substring(0, baseName.length() - suffix.length()); } @@ -1077,7 +1077,7 @@ public class TestEnv { programManager.disposeOpenPrograms(); ProgramBuilder.disposeAllBuilders(); - + if (gp.getProject() == null) { throw new IllegalStateException("The TestEnv's GhidraProject has already been closed!"); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ProcessorEmulatorTestAdapter.java b/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ProcessorEmulatorTestAdapter.java index caa5bec67c..df8809223b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ProcessorEmulatorTestAdapter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ProcessorEmulatorTestAdapter.java @@ -42,10 +42,12 @@ import ghidra.pcode.emu.EmulatorUtilities; import ghidra.pcode.floatformat.FloatFormat; import ghidra.pcode.floatformat.FloatFormatFactory; import ghidra.program.database.ProgramDB; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.disassemble.DisassemblerContextImpl; import ghidra.program.model.address.*; import ghidra.program.model.data.*; -import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; +import ghidra.program.model.dtarchive.ArchiveWarning; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.program.model.lang.*; import ghidra.program.model.listing.*; import ghidra.program.model.listing.Function.FunctionUpdateType; @@ -207,7 +209,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E private Collection applicationRootDirectories; private File resourcesTestDataDir; - private FileDataTypeManager archiveDtMgr; + private FileDataTypeArchive fileArchive; private Structure testInfoStruct; private Structure groupInfoStruct; @@ -967,16 +969,18 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E } ResourceFile emuTestingArchive = Application.getModuleDataFile("pcodetest/EmuTesting.gdt"); - archiveDtMgr = FileDataTypeManager.openFileArchive(emuTestingArchive, false); - assertEquals(ArchiveWarning.NONE, archiveDtMgr.getWarning()); - DataType dt = archiveDtMgr.getDataType(CategoryPath.ROOT, TEST_INFO_STRUCT_NAME); + fileArchive = + DataTypeArchiveFactory.openReadOnly(emuTestingArchive, this, TaskMonitor.DUMMY); + assertEquals(ArchiveWarning.NONE, fileArchive.getWarning()); + DataTypeManager dtm = fileArchive.getDataTypeManager(); + DataType dt = dtm.getDataType(CategoryPath.ROOT, TEST_INFO_STRUCT_NAME); if (dt == null || !(dt instanceof Structure)) { fail(TEST_INFO_STRUCT_NAME + " structure data-type not found in resource EmuTesting.gdt"); } testInfoStruct = (Structure) dt; - dt = archiveDtMgr.getDataType(CategoryPath.ROOT, GROUP_INFO_STRUCT_NAME); + dt = dtm.getDataType(CategoryPath.ROOT, GROUP_INFO_STRUCT_NAME); if (dt == null || !(dt instanceof Structure)) { fail(GROUP_INFO_STRUCT_NAME + " structure data-type not found in resource EmuTesting.gdt"); @@ -1074,8 +1078,8 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E logData.traceLog.close(); logData.traceLog = null; } - if (archiveDtMgr != null) { - archiveDtMgr.close(); + if (fileArchive != null) { + fileArchive.release(this); } if (env != null) { env.dispose(); @@ -1601,7 +1605,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E // Apply known function signatures // Signatures with Float types have been excluded due to limited calling convention support ArrayList dtMgrList = new ArrayList<>(); - dtMgrList.add(archiveDtMgr); + dtMgrList.add(fileArchive.getDataTypeManager()); ApplyFunctionDataTypesCmd cmd = new ApplyFunctionDataTypesCmd(dtMgrList, null, SourceType.ANALYSIS, true, false); cmd.applyTo(program); diff --git a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj index 3b990dbaa8..16d1b49e4a 100644 --- a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj +++ b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj @@ -81,21 +81,22 @@ PARSER_BEGIN(CParser) package ghidra.app.util.cparser.C; -import ghidra.program.database.data.DataTypeUtilities; -import ghidra.program.model.data.*; -import ghidra.program.model.data.Enum; -import ghidra.program.model.lang.CompilerSpec; -import ghidra.util.Msg; -import ghidra.util.task.TaskMonitor; -import ghidra.util.InvalidNameException; -import ghidra.util.exception.DuplicateNameException; -import ghidra.util.exception.InvalidInputException; - import java.io.ByteArrayInputStream; import java.io.InputStream; import java.math.BigInteger; import java.util.*; +import ghidra.program.database.data.DataTypeUtilities; +import ghidra.program.database.data.TransientDataTypeManager; +import ghidra.program.model.data.*; +import ghidra.program.model.data.Enum; +import ghidra.program.model.lang.CompilerSpec; +import ghidra.util.InvalidNameException; +import ghidra.util.Msg; +import ghidra.util.exception.DuplicateNameException; +import ghidra.util.exception.InvalidInputException; +import ghidra.util.task.TaskMonitor; + @SuppressWarnings("all") // ignore warnings from generated code public class CParser { @@ -992,7 +993,7 @@ public class CParser { public CParser() { this(System.in); - dtMgr= new StandAloneDataTypeManager("parsed"); + dtMgr= new TransientDataTypeManager("parsed"); } public CParser(DataTypeManager dtmgr) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/DummyArchiveMergeManager.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/DummyArchiveMergeManager.java index e1415e008a..ac46bd5cbd 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/DummyArchiveMergeManager.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/DummyArchiveMergeManager.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,19 +15,20 @@ */ package ghidra.app.merge; -import ghidra.program.model.data.DataTypeManagerDomainObject; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; import ghidra.program.model.listing.DataTypeArchiveChangeSet; public class DummyArchiveMergeManager extends DataTypeArchiveMergeManager { - public DummyArchiveMergeManager( DataTypeManagerDomainObject resultDtArchive, - DataTypeManagerDomainObject myDtArchive, - DataTypeManagerDomainObject originalDtArchive, - DataTypeManagerDomainObject latestDtArchive, - DataTypeArchiveChangeSet latestChangeSet, - DataTypeArchiveChangeSet myChangeSet) { - - super(resultDtArchive, myDtArchive, originalDtArchive, latestDtArchive, latestChangeSet, myChangeSet); + public DummyArchiveMergeManager(ProjectDataTypeArchive resultDtArchive, + ProjectDataTypeArchive myDtArchive, + ProjectDataTypeArchive originalDtArchive, + ProjectDataTypeArchive latestDtArchive, + DataTypeArchiveChangeSet latestChangeSet, + DataTypeArchiveChangeSet myChangeSet) { + + super(resultDtArchive, myDtArchive, originalDtArchive, latestDtArchive, latestChangeSet, + myChangeSet); } @Override diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/CommonTestData.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/CommonTestData.java index b66d3f9877..0cc80d3702 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/CommonTestData.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/CommonTestData.java @@ -19,36 +19,37 @@ import java.awt.event.KeyEvent; import javax.swing.KeyStroke; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.program.model.data.*; import ghidra.util.InvalidNameException; public class CommonTestData { - public static CycleGroup byteCycleGroup = new CycleGroup( - "Cycle: byte,word,dword,qword", - new DataType[] {new ByteDataType(), - new WordDataType(), - new DWordDataType(), - new QWordDataType()}, - KeyStroke.getKeyStroke(KeyEvent.VK_B, 0)); - public static CycleGroup floatCycleGroup = new CycleGroup( - "Cycle: float,double,longdouble", - new DataType[] {new FloatDataType(), + public static CycleGroup byteCycleGroup = new CycleGroup( + "Cycle: byte,word,dword,qword", + new DataType[] { new ByteDataType(), + new WordDataType(), + new DWordDataType(), + new QWordDataType() }, + KeyStroke.getKeyStroke(KeyEvent.VK_B, 0)); + public static CycleGroup floatCycleGroup = new CycleGroup( + "Cycle: float,double,longdouble", + new DataType[] { new FloatDataType(), new DoubleDataType(), new LongDoubleDataType() }, - KeyStroke.getKeyStroke(KeyEvent.VK_F, 0)); - public static CycleGroup asciiCycleGroup = new CycleGroup( - "Cycle: char, string, unicode", - new DataType[] {new CharDataType(), - new StringDataType(), - new UnicodeDataType()}, - KeyStroke.getKeyStroke(KeyEvent.VK_QUOTE, 0)); - - public static StandAloneDataTypeManager dataTypeManager; - public static Category rootCat; - public static Category category; - public static Category aaCategory; - public static Category bbCategory; + KeyStroke.getKeyStroke(KeyEvent.VK_F, 0)); + public static CycleGroup asciiCycleGroup = new CycleGroup( + "Cycle: char, string, unicode", + new DataType[] { new CharDataType(), + new StringDataType(), + new UnicodeDataType() }, + KeyStroke.getKeyStroke(KeyEvent.VK_QUOTE, 0)); + + public static DataTypeManager dataTypeManager; + public static Category rootCat; + public static Category category; + public static Category aaCategory; + public static Category bbCategory; public static Structure emptyStructure; public static Union emptyUnion = new UnionDataType("emptyUnion"); public static Structure simpleStructure; @@ -58,15 +59,16 @@ public class CommonTestData { public static Union complexUnion; public static Union refUnion; public static TypeDef simpleStructureTypedef; - public static DataType arrayDt; + public static DataType arrayDt; public static int transactionID; - + public static void cleanUp() { dataTypeManager.endTransaction(transactionID, true); dataTypeManager.close(); } - public static void initialize() { - try { + + public static void initialize() { + try { emptyStructure = new StructureDataType("emptyStructure", 0); emptyUnion = new UnionDataType("emptyUnion"); simpleStructure = new StructureDataType("simpleStructure", 0); @@ -76,101 +78,105 @@ public class CommonTestData { complexUnion = new UnionDataType("complexUnion"); refUnion = new UnionDataType("refUnion"); simpleStructureTypedef = new TypedefDataType("simpleStructureTypedef", simpleStructure); - arrayDt = new ArrayDataType(new Pointer32DataType(new Pointer32DataType(simpleStructureTypedef)),3,4); - dataTypeManager = new StandAloneDataTypeManager("test"); - transactionID = dataTypeManager.startTransaction(""); + arrayDt = new ArrayDataType( + new Pointer32DataType(new Pointer32DataType(simpleStructureTypedef)), 3, 4); + dataTypeManager = new TransientDataTypeManager("test"); + transactionID = dataTypeManager.startTransaction(""); rootCat = dataTypeManager.getRootCategory(); - category = rootCat.createCategory("testCat"); - aaCategory = rootCat.createCategory("aa"); - bbCategory = aaCategory.createCategory("bb"); - } catch (InvalidNameException e) { - e.printStackTrace(); - } + category = rootCat.createCategory("testCat"); + aaCategory = rootCat.createCategory("aa"); + bbCategory = aaCategory.createCategory("bb"); + } + catch (InvalidNameException e) { + e.printStackTrace(); + } - emptyStructure = (Structure)rootCat.addDataType(emptyStructure, null); - emptyUnion = (Union)rootCat.addDataType(emptyUnion, null); + emptyStructure = (Structure) rootCat.addDataType(emptyStructure, null); + emptyUnion = (Union) rootCat.addDataType(emptyUnion, null); - simpleStructure.add(DataType.DEFAULT); // component 0 - simpleStructure.add(new ByteDataType()); // component 1 - simpleStructure.add(new WordDataType()); // component 2 - simpleStructure.add(new DWordDataType()); // component 3 - simpleStructure.add(new QWordDataType()); // component 4 - simpleStructure.add(new FloatDataType()); // component 5 - simpleStructure.add(new DoubleDataType()); // component 6 - simpleStructure.add(new CharDataType()); // component 7 - simpleStructure.setDescription("My simple structure."); - simpleStructure = (Structure)bbCategory.addDataType(simpleStructure, null); + simpleStructure.add(DataType.DEFAULT); // component 0 + simpleStructure.add(new ByteDataType()); // component 1 + simpleStructure.add(new WordDataType()); // component 2 + simpleStructure.add(new DWordDataType()); // component 3 + simpleStructure.add(new QWordDataType()); // component 4 + simpleStructure.add(new FloatDataType()); // component 5 + simpleStructure.add(new DoubleDataType()); // component 6 + simpleStructure.add(new CharDataType()); // component 7 + simpleStructure.setDescription("My simple structure."); + simpleStructure = (Structure) bbCategory.addDataType(simpleStructure, null); - simpleUnion.add(new ByteDataType()); - simpleUnion.add(new WordDataType()); - simpleUnion.add(new DWordDataType()); - simpleUnion.add(new QWordDataType()); - simpleUnion.add(new FloatDataType()); - simpleUnion.add(new DoubleDataType()); - simpleUnion.add(new CharDataType()); - simpleUnion.setDescription("My simple union."); - simpleUnion = (Union)bbCategory.addDataType(simpleUnion, null); + simpleUnion.add(new ByteDataType()); + simpleUnion.add(new WordDataType()); + simpleUnion.add(new DWordDataType()); + simpleUnion.add(new QWordDataType()); + simpleUnion.add(new FloatDataType()); + simpleUnion.add(new DoubleDataType()); + simpleUnion.add(new CharDataType()); + simpleUnion.setDescription("My simple union."); + simpleUnion = (Union) bbCategory.addDataType(simpleUnion, null); - complexStructure = (Structure)category.addDataType(complexStructure, null); + complexStructure = (Structure) category.addDataType(complexStructure, null); - refStructure.add(new Pointer32DataType(complexStructure), 4); - refStructure = (Structure)category.addDataType(refStructure, null); + refStructure.add(new Pointer32DataType(complexStructure), 4); + refStructure = (Structure) category.addDataType(refStructure, null); - // Note: complexStructure already has a single Undefined byte. - complexStructure.add(new ByteDataType()); - complexStructure.add(new WordDataType()); - complexStructure.add(new Pointer32DataType(DataType.DEFAULT),4); // Pointer - complexStructure.add(simpleUnion); - complexStructure.add(new Pointer16DataType(new Pointer32DataType()),2); // Pointer to structure - complexStructure.add(new Pointer32DataType(simpleStructure),4); // Pointer to structure - complexStructure.add(new Pointer16DataType(new StringDataType()),2); // Pointer to union - complexStructure.add(new Pointer64DataType(new Pointer32DataType(new ByteDataType())),8); // Pointer to union - complexStructure.add(new Pointer8DataType(simpleUnion),1); // Pointer to union - complexStructure.add(new Pointer32DataType(complexStructure),4); // Pointer to this structure - complexStructure.add(DataType.DEFAULT); - complexStructure.add(DataType.DEFAULT); - complexStructure.add(DataType.DEFAULT); - complexStructure.add(new ArrayDataType(new ByteDataType(), 7, 1)); // Basic Array - complexStructure.add(new ArrayDataType(new StringDataType(), 5, 9)); // Variable array - complexStructure.add(new ArrayDataType(simpleStructure, 3, simpleStructure.getLength())); // structure array - complexStructure.add(new ArrayDataType(new Pointer64DataType(simpleStructure), 7, 8)); // structure pointer array - complexStructure.add(new TypedefDataType("FloatTypedef", new FloatDataType())); // typedef of a basic data type. - TypeDef tempSimpleStructureTypedef = new TypedefDataType("simpleStructureTypedef", simpleStructure); - complexStructure.add(tempSimpleStructureTypedef); // typedef of a variable data type. - DataType tempArrayDt = new ArrayDataType(new Pointer32DataType(new Pointer32DataType(tempSimpleStructureTypedef)), 3, 4); - complexStructure.add(new ArrayDataType(tempArrayDt, 2, tempArrayDt.getLength())); // Pointer to a structure containing pointer to this structure. - complexStructure.add(simpleStructure); - complexStructure.add(new Pointer32DataType(refStructure), 4); // Pointer to structure containing pointer to this structure. + // Note: complexStructure already has a single Undefined byte. + complexStructure.add(new ByteDataType()); + complexStructure.add(new WordDataType()); + complexStructure.add(new Pointer32DataType(DataType.DEFAULT), 4); // Pointer + complexStructure.add(simpleUnion); + complexStructure.add(new Pointer16DataType(new Pointer32DataType()), 2); // Pointer to structure + complexStructure.add(new Pointer32DataType(simpleStructure), 4); // Pointer to structure + complexStructure.add(new Pointer16DataType(new StringDataType()), 2); // Pointer to union + complexStructure.add(new Pointer64DataType(new Pointer32DataType(new ByteDataType())), 8); // Pointer to union + complexStructure.add(new Pointer8DataType(simpleUnion), 1); // Pointer to union + complexStructure.add(new Pointer32DataType(complexStructure), 4); // Pointer to this structure + complexStructure.add(DataType.DEFAULT); + complexStructure.add(DataType.DEFAULT); + complexStructure.add(DataType.DEFAULT); + complexStructure.add(new ArrayDataType(new ByteDataType(), 7, 1)); // Basic Array + complexStructure.add(new ArrayDataType(new StringDataType(), 5, 9)); // Variable array + complexStructure.add(new ArrayDataType(simpleStructure, 3, simpleStructure.getLength())); // structure array + complexStructure.add(new ArrayDataType(new Pointer64DataType(simpleStructure), 7, 8)); // structure pointer array + complexStructure.add(new TypedefDataType("FloatTypedef", new FloatDataType())); // typedef of a basic data type. + TypeDef tempSimpleStructureTypedef = + new TypedefDataType("simpleStructureTypedef", simpleStructure); + complexStructure.add(tempSimpleStructureTypedef); // typedef of a variable data type. + DataType tempArrayDt = new ArrayDataType( + new Pointer32DataType(new Pointer32DataType(tempSimpleStructureTypedef)), 3, 4); + complexStructure.add(new ArrayDataType(tempArrayDt, 2, tempArrayDt.getLength())); // Pointer to a structure containing pointer to this structure. + complexStructure.add(simpleStructure); + complexStructure.add(new Pointer32DataType(refStructure), 4); // Pointer to structure containing pointer to this structure. - complexStructure.setDescription("A complex structure."); - complexUnion = (Union)category.addDataType(complexUnion, null); + complexStructure.setDescription("A complex structure."); + complexUnion = (Union) category.addDataType(complexUnion, null); - refUnion.add(new Pointer32DataType(complexUnion), 4); - refUnion = (Union)category.addDataType(refUnion, null); + refUnion.add(new Pointer32DataType(complexUnion), 4); + refUnion = (Union) category.addDataType(refUnion, null); + + complexUnion.add(new ByteDataType()); + complexUnion.add(new WordDataType()); + complexUnion.add(new Pointer32DataType(DataType.DEFAULT), 4); // Pointer + complexUnion.add(simpleUnion); + complexUnion.add(new Pointer16DataType(new Pointer32DataType()), 2); // Pointer to structure + complexUnion.add(new Pointer32DataType(simpleStructure), 4); // Pointer to structure + complexUnion.add(new Pointer16DataType(new StringDataType()), 2); // Pointer to union + complexUnion.add(new Pointer64DataType(new Pointer32DataType(new ByteDataType())), 8); // Pointer to union + complexUnion.add(new Pointer8DataType(simpleUnion), 1); // Pointer to union + complexUnion.add(new Pointer32DataType(complexStructure), 4); // Pointer to this structure + complexUnion.add(new ArrayDataType(new ByteDataType(), 7, 1)); // Basic Array + complexUnion.add(new ArrayDataType(new StringDataType(), 5, 9)); // Variable array + complexUnion.add(new ArrayDataType(simpleStructure, 3, simpleStructure.getLength())); // structure array + complexUnion.add(new ArrayDataType(new Pointer64DataType(simpleStructure), 7, 8)); // structure pointer array + complexUnion.add(new TypedefDataType("FloatTypedef", new FloatDataType())); // typedef of a basic data type. + complexUnion.add(tempSimpleStructureTypedef); // typedef of a variable data type. + TypeDef simpleUnionTypedef = new TypedefDataType("simpleUnionTypedef", simpleUnion); + complexUnion.add(simpleUnionTypedef); // typedef of a variable data type. + complexUnion.add(new ArrayDataType(tempArrayDt, 2, tempArrayDt.getLength())); // Pointer to a structure containing pointer to this structure. + complexUnion.add(new Pointer32DataType(refStructure), 4); // Pointer to structure containing pointer to this structure. + complexUnion.add(new Pointer32DataType(refUnion), 4); // Pointer to union containing pointer to this union. + complexUnion.add(simpleStructure); + complexUnion.setDescription("A complex union."); + } - complexUnion.add(new ByteDataType()); - complexUnion.add(new WordDataType()); - complexUnion.add(new Pointer32DataType(DataType.DEFAULT),4); // Pointer - complexUnion.add(simpleUnion); - complexUnion.add(new Pointer16DataType(new Pointer32DataType()),2); // Pointer to structure - complexUnion.add(new Pointer32DataType(simpleStructure),4); // Pointer to structure - complexUnion.add(new Pointer16DataType(new StringDataType()),2); // Pointer to union - complexUnion.add(new Pointer64DataType(new Pointer32DataType(new ByteDataType())), 8); // Pointer to union - complexUnion.add(new Pointer8DataType(simpleUnion),1); // Pointer to union - complexUnion.add(new Pointer32DataType(complexStructure),4); // Pointer to this structure - complexUnion.add(new ArrayDataType(new ByteDataType(), 7, 1)); // Basic Array - complexUnion.add(new ArrayDataType(new StringDataType(), 5, 9)); // Variable array - complexUnion.add(new ArrayDataType(simpleStructure, 3, simpleStructure.getLength())); // structure array - complexUnion.add(new ArrayDataType(new Pointer64DataType(simpleStructure), 7, 8)); // structure pointer array - complexUnion.add(new TypedefDataType("FloatTypedef", new FloatDataType())); // typedef of a basic data type. - complexUnion.add(tempSimpleStructureTypedef); // typedef of a variable data type. - TypeDef simpleUnionTypedef = new TypedefDataType("simpleUnionTypedef", simpleUnion); - complexUnion.add(simpleUnionTypedef); // typedef of a variable data type. - complexUnion.add(new ArrayDataType(tempArrayDt, 2, tempArrayDt.getLength())); // Pointer to a structure containing pointer to this structure. - complexUnion.add(new Pointer32DataType(refStructure),4); // Pointer to structure containing pointer to this structure. - complexUnion.add(new Pointer32DataType(refUnion),4); // Pointer to union containing pointer to this union. - complexUnion.add(simpleStructure); - complexUnion.setDescription("A complex union."); - } - } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorArchiveTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorArchiveTest.java index 4fbc669d46..48a0700f00 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorArchiveTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorArchiveTest.java @@ -133,7 +133,7 @@ public class StructureEditorArchiveTest extends AbstractStructureEditorTest { invoke(manageDts); createNewArchive("New Archive.gdt", true); - openForEditing = getAction(plugin, "Lock Archive"); + openForEditing = getAction(plugin, "Open Archive For Editing"); // checkOut = getDockingAction(plugin, "Check Out"); // openArchive = getDockingAction(plugin, "Open Data Type Archive"); closeArchive = getAction(plugin, "Close Archive"); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogParsingAndPromptsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogParsingAndPromptsTest.java index 9f9b8d35db..524c8ba8c5 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogParsingAndPromptsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/cparser/ParseDialogParsingAndPromptsTest.java @@ -35,10 +35,13 @@ import ghidra.app.plugin.core.navigation.GoToAddressLabelPlugin; import ghidra.app.plugin.core.processors.SetLanguageDialog; import ghidra.framework.plugintool.PluginTool; import ghidra.plugin.importer.NewLanguagePanel; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.program.model.lang.*; import ghidra.program.model.listing.Program; import ghidra.test.*; +import ghidra.util.task.TaskMonitor; import utilities.util.FileUtilities; public class ParseDialogParsingAndPromptsTest extends AbstractGhidraHeadedIntegrationTest { @@ -439,16 +442,16 @@ public class ParseDialogParsingAndPromptsTest extends AbstractGhidraHeadedIntegr waitForBusyTool(tool); - // open the file archive - FileDataTypeManager fileArchive = - FileDataTypeManager.openFileArchive(GDTarchiveFile, false); + // open the file archive + FileDataTypeArchive archive = + DataTypeArchiveFactory.openReadOnly(GDTarchiveFile, this, TaskMonitor.DUMMY); + DataTypeManager dtm = archive.getDataTypeManager(); try { - DataType dataType = - fileArchive.getDataType("/" + dummyHeader.getName() + "/" + "mystruct"); + DataType dataType = dtm.getDataType("/" + dummyHeader.getName() + "/" + "mystruct"); assertNotNull("mystruct parsed into program", dataType); } finally { - fileArchive.close(); + archive.release(this); } } @@ -511,11 +514,11 @@ public class ParseDialogParsingAndPromptsTest extends AbstractGhidraHeadedIntegr pressButtonByText(waitForDialogComponent("C-Parse Completed"), "OK", false); // open the file archive - FileDataTypeManager fileArchive = - FileDataTypeManager.openFileArchive(GDTarchiveFile, false); + FileDataTypeArchive archive = + DataTypeArchiveFactory.openReadOnly(GDTarchiveFile, this, TaskMonitor.DUMMY); + DataTypeManager dtm = archive.getDataTypeManager(); try { - DataType dataType = - fileArchive.getDataType("/" + dummyHeader.getName() + "/" + "mystruct"); + DataType dataType = dtm.getDataType("/" + dummyHeader.getName() + "/" + "mystruct"); assertNotNull("mystruct parsed into program", dataType); @@ -525,7 +528,7 @@ public class ParseDialogParsingAndPromptsTest extends AbstractGhidraHeadedIntegr assertEquals(component.getDataType().getName(), "wint_t"); } finally { - fileArchive.close(); + archive.release(this); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/ApplyDataTypeToBrowserTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/ApplyDataTypeToBrowserTest.java index f36c852d09..8edf3b418e 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/ApplyDataTypeToBrowserTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/ApplyDataTypeToBrowserTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.*; import javax.swing.*; import org.apache.commons.lang3.StringUtils; +import org.hamcrest.MatcherAssert; import org.junit.*; import docking.*; @@ -63,7 +64,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT private ConflictHandlerModesAction conflictHandlerModesAction; private DataTypeArchiveGTree tree; private ArchiveRootNode archiveRootNode; - private ArchiveNode programNode; + private DataTypeStoreNode programNode; private CodeViewerProvider codeViewerProvider; @Before @@ -92,7 +93,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT tree = provider.getGTree(); waitForTree(); archiveRootNode = (ArchiveRootNode) tree.getViewRoot(); - programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME); + programNode = (DataTypeStoreNode) archiveRootNode.getChild(PROGRAM_FILENAME); assertNotNull("Did not successfully wait for the program node to load", programNode); tool.showComponentProvider(provider, true); @@ -738,7 +739,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT Object rootNode = TestUtils.invokeInstanceMethod("getRootNode", windowManager); StatusBar statusBar = (StatusBar) TestUtils.getInstanceField("statusBar", rootNode); String actualMessage = runSwing(() -> statusBar.getStatusText()); - assertThat("The tool's status text was not set", actualMessage, + MatcherAssert.assertThat("The tool's status text was not set", actualMessage, containsString(expectedMessage)); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/AbstractCreateArchiveTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/AbstractCreateArchiveTest.java index 7d62186b67..b2ef1a8240 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/AbstractCreateArchiveTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/AbstractCreateArchiveTest.java @@ -37,7 +37,8 @@ import ghidra.framework.plugintool.PluginTool; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; import ghidra.program.model.data.*; -import ghidra.program.model.data.StandAloneDataTypeManager.LanguageUpdateOption; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.program.model.dtarchive.DataTypeArchive.LanguageUpdateOption; import ghidra.program.model.lang.*; import ghidra.test.*; import ghidra.util.InvalidNameException; @@ -95,7 +96,7 @@ public abstract class AbstractCreateArchiveTest extends AbstractGhidraHeadedInte waitForTree(); } - protected DataType resolveDataType(StandAloneDataTypeManager archiveDtm, DataType dt) + protected DataType resolveDataType(DataTypeManager archiveDtm, DataType dt) throws Exception { int id = archiveDtm.startTransaction("resolve datatype"); try { @@ -107,29 +108,29 @@ public abstract class AbstractCreateArchiveTest extends AbstractGhidraHeadedInte } } - protected void setArchitecture(StandAloneDataTypeManager archiveDtm, String languageId, + protected void setArchitecture(PersistentDataTypeArchive archive, String languageId, String compilerSpecId) throws Exception { LanguageService languageService = getLanguageService(); Language language = languageService.getLanguage(new LanguageID(languageId)); - int id = archiveDtm.startTransaction("set architecture"); + int id = archive.startTransaction("set architecture"); try { - archiveDtm.setProgramArchitecture(language, new CompilerSpecID(compilerSpecId), + archive.setProgramArchitecture(language, new CompilerSpecID(compilerSpecId), LanguageUpdateOption.CLEAR, TaskMonitor.DUMMY); } finally { - archiveDtm.endTransaction(id, true); + archive.endTransaction(id, true); } waitForTree(); } - protected void removeArchitecture(StandAloneDataTypeManager archiveDtm) throws Exception { - int id = archiveDtm.startTransaction("remove architecture"); + protected void removeArchitecture(PersistentDataTypeArchive archive) throws Exception { + int id = archive.startTransaction("remove architecture"); try { - archiveDtm.clearProgramArchitecture(TaskMonitor.DUMMY); + archive.clearProgramArchitecture(TaskMonitor.DUMMY); } finally { - archiveDtm.endTransaction(id, true); + archive.endTransaction(id, true); } waitForTree(); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/ArchiveRemappedHeadedTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/ArchiveRemappedHeadedTest.java index 0dc54260ae..bbc7459432 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/ArchiveRemappedHeadedTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/ArchiveRemappedHeadedTest.java @@ -18,16 +18,11 @@ package ghidra.app.plugin.core.datamgr; import static org.junit.Assert.*; import java.io.File; +import java.util.List; import org.junit.*; -import docking.ActionContext; -import docking.action.DockingActionIf; -import docking.widgets.tree.GTreeNode; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; -import ghidra.app.plugin.core.datamgr.actions.UpdateSourceArchiveNamesAction; -import ghidra.app.plugin.core.datamgr.archive.Archive; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.plugin.core.datamgr.tree.*; import ghidra.app.services.DataTypeManagerService; import ghidra.app.services.ProgramManager; @@ -36,6 +31,8 @@ import ghidra.framework.plugintool.PluginTool; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.program.model.dtarchive.DataTypeStore; import ghidra.test.*; import ghidra.util.task.TaskMonitor; import utilities.util.FileUtilities; @@ -54,6 +51,7 @@ public class ArchiveRemappedHeadedTest extends AbstractGhidraHeadedIntegrationTe private File win32ArchiveDir; private File vs12ArchiveFile; private File vs9ArchiveFile; + private ArchiveManager archiveManager; @Before public void setUp() throws Exception { @@ -80,10 +78,11 @@ public class ArchiveRemappedHeadedTest extends AbstractGhidraHeadedIntegrationTe assertNotNull(service); // Close any archives which may have opened with tool - for (DataTypeManager dtm : service.getDataTypeManagers()) { - System.out.println("Closing " + dtm.getName()); - dtm.close(); + List dataTypeArchives = service.getDataTypeArchives(); + for (PersistentDataTypeArchive archive : dataTypeArchives) { + service.closeArchive(archive); } + archiveManager = plugin.getArchiveManager(); program = buildProgram(); @@ -115,19 +114,8 @@ public class ArchiveRemappedHeadedTest extends AbstractGhidraHeadedIntegrationTe return builder.getProgram(); } - private Archive getArchive(DataTypeManager dtm) { - DataTypeManagerHandler dtmHandler = plugin.getDataTypeManagerHandler(); - for (Archive archive : dtmHandler.getAllArchives()) { - if (dtm == archive.getDataTypeManager()) { - return archive; - } - } - return null; - } - - private Archive getArchive(String achiveName) { - DataTypeManagerHandler dtmHandler = plugin.getDataTypeManagerHandler(); - for (Archive archive : dtmHandler.getAllArchives()) { + private PersistentDataTypeArchive getArchive(String achiveName) { + for (PersistentDataTypeArchive archive : archiveManager.getOpenArchives()) { if (achiveName.equals(archive.getName())) { return archive; } @@ -144,20 +132,16 @@ public class ArchiveRemappedHeadedTest extends AbstractGhidraHeadedIntegrationTe return null; } - private ActionContext createContext(GTreeNode node) { - return new DataTypesActionContext(provider, program, tree, node); - } - @Test public void testGetRemappedArchive() throws Exception { - DataTypeManager vs9dtm = service.openDataTypeArchive("windows_VS9"); - assertNotNull(vs9dtm); + PersistentDataTypeArchive vs9Archive = service.openFileArchive("windows_VS9", TaskMonitor.DUMMY); + assertNotNull(vs9Archive); try { - assertEquals("windows_VS9", vs9dtm.getName()); + assertEquals("windows_VS9", vs9Archive.getName()); } finally { - close(vs9dtm); + service.closeArchive(vs9Archive); } waitForTree(tree);// archive does NOT appear in tree @@ -166,37 +150,41 @@ public class ArchiveRemappedHeadedTest extends AbstractGhidraHeadedIntegrationTe vs9ArchiveFile.delete(); assertFalse("windows_VS9.gdt should not exist", vs9ArchiveFile.exists()); - DataTypeManager vs12dtm = service.openDataTypeArchive("windows_VS9"); - assertNotNull(vs12dtm); + PersistentDataTypeArchive vs12Archive = service.openFileArchive("windows_VS9", TaskMonitor.DUMMY); + assertNotNull(vs12Archive); try { - assertEquals("windows_vs12_32", vs12dtm.getName()); + assertEquals("windows_vs12_32", vs12Archive.getName()); } finally { - close(vs12dtm); + service.closeArchive(vs12Archive); } waitForTree(tree);// archive does NOT appear in tree } private void close(DataTypeManager dtm) { - runSwing(() -> getArchive(dtm).close()); + + runSwing(() -> { + DataTypeStore dataStore = dtm.getDataStore(); + if (dataStore instanceof PersistentDataTypeArchive archive) { + archiveManager.closeArchive(archive); + } + }); } @Test public void testGetProgramRemappedArchive() throws Exception { - DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler(); - DataTypeManager programDtm = program.getDataTypeManager(); // Add datatype from vs9 archive into program // which reference to vs9 archive - DataTypeManager vs9dtm = service.openDataTypeArchive("windows_VS9"); - assertNotNull(vs9dtm); + PersistentDataTypeArchive vs9Archive = service.openFileArchive("windows_VS9", TaskMonitor.DUMMY); + assertNotNull(vs9Archive); int txId = program.startTransaction("Add vs9 types"); try { - assertEquals("windows_VS9", vs9dtm.getName()); - + assertEquals("windows_VS9", vs9Archive.getName()); + DataTypeManager vs9dtm = vs9Archive.getDataTypeManager(); DataType dataType = vs9dtm.getDataType(new CategoryPath("/winnt.h"), "_PRIVILEGE_SET"); assertNotNull("winnt.h/_PRIVILEGE_SET type not found", dataType); @@ -207,13 +195,13 @@ public class ArchiveRemappedHeadedTest extends AbstractGhidraHeadedIntegrationTe } finally { program.endTransaction(txId, true); - close(vs9dtm); + service.closeArchive(vs9Archive); } SourceArchive sourceArchive = getSourceArchive(programDtm, "windows_VS9"); assertNotNull(sourceArchive); - DataTypeManager archiveDtm = handler.getDataTypeManager(sourceArchive); + DataTypeManager archiveDtm = archiveManager.getDataTypeManager(sourceArchive); assertNull(archiveDtm);// archive not yet opened // Remove archive to force use of re-mapping @@ -230,32 +218,21 @@ public class ArchiveRemappedHeadedTest extends AbstractGhidraHeadedIntegrationTe assertNull(getArchive("windows_vs12")); assertNotNull(getArchive("windows_vs12_32")); - archiveDtm = handler.getDataTypeManager(sourceArchive); + archiveDtm = archiveManager.getDataTypeManager(sourceArchive); assertNotNull(archiveDtm); assertEquals("windows_vs12_32", archiveDtm.getName()); ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getModelRoot(); - ArchiveNode archiveNode = (ArchiveNode) archiveRootNode.getChild("windows_vs12_32"); + DataTypeStoreNode archiveNode = + (DataTypeStoreNode) archiveRootNode.getChild("windows_vs12_32"); assertNotNull(archiveNode); - ArchiveNode programNode = (ArchiveNode) archiveRootNode.getChild(program.getName()); + DataTypeStoreNode programNode = + (DataTypeStoreNode) archiveRootNode.getChild(program.getName()); assertNotNull(programNode); - // Popup action instantiated on-the-fly and does not persist within tool - DockingActionIf updateSourceArchiveNameActionForProgram = - new UpdateSourceArchiveNamesAction(plugin, programDtm); - assertTrue(updateSourceArchiveNameActionForProgram.isEnabledForContext( - createContext(programNode))); - DockingActionIf updateSourceArchiveNameActionForArchive = - new UpdateSourceArchiveNamesAction(plugin, archiveDtm); - assertFalse(updateSourceArchiveNameActionForArchive.isEnabledForContext( - createContext(programNode))); - - updateSourceArchiveNameActionForProgram.actionPerformed(null);// action does not rely on context - relies on construction instead - - assertFalse(updateSourceArchiveNameActionForProgram.isEnabledForContext( - createContext(programNode))); - assertFalse(updateSourceArchiveNameActionForArchive.isEnabledForContext( - createContext(programNode))); + List archives = program.getDataTypeManager().getSourceArchives(); + assertEquals(1, archives.size()); + assertEquals("windows_vs12_32", archives.get(0).getName()); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/ArchiveRemappedHeadlessTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/ArchiveRemappedHeadlessTest.java index 99a8d60ec0..42bf320d14 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/ArchiveRemappedHeadlessTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/ArchiveRemappedHeadlessTest.java @@ -27,7 +27,7 @@ import ghidra.app.services.DataTypeManagerService; import ghidra.framework.Application; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; -import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.test.ToyProgramBuilder; import ghidra.util.task.TaskMonitor; @@ -75,26 +75,26 @@ public class ArchiveRemappedHeadlessTest extends AbstractGhidraHeadlessIntegrati @Test public void testGetRemappedArchive() throws Exception { - DataTypeManager vs9dtm = service.openDataTypeArchive("windows_VS9"); - assertNotNull(vs9dtm); + FileDataTypeArchive vs9Archive = service.openFileArchive("windows_VS9", TaskMonitor.DUMMY); + assertNotNull(vs9Archive); try { - assertEquals("windows_VS9", vs9dtm.getName()); + assertEquals("windows_VS9", vs9Archive.getName()); } finally { - vs9dtm.close(); + service.closeArchive(vs9Archive); } // Remove archive to force use of remapping vs9ArchiveFile.delete(); assertFalse("windows_VS9.gdt should not exist", vs9ArchiveFile.exists()); - DataTypeManager vs12dtm = service.openDataTypeArchive("windows_VS9"); - assertNotNull(vs12dtm); + FileDataTypeArchive vs12Archive = service.openFileArchive("windows_VS9", TaskMonitor.DUMMY); + assertNotNull(vs12Archive); try { - assertEquals("windows_vs12_32", vs12dtm.getName()); + assertEquals("windows_vs12_32", vs12Archive.getName()); } finally { - vs12dtm.close(); + service.closeArchive(vs12Archive); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateArchive1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateArchive1Test.java index 6217db7569..a6e048a4de 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateArchive1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateArchive1Test.java @@ -28,12 +28,13 @@ import org.junit.Test; import docking.action.DockingActionIf; import docking.widgets.OptionDialog; import docking.widgets.tree.GTreeNode; -import ghidra.app.plugin.core.datamgr.archive.Archive; -import ghidra.app.plugin.core.datamgr.archive.FileArchive; import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; +import ghidra.app.plugin.core.datamgr.tree.DataTypeStoreNode; import ghidra.framework.GenericRunInfo; import ghidra.program.database.ProgramBuilder; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.program.model.lang.ProgramArchitecture; import ghidra.util.Msg; @@ -48,7 +49,7 @@ public class CreateArchive1Test extends AbstractCreateArchiveTest { assertNotNull(archiveNode); // verify that the archive is checked out - assertTrue(((FileArchive) archiveNode.getArchive()).hasWriteLock()); + assertTrue(archiveNode.getArchive().isChangeable()); } @@ -61,11 +62,10 @@ public class CreateArchive1Test extends AbstractCreateArchiveTest { // reopen the archive // check that the changes are there. String string = "MyArchive"; - createNewArchive(string + FileDataTypeManager.SUFFIX, true); + createNewArchive(string + FileDataTypeArchive.SUFFIX, true); - ArchiveNode archiveNode = (ArchiveNode) archiveRootNode.getChild("MyArchive"); - StandAloneDataTypeManager dtm = - (StandAloneDataTypeManager) archiveNode.getCategory().getDataTypeManager(); + DataTypeStoreNode archiveNode = (DataTypeStoreNode) archiveRootNode.getChild("MyArchive"); + DataTypeManager dtm = archiveNode.getCategory().getDataTypeManager(); assertNull(dtm.getProgramArchitecture()); createCategory(archiveNode.getCategory(), "bob"); @@ -85,7 +85,7 @@ public class CreateArchive1Test extends AbstractCreateArchiveTest { DataTypeTestUtils.performAction(action, tree); waitForTree(); - archiveNode = (ArchiveNode) archiveRootNode.getChild("MyArchive"); + archiveNode = (DataTypeStoreNode) archiveRootNode.getChild("MyArchive"); assertNull(archiveNode); archiveNode = @@ -101,13 +101,14 @@ public class CreateArchive1Test extends AbstractCreateArchiveTest { public void testCreateAndPopulateWithArchitecture() throws Exception { String string = "MyArchive"; - createNewArchive(string + FileDataTypeManager.SUFFIX, true); + createNewArchive(string + FileDataTypeArchive.SUFFIX, true); ArchiveNode archiveNode = (ArchiveNode) archiveRootNode.getChild("MyArchive"); - StandAloneDataTypeManager dtm = - (StandAloneDataTypeManager) archiveNode.getCategory().getDataTypeManager(); + PersistentDataTypeArchive archive = archiveNode.getArchive(); + DataTypeManager dtm = archive.getDataTypeManager(); + assertNull(dtm.getProgramArchitecture()); - setArchitecture(dtm, ProgramBuilder._TOY64_LE, "default"); + setArchitecture(archive, ProgramBuilder._TOY64_LE, "default"); assertEquals(8, dtm.getPointer(DataType.DEFAULT).getLength()); @@ -142,7 +143,7 @@ public class CreateArchive1Test extends AbstractCreateArchiveTest { assertNotNull(archiveNode.getChild("bob")); assertNotNull(archiveNode.getChild("joe")); - dtm = (StandAloneDataTypeManager) archiveNode.getCategory().getDataTypeManager(); + dtm = archiveNode.getCategory().getDataTypeManager(); ProgramArchitecture arch = dtm.getProgramArchitecture(); assertNotNull("Expected architecture to be set", arch); assertEquals(ProgramBuilder._TOY64_LE, arch.getLanguage().getLanguageID().toString()); @@ -179,7 +180,7 @@ public class CreateArchive1Test extends AbstractCreateArchiveTest { pressButton(button); waitForTree(); - ArchiveNode archiveNode = (ArchiveNode) archiveRootNode.getChild("MyArchive"); + DataTypeStoreNode archiveNode = (DataTypeStoreNode) archiveRootNode.getChild("MyArchive"); assertNotNull(archiveNode); } @@ -240,7 +241,7 @@ public class CreateArchive1Test extends AbstractCreateArchiveTest { try { archiveNode = DataTypeTestUtils.openArchive(GenericRunInfo.getProjectsDirPath(), "MyArchive.gdt", false, plugin); - Archive archive = archiveNode.getArchive(); + PersistentDataTypeArchive archive = archiveNode.getArchive(); Assert.fail("Should not have been able to open this archive " + archive.getName()); } catch (Exception e) { @@ -270,14 +271,14 @@ public class CreateArchive1Test extends AbstractCreateArchiveTest { waitForSwing(); waitForTree(); - ArchiveNode archiveNode = (ArchiveNode) archiveRootNode.getChild("MyArchive"); + DataTypeStoreNode archiveNode = (DataTypeStoreNode) archiveRootNode.getChild("MyArchive"); // debug if (archiveNode == null) { Msg.trace(this, "Did not find a newly created node!!!!"); // ...try waiting some more, to see if we beat the update waitForTree(); - archiveNode = (ArchiveNode) archiveRootNode.getChild("MyArchive"); + archiveNode = (DataTypeStoreNode) archiveRootNode.getChild("MyArchive"); Msg.trace(this, "\tand after waiting some more did we?: " + archiveNode); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateEnumFromSelectionTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateEnumFromSelectionTest.java index e1d9a37242..b7d4967679 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateEnumFromSelectionTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateEnumFromSelectionTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -52,7 +52,7 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration private DataTypesProvider provider; private DataTypeArchiveGTree tree; private ArchiveRootNode archiveRootNode; - private ArchiveNode programNode; + private DataTypeStoreNode programNode; private DockingActionIf action; @@ -67,7 +67,7 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration tree = provider.getGTree(); waitForTree(); archiveRootNode = (ArchiveRootNode) tree.getModelRoot(); - programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME); + programNode = (DataTypeStoreNode) archiveRootNode.getChild(PROGRAM_FILENAME); assertNotNull("Did not successfully wait for the program node to load", programNode); tool.showComponentProvider(provider, true); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateLabelsFromEnumsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateLabelsFromEnumsTest.java index 7b0fc56715..5308232f71 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateLabelsFromEnumsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateLabelsFromEnumsTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -61,7 +61,7 @@ public class CreateLabelsFromEnumsTest extends AbstractGhidraHeadedIntegrationTe private DataTypesProvider provider; private DataTypeArchiveGTree tree; private ArchiveRootNode archiveRootNode; - private ArchiveNode programNode; + private DataTypeStoreNode programNode; @Before public void setUp() throws Exception { @@ -85,7 +85,7 @@ public class CreateLabelsFromEnumsTest extends AbstractGhidraHeadedIntegrationTe tree = provider.getGTree(); waitForTree(); archiveRootNode = (ArchiveRootNode) tree.getModelRoot(); - programNode = (ArchiveNode) archiveRootNode.getChild(testName.getMethodName()); + programNode = (DataTypeStoreNode) archiveRootNode.getChild(testName.getMethodName()); assertNotNull("Did not successfully wait for the program node to load", programNode); tool.showComponentProvider(provider, true); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java index 9c74987dbd..5fffa7351c 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java @@ -27,8 +27,13 @@ import org.junit.Test; import generic.jar.ResourceFile; import generic.test.AbstractGenericTest; import ghidra.framework.Application; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.data.*; -import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; +import ghidra.program.model.dtarchive.ArchiveWarning; +import ghidra.program.model.dtarchive.FileDataTypeArchive; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; public class DataTypeArchiveIDTest extends AbstractGenericTest { @@ -64,17 +69,18 @@ public class DataTypeArchiveIDTest extends AbstractGenericTest { } private String getGdtUniversalId(ResourceFile gdtFile) { - FileDataTypeManager dtm = null; + FileDataTypeArchive archive = null; try { - dtm = FileDataTypeManager.openFileArchive(gdtFile, false); - assertEquals(dtm.getWarningMessage(true), ArchiveWarning.NONE, dtm.getWarning()); - return dtm.getUniversalID().toString(); + archive = DataTypeArchiveFactory.openReadOnly(gdtFile, this, TaskMonitor.DUMMY); + assertEquals(archive.getWarningMessage(true), ArchiveWarning.NONE, + archive.getWarning()); + return archive.getUniversalID().toString(); } - catch (IOException e) { + catch (IOException | VersionException | CancelledException e) { return "failed to read " + gdtFile.getName(); } finally { - dtm.close(); + archive.release(this); } } @@ -126,10 +132,12 @@ public class DataTypeArchiveIDTest extends AbstractGenericTest { } @Test - public void spotCheckWindowsVS12_32() throws IOException { + public void spotCheckWindowsVS12_32() throws Exception { ResourceFile gdtFile = Application.getModuleDataFile(WIN_VS12_32_GDT_PATH); - FileDataTypeManager dtm = FileDataTypeManager.openFileArchive(gdtFile, false); - assertEquals(ArchiveWarning.NONE, dtm.getWarning()); + FileDataTypeArchive archive = + DataTypeArchiveFactory.openReadOnly(gdtFile, this, TaskMonitor.DUMMY); + DataTypeManager dtm = archive.getDataTypeManager(); + assertEquals(ArchiveWarning.NONE, archive.getWarning()); try { DataType dt = dtm.getDataType("/winsock.h/fd_set"); assertNotNull(dt); @@ -137,15 +145,17 @@ public class DataTypeArchiveIDTest extends AbstractGenericTest { verifyArchive(dt, WIN_VS12_32_GDT_PATH); } finally { - dtm.close(); + archive.release(this); } } @Test - public void spotCheckWindowsVS12_64() throws IOException { + public void spotCheckWindowsVS12_64() throws Exception { ResourceFile gdtFile = Application.getModuleDataFile(WIN_VS12_64_GDT_PATH); - FileDataTypeManager dtm = FileDataTypeManager.openFileArchive(gdtFile, false); - assertEquals(ArchiveWarning.NONE, dtm.getWarning()); + FileDataTypeArchive archive = + DataTypeArchiveFactory.openReadOnly(gdtFile, this, TaskMonitor.DUMMY); + DataTypeManager dtm = archive.getDataTypeManager(); + assertEquals(ArchiveWarning.NONE, archive.getWarning()); try { DataType dt = dtm.getDataType("/winsock.h/fd_set"); assertNotNull(dt); @@ -153,15 +163,17 @@ public class DataTypeArchiveIDTest extends AbstractGenericTest { verifyArchive(dt, WIN_VS12_64_GDT_PATH); } finally { - dtm.close(); + archive.release(this); } } @Test - public void spotCheckGenericCLib32() throws IOException { + public void spotCheckGenericCLib32() throws Exception { ResourceFile gdtFile = Application.getModuleDataFile(GENERIC_CLIB_32_GDT_PATH); - FileDataTypeManager dtm = FileDataTypeManager.openFileArchive(gdtFile, false); - assertEquals(ArchiveWarning.NONE, dtm.getWarning()); + FileDataTypeArchive archive = + DataTypeArchiveFactory.openReadOnly(gdtFile, this, TaskMonitor.DUMMY); + DataTypeManager dtm = archive.getDataTypeManager(); + assertEquals(ArchiveWarning.NONE, archive.getWarning()); try { DataType dt = dtm.getDataType("/select.h/fd_set"); assertNotNull(dt); @@ -169,15 +181,18 @@ public class DataTypeArchiveIDTest extends AbstractGenericTest { verifyArchive(dt, GENERIC_CLIB_32_GDT_PATH); } finally { - dtm.close(); + archive.release(this); } } @Test - public void spotCheckGenericCLib64() throws IOException { + public void spotCheckGenericCLib64() throws Exception { ResourceFile gdtFile = Application.getModuleDataFile(GENERIC_CLIB_64_GDT_PATH); - FileDataTypeManager dtm = FileDataTypeManager.openFileArchive(gdtFile, false); - assertEquals(ArchiveWarning.NONE, dtm.getWarning()); + FileDataTypeArchive archive = + DataTypeArchiveFactory.openReadOnly(gdtFile, this, TaskMonitor.DUMMY); + DataTypeManager dtm = archive.getDataTypeManager(); + + assertEquals(ArchiveWarning.NONE, archive.getWarning()); try { DataType dt = dtm.getDataType("/select.h/fd_set"); assertNotNull(dt); @@ -185,15 +200,18 @@ public class DataTypeArchiveIDTest extends AbstractGenericTest { verifyArchive(dt, GENERIC_CLIB_64_GDT_PATH); } finally { - dtm.close(); + archive.release(this); } } @Test - public void spotCheckMacOS10_9() throws IOException { + public void spotCheckMacOS10_9() throws Exception { ResourceFile gdtFile = Application.getModuleDataFile(MAC_OS_10_9_GDT_PATH); - FileDataTypeManager dtm = FileDataTypeManager.openFileArchive(gdtFile, false); - assertEquals(ArchiveWarning.NONE, dtm.getWarning()); + FileDataTypeArchive archive = + DataTypeArchiveFactory.openReadOnly(gdtFile, this, TaskMonitor.DUMMY); + DataTypeManager dtm = archive.getDataTypeManager(); + + assertEquals(ArchiveWarning.NONE, archive.getWarning()); try { DataType dt = dtm.getDataType("/_fd_def.h/fd_set"); assertNotNull(dt); @@ -201,7 +219,7 @@ public class DataTypeArchiveIDTest extends AbstractGenericTest { verifyArchive(dt, MAC_OS_10_9_GDT_PATH); } finally { - dtm.close(); + archive.release(this); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeCopyMoveDragTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeCopyMoveDragTest.java index 9454bd168e..edb9df96c7 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeCopyMoveDragTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeCopyMoveDragTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -63,7 +63,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes private ProgramActionContext treeContext; private DataTypeArchiveGTree tree; private ArchiveRootNode archiveRootNode; - private ArchiveNode programNode; + private DataTypeStoreNode programNode; @Before public void setUp() throws Exception { @@ -88,7 +88,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes tree = provider.getGTree(); waitForTree(); archiveRootNode = (ArchiveRootNode) tree.getModelRoot(); - programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME); + programNode = (DataTypeStoreNode) archiveRootNode.getChild(PROGRAM_FILENAME); assertNotNull("Did not successfully wait for the program node to load", programNode); tool.showComponentProvider(provider, true); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java index 1974e904d1..1143f8b9fc 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java @@ -48,8 +48,6 @@ import docking.widgets.tree.GTreeNode; import ghidra.app.context.ProgramActionContext; import ghidra.app.plugin.core.compositeeditor.ApplyAction; import ghidra.app.plugin.core.datamgr.actions.*; -import ghidra.app.plugin.core.datamgr.archive.Archive; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.plugin.core.datamgr.tree.*; import ghidra.app.plugin.core.function.AbstractEditFunctionSignatureDialog; import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin; @@ -64,6 +62,7 @@ import ghidra.program.database.ProgramDB; import ghidra.program.database.data.ProgramDataTypeManager; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; +import ghidra.program.model.dtarchive.DataTypeStore; import ghidra.program.model.listing.Program; import ghidra.test.*; import util.CollectionUtils; @@ -86,7 +85,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe private JTree jTree; private ProgramActionContext treeContext; - private ArchiveNode programNode; + private DataTypeStoreNode programNode; private DockingActionIf cutAction; private DockingActionIf pasteAction; private DataTypesProvider provider; @@ -106,7 +105,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe jTree = (JTree) invokeInstanceMethod("getJTree", tree); waitForTree(); ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getModelRoot(); - programNode = (ArchiveNode) archiveRootNode.getChild(program.getName()); + programNode = (DataTypeStoreNode) archiveRootNode.getChild(program.getName()); assertNotNull("Did not successfully wait for the program node to load", programNode); tool.showComponentProvider(provider, true); @@ -188,10 +187,9 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe @Test public void testInvalidArchive() throws Exception { - final DataTypeManagerHandler managerHandler = plugin.getDataTypeManagerHandler(); - final String[] invalidNames = { "BADARCHIVENAME.gdt" }; - runSwing(() -> invokeInstanceMethod("openArchives", managerHandler, - new Class[] { String[].class }, new Object[] { invalidNames })); + List invalidNames = Arrays.asList("BADARCHIVENAME.gdt"); + runSwing(() -> invokeInstanceMethod("openArchives", plugin, + new Class[] { List.class }, new Object[] { invalidNames })); GTreeNode rootNode = tree.getModelRoot(); GTreeNode invalidChild = rootNode.getChild("BADARCHIVENAME"); @@ -261,7 +259,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe // disablePointerFilter();// make sure our new type is not filtered out - ArchiveNode builtInNode = getBuiltInNode(); + DataTypeStoreNode builtInNode = getBuiltInNode(); expandNode(builtInNode); String boolNodeName = "bool"; GTreeNode boolNode = builtInNode.getChild(boolNodeName); @@ -514,7 +512,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe cat2Node = (CategoryNode) cat1Node.getChild("Category2"); assertNull(cat2Node.getChild("Category4")); ArrayList list = new ArrayList<>(); - Archive archive = cat2Node.getArchiveNode().getArchive(); + DataTypeStore archive = cat2Node.getArchiveNode().getDataTypeStore(); archive.getDataTypeManager().findDataTypes("CharStruct", list); assertEquals(0, list.size()); @@ -555,7 +553,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe cat2Node = (CategoryNode) cat1Node.getChild("Category2"); assertNull(cat1Node.getChild("Category2")); ArrayList list = new ArrayList<>(); - Archive archive = cat1Node.getArchiveNode().getArchive(); + DataTypeStore archive = cat1Node.getArchiveNode().getDataTypeStore(); archive.getDataTypeManager().findDataTypes("CharStruct", list); assertEquals(0, list.size()); archive.getDataTypeManager().findDataTypes("IntStruct", list); @@ -566,7 +564,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe cat2Node = (CategoryNode) cat1Node.getChild("Category2"); assertNotNull(cat2Node); list = new ArrayList<>(); - archive = cat1Node.getArchiveNode().getArchive(); + archive = cat1Node.getArchiveNode().getDataTypeStore(); archive.getDataTypeManager().findDataTypes("CharStruct", list); assertEquals(1, list.size()); list.clear(); @@ -578,7 +576,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe cat1Node = (CategoryNode) programNode.getChild("Category1"); assertNull(cat1Node.getChild("Category2")); list = new ArrayList<>(); - archive = cat1Node.getArchiveNode().getArchive(); + archive = cat1Node.getArchiveNode().getDataTypeStore(); archive.getDataTypeManager().findDataTypes("CharStruct", list); assertEquals(0, list.size()); archive.getDataTypeManager().findDataTypes("IntStruct", list); @@ -1433,9 +1431,9 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe runSwing(() -> provider.setPreviewWindowVisible(true)); } - private ArchiveNode getBuiltInNode() { + private DataTypeStoreNode getBuiltInNode() { ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getModelRoot(); - ArchiveNode builtinNode = (ArchiveNode) archiveRootNode.getChild(BUILTIN_NAME); + DataTypeStoreNode builtinNode = (DataTypeStoreNode) archiveRootNode.getChild(BUILTIN_NAME); assertNotNull(builtinNode); return builtinNode; } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeTestUtils.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeTestUtils.java index b7663d443e..b31afe10da 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeTestUtils.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeTestUtils.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,12 +24,15 @@ import docking.ActionContext; import docking.action.DockingActionIf; import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; +import generic.jar.ResourceFile; import generic.test.AbstractGenericTest; import generic.test.AbstractGuiTest; -import ghidra.app.plugin.core.datamgr.archive.*; -import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; -import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; -import ghidra.program.model.data.*; +import ghidra.app.plugin.core.datamgr.tree.*; +import ghidra.app.services.Upgrade; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; +import ghidra.program.model.data.Category; +import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.dtarchive.*; import ghidra.program.model.listing.Program; import ghidra.util.Msg; import ghidra.util.Swing; @@ -38,7 +41,7 @@ import utilities.util.FileUtilities; public class DataTypeTestUtils { - private static final String ARCHIVE_FILE_EXTENSION = FileDataTypeManager.SUFFIX; + private static final String ARCHIVE_FILE_EXTENSION = FileDataTypeArchive.SUFFIX; private static File tempArchiveDir; static DataTypeArchiveGTree archiveTree; @@ -83,11 +86,11 @@ public class DataTypeTestUtils { File tempDbFile = new File(tempDir, "copy." + existingFilename); tempDbFile.deleteOnExit(); FileUtilities.copyFile(packedDbFile, tempDbFile, false, TaskMonitor.DUMMY); - - boolean openForUpdate = true; // open for update to allow auto-upgrade to occur if needed - FileDataTypeManager fm = FileDataTypeManager.openFileArchive(tempDbFile, openForUpdate); - fm.saveAs(scratchFile); - fm.close(); + Object consumer = new Object(); + FileDataTypeArchive archive = + DataTypeArchiveFactory.openForUpdate(tempDbFile, true, consumer, TaskMonitor.DUMMY); + archive.saveAs(scratchFile, TaskMonitor.DUMMY); + archive.release(consumer); Msg.debug(DataTypeTestUtils.class, "Created test archive: " + scratchFile); @@ -98,10 +101,11 @@ public class DataTypeTestUtils { boolean checkout, DataTypeManagerPlugin plugin) throws Exception { File file = new File(archiveDirPath, archiveName); - DataTypeManagerHandler dataTypeManagerHandler = plugin.getDataTypeManagerHandler(); + ArchiveManager archiveManager = plugin.getArchiveManager(); // this opens the archive and triggers the tree to rebuild - dataTypeManagerHandler.openArchive(file, checkout, false); + archiveManager.openFileArchive(new ResourceFile(file), checkout, Upgrade.NO, false, + TaskMonitor.DUMMY); waitForTree(plugin); GTree tree = plugin.getProvider().getGTree(); @@ -134,10 +138,12 @@ public class DataTypeTestUtils { File tempDir = getTempDir(); File file = new File(tempDir, archiveName); - DataTypeManagerHandler dataTypeManagerHandler = plugin.getDataTypeManagerHandler(); + ResourceFile resourceFile = new ResourceFile(file); + ArchiveManager archiveManager = plugin.getArchiveManager(); // this opens the archive and triggers the tree to rebuild - dataTypeManagerHandler.openArchive(file, checkout, isUserAction); + archiveManager.openFileArchive(resourceFile, checkout, Upgrade.NO, isUserAction, + TaskMonitor.DUMMY); archiveTree = plugin.getProvider().getGTree(); GTreeNode rootNode = archiveTree.getViewRoot(); @@ -145,12 +151,13 @@ public class DataTypeTestUtils { return (ArchiveNode) rootNode.getChild(trimFullArchiveName(archiveName)); } - public static void closeArchive(final ArchiveNode archiveNode, final boolean deleteFile) + public static void closeArchive(ArchiveNode archiveNode, boolean deleteFile, + DataTypeManagerPlugin plugin) throws Exception { Exception exception = Swing.runNow(() -> { try { - doCloseArchive(archiveNode, deleteFile); + doCloseArchive(archiveNode, deleteFile, plugin); return null; } catch (Exception e) { @@ -163,52 +170,27 @@ public class DataTypeTestUtils { } } - private static void doCloseArchive(ArchiveNode archiveNode, boolean deleteFile) + private static void doCloseArchive(ArchiveNode archiveNode, boolean deleteFile, + DataTypeManagerPlugin plugin) throws Exception { if (archiveNode == null) { return; } - - Archive archive = archiveNode.getArchive(); - File file = null; - if ((archive instanceof FileArchive) && deleteFile) { - file = ((FileArchive) archive).getFile().getFile(false); + ArchiveManager archiveManager = plugin.getArchiveManager(); + PersistentDataTypeArchive archive = archiveNode.getArchive(); + if (archive instanceof FileDataTypeArchive fileArchive) { + File file = fileArchive.getFile().getFile(false); + archiveManager.closeArchive(fileArchive); + if (deleteFile) { + fileArchive.delete(); + } } - - archiveNode.getArchive().close(); - - if (file != null) { - FileDataTypeManager.delete(file); + if (archive instanceof ProjectDataTypeArchive projectArchive) { + archiveManager.closeArchive(projectArchive); } } - /** - * Checks out the archive by the given name. - * - * @param archiveName The name of the archive to open. This must be a child off of the root node. - * @param plugin The plugin that contains the tree and actions under test - * @return The archive node associated with the open archive - * @throws Exception If there is any problem finding or opening the archive for the given name - */ - public static ArchiveNode checkOutArchive(String archiveName, - final DataTypeManagerPlugin plugin) throws Exception { - - String archiveNodeName = trimFullArchiveName(archiveName); - GTree tree = plugin.getProvider().getGTree(); - GTreeNode rootNode = tree.getModelRoot(); - ArchiveNode archiveNode = (ArchiveNode) rootNode.getChild(archiveNodeName); - if (archiveNode == null) { - throw new IllegalArgumentException( - "Unable to locate an archive by the name: " + archiveNodeName); - } - - ArchiveUtils.lockArchive((FileArchive) archiveNode.getArchive()); - - // checking out the archive causes the trees nodes to be recreated - return (ArchiveNode) rootNode.getChild(archiveNodeName); - } - /** * Trims the given string if it ends with {@link #ARCHIVE_FILE_EXTENSION}. * @param archiveName The name to trim @@ -228,7 +210,7 @@ public class DataTypeTestUtils { return openArchive(archiveName, true, plugin); } - public static ArchiveNode copyOpenAndCheckoutArchive(String archiveName, + public static DataTypeStoreNode copyOpenAndCheckoutArchive(String archiveName, DataTypeManagerPlugin plugin) throws Exception { copyArchive(archiveName); return openArchive(archiveName, true, plugin); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/FavoritesAndMiscTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/FavoritesAndMiscTest.java index 0e4d6134e7..ae7404713c 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/FavoritesAndMiscTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/FavoritesAndMiscTest.java @@ -52,9 +52,9 @@ public class FavoritesAndMiscTest extends AbstractGhidraHeadedIntegrationTest { private DataTypesProvider provider; private DataTypeArchiveGTree tree; private ArchiveRootNode archiveRootNode; - private ArchiveNode builtInNode; + private DataTypeStoreNode builtInNode; private ToggleDockingAction favoritesAction; - private ArchiveNode programNode; + private DataTypeStoreNode programNode; @Before public void setUp() throws Exception { @@ -70,8 +70,8 @@ public class FavoritesAndMiscTest extends AbstractGhidraHeadedIntegrationTest { waitForTree(); archiveRootNode = (ArchiveRootNode) tree.getModelRoot(); - builtInNode = (ArchiveNode) archiveRootNode.getChild("BuiltInTypes"); - programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_NAME); + builtInNode = (DataTypeStoreNode) archiveRootNode.getChild("BuiltInTypes"); + programNode = (DataTypeStoreNode) archiveRootNode.getChild(PROGRAM_NAME); tool.showComponentProvider(provider, true); favoritesAction = (ToggleDockingAction) getAction(plugin, "Set Favorite Data Type"); @@ -348,7 +348,7 @@ public class FavoritesAndMiscTest extends AbstractGhidraHeadedIntegrationTest { waitForTree(gtree); ArchiveRootNode root = (ArchiveRootNode) gtree.getModelRoot(); - ArchiveNode programNode1 = (ArchiveNode) root.getChild(PROGRAM_NAME); + DataTypeStoreNode programNode1 = (DataTypeStoreNode) root.getChild(PROGRAM_NAME); assertNotNull("could not find " + PROGRAM_NAME + " in " + root, programNode1); expandNode(programNode1); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/FileArchiveEnumEditorUndoRedoTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/FileArchiveEnumEditorUndoRedoTest.java index b67b0d46bc..c861f5a1e2 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/FileArchiveEnumEditorUndoRedoTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/FileArchiveEnumEditorUndoRedoTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,13 +23,12 @@ import java.io.IOException; import org.junit.After; import org.junit.Before; -import ghidra.app.plugin.core.datamgr.archive.Archive; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.dtarchive.FileDataTypeArchive; public class FileArchiveEnumEditorUndoRedoTest extends AbstractEnumEditorUndoRedoTest { private File tempGdt; - private Archive fileArchive; + private FileDataTypeArchive fileArchive; @Before @Override @@ -39,9 +38,9 @@ public class FileArchiveEnumEditorUndoRedoTest extends AbstractEnumEditorUndoRed tempGdt = createTempFileForTest(".gdt"); tempGdt.delete(); - fileArchive = plugin.getDataTypeManagerHandler().createArchive(tempGdt); + fileArchive = plugin.getArchiveManager().createFileArchive(tempGdt); - assertTrue(fileArchive.isModifiable()); + assertTrue(fileArchive.isChangeable()); dtm = fileArchive.getDataTypeManager(); } @@ -50,7 +49,7 @@ public class FileArchiveEnumEditorUndoRedoTest extends AbstractEnumEditorUndoRed @Override public void tearDown() throws Exception { if (fileArchive != null) { - plugin.getDataTypeManagerHandler().closeArchive(fileArchive); + plugin.getArchiveManager().closeArchive(fileArchive); tempGdt.delete(); } super.tearDown(); @@ -58,14 +57,12 @@ public class FileArchiveEnumEditorUndoRedoTest extends AbstractEnumEditorUndoRed @Override void undo() throws IOException { - FileDataTypeManager fileDtm = (FileDataTypeManager) fileArchive.getDataTypeManager(); - fileDtm.undo(); + fileArchive.undo(); } @Override void redo() throws IOException { - FileDataTypeManager fileDtm = (FileDataTypeManager) fileArchive.getDataTypeManager(); - fileDtm.redo(); + fileArchive.redo(); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/ProjectArchiveEnumEditorUndoRedoTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/ProjectArchiveEnumEditorUndoRedoTest.java index 6703373f87..6a089d67fc 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/ProjectArchiveEnumEditorUndoRedoTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/ProjectArchiveEnumEditorUndoRedoTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,14 +22,17 @@ import java.io.IOException; import org.junit.After; import org.junit.Before; -import ghidra.app.plugin.core.datamgr.archive.Archive; +import ghidra.app.services.Recover; +import ghidra.app.services.Upgrade; +import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainFolder; -import ghidra.program.database.DataTypeArchiveDB; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; +import ghidra.util.task.TaskMonitor; public class ProjectArchiveEnumEditorUndoRedoTest extends AbstractEnumEditorUndoRedoTest { - Archive projectArchive; - DataTypeArchiveDB dataTypeArchiveDB; + ProjectDataTypeArchive projectArchive; @Before @Override @@ -37,33 +40,35 @@ public class ProjectArchiveEnumEditorUndoRedoTest extends AbstractEnumEditorUndo super.setUp(); DomainFolder rootFolder = tool.getProject().getProjectData().getRootFolder(); + projectArchive = DataTypeArchiveFactory.createProjectArchive(rootFolder, "Test", tool); + DomainFile domainFile = projectArchive.getDomainFile(); + projectArchive.release(tool); + projectArchive = plugin.getArchiveManager() + .openProjectArchive(domainFile, DomainFile.DEFAULT_VERSION, Upgrade.NO, Recover.NO, + false, TaskMonitor.DUMMY); - dataTypeArchiveDB = new DataTypeArchiveDB(rootFolder, "Test", tool); + assertTrue(projectArchive.isChangeable()); - projectArchive = plugin.getDataTypeManagerHandler().openArchive(dataTypeArchiveDB); - - assertTrue(projectArchive.isModifiable()); - - dtm = dataTypeArchiveDB.getDataTypeManager(); + dtm = projectArchive.getDataTypeManager(); } @After @Override public void tearDown() throws Exception { if (projectArchive != null) { - plugin.getDataTypeManagerHandler().closeArchive(projectArchive); + plugin.getArchiveManager().closeArchive(projectArchive); } super.tearDown(); } @Override void undo() throws IOException { - dataTypeArchiveDB.undo(); + projectArchive.undo(); } @Override void redo() throws IOException { - dataTypeArchiveDB.redo(); + projectArchive.redo(); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFConflictHandlerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFConflictHandlerTest.java index d8da8051ba..ee3deb398c 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFConflictHandlerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFConflictHandlerTest.java @@ -21,6 +21,7 @@ import org.junit.*; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.program.model.data.*; import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult; import ghidra.test.AbstractGhidraHeadedIntegrationTest; @@ -443,7 +444,9 @@ public class DWARFConflictHandlerTest extends AbstractGhidraHeadedIntegrationTes @Test public void testResolveDataTypeStructConflict() throws Exception { - DataTypeManager dtm = new StandAloneDataTypeManager("Test"); + + DataTypeManager dtm = new TransientDataTypeManager("Test"); + int id = dtm.startTransaction(""); Category otherRoot = dataMgr.getRootCategory(); Category subc = otherRoot.createCategory("subc"); @@ -475,7 +478,8 @@ public class DWARFConflictHandlerTest extends AbstractGhidraHeadedIntegrationTes @Test public void testResolveDataTypeNonStructConflict() throws Exception { - DataTypeManager dtm = new StandAloneDataTypeManager("Test"); + + DataTypeManager dtm = new TransientDataTypeManager("Test"); int id = dtm.startTransaction(""); Category otherRoot = dataMgr.getRootCategory(); Category subc = otherRoot.createCategory("subc"); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java index 7a69576edb..e9a23e7e2f 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java @@ -46,9 +46,8 @@ import generic.test.AbstractGTest; import generic.theme.GThemeDefaults.Colors.Palette; import generic.util.WindowUtilities; import generic.util.image.ImageUtils; +import ghidra.app.plugin.core.datamgr.ArchiveManager; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.archive.Archive; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; import ghidra.app.plugin.core.datamgr.util.DataTypeChooserDialog; @@ -60,6 +59,7 @@ import ghidra.framework.plugintool.ServiceProvider; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.data.ProgramDataTypeManager; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; import ghidra.program.model.listing.Program; import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.TestEnv; @@ -77,7 +77,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration private ReportingDataListener reportingListener = new ReportingDataListener(); - private Set archivesToClose = new HashSet<>(); + private Set archivesToClose = new HashSet<>(); private SpyDropDownSelectionTextField spyTextField; @@ -152,10 +152,10 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration // close all archives but the builtin and the program archive private void closeUndesiredArchives() { DataTypeManagerPlugin plugin = env.getPlugin(DataTypeManagerPlugin.class); - DataTypeManagerHandler dataTypeManagerHandler = plugin.getDataTypeManagerHandler(); - List archives = dataTypeManagerHandler.getAllFileOrProjectArchives(); - for (Archive archive : archives) { - dataTypeManagerHandler.closeArchive(archive); + ArchiveManager archiveManager = plugin.getArchiveManager(); + List archives = archiveManager.getOpenArchives(); + for (PersistentDataTypeArchive archive : archives) { + archiveManager.closeArchive(archive); } } @@ -163,7 +163,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration public void tearDown() throws Exception { System.err.println("tearDown() - " + testName.getMethodName() + "\n"); - for (Archive archive : archivesToClose) { + for (PersistentDataTypeArchive archive : archivesToClose) { closeArchive(archive); } @@ -556,7 +556,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration @Test public void testPickFromListThenAddPointerChar() { - Archive archive = createTestFileArchive(); + PersistentDataTypeArchive archive = createTestFileArchive(); String structureName = "foo"; Structure structure = createStructure(archive, structureName); @@ -605,7 +605,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration // // - Archive archive = createTestFileArchive(); + PersistentDataTypeArchive archive = createTestFileArchive(); String structureName = "foo"; Structure structure = createStructure(archive, structureName); @@ -641,8 +641,8 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration // // Same as the above test, but with multiple archives form which to choose // - Archive archive1 = createTestFileArchive(); - Archive archive2 = createTestFileArchive(); + PersistentDataTypeArchive archive1 = createTestFileArchive(); + PersistentDataTypeArchive archive2 = createTestFileArchive(); String structureName = "foo"; Structure structure = createStructure(archive1, structureName); @@ -680,7 +680,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration // Support methods //================================================================================================== - private void addType(final Archive archive, final DataType dt) { + private void addType(PersistentDataTypeArchive archive, DataType dt) { runSwing(() -> { DataTypeManager dtm = archive.getDataTypeManager(); int txID = dtm.startTransaction("Test Add Data Type"); @@ -693,7 +693,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration }); } - private Structure createStructure(Archive archive, String name) { + private Structure createStructure(PersistentDataTypeArchive archive, String name) { StructureDataType structure = new StructureDataType(name, 0); structure.add(IntegerDataType.dataType, "field1", "Comment 1"); structure.add(IntegerDataType.dataType, "field2", "Comment 2"); @@ -704,23 +704,23 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration return structure; } - private TypeDef createTypeDef(Archive archive, Structure structure, String name) { + private TypeDef createTypeDef(PersistentDataTypeArchive archive, Structure structure, String name) { TypedefDataType dt = new TypedefDataType(name, structure); addType(archive, dt); return dt; } - private Pointer createPointer(Archive archive, DataType dt) { + private Pointer createPointer(PersistentDataTypeArchive archive, DataType dt) { PointerDataType pointer = new PointerDataType(dt); addType(archive, pointer); return pointer; } - private Archive createTestFileArchive() { - final AtomicReference ref = new AtomicReference<>(); + private PersistentDataTypeArchive createTestFileArchive() { + final AtomicReference ref = new AtomicReference<>(); runSwing(() -> { DataTypeManagerPlugin plugin = env.getPlugin(DataTypeManagerPlugin.class); - DataTypeManagerHandler dataTypeManagerHandler = plugin.getDataTypeManagerHandler(); + ArchiveManager archiveManager = plugin.getArchiveManager(); File tempArchiveFile; try { tempArchiveFile = Application.createTempFile("TestFileArchive", ".gdt"); @@ -734,21 +734,21 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration tempArchiveFile.delete(); } tempArchiveFile.deleteOnExit(); - Archive archive = dataTypeManagerHandler.createArchive(tempArchiveFile); + PersistentDataTypeArchive archive = archiveManager.createFileArchive(tempArchiveFile); ref.set(archive); }); - Archive archive = ref.get(); + PersistentDataTypeArchive archive = ref.get(); assertNotNull(archive); archivesToClose.add(archive); return archive; } - private void closeArchive(final Archive archive) { + private void closeArchive(PersistentDataTypeArchive archive) { runSwing(() -> { DataTypeManagerPlugin plugin = env.getPlugin(DataTypeManagerPlugin.class); - DataTypeManagerHandler dataTypeManagerHandler = plugin.getDataTypeManagerHandler(); - dataTypeManagerHandler.closeArchive(archive); + ArchiveManager archiveManager = plugin.getArchiveManager(); + archiveManager.closeArchive(archive); }); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionTextFieldTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionTextFieldTest.java index 07f3201f7b..4414a39798 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionTextFieldTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionTextFieldTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,8 +15,7 @@ */ package ghidra.app.util.datatype; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.util.ArrayList; import java.util.List; @@ -24,14 +23,14 @@ import java.util.List; import org.junit.*; import docking.widgets.*; +import ghidra.app.plugin.core.datamgr.ArchiveManager; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.archive.Archive; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.services.DataTypeManagerService; import ghidra.framework.ApplicationConfiguration; import ghidra.framework.GhidraApplicationConfiguration; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; import ghidra.program.model.listing.Program; import ghidra.test.*; @@ -82,10 +81,10 @@ public class DataTypeSelectionTextFieldTest extends AbstractDropDownTextFieldTes // close all archives but the builtin and the program archive private void closeUndesiredArchives() { DataTypeManagerPlugin plugin = env.getPlugin(DataTypeManagerPlugin.class); - DataTypeManagerHandler dataTypeManagerHandler = plugin.getDataTypeManagerHandler(); - List archivesToClose = dataTypeManagerHandler.getAllFileOrProjectArchives(); - for (Archive archive : archivesToClose) { - dataTypeManagerHandler.closeArchive(archive); + ArchiveManager archiveManager = plugin.getArchiveManager(); + List archivesToClose = archiveManager.getOpenArchives(); + for (PersistentDataTypeArchive archive : archivesToClose) { + archiveManager.closeArchive(archive); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/code/CodeUnitIteratorTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/code/CodeUnitIteratorTest.java index e4e861e2de..a2351ff2da 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/code/CodeUnitIteratorTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/code/CodeUnitIteratorTest.java @@ -31,6 +31,7 @@ import ghidra.app.services.ProgramManager; import ghidra.framework.plugintool.PluginTool; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.program.model.address.*; import ghidra.program.model.data.*; import ghidra.program.model.lang.*; @@ -1197,7 +1198,7 @@ public class CodeUnitIteratorTest extends AbstractGhidraHeadedIntegrationTest { Structure struct = new StructureDataType("struct_1", 100); Structure struct2 = new StructureDataType("struct_2", 0); Structure struct3 = new StructureDataType("struct_3", 0); - DataTypeManager dtm = new StandAloneDataTypeManager("dummyDTM"); + DataTypeManager dtm = createTransientDataTypeManager("dummyDTM"); int id = dtm.startTransaction(""); struct = (Structure) dtm.resolve(struct, null); struct2 = (Structure) dtm.resolve(struct2, null); @@ -1252,7 +1253,7 @@ public class CodeUnitIteratorTest extends AbstractGhidraHeadedIntegrationTest { Structure struct = new StructureDataType("struct_1", 100); Structure struct2 = new StructureDataType("struct_2", 0); Structure struct3 = new StructureDataType("struct_3", 0); - DataTypeManager dtm = new StandAloneDataTypeManager("dummyDTM"); + DataTypeManager dtm = createTransientDataTypeManager("dummyDTM"); int id = dtm.startTransaction(""); struct = (Structure) dtm.resolve(struct, null); struct2 = (Structure) dtm.resolve(struct2, null); @@ -1324,7 +1325,7 @@ public class CodeUnitIteratorTest extends AbstractGhidraHeadedIntegrationTest { Structure struct = new StructureDataType("struct_1", 100); Structure struct2 = new StructureDataType("struct_2", 0); Structure struct3 = new StructureDataType("struct_3", 0); - DataTypeManager dtm = new StandAloneDataTypeManager("dummyDTM"); + DataTypeManager dtm = createTransientDataTypeManager("dummyDTM"); int id = dtm.startTransaction(""); struct = (Structure) dtm.resolve(struct, null); struct2 = (Structure) dtm.resolve(struct2, null); @@ -1385,7 +1386,7 @@ public class CodeUnitIteratorTest extends AbstractGhidraHeadedIntegrationTest { Structure struct = new StructureDataType("struct_1", 100); Structure struct2 = new StructureDataType("struct_2", 0); Structure struct3 = new StructureDataType("struct_3", 0); - DataTypeManager dtm = new StandAloneDataTypeManager("dummyDTM"); + DataTypeManager dtm = createTransientDataTypeManager("dummyDTM"); int id = dtm.startTransaction(""); struct = (Structure) dtm.resolve(struct, null); struct2 = (Structure) dtm.resolve(struct2, null); @@ -1590,4 +1591,8 @@ public class CodeUnitIteratorTest extends AbstractGhidraHeadedIntegrationTest { private void endTransaction() { program.endTransaction(transactionID, true); } + + private TransientDataTypeManager createTransientDataTypeManager(String name) { + return new TransientDataTypeManager(name); + } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java index 468801f59a..87472cb6f1 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java @@ -519,7 +519,7 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { */ @Test public void testResolveDataTypeNonStructConflict() throws Exception { - DataTypeManager dtm = new StandAloneDataTypeManager("Test"); + DataTypeManager dtm = new TransientDataTypeManager("Test"); int id = dtm.startTransaction(""); try { Category otherRoot = dataMgr.getRootCategory(); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/DataManagerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/DataManagerTest.java index 1c81a4e210..9453b17de8 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/DataManagerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/DataManagerTest.java @@ -25,7 +25,6 @@ import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; import ghidra.program.model.data.*; import ghidra.test.AbstractGhidraHeadedIntegrationTest; -import ghidra.util.InvalidNameException; import ghidra.util.task.TaskMonitor; public class DataManagerTest extends AbstractGhidraHeadedIntegrationTest { @@ -46,13 +45,6 @@ public class DataManagerTest extends AbstractGhidraHeadedIntegrationTest { program.release(this); } - @Test - public void testSetName() throws InvalidNameException { - String newName = "NewName"; - dataMgr.setName(newName); - assertEquals(newName, dataMgr.getName()); - } - @Test public void testGetUniqueName() throws Exception { DataType bt = new EnumDataType("test", 2); @@ -461,7 +453,7 @@ public class DataManagerTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testResolveDataType() { - StandAloneDataTypeManager dtm = new StandAloneDataTypeManager("Test"); + TransientDataTypeManager dtm = createTransientDataTypeManager("Test"); int id = dtm.startTransaction(""); DataType byteDT = dtm.resolve(new ByteDataType(), null); DataType myByteDT = dataMgr.resolve(byteDT, null); @@ -474,7 +466,7 @@ public class DataManagerTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testResolveDataType2() throws Exception { - StandAloneDataTypeManager dtm = new StandAloneDataTypeManager("Test"); + TransientDataTypeManager dtm = createTransientDataTypeManager("Test"); int id = dtm.startTransaction(""); Category otherRoot = dataMgr.getRootCategory(); Category subc = otherRoot.createCategory("subc"); @@ -487,9 +479,13 @@ public class DataManagerTest extends AbstractGhidraHeadedIntegrationTest { dtm.close(); } + private TransientDataTypeManager createTransientDataTypeManager(String name) { + return new TransientDataTypeManager(name); + } + @Test public void testResolveDataType3() throws Exception { - StandAloneDataTypeManager dtm = new StandAloneDataTypeManager("Test"); + TransientDataTypeManager dtm = createTransientDataTypeManager("Test"); int id = dtm.startTransaction(""); Category otherRoot = dataMgr.getRootCategory(); Category subc = otherRoot.createCategory("subc"); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/services/TestDoubleDataTypeManagerService.java b/Ghidra/Features/Base/src/test/java/ghidra/app/services/TestDoubleDataTypeManagerService.java index e0971a51ca..c2b7270ed4 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/services/TestDoubleDataTypeManagerService.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/services/TestDoubleDataTypeManagerService.java @@ -15,7 +15,6 @@ */ package ghidra.app.services; -import java.io.File; import java.io.IOException; import java.util.List; import java.util.Set; @@ -23,11 +22,10 @@ import java.util.Set; import javax.swing.tree.TreePath; import generic.jar.ResourceFile; -import ghidra.app.plugin.core.datamgr.archive.Archive; import ghidra.app.plugin.core.datamgr.archive.DuplicateIdException; import ghidra.framework.model.DomainFile; import ghidra.program.model.data.*; -import ghidra.program.model.listing.DataTypeArchive; +import ghidra.program.model.dtarchive.*; import ghidra.util.HelpLocation; import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; @@ -129,23 +127,17 @@ public class TestDoubleDataTypeManagerService implements DataTypeManagerService throw new UnsupportedOperationException(); } + @Override + public void closeArchive(PersistentDataTypeArchive archive) { + throw new UnsupportedOperationException(); + } + @Override public DataTypeManager openDataTypeArchive(String archiveName) throws IOException, DuplicateIdException { throw new UnsupportedOperationException(); } - @Override - public Archive openArchive(DataTypeArchive dataTypeArchive) { - throw new UnsupportedOperationException(); - } - - @Override - public Archive openArchive(File file, boolean acquireWriteLock) - throws IOException, DuplicateIdException { - throw new UnsupportedOperationException(); - } - @Override public void setDataTypeSelected(DataType dataType) { throw new UnsupportedOperationException(); @@ -182,14 +174,27 @@ public class TestDoubleDataTypeManagerService implements DataTypeManagerService } @Override - public DataTypeManager openArchive(ResourceFile file, boolean acquireWriteLock) + public FileDataTypeArchive openFileArchive(String archiveName, TaskMonitor monitor) throws IOException, DuplicateIdException { throw new UnsupportedOperationException(); } @Override - public DataTypeManager openArchive(DomainFile domainFile, TaskMonitor monitor) + public FileDataTypeArchive openFileArchive(ResourceFile file, boolean openForUpdate, + Upgrade upgradeStrategy, TaskMonitor monitor) + throws IOException, VersionException, DuplicateIdException, CancelledException { + throw new UnsupportedOperationException(); + } + + @Override + public ProjectDataTypeArchive openProjectArchive(DomainFile domainFile, Upgrade upgradeStrategy, + Recover recoverStrategy, TaskMonitor monitor) throws VersionException, CancelledException, IOException, DuplicateIdException { throw new UnsupportedOperationException(); } + + @Override + public List getDataTypeArchives() { + throw new UnsupportedOperationException(); + } } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/CParserTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/CParserTest.java index dde1e49fd8..24e89d9fbb 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/CParserTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/CParserTest.java @@ -25,6 +25,7 @@ import org.junit.Test; import ghidra.app.util.cparser.C.CParser; import ghidra.app.util.cparser.C.ParseException; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; import ghidra.program.model.lang.CompilerSpec; @@ -47,12 +48,11 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { DataType dt = parser.getDataTypeManager().getDataType("/int32_t"); assertTrue(dt != null); assertTrue(dt instanceof Int32TDataType); - dt = parser.parse("struct mystruct {" + " int32_t field1;" + " char field2;" + " };"); - + assertTrue(dt != null); assertTrue(dt instanceof Structure); Structure sdt = (Structure) dt; @@ -61,7 +61,7 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { assertEquals("int32_t", comp.getDataType().getName()); comp = sdt.getComponent(1); assertEquals("field2", comp.getFieldName()); - assertEquals(comp.getDataType().getName(),"char"); + assertEquals(comp.getDataType().getName(), "char"); } @Test @@ -185,7 +185,7 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { assertEquals(8, pdt64.getLength()); } - + @Test public void testTypedef() throws Exception { CParser parser; @@ -196,9 +196,9 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { assertTrue(tdDt != null); assertTrue(tdDt instanceof TypeDef); System.out.println(tdDt.getPathName()); - System.out.println(((TypeDef)tdDt).getDataType().getPathName()); + System.out.println(((TypeDef) tdDt).getDataType().getPathName()); assertEquals("foo", tdDt.getName()); - assertEquals("foo.conflict *", ((TypeDef)tdDt).getDataType().getName()); + assertEquals("foo.conflict *", ((TypeDef) tdDt).getDataType().getName()); assertEquals(4, tdDt.getLength()); DataType dt = parser.getDataTypeManager().getDataType("/foo"); @@ -206,44 +206,41 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { assertTrue(dt instanceof TypeDef); } - + @Test public void testWcharT() throws Exception { DataType dt; Structure sdt; DataTypeComponent comp; - + CParser parser; - parser = new CParser(); // uses default data organization (i.e., x86-32) dt = parser.parse("typedef short wchar_t;"); - assertTrue(dt instanceof WideCharDataType); parser = new CParser(); dt = parser.parse("struct mystruct {" + - " wchar_t defined_wchar_t;" + - "};"); + " wchar_t defined_wchar_t;" + + "};"); sdt = (Structure) dt; comp = sdt.getComponent(0); assertTrue(comp.getDataType() instanceof WideCharDataType); - + parser = new CParser(); dt = parser.parse("typedef int wchar_t;" + - "struct mystruct {" + - " wchar_t defined_wchar_t;" + - "};"); - + "struct mystruct {" + + " wchar_t defined_wchar_t;" + + "};"); + sdt = (Structure) dt; comp = sdt.getComponent(0); assertTrue(comp.getDataType() instanceof WideCharDataType); - parser = new CParser(); dt = parser.parse("typedef short wchar_t;" + - " typedef wchar_t foo;"); + " typedef wchar_t foo;"); assertTrue(dt != null); assertTrue(dt instanceof TypeDef); @@ -263,7 +260,7 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { @Test public void testParseDataType_NoSubArchive() throws Exception { - DataTypeManager primary = new StandAloneDataTypeManager("primary"); + DataTypeManager primary = new TransientDataTypeManager("primary"); DataTypeManager[] subs = new DataTypeManager[] {}; @@ -280,7 +277,7 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { @Test public void testParseDataType_WithSubArchive() throws Exception { - DataTypeManager primary = new StandAloneDataTypeManager("primary"); + DataTypeManager primary = new TransientDataTypeManager("primary"); DataTypeManager[] subs = new DataTypeManager[] { createDataTypeManagerWithABar() }; @@ -290,7 +287,8 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { } private DataTypeManager createDataTypeManagerWithABar() { - DataTypeManager dtm = new StandAloneDataTypeManager("sub 0"); + + DataTypeManager dtm = new TransientDataTypeManager("sub 0"); int txID = dtm.startTransaction("Add DataType"); try { @@ -332,7 +330,7 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { String parseMessages = parser.getParseMessages(); System.out.println(parseMessages); - + DataType dt; DataType pointedToDT; ParameterDefinition[] funcArgs; @@ -341,19 +339,21 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { dt = dtMgr.getDataType(new CategoryPath("/"), "pragmaPassed"); assertNotNull("Structure after pragma not parsed", dt); - - assertTrue("Duplicate ENUM message missing", parseMessages.contains("duplicate enum value: options_enum : PLUS_SET : 16")); - - assertTrue("Static assert fail missing", parseMessages.contains("Static_Assert possibly failed \"\"math fail!\"\"")); - - assertTrue("Static assert fail missing", parseMessages.contains("Static_Assert possibly failed \"\"1 + 1 == 3, fail!\"\"")); - + + assertTrue("Duplicate ENUM message missing", + parseMessages.contains("duplicate enum value: options_enum : PLUS_SET : 16")); + + assertTrue("Static assert fail missing", + parseMessages.contains("Static_Assert possibly failed \"\"math fail!\"\"")); + + assertTrue("Static assert fail missing", + parseMessages.contains("Static_Assert possibly failed \"\"1 + 1 == 3, fail!\"\"")); + dt = dtMgr.getDataType(new CategoryPath("/"), "_IO_FILE_complete"); Structure sldt = (Structure) dt; DataTypeComponent data3 = sldt.getComponent(2); assertEquals("Computed Array correct", 40, data3.getLength()); - dt = dtMgr.getDataType(new CategoryPath("/"), "fnptr"); // typedef int (*fnptr)(struct fstruct); // "fnptr" named typedef of pointer to "int fnptr(fstruct )" --- should an anonymous function name be used? assertTrue("typedefed fnptr", dt instanceof TypeDef); @@ -365,8 +365,7 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { funcArgs = funcDef.getArguments(); assertTrue("struct fstruct", funcArgs[0].getDataType() instanceof Structure); str = funcDef.getPrototypeString(); - assertEquals("signature not correct", "int fnptr(fstruct )", replaceAnonFuncName(str)); - + assertEquals("signature not correct", "int fnptr(fstruct )", replaceAnonFuncName(str)); // Test dt = dtMgr.getDataType(new CategoryPath("/functions"), "_Once"); // void __cdecl _Once(_Once_t *, void (__cdecl *)(void)); @@ -374,13 +373,14 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { assertTrue("_Once function definition", dt instanceof FunctionDefinition); funcDef = (FunctionDefinition) dt; str = funcDef.getPrototypeString(); - assertEquals("signature not correct", "void _Once(_Once_t * , _func_anon_ * )", replaceAnonFuncName(str)); + assertEquals("signature not correct", "void _Once(_Once_t * , _func_anon_ * )", + replaceAnonFuncName(str)); assertEquals("calling convention _Once", "__cdecl", funcDef.getCallingConventionName()); funcArgs = funcDef.getArguments(); - assertTrue("struct fstruct", funcArgs[0].getDataType() instanceof Pointer); + assertTrue("struct fstruct", funcArgs[0].getDataType() instanceof Pointer); assertTrue("ptr", funcArgs[1].getDataType() instanceof Pointer); pointedToDT = ((Pointer) funcArgs[1].getDataType()).getDataType(); - assertTrue("ptr not to a function", pointedToDT instanceof FunctionDefinition); + assertTrue("ptr not to a function", pointedToDT instanceof FunctionDefinition); funcDef = (FunctionDefinition) pointedToDT; assertEquals("calling convention _Once", "__cdecl", funcDef.getCallingConventionName()); str = funcDef.getPrototypeString(); @@ -394,7 +394,7 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { assertEquals("calling convention _Twice", "__stdcall", funcDef.getCallingConventionName()); funcArgs = funcDef.getArguments(); pointedToDT = ((Pointer) funcArgs[0].getDataType()).getDataType(); - assertTrue("ptr not to a function", pointedToDT instanceof FunctionDefinition); + assertTrue("ptr not to a function", pointedToDT instanceof FunctionDefinition); funcDef = (FunctionDefinition) pointedToDT; assertEquals("calling convention _Once", "__cdecl", funcDef.getCallingConventionName()); str = funcDef.getPrototypeString(); @@ -408,12 +408,12 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { assertEquals("calling convention _Thrice", "unknown", funcDef.getCallingConventionName()); funcArgs = funcDef.getArguments(); pointedToDT = ((Pointer) funcArgs[0].getDataType()).getDataType(); - assertTrue("ptr not to a function", pointedToDT instanceof FunctionDefinition); + assertTrue("ptr not to a function", pointedToDT instanceof FunctionDefinition); funcDef = (FunctionDefinition) pointedToDT; assertEquals("calling convention _Once", "__cdecl", funcDef.getCallingConventionName()); str = funcDef.getPrototypeString(); assertEquals("signature not correct", "void _func_anon_(void)", replaceAnonFuncName(str)); - + dt = dtMgr.getDataType(new CategoryPath("/"), "UShortInt"); assertTrue(dt instanceof TypeDef); assertTrue("signature not correct", @@ -485,7 +485,7 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { dt = dtMgr.getDataType(new CategoryPath("/functions"), "gcc_exit"); assertTrue("not a function", dt instanceof FunctionDefinition); assertTrue("Caller should noreturn", ((FunctionDefinition) dt).hasNoReturn()); - + dt = dtMgr.getDataType(new CategoryPath("/"), "UINT2"); assertTrue(dt instanceof TypeDef); assertEquals("ushort", ((TypeDef) dt).getBaseDataType().getName()); @@ -577,7 +577,7 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { ((Enum) dt).getValue("SHIFTED3")); assertEquals("enum options_enum not correct", 15 >> 3 << 3, ((Enum) dt).getValue("SHIFTED4")); - + dt = dtMgr.getDataType(new CategoryPath("/"), "_C23_enum_char"); assertTrue(dt instanceof Enum); assertEquals("enum _C23_enum_char size not correct", 1, dt.getLength()); @@ -596,31 +596,31 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { dt = dtMgr.getDataType(new CategoryPath("/"), "_C23_enum_DWORD"); assertTrue(dt instanceof Enum); assertEquals("enum _C23_enum_DWORD size not correct", 4, dt.getLength()); - + dt = dtMgr.getDataType(new CategoryPath("/"), "packed_enum_style_1"); assertTrue(dt instanceof Enum); assertEquals("enum packed_enum_style_1 size not correct", 1, dt.getLength()); - + dt = dtMgr.getDataType(new CategoryPath("/"), "packed_enum_style_2"); assertTrue(dt instanceof Enum); assertEquals("enum packed_enum_style_2 size not correct", 1, dt.getLength()); - + dt = dtMgr.getDataType(new CategoryPath("/"), "packed_enum_cpp_style_1"); assertTrue(dt instanceof Enum); assertEquals("enum packed_enum_cpp_style_1 size not correct", 1, dt.getLength()); - + dt = dtMgr.getDataType(new CategoryPath("/"), "packed_enum_cpp_style_2"); assertTrue(dt instanceof Enum); assertEquals("enum packed_enum_cpp_style_2 size not correct", 1, dt.getLength()); - + dt = dtMgr.getDataType(new CategoryPath("/"), "packed_enum_cpp_style_gnu"); assertTrue(dt instanceof Enum); assertEquals("enum packed_enum_cpp_style_gnu size not correct", 1, dt.getLength()); - + dt = dtMgr.getDataType(new CategoryPath("/"), "non_packed_enum"); assertTrue(dt instanceof Enum); assertEquals("enum non_packed_enum size not correct", 4, dt.getLength()); - + dt = dtMgr.getDataType(new CategoryPath("/"), "packed_negative_enum"); assertTrue(dt instanceof Enum); assertEquals("enum packed_negative_enum size not correct", 1, dt.getLength()); @@ -630,7 +630,7 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { ((Enum) dt).getValue("B")); assertEquals("enum packed_negative_enum value not correct", -3, ((Enum) dt).getValue("C")); - + dt = dtMgr.getDataType(new CategoryPath("/"), "normal_negative_enum"); assertTrue(dt instanceof Enum); assertEquals("enum normal_negative_enum size not correct", 4, dt.getLength()); @@ -650,13 +650,13 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { assertTrue("not a function", dt instanceof FunctionDefinition); str = ((FunctionDefinition) dt).getPrototypeString(); assertEquals("signature not correct", "int fputs(char * , void * )", str); - + dt = dtMgr.getDataType(new CategoryPath("/functions"), "funcParam"); assertTrue("not a function", dt instanceof FunctionDefinition); str = ((FunctionDefinition) dt).getPrototypeString(); - - - assertEquals("signature not correct", "void funcParam(_func_anon_ * )", replaceAnonFuncName(str)); + + assertEquals("signature not correct", "void funcParam(_func_anon_ * )", + replaceAnonFuncName(str)); funcDef = (FunctionDefinition) dt; funcArgs = funcDef.getArguments(); assertTrue("ptr", funcArgs[0].getDataType() instanceof Pointer); @@ -665,20 +665,21 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { str = ((FunctionDefinition) pointedToDT).getPrototypeString(); assertEquals("signature not correct", "void _func_anon_(void)", replaceAnonFuncName(str)); - for (int i=1; i < 11; i++) { - dt = dtMgr.getDataType(new CategoryPath("/functions"), "funcParam"+i); + for (int i = 1; i < 11; i++) { + dt = dtMgr.getDataType(new CategoryPath("/functions"), "funcParam" + i); assertTrue("not a function" + dt.getName(), dt instanceof FunctionDefinition); str = ((FunctionDefinition) dt).getPrototypeString(); funcDef = (FunctionDefinition) dt; funcArgs = funcDef.getArguments(); assertTrue("ptr", funcArgs[1].getDataType() instanceof Pointer); pointedToDT = ((Pointer) funcArgs[1].getDataType()).getDataType(); - assertTrue("ptr not to a function " + dt.getName(), pointedToDT instanceof FunctionDefinition); + assertTrue("ptr not to a function " + dt.getName(), + pointedToDT instanceof FunctionDefinition); funcArgs = ((FunctionDefinition) pointedToDT).getArguments(); assertEquals("function args != 1 " + pointedToDT, 1, funcArgs.length); assertEquals("double", funcArgs[0].getDataType().getName()); } - + dt = dtMgr.getDataType(new CategoryPath("/functions"), "funcParamNoPtr"); assertTrue("not a function", dt instanceof FunctionDefinition); str = ((FunctionDefinition) dt).getPrototypeString(); @@ -690,7 +691,7 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { assertTrue("ptr not to a function", pointedToDT instanceof FunctionDefinition); str = ((FunctionDefinition) pointedToDT).getPrototypeString(); assertEquals("signature not correct", "double func(double , void * )", str); - + dt = dtMgr.getDataType(new CategoryPath("/functions"), "funcParmWithName"); assertTrue("not a function", dt instanceof FunctionDefinition); str = ((FunctionDefinition) dt).getPrototypeString(); @@ -699,38 +700,37 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { dt = dtMgr.getDataType(new CategoryPath("/functions"), "__mem_func"); assertTrue("not a function", dt instanceof FunctionDefinition); str = ((FunctionDefinition) dt).getPrototypeString(); - assertEquals("signature not correct", "void __mem_func(" - + "void * ," - + " char * * ," - + " int * * * ," - + " _func_anon_ * ," - + " _func_anon_1 * ," - + " _func_anon_2 * )", replaceAnonFuncName(str)); + assertEquals( + "signature not correct", "void __mem_func(" + "void * ," + " char * * ," + + " int * * * ," + " _func_anon_ * ," + " _func_anon_1 * ," + " _func_anon_2 * )", + replaceAnonFuncName(str)); funcDef = (FunctionDefinition) dt; funcArgs = funcDef.getArguments(); assertTrue("ptr", funcArgs[5].getDataType() instanceof Pointer); pointedToDT = ((Pointer) funcArgs[5].getDataType()).getDataType(); assertTrue("ptr not to a function", pointedToDT instanceof FunctionDefinition); str = ((FunctionDefinition) pointedToDT).getPrototypeString(); - assertEquals("signature not correct", "void * _func_anon_(void * , size_t )", replaceAnonFuncName(str)); - + assertEquals("signature not correct", "void * _func_anon_(void * , size_t )", + replaceAnonFuncName(str)); + // ensure that temporary anonymous function definition names did not get retained ArrayList list = new ArrayList<>(); dtMgr.findDataTypes("_func_", list); assertTrue(list.isEmpty()); dtMgr.findDataTypes("_func_1", list); assertTrue( - "Expected anonymous function replaced (is blarg first function in CParserTest.h file?):" + - list, - list.isEmpty()); + "Expected anonymous function replaced (is blarg first function in CParserTest.h file?):" + + list, + list.isEmpty()); dt = dtMgr.getDataType(new CategoryPath("/functions"), "blarg"); assertTrue("named function blarg", dt instanceof FunctionDefinition); str = ((FunctionDefinition) dt).getPrototypeString(); - assertEquals("signature not correct", "void blarg(int * , long[0][0] * )", replaceAnonFuncName(str)); + assertEquals("signature not correct", "void blarg(int * , long[0][0] * )", + replaceAnonFuncName(str)); - // Structure extension + // Structure extension dt = dtMgr.getDataType(new CategoryPath("/"), "System_System_SystemException_Fields"); - assertTrue (dt instanceof Structure); + assertTrue(dt instanceof Structure); sdt = (Structure) dt; comp = sdt.getComponent(1); assertEquals("foo", comp.getFieldName()); @@ -738,23 +738,24 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { assertEquals("bar", comp.getFieldName()); assertEquals("short", comp.getDataType().getName()); comp = sdt.getComponent(0); - assertTrue (comp.getDataType() instanceof Structure); - assertEquals ("extended parent ", "System_SystemException_Fields", comp.getDataType().getName()); + assertTrue(comp.getDataType() instanceof Structure); + assertEquals("extended parent ", "System_SystemException_Fields", + comp.getDataType().getName()); sdt = (Structure) comp.getDataType(); comp = sdt.getComponent(0); - assertTrue (comp.getDataType() instanceof Structure); - assertEquals ("extended parent ", "System_Exception_Fields", comp.getDataType().getName()); - + assertTrue(comp.getDataType() instanceof Structure); + assertEquals("extended parent ", "System_Exception_Fields", comp.getDataType().getName()); // Check arrays of functions in structures dt = dtMgr.getDataType(new CategoryPath("/"), "SomeStruct"); - assertTrue (dt instanceof Structure); + assertTrue(dt instanceof Structure); sdt = (Structure) dt; - + int numComponents = sdt.getNumComponents(); - assertEquals("Number of components in struct arrays of function pointer + \n" + sdt.toString(), - 8, numComponents); - + assertEquals( + "Number of components in struct arrays of function pointer + \n" + sdt.toString(), + 8, numComponents); + DataTypeComponent component = sdt.getComponent(2); assertEquals("procArray1", component.getFieldName()); dt = component.getDataType(); @@ -763,12 +764,14 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { dt = ((Array) dt).getDataType(); assertTrue("ProcArray1 is not an Array of pointers", dt instanceof Pointer); dt = ((Pointer) dt).getDataType(); - assertTrue("procArray1 member is Array of function pointers", dt instanceof FunctionDefinition); + assertTrue("procArray1 member is Array of function pointers", + dt instanceof FunctionDefinition); funcDef = (FunctionDefinition) dt; funcArgs = funcDef.getArguments(); str = funcDef.getPrototypeString(); - assertEquals("signature not correct", "char _func_anon_(int * , short * )", replaceAnonFuncName(str)); - + assertEquals("signature not correct", "char _func_anon_(int * , short * )", + replaceAnonFuncName(str)); + dt = dtMgr.getDataType(new CategoryPath("/"), "EmptyBuffer"); assertTrue(dt instanceof Structure); sdt = (Structure) dt; @@ -891,13 +894,13 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { int num = 0; String replStr = str; String origStr = null; - + while (!replStr.equals(origStr)) { - origStr = replStr; - replStr = replStr.replaceFirst("_func_([0-9])+", "_func_anon_" + (num == 0 ? "" : num)); - num++; + origStr = replStr; + replStr = replStr.replaceFirst("_func_([0-9])+", "_func_anon_" + (num == 0 ? "" : num)); + num++; } - + return replStr; } } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/PreProcessorTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/PreProcessorTest.java index 5a1d087d61..053176ec67 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/PreProcessorTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/PreProcessorTest.java @@ -25,6 +25,7 @@ import org.junit.*; import generic.test.AbstractGenericTest; import ghidra.app.util.cparser.CPP.ParseException; import ghidra.app.util.cparser.CPP.PreProcessor; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; @@ -32,12 +33,12 @@ public class PreProcessorTest extends AbstractGenericTest { private static String resourceName = "PreProcessorTest.h"; private static CategoryPath path = new CategoryPath(new CategoryPath("/PreProcessorTest.h"), "defines"); - + private static CategoryPath definedPath = - new CategoryPath(new CategoryPath("/defined.h"), "defines"); - + new CategoryPath(new CategoryPath("/defined.h"), "defines"); + // must get rid of after all tests - private static StandAloneDataTypeManager dtMgr; + private static TransientDataTypeManager dtMgr; private static ByteArrayOutputStream baos = new ByteArrayOutputStream(); private static PreProcessor parser; @@ -87,18 +88,20 @@ public class PreProcessorTest extends AbstractGenericTest { catch (ParseException e) { e.printStackTrace(); } - + System.out.println(parser.getParseMessages()); // Uncomment to print out parse results //System.err.println(baos.toString()); - dtMgr = new StandAloneDataTypeManager("parsed"); + dtMgr = new TransientDataTypeManager("parsed"); parser.getDefinitions().populateDefineEquates(null, dtMgr); } @AfterClass public static void destroy() { + dtMgr.close(); + dtMgr = null; dtMgr = null; baos = null; parser = null; @@ -111,54 +114,54 @@ public class PreProcessorTest extends AbstractGenericTest { int end = results.lastIndexOf(";") + 1; String endStr = results.substring(end - 9, end); assertEquals("theEnd();", endStr); - + assertTrue("macro expansion _fpl(bob) failed ", results .indexOf("extern int __declspec(\"fp(\\\"l\\\", \" #bob \")\") __ifplbob;") != -1); - + assertTrue("Expanded protected macro with args", results.contains("int (getc)(FILE * );")); assertTrue("Expanded protected macro with args", results.contains("int (getchar)(void);")); assertTrue("multi line string macro args failed ", results - .indexOf("0x1 = multi_line_worked(\"Some text first line\"\n" + - "\"More text second line\")") != -1); - + .indexOf("0x1 = multi_line_worked(\"Some text first line\"\n" + + "\"More text second line\")") != -1); + assertTrue("multi line string macro args failed ", results - .indexOf("D = dual_line_worked(2,\"Caution: First line\"\n" + - "\" second line\"\n" + - "\" third line\"\n" + - "\" fourth line\")") != -1); + .indexOf("D = dual_line_worked(2,\"Caution: First line\"\n" + + "\" second line\"\n" + + "\" third line\"\n" + + "\" fourth line\")") != -1); assertTrue("multi line #pragma failed ", results - .indexOf("#pragma multiple lines pragma") != -1); - + .indexOf("#pragma multiple lines pragma") != -1); + assertTrue("#pragma with comment failed ", results - .indexOf("#pragma no comment here") != -1); - + .indexOf("#pragma no comment here") != -1); + assertTrue("#pragma with EOL comment failed ", results - .indexOf("#pragma with no EOL comment here") != -1); + .indexOf("#pragma with no EOL comment here") != -1); } - + @Test public void testCommenting() throws Exception { String results = baos.toString("ASCII"); - + assertTrue("IntShouldBeCommented", results .indexOf("///- int IntShouldBeCommented;") != -1); assertTrue("PragmaShouldBeCommented", results - .indexOf("///- #pragma PragmaShouldBeCommented") != -1); - + .indexOf("///- #pragma PragmaShouldBeCommented") != -1); + assertTrue("IntShouldNotBeCommented", results - .indexOf("int IntShouldBeCommented;") != -1); + .indexOf("int IntShouldBeCommented;") != -1); assertTrue("PragmaShouldNotBeCommented", results - .indexOf("#pragma PragmaShouldNotBeCommented") != -1); + .indexOf("#pragma PragmaShouldNotBeCommented") != -1); } @Test public void testDefines() throws Exception { long value; String defname; - + value = 32516; defname = "DefVal1"; checkDefine(dtMgr, path, value, defname); @@ -198,7 +201,7 @@ public class PreProcessorTest extends AbstractGenericTest { value = ((0x7fff) * 900L / 1000); defname = "DefVal10"; checkDefine(dtMgr, path, value, defname); - + value = 1; defname = "DefVal_1L"; checkDefine(dtMgr, path, value, defname); @@ -210,11 +213,11 @@ public class PreProcessorTest extends AbstractGenericTest { value = 3; defname = "DefVal_3U"; checkDefine(dtMgr, path, value, defname); - + value = 4; defname = "DefVal_4u"; checkDefine(dtMgr, path, value, defname); - + value = 5; defname = "DefVal_5UL"; checkDefine(dtMgr, path, value, defname); @@ -238,7 +241,7 @@ public class PreProcessorTest extends AbstractGenericTest { value = 10; defname = "DefVal_10ll"; checkDefine(dtMgr, path, value, defname); - + value = 1; defname = "DefVal_P_1L"; checkDefine(dtMgr, path, value, defname); @@ -250,11 +253,11 @@ public class PreProcessorTest extends AbstractGenericTest { value = 3; defname = "DefVal_P_3U"; checkDefine(dtMgr, path, value, defname); - + value = 4; defname = "DefVal_P_4u"; checkDefine(dtMgr, path, value, defname); - + value = 5; defname = "DefVal_P_5UL"; checkDefine(dtMgr, path, value, defname); @@ -278,7 +281,7 @@ public class PreProcessorTest extends AbstractGenericTest { value = 10; defname = "DefVal_P_10ll"; checkDefine(dtMgr, path, value, defname); - + value = 0; defname = "TOO_MANY_FISH"; checkDefine(dtMgr, path, value, defname); @@ -345,7 +348,7 @@ public class PreProcessorTest extends AbstractGenericTest { String defval = parser.getDef(defname); assertEquals(defval, "QUOTED('\"')"); } - + @Test public void testDefinesArgDef() { defname = "DID_ARG_DEF"; @@ -439,24 +442,24 @@ public class PreProcessorTest extends AbstractGenericTest { // if a define is not defined, getDef() returns name of define as value assertEquals("No INCLUDE5 define", "INCLUDE5", defval); } - + @Test public void testDefinedInclude() { defname = "DID_INCLUDE_DEFINED_INCLUDED"; value = 1; checkDefine(dtMgr, definedPath, value, defname); } - + @Test public void testVarags() { defname = "EPRINTF_VARARGS"; String defval = parser.getDef(defname); assertEquals("fprintf (stderr, \"%s:%d: \", input_file, lineno)", defval); - + defname = "VPRINTF_NO_ARGS"; defval = parser.getDef(defname); assertEquals("fprintf (stderr, \"no args!\\n\" )", defval); - + defname = "VPRINTF_ARGS"; defval = parser.getDef(defname); assertEquals("fprintf (stderr, \"%s!\\n\" , \"I have args\")", defval); @@ -468,70 +471,69 @@ public class PreProcessorTest extends AbstractGenericTest { value = 1; int length = 1; checkDefineEnumLength(dtMgr, path, value, defname, length); - + defname = "BYTE_LEN_8"; value = 8; length = 1; checkDefineEnumLength(dtMgr, path, value, defname, length); - + defname = "BYTE_LEN_1F"; value = 0x1f; length = 1; checkDefineEnumLength(dtMgr, path, value, defname, length); - + defname = "BYTE_LEN_FF"; value = 0xff; length = 1; checkDefineEnumLength(dtMgr, path, value, defname, length); - + defname = "BYTE_LEN_1FF"; value = 0x1ff; length = 2; checkDefineEnumLength(dtMgr, path, value, defname, length); - + defname = "BYTE_LEN_7FFF"; value = 0x7fff; length = 2; checkDefineEnumLength(dtMgr, path, value, defname, length); - + defname = "BYTE_LEN_10000"; value = 0x10000; length = 4; checkDefineEnumLength(dtMgr, path, value, defname, length); - + defname = "BYTE_LEN_1000000"; value = 0x1000000; length = 4; checkDefineEnumLength(dtMgr, path, value, defname, length); - + defname = "BYTE_LEN_100000000"; value = 0x100000000L; length = 8; checkDefineEnumLength(dtMgr, path, value, defname, length); - + defname = "BYTE_LEN_10000000000"; value = 0x10000000000L; length = 8; checkDefineEnumLength(dtMgr, path, value, defname, length); - + defname = "BYTE_LEN_1000000000000"; value = 0x1000000000000L; length = 8; checkDefineEnumLength(dtMgr, path, value, defname, length); - + defname = "BYTE_LEN_100000000000000"; value = 0x100000000000000L; length = 8; checkDefineEnumLength(dtMgr, path, value, defname, length); - + defname = "BYTE_LEN_neg1"; value = -1; length = 1; checkDefineEnumLength(dtMgr, path, value, defname, length); } - - - private DataType checkDefine(StandAloneDataTypeManager dtMgr, CategoryPath path, long value, + + private DataType checkDefine(DataTypeManager dtMgr, CategoryPath path, long value, String defname) { DataType dataType = dtMgr.getDataType(path, "define_" + defname); String msg = "Define Enum " + defname; @@ -540,14 +542,15 @@ public class PreProcessorTest extends AbstractGenericTest { assertEquals(msg, value, ((Enum) dataType).getValue(defname)); return dataType; } - - private void checkDefineEnumLength(StandAloneDataTypeManager dtMgr, CategoryPath path, long value, + + private void checkDefineEnumLength(DataTypeManager dtMgr, CategoryPath path, + long value, String defname, int length) { DataType dt = checkDefine(dtMgr, path, value, defname); assertEquals("Expected " + defname + " length " + length, length, dt.getLength()); } - private void checkNotDefine(StandAloneDataTypeManager dtMgr, CategoryPath path, + private void checkNotDefine(DataTypeManager dtMgr, CategoryPath path, String defname) { DataType dataType = dtMgr.getDataType(path, "define_" + defname); assertNull(dataType); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/MergeTestFacilitator.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/MergeTestFacilitator.java index 66bcf0ef1a..f660fec36c 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/MergeTestFacilitator.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/MergeTestFacilitator.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -74,7 +74,7 @@ public class MergeTestFacilitator { model.initialize(programName, modifier); } - static DomainFile copyDatabaseDomainFile(DomainFile df, String newName) + public static DomainFile copyDatabaseDomainFile(DomainFile df, String newName) throws IOException, InvalidNameException, CancelledException { return AbstractMTFModel.copyDatabaseDomainFile(df, newName); } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeTest.java index 5939c6b36b..aaf1544b78 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,6 +23,7 @@ import java.util.HashMap; import generic.test.AbstractGenericTest; import ghidra.app.util.cparser.C.CParser; import ghidra.app.util.cparser.C.ParseException; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.util.Msg; import resources.ResourceManager; @@ -92,7 +93,7 @@ abstract class AbstractCompositeTest extends AbstractGenericTest { // } protected final DataTypeManager createDataTypeManager(String name, DataOrganization dataOrg) { - return new StandAloneDataTypeManager(name, dataOrg); + return new TransientDataTypeManager(name, dataOrg); } abstract DataTypeManager getDataTypeManager(); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java index 03c480fed6..f9b3191cd5 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java @@ -23,6 +23,7 @@ import org.apache.commons.compress.utils.Sets; import org.junit.*; import generic.test.AbstractGenericTest; +import ghidra.program.database.data.TransientDataTypeManager; public class StructureDataTypeTest extends AbstractGenericTest { @@ -2745,7 +2746,7 @@ public class StructureDataTypeTest extends AbstractGenericTest { protected DataTypeManager createBigEndianDataTypeManager() { DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); dataOrg.setBigEndian(true); - return new StandAloneDataTypeManager("BEdtm", dataOrg); + return new TransientDataTypeManager("BEdtm", dataOrg); } } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionDataTypeTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionDataTypeTest.java index 53bc7ff554..f4020c821e 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionDataTypeTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionDataTypeTest.java @@ -22,6 +22,7 @@ import org.junit.*; import com.google.common.collect.Sets; import generic.test.AbstractGenericTest; +import ghidra.program.database.data.TransientDataTypeManager; public class UnionDataTypeTest extends AbstractGenericTest { @@ -650,6 +651,6 @@ public class UnionDataTypeTest extends AbstractGenericTest { protected DataTypeManager createBigEndianDataTypeManager() { DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); dataOrg.setBigEndian(true); - return new StandAloneDataTypeManager("BEdtm", dataOrg); + return new TransientDataTypeManager("BEdtm", dataOrg); } } diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeTypeTest.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeTypeTest.java index 03d8e7beeb..8b5d904fca 100644 --- a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeTypeTest.java +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeTypeTest.java @@ -26,6 +26,7 @@ import ghidra.app.util.SymbolPath; import ghidra.app.util.importer.MessageLog; import ghidra.app.util.pdb.*; import ghidra.app.util.pdb.classtype.*; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; import ghidra.program.model.data.DataUtilities.ClearDataMode; @@ -75,8 +76,8 @@ public class CppCompositeTypeTest extends AbstractGenericTest { ClearDataMode clearMode = ClearDataMode.CLEAR_ALL_CONFLICT_DATA; - private DataTypeManager dtm32 = new StandAloneDataTypeManager("32-bit win", dataOrg32); - private DataTypeManager dtm64 = new StandAloneDataTypeManager("64-bit win", dataOrg64); + private DataTypeManager dtm32 = new TransientDataTypeManager("32-bit win", dataOrg32); + private DataTypeManager dtm64 = new TransientDataTypeManager("64-bit win", dataOrg64); private static MyTestDummyDataTypeManager dtm32old = new MyTestDummyDataTypeManager(dataOrg32); private static MyTestDummyDataTypeManager dtm64old = new MyTestDummyDataTypeManager(dataOrg64); @@ -2698,12 +2699,12 @@ public class CppCompositeTypeTest extends AbstractGenericTest { Map tables = new TreeMap<>(); if (vbts != null) { for (VirtualBaseTable vbt : vbts) { - tables.put(((ProgramVirtualBaseTable) vbt).getPtrOffsetInClass(), vbt); + tables.put(vbt.getPtrOffsetInClass(), vbt); } } if (vfts != null) { for (VirtualFunctionTable vft : vfts) { - tables.put(((ProgramVirtualFunctionTable) vft).getPtrOffsetInClass(), vft); + tables.put(vft.getPtrOffsetInClass(), vft); } } StringBuilder builder2 = new StringBuilder(); diff --git a/Ghidra/Features/Rust/src/test.slow/java/ghidra/app/plugin/core/datamgr/RustDataTypeArchiveIDTest.java b/Ghidra/Features/Rust/src/test.slow/java/ghidra/app/plugin/core/datamgr/RustDataTypeArchiveIDTest.java index 61730a87a2..2b69de94be 100644 --- a/Ghidra/Features/Rust/src/test.slow/java/ghidra/app/plugin/core/datamgr/RustDataTypeArchiveIDTest.java +++ b/Ghidra/Features/Rust/src/test.slow/java/ghidra/app/plugin/core/datamgr/RustDataTypeArchiveIDTest.java @@ -27,8 +27,12 @@ import org.junit.Test; import generic.jar.ResourceFile; import generic.test.AbstractGenericTest; import ghidra.framework.Application; -import ghidra.program.model.data.FileDataTypeManager; -import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; +import ghidra.program.model.dtarchive.ArchiveWarning; +import ghidra.program.model.dtarchive.FileDataTypeArchive; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; public class RustDataTypeArchiveIDTest extends AbstractGenericTest { @@ -45,17 +49,18 @@ public class RustDataTypeArchiveIDTest extends AbstractGenericTest { } private String getGdtUniversalId(ResourceFile gdtFile) { - FileDataTypeManager dtm = null; + FileDataTypeArchive archive = null; try { - dtm = FileDataTypeManager.openFileArchive(gdtFile, false); - assertEquals(dtm.getWarningMessage(true), ArchiveWarning.NONE, dtm.getWarning()); - return dtm.getUniversalID().toString(); + archive = DataTypeArchiveFactory.openReadOnly(gdtFile, this, TaskMonitor.DUMMY); + assertEquals(archive.getWarningMessage(true), ArchiveWarning.NONE, + archive.getWarning()); + return archive.getUniversalID().toString(); } - catch (IOException e) { + catch (IOException | CancelledException | VersionException e) { return "failed to read " + gdtFile.getName(); } finally { - dtm.close(); + archive.release(this); } } diff --git a/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/emu/linux/EmuLinuxAmd64SyscallUseropLibrary.java b/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/emu/linux/EmuLinuxAmd64SyscallUseropLibrary.java index 95a89923ba..d6841bc4b9 100644 --- a/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/emu/linux/EmuLinuxAmd64SyscallUseropLibrary.java +++ b/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/emu/linux/EmuLinuxAmd64SyscallUseropLibrary.java @@ -26,9 +26,13 @@ import ghidra.pcode.emu.unix.EmuUnixFileSystem; import ghidra.pcode.emu.unix.EmuUnixUser; import ghidra.pcode.exec.SleighPcodeUseropDefinition; import ghidra.pcode.exec.SleighPcodeUseropDefinition.BuilderStage1; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.program.model.listing.Program; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * A system call library simulating Linux for amd64 / x86_64 @@ -37,7 +41,7 @@ import ghidra.program.model.listing.Program; */ public class EmuLinuxAmd64SyscallUseropLibrary extends AbstractEmuLinuxSyscallUseropLibrary { - protected FileDataTypeManager clib64; + protected FileDataTypeArchive clib64; /** * Construct the system call library for Linux-amd64 @@ -71,17 +75,17 @@ public class EmuLinuxAmd64SyscallUseropLibrary extends AbstractEmuLinuxSyscal try { ResourceFile file = Application.findDataFileInAnyModule("typeinfo/generic/generic_clib_64.gdt"); - clib64 = FileDataTypeManager.openFileArchive(file, false); - return List.of(clib64); + clib64 = DataTypeArchiveFactory.openReadOnly(file, this, TaskMonitor.DUMMY); + return List.of(clib64.getDataTypeManager()); } - catch (IOException e) { + catch (IOException | VersionException | CancelledException e) { throw new AssertionError(e); } } @Override protected void disposeAdditionalArchives() { - clib64.close(); + clib64.release(this); } @PcodeUserop diff --git a/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/emu/linux/EmuLinuxX86SyscallUseropLibrary.java b/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/emu/linux/EmuLinuxX86SyscallUseropLibrary.java index b80347bbb4..6788cf60bb 100644 --- a/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/emu/linux/EmuLinuxX86SyscallUseropLibrary.java +++ b/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/emu/linux/EmuLinuxX86SyscallUseropLibrary.java @@ -27,11 +27,15 @@ import ghidra.pcode.emu.jit.decode.CanDecode; import ghidra.pcode.emu.unix.EmuUnixFileSystem; import ghidra.pcode.emu.unix.EmuUnixUser; import ghidra.pcode.exec.*; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.address.Address; import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.program.model.listing.Program; import ghidra.program.model.pcode.Varnode; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * A system call library simulating Linux for x86 (32-bit) @@ -40,7 +44,7 @@ import ghidra.program.model.pcode.Varnode; */ public class EmuLinuxX86SyscallUseropLibrary extends AbstractEmuLinuxSyscallUseropLibrary { - protected FileDataTypeManager clib32; + protected FileDataTypeArchive clib32; /** * Construct the system call library for Linux-x86 @@ -74,17 +78,17 @@ public class EmuLinuxX86SyscallUseropLibrary extends AbstractEmuLinuxSyscallU try { ResourceFile file = Application.findDataFileInAnyModule("typeinfo/generic/generic_clib.gdt"); - clib32 = FileDataTypeManager.openFileArchive(file, false); - return List.of(clib32); + clib32 = DataTypeArchiveFactory.openReadOnly(file, this, TaskMonitor.DUMMY); + return List.of(clib32.getDataTypeManager()); } - catch (IOException e) { + catch (IOException | CancelledException | VersionException e) { throw new AssertionError(e); } } @Override protected void disposeAdditionalArchives() { - clib32.close(); + clib32.release(this); } /** diff --git a/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/struct/StructuredSleigh.java b/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/struct/StructuredSleigh.java index 0320d30573..a40cc82859 100644 --- a/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/struct/StructuredSleigh.java +++ b/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/struct/StructuredSleigh.java @@ -34,6 +34,7 @@ import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition; import ghidra.pcode.exec.SleighPcodeUseropDefinition.BuilderStage1; import ghidra.pcode.floatformat.FloatFormatFactory; import ghidra.pcode.struct.DefaultVar.Check; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.data.*; import ghidra.program.model.lang.*; @@ -1161,7 +1162,7 @@ public class StructuredSleigh { // Used to determine statement binding, e.g., for "_break" and "_result" final Deque stack = new LinkedList<>(); // Collects data types used in annotations and during code generation - final StandAloneDataTypeManager dtm; + final TransientDataTypeManager dtm; private final List dtSources = new ArrayList<>(); // The next "free" label @@ -1191,7 +1192,7 @@ public class StructuredSleigh { protected StructuredSleigh(CompilerSpec cs) { this.language = (SleighLanguage) cs.getLanguage(); this.parser = SleighProgramCompiler.createParser(language); - this.dtm = new StandAloneDataTypeManager("/", cs.getDataOrganization()); + this.dtm = new TransientDataTypeManager("/", cs.getDataOrganization()); addDataTypeSource(dtm); addDataTypeSource(BuiltInDataTypeManager.getDataTypeManager()); diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/util/EmptyVTSession.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/util/EmptyVTSession.java index 3be5f7003a..04494320f1 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/util/EmptyVTSession.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/util/EmptyVTSession.java @@ -57,6 +57,11 @@ public class EmptyVTSession implements VTSession { return "Empty"; } + @Override + public String getPath() { + return "None"; + } + @Override public Program getSourceProgram() { return null; diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainFileProxy.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainFileProxy.java index 2205485e9e..bd41578f71 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainFileProxy.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainFileProxy.java @@ -44,6 +44,7 @@ import ghidra.util.task.TaskMonitor; public class DomainFileProxy implements DomainFile { private DomainObjectAdapter domainObj; + private DomainFile originalDomainFile; private ProjectLocator projectLocation; private String name; private int version; @@ -51,7 +52,7 @@ public class DomainFileProxy implements DomainFile { private long lastModified = 0; private String fileID; - public DomainFileProxy(String name, DomainObjectAdapter doa) { + DomainFileProxy(String name, DomainObjectAdapter doa) { domainObj = doa; this.name = name; doa.setDomainFile(this); @@ -59,14 +60,15 @@ public class DomainFileProxy implements DomainFile { version = DomainFile.DEFAULT_VERSION; } - DomainFileProxy(String name, String parentPath, DomainObjectAdapter doa, int version, - String fileID, ProjectLocator projectLocation) throws IOException { - - this(name, doa); - this.parentPath = parentPath; + DomainFileProxy(DomainFile originalDomainFile, DomainObjectAdapter doa, int version) + throws IOException { + this(originalDomainFile.getName(), doa); + this.originalDomainFile = originalDomainFile; + DomainFolder parent = originalDomainFile.getParent(); + this.parentPath = parent.getPathname(); this.version = version; - this.fileID = fileID; - this.projectLocation = projectLocation; + this.fileID = originalDomainFile.getFileID(); + this.projectLocation = parent.getProjectLocator(); } @Override @@ -582,4 +584,11 @@ public class DomainFileProxy implements DomainFile { return new HashMap<>(); } + /** + * {@return the original DomainFile which was used to open the associated domain object or null} + */ + public DomainFile getOriginalDomainFile() { + return originalDomainFile; + } + } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectAdapter.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectAdapter.java index 19fa3dbf2e..107f66dfcb 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectAdapter.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectAdapter.java @@ -146,6 +146,11 @@ public abstract class DomainObjectAdapter implements DomainObject { return name; } + @Override + public String getPath() { + return domainFile.getPathname(); + } + @Override public String toString() { String classname = getClass().getName(); @@ -499,5 +504,4 @@ public abstract class DomainObjectAdapter implements DomainObject { throw new DomainObjectException(e); } - } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectDbModule.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectDbModule.java new file mode 100644 index 0000000000..6e73339c43 --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectDbModule.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.data; + +import java.io.IOException; + +import ghidra.framework.model.DomainObject; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * Common interface for modules in a {@link DomainObject}. Modules manage specific database + * subsections of a DomainObject's implementation. + * @param the DomainObject type which contains this module + */ +public interface DomainObjectDbModule { + + /** + * Callback from program used to indicate all manager have been created. + * When this method is invoked, all managers have been instantiated but may not be fully + * initialized. + * + * @param dobj the domain object implementation is set when all the initializations have been + * completed. + */ + void setDomainObject(T dobj); + + /** + * Callback from domain object made to each manager after the all module initializations have + * been completed. This method may be used by managers to perform additional upgrading which + * may have been deferred. + * + * @param openMode the mode that the program is being opened. + * @param currentRevision current program revision. If openMode is UPGRADE, this value reflects + * the pre-upgrade value. + * @param monitor the task monitor to use in any upgrade operations. + * @throws IOException if a database io error occurs. + * @throws CancelledException if the user cancelled the operation via the task monitor. + */ + void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + throws IOException, CancelledException; + + /** + * Clears all data caches. + * + * @param all if false, some managers may not need to update their cache if they can + * tell that its not necessary. If this flag is true, then all managers should clear + * their cache no matter what. + * @throws IOException if a database io error occurs. + */ + void invalidateCache(boolean all) throws IOException; + + /** + * Callback from the program after being closed to signal this manager to release memory and + * resources. + */ + default void dispose() { + // default do nothing + } +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/GhidraFileData.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/GhidraFileData.java index 3dc3db83ca..c4c8a21c5b 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/GhidraFileData.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/GhidraFileData.java @@ -18,8 +18,8 @@ package ghidra.framework.data; import java.awt.*; import java.io.*; import java.net.URL; -import java.util.HashMap; -import java.util.Map; +import java.util.*; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.Icon; @@ -309,10 +309,24 @@ public class GhidraFileData { * @throws FileInUseException if file is in-use or busy */ void checkInUse() throws FileInUseException { + synchronized (fileSystem) { + + // Check for object open for update if (busy.get() || getOpenedDomainObject() != null) { throw new FileInUseException(name + " is in use"); } + + // Check for read-only object use + GhidraFile domainFile = getDomainFile(); + List proxyObjs = new ArrayList<>(); + TransientDataManager.getTransients(proxyObjs); + for (DomainFile df : proxyObjs) { + DomainFileProxy proxy = (DomainFileProxy) df; + if (domainFile.equals(proxy.getOriginalDomainFile())) { + throw new FileInUseException(name + " is in use"); + } + } } } @@ -641,6 +655,7 @@ public class GhidraFileData { domainObj = getOpenedDomainObject(); if (domainObj != null) { if (!domainObj.addConsumer(consumer)) { + // cleanup stale object reference domainObj = null; projectData.clearDomainObject(getPathname()); } @@ -650,20 +665,9 @@ public class GhidraFileData { } ContentHandler contentHandler = getContentHandler(); if (folderItem == null) { - DomainObjectAdapter doa = contentHandler.getReadOnlyObject(versionedFolderItem, - DomainFile.DEFAULT_VERSION, true, consumer, monitor); - doa.setChanged(false); - DomainFileProxy proxy = new DomainFileProxy(name, parent.getPathname(), doa, - DomainFile.DEFAULT_VERSION, fileID, parent.getProjectLocator()); - proxy.setLastModified(getLastModifiedTime()); - // Notify file manager of in-use domain object. - // A link-file object is indirect with tracking intiated by the URL-referenced file. - if (!isLink()) { - projectData.trackDomainFileInUse(doa); - } - - return doa; + openInProgress = false; + return getReadOnlyDomainObject(consumer, DomainFile.DEFAULT_VERSION, monitor); } myFolderItem = folderItem; @@ -732,10 +736,18 @@ public class GhidraFileData { openInProgress = true; try { FolderItem item = getFolderItem(version); - + long lastModified = 0; DomainObjectAdapter doa; ContentHandler contentHandler = getContentHandler(); if (contentHandler instanceof LinkHandler linkHandler) { + + // NOTE: Links can only be used to access current version + if (version != DomainFile.DEFAULT_VERSION && version != 1) { + throw new FileNotFoundException( + "Link versions are not supported: " + version); + } + version = DomainFile.DEFAULT_VERSION; // only latest version of linked content may be accessed + String resolvedLinkPath = getLinkPath(true); if (!GhidraURL.isGhidraURL(resolvedLinkPath)) { @@ -744,13 +756,15 @@ public class GhidraFileData { throw new FileNotFoundException( "Linked file not found: " + resolvedLinkPath); } - return file.getReadOnlyDomainObject(consumer, version, monitor); + return file.getReadOnlyDomainObject(consumer, DomainFile.DEFAULT_VERSION, + monitor); } // Handle link to Ghidra URL try { URL ghidraUrl = GhidraURL.toURL(resolvedLinkPath); - doa = linkHandler.getObject(ghidraUrl, version, consumer, monitor, false); + doa = linkHandler.getObject(ghidraUrl, DomainFile.DEFAULT_VERSION, consumer, + monitor, false); } catch (IllegalArgumentException e) { // Bad URL from link path @@ -759,20 +773,23 @@ public class GhidraFileData { } } else { - doa = contentHandler.getReadOnlyObject(item, version, true, consumer, monitor); + Version versionData = getVersion(version); + //version = versionData.getVersion(); + lastModified = versionData.getCreateTime(); + doa = contentHandler.getReadOnlyObject(item, version, true, + consumer, monitor); } doa.setChanged(false); // Notify file manager of in-use domain object. - // A link-file object is indirect with tracking intiated by the URL-referenced file. + // A link-file object is indirect with tracking initiated by the URL-referenced file. if (!isLink()) { projectData.trackDomainFileInUse(doa); } - DomainFileProxy proxy = new DomainFileProxy(name, getParent().getPathname(), doa, - version, fileID, parent.getProjectLocator()); - proxy.setLastModified(getLastModifiedTime()); + DomainFileProxy proxy = new DomainFileProxy(getDomainFile(), doa, version); + proxy.setLastModified(lastModified); return doa; } finally { @@ -781,6 +798,31 @@ public class GhidraFileData { } } + private Version getVersion(int version) throws IOException { + if (version == DomainFile.DEFAULT_VERSION) { + if (folderItem != null) { + return new Version(DomainFile.DEFAULT_VERSION, + folderItem.lastModified(), null, null); + } + return new Version(versionedFolderItem.getCurrentVersion(), + versionedFolderItem.lastModified(), null, null); + } + if (versionedFolderItem == null) { + if (isVersioned()) { + throw new IOException( + "Must be connected to repository to access specific version: " + version); + } + throw new IOException("Cannot access version for non-versioned file: " + version); + } + for (Version v : versionedFolderItem.getVersions()) { + if (v.getVersion() == version) { + return v; + } + } + throw new FileNotFoundException( + "Version " + version + " not found for " + getPathname()); + } + /** * Returns a new DomainObject that cannot be changed or saved to its original file. * NOTE: The use of this method should generally be avoided since it can't @@ -808,9 +850,18 @@ public class GhidraFileData { openInProgress = true; try { FolderItem item = getFolderItem(version); + long lastModified = 0; DomainObjectAdapter doa; ContentHandler contentHandler = getContentHandler(); if (contentHandler instanceof LinkHandler linkHandler) { + + // NOTE: Links can only be used to access current version + if (version != DomainFile.DEFAULT_VERSION && version != 1) { + throw new FileNotFoundException( + "Link versions are not supported: " + version); + } + version = DomainFile.DEFAULT_VERSION; // only latest version of linked content may be accessed + String resolvedLinkPath = getLinkPath(true); if (!GhidraURL.isGhidraURL(resolvedLinkPath)) { @@ -819,13 +870,15 @@ public class GhidraFileData { throw new FileNotFoundException( "Linked file not found: " + resolvedLinkPath); } - return file.getImmutableDomainObject(consumer, version, monitor); + return file.getImmutableDomainObject(consumer, DomainFile.DEFAULT_VERSION, + monitor); } // Handle link to Ghidra URL try { URL ghidraUrl = GhidraURL.toURL(resolvedLinkPath); - doa = linkHandler.getObject(ghidraUrl, version, consumer, monitor, true); + doa = linkHandler.getObject(ghidraUrl, DomainFile.DEFAULT_VERSION, consumer, + monitor, true); } catch (IllegalArgumentException e) { // Bad URL from link path @@ -834,6 +887,9 @@ public class GhidraFileData { } } else { + Version versionData = getVersion(version); + //version = versionData.getVersion(); + lastModified = versionData.getCreateTime(); doa = contentHandler.getImmutableObject(item, consumer, version, -1, monitor); } @@ -843,9 +899,8 @@ public class GhidraFileData { projectData.trackDomainFileInUse(doa); } - DomainFileProxy proxy = new DomainFileProxy(name, getParent().getPathname(), doa, - version, fileID, parent.getProjectLocator()); - proxy.setLastModified(getLastModifiedTime()); + DomainFileProxy proxy = new DomainFileProxy(getDomainFile(), doa, version); + proxy.setLastModified(lastModified); return doa; } finally { @@ -1133,7 +1188,7 @@ public class GhidraFileData { /** * Return either the latest version if the file is not checked-out or the version that * was checked-out or a specific version that was requested. - * @return the version + * @return the version */ int getVersion() { synchronized (fileSystem) { @@ -1154,8 +1209,7 @@ public class GhidraFileData { } /** - * Returns true if this file represents the latest version of the associated domain object. - * @return true if the latest version + * {@return the latest repository version or 0 if not versioned or offline} */ int getLatestVersion() { synchronized (fileSystem) { @@ -1643,9 +1697,8 @@ public class GhidraFileData { DomainObjectAdapter checkinObj = contentHandler.getDomainObject(versionedFolderItem, null, folderItem.getCheckoutId(), false, false, this, monitor); - checkinObj.setDomainFile(new DomainFileProxy(name, getParent().getPathname(), - checkinObj, versionedFolderItem.getCurrentVersion() + 1, fileID, - parent.getProjectLocator())); + checkinObj.setDomainFile(new DomainFileProxy(getDomainFile(), checkinObj, + versionedFolderItem.getCurrentVersion() + 1)); DomainObject sourceObj = null; DomainObject originalObj = null; @@ -1714,9 +1767,9 @@ public class GhidraFileData { // On error disassociate open domain object from this file projectData.clearDomainObject(getPathname()); // An invalid version (-2) is specified to avoid file match - inUseDomainObj.setDomainFile(new DomainFileProxy(name, - parent.getPathname(), inUseDomainObj, -2, fileID, - parent.getProjectLocator())); + inUseDomainObj + .setDomainFile(new DomainFileProxy(getDomainFile(), + inUseDomainObj, -2)); inUseDomainObj.setTemporary(true); } undoCheckout(false, true); @@ -2296,7 +2349,9 @@ public class GhidraFileData { /** * Get the appropriate folder item (private or versioned) based upon the current state and - * targeted file version. + * targeted file version. Link files do not support versioning so result is not affected + * in such cases by specified version. With file-links only the latest referenced file + * is supported. * @param version file version * @return folder item to be used */ diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/TransientDataManager.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/TransientDataManager.java index 69d838db1d..e20ffd92f2 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/TransientDataManager.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/TransientDataManager.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,6 +19,7 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArraySet; import ghidra.framework.model.DomainFile; +import ghidra.framework.model.DomainObject; /** * Simple static class to keep track of transient domain file/domain objects. @@ -61,7 +62,8 @@ public class TransientDataManager { } /** - * Populates the given array list with all the transients. + * Populates the given array list with all the transient ({@link DomainFileProxy} + * currently in-use. * @param l the list populate with the transients */ public static void getTransients(List l) { @@ -74,7 +76,13 @@ public class TransientDataManager { */ public static void releaseFiles(Object consumer) { for (DomainFileProxy df : set) { - df.release(consumer); + DomainObject dobj = df.getOpenedDomainObject(TransientDataManager.class); + if (dobj != null) { + if (dobj.getConsumerList().contains(consumer)) { + dobj.release(consumer); + } + dobj.release(TransientDataManager.class); + } } } } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatable/DomainFileProviderContextAction.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatable/DomainFileProviderContextAction.java index fbcd32432f..a027239fc3 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatable/DomainFileProviderContextAction.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatable/DomainFileProviderContextAction.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,7 +27,7 @@ public abstract class DomainFileProviderContextAction extends DockingAction { } @Override - public final boolean isEnabledForContext(ActionContext actionContext) { + public boolean isEnabledForContext(ActionContext actionContext) { if (!(actionContext instanceof DomainFileContext)) { return false; } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/VersionControlDialog.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/VersionControlDialog.java index d468adef06..6e3de00c59 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/VersionControlDialog.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/VersionControlDialog.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -47,7 +47,7 @@ public class VersionControlDialog extends DialogComponentProvider { private JLabel descriptionLabel; private JTextArea commentsTextArea; private JButton allButton; // apply to all - private int actionID; + private int actionID = CANCEL; private boolean addToVersionControl; // true if the dialog is for adding /** diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DomainObject.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DomainObject.java index 9cd1190842..f6a7818949 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DomainObject.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DomainObject.java @@ -245,6 +245,11 @@ public interface DomainObject { */ public String getName(); + /** + * {@return the full path of the DomainFile associated with this domain object} + */ + public String getPath(); + /** * Set the name for this domain object. * @param name object name @@ -454,6 +459,7 @@ public interface DomainObject { * @return the result returned by the supplier * @throws E any exception that may be thrown in the given callback */ + public default T withTransaction(String description, ExceptionalSupplier supplier) throws E { T t = null; diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/RuntimeIOException.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/RuntimeIOException.java index 0293e14d6c..9cad39fd21 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/RuntimeIOException.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/RuntimeIOException.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -30,4 +30,13 @@ public class RuntimeIOException extends RuntimeException { public RuntimeIOException(IOException e) { super(e); } + + /** + * Throw wrapped IOException + * @throws IOException wrapped IOException + */ + public void throwIOException() throws IOException { + IOException cause = (IOException) getCause(); + throw cause; + } } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/DefaultProject.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/DefaultProject.java index 5d13d2aefc..1b470ee335 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/DefaultProject.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/DefaultProject.java @@ -639,7 +639,10 @@ public class DefaultProject implements Project { TransientDataManager.getTransients(list); for (DomainFile df : list) { if (df != null && df.isOpen()) { - openFiles.add(df); + DomainObject openDomainObject = df.getOpenedDomainObject(this); + if (openDomainObject.isChangeable() && !openDomainObject.isTemporary()) { + openFiles.add(df); + } } } return openFiles; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java deleted file mode 100644 index 69c86e69f3..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java +++ /dev/null @@ -1,575 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.program.database; - -import java.io.File; -import java.io.IOException; -import java.util.*; - -import db.*; -import ghidra.framework.Application; -import ghidra.framework.data.DomainObjectAdapterDB; -import ghidra.framework.data.OpenMode; -import ghidra.framework.model.DomainFile; -import ghidra.framework.model.DomainFolder; -import ghidra.framework.options.Options; -import ghidra.program.model.data.PointerDataType; -import ghidra.program.model.data.StandAloneDataTypeManager; -import ghidra.program.model.listing.DataTypeArchive; -import ghidra.program.model.listing.Program; -import ghidra.program.util.ProgramChangeRecord; -import ghidra.program.util.ProgramEvent; -import ghidra.util.InvalidNameException; -import ghidra.util.Lock.Closeable; -import ghidra.util.exception.*; -import ghidra.util.task.TaskMonitor; - -/** - * Database implementation for Data Type Archive. - */ -public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataTypeArchive { - - /** - * DB_VERSION should be incremented any time a change is made to the overall - * database schema associated with any of the managers. - * 18-Sep-2008 - version 1 - Added fields for synchronizing program data types with project archives. - * 03-Dec-2009 - version 2 - Added source archive updating (consolidating windows.gdt, clib.gdt, ntddk.gdt) - * 14-Nov-2019 - version 3 - Corrected fixed length indexing implementation causing change - * in index table low-level storage for newly created tables. - * 20-Apr-2023 - version 4 - Added architecture support and string-based function calling - * convention specification. - * - * NOTE: The true versioning is based on the underlying {@link StandAloneDataTypeManager} - * implementation and its ability to detect and manage versioning concerns. Due to the need to - * always support opening in a read-only fashion we are unable to impose a forced upgrade - * requirement. - */ - static final int DB_VERSION = 4; - - /** - * UPGRADE_REQUIRED_BEFORE_VERSION should be changed to DB_VERSION any time the - * latest version requires a forced upgrade (i.e., Read-only mode not supported - * until upgrade is performed). It is assumed that read-only mode is supported - * if the data's version is >= UPGRADE_REQUIRED_BEFORE_VERSION and <= DB_VERSION. - */ - private static final int UPGRADE_REQUIRED_BEFORE_VERSION = 1; - - /** Name of data type archive information property list */ - public static final String ARCHIVE_INFO = "Data Type Archive Information"; - - /** Name of data type archive settings property list */ - public static final String ARCHIVE_SETTINGS = "Data Type Archive Settings"; - - /** Name of date created property */ - public static final String DATE_CREATED = "Date Created"; - - /** Name of Ghidra version property */ - public static final String CREATED_WITH_GHIDRA_VERSION = "Created With Ghidra Version"; - - /** A date from January 1, 1970 */ - public static final Date JANUARY_1_1970 = new Date(0); - - private static final String ARCHIVE_DB_VERSION = "DB Version"; - private static final String TABLE_NAME = "Data Type Archive"; - - private static final String DEFAULT_POINTER_SIZE = "Default Pointer Size"; - - private final static Field[] COL_FIELDS = new Field[] { StringField.INSTANCE }; - private final static String[] COL_TYPES = new String[] { "Value" }; - private final static Schema SCHEMA = - new Schema(0, StringField.INSTANCE, "Key", COL_FIELDS, COL_TYPES); - - private ProjectDataTypeManager dataTypeManager; - - private boolean recordChanges; - private boolean changeable = true; - private Table table; - - /** - * Constructs a new DataTypeArchiveDB within a project folder. - * @param folder folder within which the project archive will be created - * @param name the name of the data type archive - * @param consumer the object that is using this data type archive. - * @throws IOException if there is an error accessing the database. - * @throws InvalidNameException - * @throws DuplicateNameException - */ - public DataTypeArchiveDB(DomainFolder folder, String name, Object consumer) - throws IOException, DuplicateNameException, InvalidNameException { - super(new DBHandle(), name, 500, consumer); - this.name = name; - - recordChanges = false; - boolean success = false; - try { - int id = startTransaction("create data type archive"); - - createDatabase(); - if (createManagers(OpenMode.CREATE, TaskMonitor.DUMMY) != null) { - throw new AssertException("Unexpected version exception on create"); - } - changeSet = new DataTypeArchiveDBChangeSet(NUM_UNDOS); - initManagers(OpenMode.CREATE, TaskMonitor.DUMMY); - propertiesCreate(); - endTransaction(id, true); - clearUndo(false); - - if (folder != null) { - folder.createFile(name, this, TaskMonitor.DUMMY); - } - - success = true; - } - catch (CancelledException e) { - throw new AssertException(); - } - finally { - dbh.closeScratchPad(); - if (!success) { - release(consumer); - dbh.close(); - } - } - - } - - /** - * Constructs a new DataTypeArchiveDB - * @param dbh a handle to an open data type archive database. - * @param openMode one of: - * READ_ONLY: the original database will not be modified - * UPDATE: the database can be written to. - * UPGRADE: the database is upgraded to the latest schema as it is opened. - * @param monitor TaskMonitor that allows the open to be canceled. - * @param consumer the object that keeping the program open. - * @throws IOException if an error accessing the database occurs. - * @throws VersionException if database version does not match implementation, UPGRADE may be possible. - * @throws CancelledException if instantiation is canceled by monitor - */ - public DataTypeArchiveDB(DBHandle dbh, OpenMode openMode, TaskMonitor monitor, Object consumer) - throws IOException, VersionException, CancelledException { - - super(dbh, "Untitled", 500, consumer); - if (monitor == null) { - monitor = TaskMonitor.DUMMY; - } - boolean success = false; - try { - int id = startTransaction("create data type archive"); - recordChanges = false; - changeable = (openMode != OpenMode.IMMUTABLE); - - // check DB version and read name - VersionException dbVersionExc = initializeDatabase(openMode); - - VersionException versionExc = createManagers(openMode, monitor); - if (dbVersionExc != null) { - versionExc = dbVersionExc.combine(versionExc); - } - if (versionExc != null) { - throw versionExc; - } - - changeSet = new DataTypeArchiveDBChangeSet(NUM_UNDOS); - - initManagers(openMode, monitor); - - if (openMode == OpenMode.UPGRADE) { - upgradeDatabase(); - changed = true; - } - propertiesRestore(); - recordChanges = true; - endTransaction(id, true); - clearUndo(false); - success = true; - } - finally { - dbh.closeScratchPad(); - if (!success) { - release(consumer); - } - } - - if (openMode == OpenMode.IMMUTABLE) { - setImmutable(); - } - - } - - @Override - protected void close() { - super.close(); - if (dataTypeManager != null) { - dataTypeManager.dispose(); - } - } - - @Override - protected void setDomainFile(DomainFile df) { - super.setDomainFile(df); - recordChanges = true; - } - - private void propertiesRestore() { - Options pl = getOptions(ARCHIVE_INFO); - boolean origChangeState = changed; - pl.registerOption(CREATED_WITH_GHIDRA_VERSION, "4.3", null, - "Version of Ghidra used to create this program."); - pl.registerOption(DATE_CREATED, JANUARY_1_1970, null, "Date this program was created"); -// registerDefaultPointerSize(); - changed = origChangeState; - } - - private void propertiesCreate() { - Options pl = getOptions(ARCHIVE_INFO); - boolean origChangeState = changed; - pl.setString(CREATED_WITH_GHIDRA_VERSION, Application.getApplicationVersion()); - pl.setDate(DATE_CREATED, new Date()); -// registerDefaultPointerSize(); - changed = origChangeState; - } - - @Override - protected boolean propertyChanged(String propertyName, Object oldValue, Object newValue) { - if (propertyName.endsWith(DEFAULT_POINTER_SIZE) && (newValue instanceof Integer)) { - if (!isValidDefaultpointerSize((Integer) newValue)) { - return false; - } - } - return super.propertyChanged(propertyName, oldValue, newValue); - } - - private boolean isValidDefaultpointerSize(int pointerSize) { - return pointerSize > 0 && pointerSize <= PointerDataType.MAX_POINTER_SIZE_BYTES; - } - - @Override - public ProjectDataTypeManager getDataTypeManager() { - return dataTypeManager; - } - - @Override - public Date getCreationDate() { - Options pl = getOptions(ARCHIVE_INFO); - return pl.getDate(DATE_CREATED, new Date(0)); - } - - @Override - public int getDefaultPointerSize() { - // Not sure what size this should be so use 4 for now. - // Maybe this should prompt the user when it hasn't been set yet. - Options pl = getOptions(ARCHIVE_SETTINGS); - return pl.getInt(DEFAULT_POINTER_SIZE, 4); - } - - @Override - public DataTypeArchiveDBChangeSet getChanges() { - return (DataTypeArchiveDBChangeSet) changeSet; - } - - /** - * notification the a data type has changed - * @param dataTypeID the id of the data type that changed. - * @param eventType the type of the change (moved, renamed, etc.) - * @param isAutoResponseChange true if change is an auto-response change caused by - * another datatype's change (e.g., size, alignment), else false in which case this - * change will be added to archive change-set to aid merge conflict detection. - * @param oldValue the old data type. - * @param newValue the new data type. - */ - public void dataTypeChanged(long dataTypeID, ProgramEvent eventType, - boolean isAutoResponseChange, Object oldValue, Object newValue) { - if (recordChanges && !isAutoResponseChange) { - ((DataTypeArchiveDBChangeSet) changeSet).dataTypeChanged(dataTypeID); - } - changed = true; - fireEvent(new ProgramChangeRecord(eventType, oldValue, newValue)); - } - - /** - * Notification that a data type was added. - * @param dataTypeID the id if the data type that was added. - * @param eventType should always be DATATYPE_ADDED - * @param oldValue always null - * @param newValue the data type added. - */ - public void dataTypeAdded(long dataTypeID, ProgramEvent eventType, Object oldValue, - Object newValue) { - if (recordChanges) { - ((DataTypeArchiveDBChangeSet) changeSet).dataTypeAdded(dataTypeID); - } - changed = true; - fireEvent(new ProgramChangeRecord(eventType, oldValue, newValue)); - } - - /** - * Notification that a category was changed. - * @param categoryID the id of the data type that was added. - * @param eventType the type of change - * @param oldValue old value depends on the type. - * @param newValue new value depends on the type. - */ - public void categoryChanged(long categoryID, ProgramEvent eventType, Object oldValue, - Object newValue) { - if (recordChanges) { - ((DataTypeArchiveDBChangeSet) changeSet).categoryChanged(categoryID); - } - changed = true; - fireEvent(new ProgramChangeRecord(eventType, oldValue, newValue)); - } - - /** - * Notification that a category was added. - * @param categoryID the id of the data type that was added. - * @param eventType the type of change (should always be CATEGORY_ADDED) - * @param oldValue always null - * @param newValue new value depends on the type. - */ - public void categoryAdded(long categoryID, ProgramEvent eventType, Object oldValue, - Object newValue) { - if (recordChanges) { - ((DataTypeArchiveDBChangeSet) changeSet).categoryAdded(categoryID); - } - changed = true; - fireEvent(new ProgramChangeRecord(eventType, oldValue, newValue)); - } - - /** - * Mark the state this Data Type Archive as having changed and generate - * the event. Any or all parameters may be null. - * @param eventType event type - * @param oldValue original value - * @param newValue new value - */ - public void setChanged(ProgramEvent eventType, Object oldValue, Object newValue) { - - changed = true; - - fireEvent(new ProgramChangeRecord(eventType, oldValue, newValue)); - } - - @Override - public void setName(String newName) { - } - - @Override - public String getDescription() { - return "Data Type Archive"; - } - - private void createDatabase() throws IOException { - table = dbh.createTable(TABLE_NAME, SCHEMA); - - DBRecord record = SCHEMA.createRecord(new StringField(ARCHIVE_DB_VERSION)); - record.setString(0, Integer.toString(DB_VERSION)); - table.putRecord(record); - } - - /** - * Initialize the following fields from the database and check the database version for an existing database: - *

    - *
  • name
  • - *
  • languageName
  • - *
  • languageVersion
  • - *
  • LanguageMinorVersion
  • - *
- * @param openMode program open mode - * @return version exception if the current version is out of date and can be upgraded. - * @throws IOException - * @throws VersionException if the data is newer than this version of Ghidra and can not be - * upgraded or opened. - */ - private VersionException initializeDatabase(OpenMode openMode) - throws IOException, VersionException { - - table = dbh.getTable(TABLE_NAME); - if (table == null) { - if (openMode == OpenMode.UPGRADE) { - createDatabase(); - } - else { - throw new VersionException(true); - } - } - - int storedVersion = getStoredVersion(); - if (storedVersion > DB_VERSION) { - throw new VersionException(VersionException.NEWER_VERSION, false); - } - if (openMode != OpenMode.UPGRADE && storedVersion < UPGRADE_REQUIRED_BEFORE_VERSION) { - return new VersionException(true); - } - if (openMode == OpenMode.UPDATE && storedVersion < DB_VERSION) { - return new VersionException(true); - } - return null; - } - - private void upgradeDatabase() throws IOException { - - table = dbh.getTable(TABLE_NAME); - DBRecord record = SCHEMA.createRecord(new StringField(ARCHIVE_DB_VERSION)); - record.setString(0, Integer.toString(DB_VERSION)); - table.putRecord(record); - } - - private int getStoredVersion() throws IOException { - DBRecord record = table.getRecord(new StringField(ARCHIVE_DB_VERSION)); - - // DB Version - // if record does not exist return 1; - - if (record != null) { - String s = record.getString(0); - try { - return Integer.parseInt(s); - } - catch (NumberFormatException e) { - } - } - return 1; - } - - private void checkOldProperties(OpenMode openMode) { -// Record record = table.getRecord(new StringField(EXECUTE_PATH)); -// if (record != null) { -// if (openMode == READ_ONLY) { -// return; // not important, get on path or format will return "unknown" -// } -// if (openMode != UPGRADE) { -// throw new VersionException(true); -// } -// options pl = getPropertyList(ARCHIVE_INFO); -// String value = record.getString(0); -// pl.setValue(EXECUTABLE_PATH, value); -// table.deleteRecord(record.getKeyField()); -// record = table.getRecord(new StringField(EXECUTE_FORMAT)); -// if (record != null) { -// pl.setValue(EXECUTABLE_FORMAT, value); -// table.deleteRecord(record.getKeyField()); -// } -// } -// int storedVersion = getStoredVersion(); - } - - private VersionException createManagers(OpenMode openMode, TaskMonitor monitor) - throws CancelledException, IOException { - - VersionException versionExc = null; - monitor.checkCancelled(); - -// try { - checkOldProperties(openMode); -// } catch (VersionException e) { -// versionExc = e.combine(versionExc); -// } - - try { - dataTypeManager = new ProjectDataTypeManager(this, dbh, openMode, this, lock, monitor); - } - catch (VersionException e) { - versionExc = e.combine(versionExc); - } - monitor.checkCancelled(); - - return versionExc; - } - - private void initManagers(OpenMode openMode, TaskMonitor monitor) - throws IOException, CancelledException { - monitor.checkCancelled(); - dataTypeManager.archiveReady(openMode, monitor); - } - - @Override - protected void clearCache(boolean all) { - try (Closeable c = lock.write()) { - super.clearCache(all); - dataTypeManager.invalidateCache(); - } - } - - @Override - public boolean isChangeable() { - return changeable; - } - - @Override - protected void setChanged(boolean b) { - super.setChanged(b); - } - - void setChangeSet(DataTypeArchiveDBChangeSet changeSet) { - this.changeSet = changeSet; - } - - @Override - public void save(String comment, TaskMonitor monitor) throws IOException, CancelledException { - try { - super.save(comment, monitor); - } - finally { - dataTypeManager.clearUndo(); - } - } - - @Override - public void saveToPackedFile(File outputFile, TaskMonitor monitor) - throws IOException, CancelledException { - try { - super.saveToPackedFile(outputFile, monitor); - } - finally { - dataTypeManager.clearUndo(); - } - } - - @Override - public Map getMetadata() { - - metadata.clear(); - metadata.put("Data Type Archive Name", getName()); - metadata.put("# of Data Types", "" + getDataTypeManager().getDataTypeCount(true)); - metadata.put("# of Data Type Categories", "" + getDataTypeManager().getCategoryCount()); - - Options propList = getOptions(Program.PROGRAM_INFO); - List propNames = propList.getOptionNames(); - Collections.sort(propNames); - for (String propName : propNames) { - if (propName.indexOf(Options.DELIMITER) >= 0) { - continue; // ignore second tier options - } - String valueAsString = propList.getValueAsString(propName); - if (valueAsString != null) { - metadata.put(propName, propList.getValueAsString(propName)); - } - } - return metadata; - } - - @Override - protected void updateMetadata() throws IOException { - getMetadata(); // updates metadata map - super.updateMetadata(); - } - - @Override - protected void domainObjectRestored() { - super.domainObjectRestored(); - dataTypeManager.notifyRestored(); - } -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramAddressFactory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramAddressFactory.java index 8fcb2abb7f..24142dc331 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramAddressFactory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramAddressFactory.java @@ -46,7 +46,7 @@ public class ProgramAddressFactory extends DefaultAddressFactory { * @param compilerSpec compiler specification * @param overlayRegionSupplier overlay space defined region supplier which will be invoked when * specific queries are performed on overlay address spaces. If memory is not yet available - * a null AddressSet may be returned by the supplier. + * a null AddressSet may be returned by the supplier. A null supplier may also be specified. */ public ProgramAddressFactory(Language language, CompilerSpec compilerSpec, OverlayRegionSupplier overlayRegionSupplier) { @@ -224,7 +224,7 @@ public class ProgramAddressFactory extends DefaultAddressFactory { /** * Remove an overlay space. - * It may be neccessary to invoke {@link #refreshStaleOverlayStatus()} when an overlay is + * It may be necessary to invoke {@link #refreshStaleOverlayStatus()} when an overlay is * removed. * @param name overlay space name */ diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramContentHandler.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramContentHandler.java index ca4ebe3731..53c8ea2f44 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramContentHandler.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramContentHandler.java @@ -29,6 +29,7 @@ import ghidra.framework.model.DomainObject; import ghidra.framework.store.*; import ghidra.framework.store.local.LocalDatabaseItem; import ghidra.program.model.lang.LanguageNotFoundException; +import ghidra.program.model.listing.Program; import ghidra.util.InvalidNameException; import ghidra.util.Msg; import ghidra.util.exception.CancelledException; @@ -370,8 +371,8 @@ public class ProgramContentHandler extends DBWithUserDataContentHandler { /** * Delete all objects which have been applied to the address range startAddr to endAddr @@ -83,10 +52,4 @@ public interface ManagerDB { void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor) throws AddressOverflowException, CancelledException; - /** - * Callback from the program after being closed to signal this manager to release memory and resources. - */ - default void dispose() { - // default do nothing - } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramMultiUserMergeManagerFactory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramMultiUserMergeManagerFactory.java index da1bbc0256..21b111e8ce 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramMultiUserMergeManagerFactory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramMultiUserMergeManagerFactory.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,9 +15,9 @@ */ package ghidra.program.database; -import ghidra.framework.*; -import ghidra.framework.data.*; -import ghidra.framework.model.*; +import ghidra.framework.PluggableServiceRegistry; +import ghidra.framework.data.DomainObjectMergeManager; +import ghidra.program.model.listing.Program; public class ProgramMultiUserMergeManagerFactory { static { @@ -26,15 +25,15 @@ public class ProgramMultiUserMergeManagerFactory { ProgramMultiUserMergeManagerFactory.class, new ProgramMultiUserMergeManagerFactory()); } - public static DomainObjectMergeManager getMergeManager(DomainObject resultsObj, - DomainObject sourceObj, DomainObject originalObj, DomainObject latestObj) { + public static DomainObjectMergeManager getMergeManager(Program resultsObj, + Program sourceObj, Program originalObj, Program latestObj) { ProgramMultiUserMergeManagerFactory factory = PluggableServiceRegistry.getPluggableService(ProgramMultiUserMergeManagerFactory.class); return factory.doGetMergeManager(resultsObj, sourceObj, originalObj, latestObj); } - protected DomainObjectMergeManager doGetMergeManager(DomainObject resultsObj, - DomainObject sourceObj, DomainObject originalObj, DomainObject latestObj) { + protected DomainObjectMergeManager doGetMergeManager(Program resultsObj, + Program sourceObj, Program originalObj, Program latestObj) { return null; } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramOverlayAddressSpace.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramOverlayAddressSpace.java index 8c47c10db3..ae5d6308ab 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramOverlayAddressSpace.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramOverlayAddressSpace.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -34,7 +34,8 @@ public class ProgramOverlayAddressSpace extends OverlayAddressSpace { * @param baseSpace base address space (type should be restricted as neccessary by caller) * @param unique assigned unique ID * @param overlayRegionSupplier callback handler which supplies the defined address set - * for a specified overlay address space. + * for a specified overlay address space. If memory is not yet available + * a null AddressSet may be returned by the supplier. A null supplier may also be specified. * @param factory used to determine a suitable ordered overlay ordered-key used for * {@link #equals(Object)} and {@link #compareTo(AddressSpace)}. * @throws DuplicateNameException if specified name duplicates an existing address space name diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProjectDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProjectDataTypeManager.java deleted file mode 100644 index 80bcc7d3c9..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProjectDataTypeManager.java +++ /dev/null @@ -1,322 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.program.database; - -import java.io.IOException; -import java.util.*; - -import javax.help.UnsupportedOperationException; - -import db.DBHandle; -import db.Transaction; -import db.util.ErrorHandler; -import ghidra.framework.data.OpenMode; -import ghidra.framework.model.DomainFile; -import ghidra.framework.store.LockException; -import ghidra.program.model.data.*; -import ghidra.program.model.lang.*; -import ghidra.program.model.listing.IncompatibleLanguageException; -import ghidra.program.util.ProgramEvent; -import ghidra.util.InvalidNameException; -import ghidra.util.Lock; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - -/** - * Class for managing data types in a project archive - * NOTE: default data organization is used. - */ -public class ProjectDataTypeManager extends StandAloneDataTypeManager - implements ProjectArchiveBasedDataTypeManager { - - private final DataTypeArchiveDB dataTypeArchive; - - /** - * Constructor for a data-type manager using a specified DBHandle. - *

- * NOTE: If archive has an assigned architecture, issues may arise due to a revised or - * missing {@link Language}/{@link CompilerSpec} which will result in a warning but not - * prevent the archive from being opened. Such a warning condition will ne logged and may - * result in missing or stale information for existing datatypes which have architecture related - * data. In some case it may be appropriate to - * {@link FileDataTypeManager#getWarning() check for warnings} on the returned archive - * object prior to its use. - * - * @param dataTypeArchive associated archive - * @param handle open database handle - * @param openMode the program open mode - * @param errHandler the database I/O error handler - * @param lock the program synchronization lock - * @param monitor the progress monitor - * @throws CancelledException if the user cancels an upgrade - * @throws VersionException if the database does not match the expected version. - * @throws IOException if a database I/O error occurs. - */ - ProjectDataTypeManager(DataTypeArchiveDB dataTypeArchive, DBHandle handle, OpenMode openMode, - ErrorHandler errHandler, Lock lock, TaskMonitor monitor) - throws CancelledException, VersionException, IOException { - super(handle, openMode, errHandler, lock, monitor); - this.dataTypeArchive = dataTypeArchive; - logWarning(); - } - - @Override - public String getName() { - return dataTypeArchive.getDomainFile().getName(); - } - - @Override - public void setName(String name) throws InvalidNameException { - if (name == null || name.length() == 0) { - throw new InvalidNameException("Name is invalid: " + name); - } - - dataTypeArchive.setName(name); - categoryRenamed(CategoryPath.ROOT, null); - } - - @Override - public void clearProgramArchitecture(TaskMonitor monitor) - throws CancelledException, IOException, LockException { - dataTypeArchive.checkExclusiveAccess(); - super.clearProgramArchitecture(monitor); - } - - @Override - public void setProgramArchitecture(Language language, CompilerSpecID compilerSpecId, - LanguageUpdateOption updateOption, TaskMonitor monitor) - throws CompilerSpecNotFoundException, LanguageNotFoundException, IOException, - LockException, UnsupportedOperationException, IncompatibleLanguageException, - CancelledException { - dataTypeArchive.checkExclusiveAccess(); - super.setProgramArchitecture(language, compilerSpecId, updateOption, monitor); - } - - @Override - public void dataTypeChanged(DataType dt, boolean isAutoChange) { - super.dataTypeChanged(dt, isAutoChange); - // NOTE: During upgrades at time of instantiation dataTypeArchive will be null - if (dataTypeArchive != null) { - dataTypeArchive.dataTypeChanged(getID(dt), ProgramEvent.DATA_TYPE_CHANGED, isAutoChange, - null, dt); - } - } - - @Override - protected void dataTypeAdded(DataType newDt, DataType originalDataType) { - super.dataTypeAdded(newDt, originalDataType); - dataTypeArchive.dataTypeAdded(getID(newDt), ProgramEvent.DATA_TYPE_ADDED, null, newDt); - } - - @Override - protected void dataTypeReplaced(long existingDtID, DataTypePath existingPath, - DataType replacementDt) { - super.dataTypeReplaced(existingDtID, existingPath, replacementDt); - dataTypeArchive.dataTypeChanged(existingDtID, ProgramEvent.DATA_TYPE_REPLACED, false, - existingPath, replacementDt); - } - - @Override - protected void dataTypeDeleted(long deletedID, DataTypePath deletedDataTypePath) { - super.dataTypeDeleted(deletedID, deletedDataTypePath); - dataTypeArchive.dataTypeChanged(deletedID, ProgramEvent.DATA_TYPE_REMOVED, false, - deletedDataTypePath, null); - } - - @Override - protected void dataTypeMoved(DataType dt, DataTypePath oldPath, DataTypePath newPath) { - super.dataTypeMoved(dt, oldPath, newPath); - Category category = getCategory(oldPath.getCategoryPath()); - dataTypeArchive.dataTypeChanged(getID(dt), ProgramEvent.DATA_TYPE_MOVED, false, category, - dt); - } - - @Override - protected void dataTypeNameChanged(DataType dt, String oldName) { - super.dataTypeNameChanged(dt, oldName); - dataTypeArchive.dataTypeChanged(getID(dt), ProgramEvent.DATA_TYPE_RENAMED, false, oldName, - dt); - } - - @Override - protected void categoryCreated(Category newCategory) { - super.categoryCreated(newCategory); - dataTypeArchive.categoryAdded(newCategory.getID(), ProgramEvent.DATA_TYPE_CATEGORY_ADDED, - newCategory.getParent(), newCategory); - } - - @Override - protected void categoryRenamed(CategoryPath oldPath, Category category) { - super.categoryRenamed(oldPath, category); - dataTypeArchive.categoryChanged(category.getID(), ProgramEvent.DATA_TYPE_CATEGORY_RENAMED, - oldPath.getName(), category); - } - - @Override - protected void categoryRemoved(Category parent, String categoryName, long categoryID) { - super.categoryRemoved(parent, categoryName, categoryID); - dataTypeArchive.categoryChanged(categoryID, ProgramEvent.DATA_TYPE_CATEGORY_REMOVED, parent, - categoryName); - } - - @Override - protected void categoryMoved(CategoryPath oldPath, Category category) { - super.categoryMoved(oldPath, category); - dataTypeArchive.categoryChanged(category.getID(), ProgramEvent.DATA_TYPE_CATEGORY_MOVED, - oldPath.getParent(), category); - } - - @Override - protected void favoritesChanged(DataType dataType, boolean isFavorite) { - super.favoritesChanged(dataType, isFavorite); - } - - @Override - protected void replaceDataTypesUsed(Map dataTypeReplacementMap) { - // do nothing - } - - @Override - protected void deleteDataTypesUsed(Set deletedIds) { - // do nothing - } - - @Override - protected void initTransactionState() { - // do nothing - rely on DataTypeArchiveDB - } - - @Override - public Transaction openTransaction(String description) throws IllegalStateException { - return dataTypeArchive.openTransaction(description); - } - - @SuppressWarnings("sync-override") - @Override - public int startTransaction(String description) { - return dataTypeArchive.startTransaction(description); - } - - @Override - public boolean endTransaction(int transactionID, boolean commit) { - return dataTypeArchive.endTransaction(transactionID, commit); - } - - @Override - public void undo() { - try { - dataTypeArchive.undo(); - } - catch (IOException e) { - dbError(e); - } - } - - @Override - public void redo() { - try { - dataTypeArchive.redo(); - } - catch (IOException e) { - dbError(e); - } - } - - @SuppressWarnings("sync-override") - @Override - public void clearUndo() { - dataTypeArchive.clearUndo(); - } - - @SuppressWarnings("sync-override") - @Override - public boolean canRedo() { - return dataTypeArchive.canRedo(); - } - - @SuppressWarnings("sync-override") - @Override - public boolean canUndo() { - return dataTypeArchive.canUndo(); - } - - @SuppressWarnings("sync-override") - @Override - public String getRedoName() { - return dataTypeArchive.getRedoName(); - } - - @SuppressWarnings("sync-override") - @Override - public String getUndoName() { - return dataTypeArchive.getUndoName(); - } - - @SuppressWarnings("sync-override") - @Override - public List getAllUndoNames() { - return dataTypeArchive.getAllUndoNames(); - } - - @SuppressWarnings("sync-override") - @Override - public List getAllRedoNames() { - return dataTypeArchive.getAllRedoNames(); - } - - @Override - public void flushEvents() { - dataTypeArchive.flushEvents(); - } - - @Override - public DomainFile getDomainFile() { - return dataTypeArchive.getDomainFile(); - } - - @Override - public String getDomainFileID() { - DomainFile domainFile = getDomainFile(); // Can be null if it has never been saved. - return (domainFile != null) ? domainFile.getFileID() : null; - } - - @Override - public String getPath() { - DomainFile domainFile = getDomainFile(); // Can be null if it has never been saved. - return (domainFile != null) ? domainFile.getPathname() : null; - } - - @Override - public ArchiveType getType() { - return ArchiveType.PROJECT; - } - - public void archiveReady(OpenMode openMode, TaskMonitor monitor) - throws IOException, CancelledException { - if (openMode == OpenMode.UPGRADE) { - doSourceArchiveUpdates(monitor); - migrateOldFlexArrayComponentsIfRequired(monitor); - } - } - - @Override - public synchronized void close() { - // do nothing - cannot close a project data type manager - // dispose should be invoked by the owner of the instance - } - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDBManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDBManager.java index a436c635dc..57b90f5534 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDBManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDBManager.java @@ -41,7 +41,7 @@ import ghidra.util.Lock.Closeable; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; -public class BookmarkDBManager implements BookmarkManager, ErrorHandler, ManagerDB { +public class BookmarkDBManager implements BookmarkManager, ErrorHandler, ProgramDBModule { final static Icon DEFAULT_ICON = new GIcon("icon.plugin.bookmark.type.default"); private ProgramDB program; @@ -80,7 +80,7 @@ public class BookmarkDBManager implements BookmarkManager, ErrorHandler, Manager } @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { if (this.program != null) { throw new AssertException(); } @@ -114,7 +114,7 @@ public class BookmarkDBManager implements BookmarkManager, ErrorHandler, Manager } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { // Nothing to do } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java index 5af472d8c1..3df9d6a593 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java @@ -47,7 +47,7 @@ import ghidra.util.task.TaskMonitor; /** * Class to manage database tables for data and instructions. */ -public class CodeManager implements ErrorHandler, ManagerDB { +public class CodeManager implements ErrorHandler, ProgramDBModule { private DBHandle dbHandle; private AddressMap addrMap; @@ -226,7 +226,7 @@ public class CodeManager implements ErrorHandler, ManagerDB { * @param program The program object that this manager belongs to. */ @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { this.program = program; equateTable = program.getEquateTable(); symbolTable = program.getSymbolTable(); @@ -238,7 +238,7 @@ public class CodeManager implements ErrorHandler, ManagerDB { } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { if (openMode == OpenMode.UPGRADE) { upgradeOldFallThroughMaps(monitor); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArchiveDataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArchiveDataTypeManagerDB.java new file mode 100644 index 0000000000..bda06015d9 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArchiveDataTypeManagerDB.java @@ -0,0 +1,428 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.database.data; + +import java.io.IOException; +import java.util.*; + +import db.DBHandle; +import db.Transaction; +import db.util.ErrorHandler; +import ghidra.framework.data.OpenMode; +import ghidra.framework.model.RuntimeIOException; +import ghidra.program.database.DBStringMapAdapter; +import ghidra.program.database.dtarchive.DataTypeStoreDBModule; +import ghidra.program.database.dtarchive.DataTypeArchiveDB; +import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.DataTypeArchive; +import ghidra.program.model.lang.LanguageVersionException; +import ghidra.program.util.ProgramEvent; +import ghidra.util.Lock; +import ghidra.util.exception.*; +import ghidra.util.task.TaskMonitor; + +/** + * Basic implementation of the DataTypeManger interface for those data type managers that + * originate in an archive and not a program. + */ +public abstract class ArchiveDataTypeManagerDB extends DataTypeManagerDB + implements ArchiveDataTypeManager, DataTypeStoreDBModule { + + protected DataTypeArchiveDB archive; + + private boolean detectedReadOnlyDataOrgChange; + + /** + * Constructor for a transient data-type manager instance using a new {@link DBHandle}. + * + * @throws RuntimeIOException if there is a problem creating the database + */ + protected ArchiveDataTypeManagerDB() throws RuntimeIOException { + super(); + } + + /** + * Constructor for a data-type manager using a specified DBHandle. + * + * @param handle open database handle + * @param openMode open mode CREATE, READ_ONLY or UPDATE + * @param errHandler the database I/O error handler + * @param lock the program synchronization lock + * @param monitor the progress monitor + * @throws CancelledException if task cancelled + * @throws VersionException if the database does not match the expected version. + * @throws IOException if a database I/O error occurs. + */ + protected ArchiveDataTypeManagerDB(DBHandle handle, OpenMode openMode, + ErrorHandler errHandler, Lock lock, TaskMonitor monitor) + throws CancelledException, VersionException, IOException { + super(handle, openMode, null, null, errHandler, lock, monitor); + if (openMode != OpenMode.CREATE && hasDataOrganizationChange(true)) { + handleDataOrganizationChange(openMode, monitor); + } + } + + /** + * Get the manager's data map which may be needed by DtArchiveDB to access properties + * which get migrated to DtArchiveDB's data map during an upgrade. This data map is used to + * retain a copy of the data organization and had previously been used + * @return manager's data map if one exists + * @throws IOException if an IO error occurs + */ + public DBStringMapAdapter getDataMap() throws IOException { + return getDataMap(false); + } + + /** + * Save the current data organization to facilitate future change detection and + * upgrades. This method must be invoked by {@link DataTypeArchiveDB} during the final + * stage of archive creation (i.e., openMode == CREATE). + * @throws IOException if failure occurred while saving data organization. + */ + @Override + public void saveDataOrganization() throws IOException { + super.saveDataOrganization(); + } + + @Override + public void invalidateCache(boolean all) throws IOException { + invalidateCache(); + } + + @Override + public void setDomainObject(DataTypeArchiveDB archive) { + this.archive = archive; + try { + // Set the program architecture for this datatype manager as established + // by the archive during its instantiation process. Any architecture related + // upgrades/changes must already be complete. + + // + // NOTE: There are currently two facets that are affected by a language + // version change: + // + // 1. Variable storage upgrades - handled by StandAloneDataStoreDB during + // architecture initialization (e.g., register address changes), + // + // 2. Data organization changes - handled at time of ArchivDataTypeManagerDB + // instantiation by handleDataOrganizationChange method. + // + + setProgramArchitecture(archive.getProgramArchitecture(), + archive.getVariableStorageManager(), false, TaskMonitor.DUMMY); + } + catch (CancelledException e) { + throw new AssertException(e); // unexpected - no IO performed + } + catch (IOException e) { + errHandler.dbError(e); + } + } + + @Override + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + throws IOException, CancelledException { + if (openMode == OpenMode.UPGRADE) { + doSourceArchiveUpdates(monitor); + migrateOldFlexArrayComponentsIfRequired(monitor); + } + } + + @Override + public DataTypeArchive getDataStore() { + return archive; + } + + /** + * Complete changes related to an archive's change in program architecture. + *

+ * The data organization will be obtained from the compiler spec specified by + * the program architecture. Fixup of all composites will be performed, if store is + * true, to reflect any changes in the data organization. + * The caller is responsible for ensuring that this setting is done consistent + * with the {@link #addrMap} setting used during construction if applicable. + *
+ * If not storing caller may need to check for data organization change to communicate + * change or to facilitate an upgrade situation. + * + * @param monitor the task monitor + * @throws CancelledException if the change is cancelled + * @throws IOException if an I/O exception occurs during the operation + */ + public void programArchitectureChanged(TaskMonitor monitor) + throws CancelledException, IOException { + lock.writeLock().lock(); + try { + setProgramArchitecture(archive.getProgramArchitecture(), + archive.getVariableStorageManager(), true, monitor); + } + finally { + invalidateCache(); + lock.writeLock().unlock(); + } + } + + @Override + protected void handleDataOrganizationChange(OpenMode openMode, TaskMonitor monitor) + throws LanguageVersionException, CancelledException, IOException { + if (openMode == OpenMode.IMMUTABLE) { + detectedReadOnlyDataOrgChange = true; + } + super.handleDataOrganizationChange(openMode, monitor); + } + + public boolean hasReadOnlyDataOrgChange() { + return detectedReadOnlyDataOrgChange; + } + + /** + * Determine if a program architecture change is permitted + * @return true if change allowed else false if disallowed + */ + protected boolean isArchitectureChangeAllowed() { + return true; + } + + @Override + public String getName() { + return archive.getName(); + } + + @Override + public Transaction openTransaction(String description) throws IllegalStateException { + return archive.openTransaction(description); + } + + @Override + public int startTransaction(String description) { + return archive.startTransaction(description); + } + + @Override + public boolean endTransaction(int transactionID, boolean commit) { + return archive.endTransaction(transactionID, commit); + } + + @Override + public void undo() { + try { + archive.undo(); + } + catch (IOException e) { + dbError(e); + } + } + + @Override + public void redo() { + try { + archive.redo(); + } + catch (IOException e) { + dbError(e); + } + } + + /** + * Determine if there is a transaction previously undone (see {@link #undo()}) that can be + * redone (see {@link #redo()}). + * + * @return true if there is a transaction previously undone that can be redone, else false + */ + @Override + public boolean canRedo() { + return archive.canRedo(); + } + + /** + * Determine if there is a previous transaction that can be reverted/undone (see {@link #undo()}). + * + * @return true if there is a previous transaction that can be reverted/undone, else false. + */ + @Override + public boolean canUndo() { + return archive.canUndo(); + } + + /** + * Get the transaction name that is available for {@link #redo()} (see {@link #canRedo()}). + * @return transaction name that is available for {@link #redo()} or empty String. + */ + @Override + public String getRedoName() { + return archive.getRedoName(); + } + + /** + * Get the transaction name that is available for {@link #undo()} (see {@link #canUndo()}). + * @return transaction name that is available for {@link #undo()} or empty String. + */ + @Override + public String getUndoName() { + return archive.getRedoName(); + } + + /** + * Get all transaction names that are available within the {@link #undo()} stack. + * + * @return all transaction names that are available within the {@link #undo()} stack. + */ + @Override + public synchronized List getAllUndoNames() { + return archive.getAllUndoNames(); + } + + /** + * Get all transaction names that are available within the {@link #redo()} stack. + * + * @return all transaction names that are available within the {@link #redo()} stack. + */ + @Override + public synchronized List getAllRedoNames() { + return archive.getAllRedoNames(); + } + + @Override + public void flushEvents() { + archive.flushEvents(); + } + + @Override + public void dataTypeChanged(DataType dt, boolean isAutoChange) { + super.dataTypeChanged(dt, isAutoChange); + // NOTE: During upgrades at time of instantiation dataTypeArchive will be null + if (archive != null) { + archive.dataTypeChanged(getID(dt), ProgramEvent.DATA_TYPE_CHANGED, isAutoChange, + null, dt); + } + } + + @Override + protected void dataTypeAdded(DataType newDt, DataType originalDataType) { + super.dataTypeAdded(newDt, originalDataType); + archive.dataTypeAdded(getID(newDt), ProgramEvent.DATA_TYPE_ADDED, null, newDt); + } + + @Override + protected void dataTypeReplaced(long existingDtID, DataTypePath existingPath, + DataType replacementDt) { + super.dataTypeReplaced(existingDtID, existingPath, replacementDt); + archive.dataTypeChanged(existingDtID, ProgramEvent.DATA_TYPE_REPLACED, false, + existingPath, replacementDt); + } + + @Override + protected void dataTypeDeleted(long deletedID, DataTypePath deletedDataTypePath) { + super.dataTypeDeleted(deletedID, deletedDataTypePath); + archive.dataTypeChanged(deletedID, ProgramEvent.DATA_TYPE_REMOVED, false, + deletedDataTypePath, null); + } + + @Override + protected void dataTypeMoved(DataType dt, DataTypePath oldPath, DataTypePath newPath) { + super.dataTypeMoved(dt, oldPath, newPath); + Category category = getCategory(oldPath.getCategoryPath()); + archive.dataTypeChanged(getID(dt), ProgramEvent.DATA_TYPE_MOVED, false, category, + dt); + } + + @Override + protected void dataTypeNameChanged(DataType dt, String oldName) { + super.dataTypeNameChanged(dt, oldName); + archive.dataTypeChanged(getID(dt), ProgramEvent.DATA_TYPE_RENAMED, false, oldName, + dt); + } + + @Override + protected void categoryCreated(Category newCategory) { + super.categoryCreated(newCategory); + archive.categoryAdded(newCategory.getID(), ProgramEvent.DATA_TYPE_CATEGORY_ADDED, + newCategory.getParent(), newCategory); + } + + @Override + protected void categoryRenamed(CategoryPath oldPath, Category category) { + super.categoryRenamed(oldPath, category); + archive.categoryChanged(category.getID(), ProgramEvent.DATA_TYPE_CATEGORY_RENAMED, + oldPath.getName(), category); + } + + @Override + protected void categoryRemoved(Category parent, String categoryName, long categoryID) { + super.categoryRemoved(parent, categoryName, categoryID); + archive.categoryChanged(categoryID, ProgramEvent.DATA_TYPE_CATEGORY_REMOVED, parent, + categoryName); + } + + @Override + protected void categoryMoved(CategoryPath oldPath, Category category) { + super.categoryMoved(oldPath, category); + archive.categoryChanged(category.getID(), ProgramEvent.DATA_TYPE_CATEGORY_MOVED, + oldPath.getParent(), category); + } + + @Override + protected void replaceDataTypesUsed(Map dataTypeReplacementMap) { + // do nothing + } + + @Override + protected void deleteDataTypesUsed(Set deletedIds) { + // do nothing + } + + @Override + protected String getDomainFileID() { + return null; + } + + /** + * Update custom storage for function definitions to be unassigned. + * @param monitor task monitor + * @throws CancelledException if task cancelled + */ + public void clearCustomStorageUse(TaskMonitor monitor) throws CancelledException { + // This method is for future development related to custom storage +// +// lock.writeLock().lock(); +// try { +// +// // Get copy of all function defs to avoid concurrent modification of underlaying table +// List defs = CollectionUtils.asList(getAllFunctionDefinitions()); +// +// monitor.initialize(defs.size()); +// monitor.setMessage("Clear custom storage use..."); +// +// // for (FunctionDefinition def : ImmutableList.copyOf(getAllFunctionDefinitions())) { +// monitor.checkCancelled(); +// // monitor.incrementProgress(1); +// // +// // // TODO: update function definition +// // if (def.hasCustomStorage()) { +// // +// // +// // } +// // } +// +// } +// finally { +// invalidateCache(); +// lock.writeLock().unlock(); +// } +// + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BitFieldDBDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BitFieldDBDataType.java index 856b0dc899..701acca2ce 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BitFieldDBDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BitFieldDBDataType.java @@ -150,7 +150,7 @@ class BitFieldDBDataType extends BitFieldDataType { * @param dtm data type manager * @return bit-field data type */ - static final BitFieldDataType getBitFieldDataType(long id, DataTypeManagerDB dtm) { + static final BitFieldDataType getBitFieldDataType(long id, DataTypeManager dtm) { int bitSize = (int) (id & 0xff); // 8-bits int bitOffset = (int) ((id >> BIT_OFFSET_SHIFT) & 0xff); // 8-bits diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltInDataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltInDataTypeManagerDB.java new file mode 100644 index 0000000000..6c6c6d8bfb --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltInDataTypeManagerDB.java @@ -0,0 +1,136 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.database.data; + +import java.io.IOException; + +import javax.help.UnsupportedOperationException; + +import db.DBHandle; +import db.util.ErrorHandler; +import ghidra.framework.data.OpenMode; +import ghidra.program.database.dtarchive.DataTypeArchiveDB; +import ghidra.program.database.symbol.VariableStorageManager; +import ghidra.program.model.data.*; +import ghidra.program.model.lang.ProgramArchitecture; +import ghidra.util.Lock; +import ghidra.util.UniversalID; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; + +/** + * Data type manager for built in types that do not live anywhere except + * in memory. + */ +public final class BuiltInDataTypeManagerDB extends ArchiveDataTypeManagerDB + implements BuiltInDataTypeManager { + + public BuiltInDataTypeManagerDB(DBHandle handle, + OpenMode openMode, ErrorHandler errHandler, Lock lock, TaskMonitor monitor) + throws IOException, CancelledException, VersionException { + super(handle, openMode, errHandler, lock, monitor); + } + + @Override + public void setDomainObject(DataTypeArchiveDB archive) { + this.archive = archive; + } + + @Override + protected final void setProgramArchitecture(ProgramArchitecture programArchitecture, + VariableStorageManager variableStorageMgr, boolean force, TaskMonitor monitor) + throws IOException, CancelledException { + throw new UnsupportedOperationException("program architecture change not permitted"); + } + + @Override + protected final boolean isArchitectureChangeAllowed() { + return false; + } + + @Override + public Category createCategory(CategoryPath path) { + if (path != CategoryPath.ROOT) { + throw new UnsupportedOperationException( + "Built-in category limited to root category only"); + } + return super.createCategory(path); + } + + protected UniversalID resolveSourceArchiveID(DataType dataType) { + if (dataType instanceof BuiltInDataType) { + return DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID; + } + throw new IllegalArgumentException("Only Built-in data types can be resolved by the " + + getClass().getSimpleName() + " manager."); + } + + @Override + public ArchiveType getType() { + return ArchiveType.BUILT_IN; + } + + @Override + public DataType resolve(DataType dataType, DataTypeConflictHandler handler) { + return super.resolve(dataType, BUILT_IN_MANAGER_HANDLER); + } + + @Override + public DataType addDataType(DataType originalDataType, DataTypeConflictHandler handler) { + throw new UnsupportedOperationException(); + } + + @Override + public void associateDataTypeWithArchive(DataType datatype, SourceArchive archive) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(DataType dataType) { + throw new UnsupportedOperationException(); + } + + @Override + public DataType replaceDataType(DataType existingDt, DataType replacementDt, + boolean updateCategoryPath) throws DataTypeDependencyException { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized void close() { + // cannot close a built-in data type manager; close performed automatically during shutdown + } + + private final static DataTypeConflictHandler BUILT_IN_MANAGER_HANDLER = + new DataTypeConflictHandler() { + @Override + public ConflictResult resolveConflict(DataType addedDataType, DataType existingDataType) { + throw new UnsupportedOperationException( + "Built-in data-types may not be substantially changed while Ghidra is running"); + } + + @Override + public boolean shouldUpdate(DataType sourceDataType, DataType localDataType) { + return false; + } + + @Override + public DataTypeConflictHandler getSubsequentHandler() { + return this; + } + }; +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDB.java index 67f7f85574..46da81b1fe 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDB.java @@ -138,7 +138,6 @@ class CategoryDB extends DbObject implements Category { checkDeleted(); CategoryPath oldPath = getCategoryPath(); if (isRoot()) { - mgr.setName(newName); return; } if (newName.equals(name)) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveMergeManagerFactory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeArchiveMergeManagerFactory.java similarity index 62% rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveMergeManagerFactory.java rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeArchiveMergeManagerFactory.java index f4146e20f8..691266315c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveMergeManagerFactory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeArchiveMergeManagerFactory.java @@ -1,24 +1,23 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.program.database; +package ghidra.program.database.data; -import ghidra.framework.*; -import ghidra.framework.data.*; -import ghidra.framework.model.*; +import ghidra.framework.PluggableServiceRegistry; +import ghidra.framework.data.DomainObjectMergeManager; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; public class DataTypeArchiveMergeManagerFactory { static { @@ -26,15 +25,17 @@ public class DataTypeArchiveMergeManagerFactory { new DataTypeArchiveMergeManagerFactory()); } - public static DomainObjectMergeManager getMergeManager(DomainObject resultsObj, - DomainObject sourceObj, DomainObject originalObj, DomainObject latestObj) { + public static DomainObjectMergeManager getMergeManager(ProjectDataTypeArchive resultsObj, + ProjectDataTypeArchive sourceObj, ProjectDataTypeArchive originalObj, + ProjectDataTypeArchive latestObj) { DataTypeArchiveMergeManagerFactory factory = PluggableServiceRegistry.getPluggableService(DataTypeArchiveMergeManagerFactory.class); return factory.doGetMergeManager(resultsObj, sourceObj, originalObj, latestObj); } - protected DomainObjectMergeManager doGetMergeManager(DomainObject resultsObj, - DomainObject sourceObj, DomainObject originalObj, DomainObject latestObj) { + protected DomainObjectMergeManager doGetMergeManager(ProjectDataTypeArchive resultsObj, + ProjectDataTypeArchive sourceObj, ProjectDataTypeArchive originalObj, + ProjectDataTypeArchive latestObj) { return null; } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeArchiveTransformer.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeArchiveTransformer.java index 2185f0465c..a46a55bfba 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeArchiveTransformer.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeArchiveTransformer.java @@ -33,10 +33,12 @@ import ghidra.GhidraApplicationLayout; import ghidra.GhidraLaunchable; import ghidra.framework.Application; import ghidra.framework.ApplicationConfiguration; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.data.*; import ghidra.program.model.data.Composite; import ghidra.program.model.data.Enum; -import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; +import ghidra.program.model.dtarchive.ArchiveWarning; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.util.*; import ghidra.util.classfinder.ClassSearcher; import ghidra.util.exception.*; @@ -54,58 +56,61 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { public static void transform(File oldFile, File newFile, File destinationFile, boolean useOldFileID, TaskMonitor monitor) - throws InvalidInputException, DuplicateFileException, IOException, CancelledException { + throws InvalidInputException, DuplicateFileException, IOException, CancelledException, + VersionException { monitor.setMessage("Beginning transformation..."); validate(oldFile, newFile, destinationFile); - - FileDataTypeManager oldFileArchive = null; - FileDataTypeManager newFileArchive = null; + FileDataTypeArchive oldArchive = null; + FileDataTypeArchive newArchive = null; + Object consumer = new Object(); try { monitor.initialize(100); - - oldFileArchive = FileDataTypeManager.openFileArchive(oldFile, false); - ArchiveWarning warning = oldFileArchive.getWarning(); + oldArchive = DataTypeArchiveFactory.openReadOnly(oldFile, consumer, TaskMonitor.DUMMY); + DataTypeManager oldFileDtManager = oldArchive.getDataTypeManager(); + ArchiveWarning warning = oldArchive.getWarning(); if (warning == ArchiveWarning.LANGUAGE_UPGRADE_REQURED) { throw new IOException("Archive requires language upgrade: " + oldFile); } if (warning != ArchiveWarning.NONE) { throw new IOException("Archive language error occured: " + oldFile, - oldFileArchive.getWarningDetail()); + oldArchive.getWarningDetail()); } + newArchive = + DataTypeArchiveFactory.openForUpdate(newFile, true, consumer, TaskMonitor.DUMMY); - newFileArchive = FileDataTypeManager.openFileArchive(newFile, true); - warning = newFileArchive.getWarning(); + DataTypeManager newFileDtManager = newArchive.getDataTypeManager(); + warning = newArchive.getWarning(); if (warning == ArchiveWarning.LANGUAGE_UPGRADE_REQURED) { throw new IOException("Archive requires language upgrade: " + newFile); } if (warning != ArchiveWarning.NONE) { throw new IOException("Archive language error occured: " + newFile, - newFileArchive.getWarningDetail()); + newArchive.getWarningDetail()); } - UniversalID oldUniversalID = oldFileArchive.getUniversalID(); - UniversalID newUniversalID = newFileArchive.getUniversalID(); + UniversalID oldUniversalID = oldFileDtManager.getUniversalID(); + UniversalID newUniversalID = newFileDtManager.getUniversalID(); Msg.info(DataTypeArchiveTransformer.class, "Old file ID = " + oldUniversalID); Msg.info(DataTypeArchiveTransformer.class, "New file ID = " + newUniversalID); - transformEachDataType(oldFileArchive, newFileArchive, monitor); + transformEachDataType(oldFileDtManager, newFileDtManager, monitor); monitor.setProgress(50); - fixEachDataTypeTimestamp(oldFileArchive, newFileArchive, monitor); + fixEachDataTypeTimestamp(oldFileDtManager, newFileDtManager, monitor); monitor.setProgress(100); monitor.setMessage("Saving " + destinationFile.getAbsolutePath()); if (useOldFileID) { - saveNewArchive(oldFileArchive, newFileArchive, destinationFile); + saveNewArchive(oldArchive, newArchive, destinationFile); } else { - saveNewArchive(newFileArchive, destinationFile); + saveNewArchive(newArchive, destinationFile); } } finally { - if (oldFileArchive != null) { - oldFileArchive.close(); + if (oldArchive != null) { + oldArchive.release(consumer); } - if (newFileArchive != null) { - newFileArchive.close(); + if (newArchive != null) { + newArchive.release(consumer); } } } @@ -124,13 +129,13 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { "Destination data type archive file must be specified."); } - if (!oldFile.getPath().endsWith(FileDataTypeManager.SUFFIX)) { + if (!oldFile.getPath().endsWith(FileDataTypeArchive.SUFFIX)) { throw new InvalidInputException("Old data type archive file must end with .gdt"); } - if (!newFile.getPath().endsWith(FileDataTypeManager.SUFFIX)) { + if (!newFile.getPath().endsWith(FileDataTypeArchive.SUFFIX)) { throw new InvalidInputException("New data type archive file must end with .gdt"); } - if (!destinationFile.getPath().endsWith(FileDataTypeManager.SUFFIX)) { + if (!destinationFile.getPath().endsWith(FileDataTypeArchive.SUFFIX)) { throw new InvalidInputException( "Destination data type archive file must end with .gdt"); } @@ -163,18 +168,18 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { } - private static void transformEachDataType(FileDataTypeManager oldFileArchive, - FileDataTypeManager newFileArchive, TaskMonitor monitor) throws CancelledException { + private static void transformEachDataType(DataTypeManager oldDtm, + DataTypeManager newDtm, TaskMonitor monitor) throws CancelledException { boolean commit = false; - int transactionID = newFileArchive.startTransaction("Transforming Data Type Archive"); + int transactionID = newDtm.startTransaction("Transforming Data Type Archive"); try { // Guarantee that the data type IDs won't already match those in the old archive. // This is necessary if we re-run the transformer for an archive. - assignNewUniversalIDs(newFileArchive, monitor); + assignNewUniversalIDs(newDtm, monitor); // Perform an initial pass to match by path name if possible and check for // anonymous data types that matched by matching components. - Iterator allDataTypes = newFileArchive.getAllDataTypes(); + Iterator allDataTypes = newDtm.getAllDataTypes(); while (allDataTypes.hasNext()) { monitor.checkCancelled(); DataType newDataType = allDataTypes.next(); @@ -188,26 +193,26 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { continue; // Skip pointer array or builtin. } DataType oldDataType = - transformDataType(newDataType, oldFileArchive, newFileArchive); + transformDataType(newDataType, oldDtm, newDtm); // Now process children anonymous data types for composites. - processAnonymous(oldDataType, newDataType, oldFileArchive, newFileArchive); + processAnonymous(oldDataType, newDataType, oldDtm, newDtm); // monitor.incrementProgress(1); monitor.setMessage("Transforming ID for " + newDataType.getPathName()); } // Process any unmatched enums by trying to match within each matching category. - processUnmatchedEnums(oldFileArchive, newFileArchive, monitor); + processUnmatchedEnums(oldDtm, newDtm, monitor); commit = true; } finally { - newFileArchive.endTransaction(transactionID, commit); + newDtm.endTransaction(transactionID, commit); } } - private static void assignNewUniversalIDs(FileDataTypeManager newFileArchive, + private static void assignNewUniversalIDs(DataTypeManager newFileArchive, TaskMonitor monitor) throws CancelledException { Iterator allDataTypes = newFileArchive.getAllDataTypes(); @@ -220,13 +225,13 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { } } - private static void processUnmatchedEnums(FileDataTypeManager oldFileArchive, - FileDataTypeManager newFileArchive, TaskMonitor monitor) throws CancelledException { + private static void processUnmatchedEnums(DataTypeManager oldDtm, + DataTypeManager newDtm, TaskMonitor monitor) throws CancelledException { // Find all anonymous enums and if not already matched to a data type in the old // archive, then try to match with an anonymous enum in the same category of the // old archive. - Iterator allDataTypes = newFileArchive.getAllDataTypes(); + Iterator allDataTypes = newDtm.getAllDataTypes(); while (allDataTypes.hasNext()) { monitor.checkCancelled(); DataType newDataType = allDataTypes.next(); @@ -234,25 +239,25 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { // Does this new data type already match one in the old archive? UniversalID newDtUniversalID = newDataType.getUniversalID(); - DataType matchingDt = oldFileArchive.findDataTypeForID(newDtUniversalID); + DataType matchingDt = oldDtm.findDataTypeForID(newDtUniversalID); if (matchingDt != null) { continue; // Already matched. } // Find the matching anonymous in the old archive under the same category. - Enum oldEnum = findMatchingAnonEnum((Enum) newDataType, oldFileArchive); + Enum oldEnum = findMatchingAnonEnum((Enum) newDataType, oldDtm); if (oldEnum != null && areSameClassType(newDataType, oldEnum)) { // Found a match so set the ID. - transformDataType(newDataType, newFileArchive, oldEnum); + transformDataType(newDataType, newDtm, oldEnum); } } } } - private static Enum findMatchingAnonEnum(Enum newEnum, FileDataTypeManager oldFileArchive) { + private static Enum findMatchingAnonEnum(Enum newEnum, DataTypeManager oldDtm) { CategoryPath categoryPath = newEnum.getCategoryPath(); - Category category = oldFileArchive.getCategory(categoryPath); + Category category = oldDtm.getCategory(categoryPath); if (category == null) { return null; } @@ -306,7 +311,7 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { } private static void processAnonymous(DataType oldDataType, DataType newDataType, - FileDataTypeManager oldFileArchive, FileDataTypeManager newFileArchive) { + DataTypeManager oldDtm, DataTypeManager newDtm) { // If we have composites, then get any component with an anonymous data type in the // newDataType, and look for it by matching field name in the old composite. @@ -326,7 +331,7 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { } DataTypeComponent[] newComponents = newComposite.getComponents(); for (DataTypeComponent newComp : newComponents) { - transformAnonymousComponent(oldFileArchive, newFileArchive, oldComposite, + transformAnonymousComponent(oldDtm, newDtm, oldComposite, newComposite, newComp); } } @@ -335,14 +340,14 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { TypeDef oldTypeDef = (TypeDef) oldDataType; TypeDef newTypeDef = (TypeDef) newDataType; transformInnerAnonymousDataType(oldTypeDef.getDataType(), newTypeDef.getDataType(), - oldFileArchive, newFileArchive); + oldDtm, newDtm); } else if (newDataType instanceof Pointer && oldDataType instanceof Pointer) { // Is this pointer to an anonymous? Pointer oldPointer = (Pointer) oldDataType; Pointer newPointer = (Pointer) newDataType; transformInnerAnonymousDataType(oldPointer.getDataType(), newPointer.getDataType(), - oldFileArchive, newFileArchive); + oldDtm, newDtm); } else if (newDataType instanceof Array && oldDataType instanceof Array) { // Is this an array of anonymous? @@ -350,13 +355,13 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { Array newArray = (Array) newDataType; if (oldArray.getNumElements() == newArray.getNumElements()) { transformInnerAnonymousDataType(oldArray.getDataType(), newArray.getDataType(), - oldFileArchive, newFileArchive); + oldDtm, newDtm); } } } - private static void transformAnonymousComponent(FileDataTypeManager oldFileArchive, - FileDataTypeManager newFileArchive, Composite oldComposite, Composite newComposite, + private static void transformAnonymousComponent(DataTypeManager oldDtm, + DataTypeManager newDtm, Composite oldComposite, Composite newComposite, DataTypeComponent newComponent) { DataType newCompDt = newComponent.getDataType(); @@ -407,27 +412,27 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { if (areSameClassType(newCompDt, oldCompDt) && isAnonymousType(oldCompDt)) { // Got a match so set the ID. - transformDataType(newCompDt, newFileArchive, oldCompDt); + transformDataType(newCompDt, newDtm, oldCompDt); // Now process children anonymous data types for anonymous composites. - processAnonymous(oldCompDt, newCompDt, oldFileArchive, newFileArchive); + processAnonymous(oldCompDt, newCompDt, oldDtm, newDtm); } } } } private static void transformInnerAnonymousDataType(DataType oldDataType, DataType newDataType, - FileDataTypeManager oldFileArchive, FileDataTypeManager newFileArchive) { + DataTypeManager oldDtm, DataTypeManager newDtm) { boolean isOldAnonymous = isAnonymousType(oldDataType); boolean isNewAnonymous = isAnonymousType(newDataType); if (isOldAnonymous && isNewAnonymous && areSameClassType(newDataType, oldDataType)) { // Got a match on the anonymous data type, so set the ID. - transformDataType(newDataType, newFileArchive, oldDataType); + transformDataType(newDataType, newDtm, oldDataType); } // Now process children anonymous data types for anonymous composites. - processAnonymous(oldDataType, newDataType, oldFileArchive, newFileArchive); + processAnonymous(oldDataType, newDataType, oldDtm, newDtm); } private static DataTypeComponent getAnonymousMatch(Composite oldComposite, @@ -501,15 +506,14 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { } private static DataType transformDataType(DataType newDataType, - FileDataTypeManager oldFileArchive, FileDataTypeManager newFileArchive) { + DataTypeManager oldDtm, DataTypeManager newDtm) { - DataType matchingDataType = - getMatchingDataType(newDataType, oldFileArchive, newFileArchive); - transformDataType(newDataType, newFileArchive, matchingDataType); + DataType matchingDataType = getMatchingDataType(newDataType, oldDtm, newDtm); + transformDataType(newDataType, newDtm, matchingDataType); return matchingDataType; } - private static void transformDataType(DataType newDataType, FileDataTypeManager newFileArchive, + private static void transformDataType(DataType newDataType, DataTypeManager newFileArchive, DataType matchingDataType) { // If we got a data type with the same name that is the same kind of data type then transform it. @@ -571,7 +575,7 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { } } - private static boolean dataTypeIDExists(FileDataTypeManager newFileArchive, + private static boolean dataTypeIDExists(DataTypeManager newFileArchive, DataType newDataType, SourceArchive newSourceArchive, UniversalID oldUniversalID) { if (oldUniversalID != null) { return false; @@ -590,23 +594,23 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { } private static DataType getMatchingDataType(DataType newDataType, - FileDataTypeManager oldFileArchive, FileDataTypeManager newFileArchive) { + DataTypeManager oldDtm, DataTypeManager newDtm) { // Try to get data type with same full path name. DataType oldDataType = - oldFileArchive.getDataType(newDataType.getCategoryPath(), newDataType.getName()); + oldDtm.getDataType(newDataType.getCategoryPath(), newDataType.getName()); if (oldDataType != null) { return oldDataType; } // Get all the old data types with the same name. ArrayList oldDataTypeList = new ArrayList<>(); - oldFileArchive.findDataTypes(newDataType.getName(), oldDataTypeList); + oldDtm.findDataTypes(newDataType.getName(), oldDataTypeList); if (oldDataTypeList.isEmpty()) { return null; } // Get all new data types with the same name. ArrayList newDataTypeList = new ArrayList<>(); - newFileArchive.findDataTypes(newDataType.getName(), newDataTypeList); + newDtm.findDataTypes(newDataType.getName(), newDataTypeList); // If there is a one to one match then assume a match. if (oldDataTypeList.size() == 1 && newDataTypeList.size() == 1) { DataType oldArchiveDataType = oldDataTypeList.get(0); @@ -689,8 +693,8 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { return dataTypeManager.getLocalSourceArchive(); } - private static void fixEachDataTypeTimestamp(FileDataTypeManager oldFileArchive, - FileDataTypeManager newFileArchive, TaskMonitor monitor) throws CancelledException { + private static void fixEachDataTypeTimestamp(DataTypeManager oldFileArchive, + DataTypeManager newFileArchive, TaskMonitor monitor) throws CancelledException { boolean commit = false; int transactionID = newFileArchive.startTransaction("Fixing Data Type Archive Timestamps"); try { @@ -710,7 +714,7 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { } private static void fixDataTypeTimestamp(DataType newDataType, - FileDataTypeManager oldFileArchive, FileDataTypeManager newFileArchive) { + DataTypeManager oldFileArchive, DataTypeManager newFileArchive) { UniversalID universalID = newDataType.getUniversalID(); SourceArchive sourceArchive = newDataType.getSourceArchive(); if (sourceArchive == newFileArchive.getLocalSourceArchive()) { @@ -743,23 +747,24 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { return sourceArchiveID == universalID; } - private static void saveNewArchive(FileDataTypeManager oldFileArchive, - FileDataTypeManager newFileArchive, File destinationFile) + private static void saveNewArchive(FileDataTypeArchive oldFileArchive, + FileDataTypeArchive newFileArchive, File destinationFile) throws DuplicateFileException, IOException { - UniversalID oldUniversalID = oldFileArchive.getUniversalID(); + UniversalID oldUniversalID = oldFileArchive.getDataTypeManager().getUniversalID(); newFileArchive.saveAs(destinationFile, oldUniversalID); Msg.info(DataTypeArchiveTransformer.class, - "Resulting file ID = " + newFileArchive.dbHandle.getDatabaseId()); + "Resulting file ID = " + oldUniversalID.getValue()); } - private static void saveNewArchive(FileDataTypeManager newFileArchive, File destinationFile) - throws DuplicateFileException, IOException { - newFileArchive.saveAs(destinationFile); - - FileDataTypeManager destinationFileArchive = - FileDataTypeManager.openFileArchive(destinationFile, false); - UniversalID destinationUniversalID = destinationFileArchive.getUniversalID(); - destinationFileArchive.close(); + private static void saveNewArchive(FileDataTypeArchive newFileArchive, File destinationFile) + throws DuplicateFileException, IOException, CancelledException, VersionException { + newFileArchive.saveAs(destinationFile, TaskMonitor.DUMMY); + Object consumer = new Object(); + FileDataTypeArchive destinationFileArchive = + DataTypeArchiveFactory.openReadOnly(destinationFile, consumer, TaskMonitor.DUMMY); + UniversalID destinationUniversalID = + destinationFileArchive.getDataTypeManager().getUniversalID(); + destinationFileArchive.release(consumer); Msg.info(DataTypeArchiveTransformer.class, "Resulting file ID = " + destinationUniversalID.getValue()); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeArchiveTransformerPanel.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeArchiveTransformerPanel.java index a4d8435e93..d0cb7a145a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeArchiveTransformerPanel.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeArchiveTransformerPanel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -182,7 +182,8 @@ public class DataTypeArchiveTransformerPanel extends JPanel { } protected void transform(TaskMonitor monitor) - throws InvalidInputException, DuplicateFileException, IOException, CancelledException { + throws InvalidInputException, DuplicateFileException, IOException, CancelledException, + VersionException { DataTypeArchiveTransformer.transform(getOldFile(), getNewFile(), getDestinationFile(), useOldFileIDCheckBox.isSelected(), monitor); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeIDConverter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeIDConverter.java index 9cf7192734..a084d8d3be 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeIDConverter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeIDConverter.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,13 +22,17 @@ import ghidra.GhidraApplicationLayout; import ghidra.GhidraLaunchable; import ghidra.framework.Application; import ghidra.framework.ApplicationConfiguration; -import ghidra.program.model.data.*; -import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; +import ghidra.program.model.data.BuiltInDataType; +import ghidra.program.model.data.DataType; +import ghidra.program.model.dtarchive.ArchiveWarning; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.util.NumericUtilities; import ghidra.util.UniversalID; import ghidra.util.datastruct.LongLongHashtable; import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.NoValueException; +import ghidra.util.task.TaskMonitor; public class DataTypeIDConverter implements GhidraLaunchable { @@ -42,7 +46,8 @@ public class DataTypeIDConverter implements GhidraLaunchable { @Override public void launch(GhidraApplicationLayout layout, String[] args) { if (args.length != 3) { - System.out.println("DataTypeIDConverter "); + System.out.println( + "DataTypeIDConverter "); System.exit(1); } Application.initializeApplication(layout, new ApplicationConfiguration()); @@ -88,9 +93,9 @@ public class DataTypeIDConverter implements GhidraLaunchable { return; } - FileDataTypeManager oldFileArchive = null; + FileDataTypeArchive oldFileArchive = null; try { - oldFileArchive = FileDataTypeManager.openFileArchive(inFile, false); + oldFileArchive = DataTypeArchiveFactory.openReadOnly(inFile, this, TaskMonitor.DUMMY); if (oldFileArchive.getWarning() != ArchiveWarning.NONE) { System.out.println("Archive ID Conversion aborted"); return; @@ -103,16 +108,12 @@ public class DataTypeIDConverter implements GhidraLaunchable { oldFileArchive.saveAs(outFile, newFileID); } - catch (IOException e) { - e.printStackTrace(); - } - catch (NoValueException e) { - // TODO Auto-generated catch block + catch (Exception e) { e.printStackTrace(); } finally { if (oldFileArchive != null) { - oldFileArchive.close(); + oldFileArchive.release(this); } } } @@ -160,11 +161,11 @@ public class DataTypeIDConverter implements GhidraLaunchable { } } - private void transformDataTypes(FileDataTypeManager oldFileArchive) { + private void transformDataTypes(FileDataTypeArchive oldFileArchive) { boolean commit = false; int transactionID = oldFileArchive.startTransaction("Transforming Data Type Archive"); try { - Iterator allDataTypes = oldFileArchive.getAllDataTypes(); + Iterator allDataTypes = oldFileArchive.getDataTypeManager().getAllDataTypes(); while (allDataTypes.hasNext()) { DataType nextDt = allDataTypes.next(); if (!(nextDt instanceof DataTypeDB)) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java index 371f9c7acd..4962b3a662 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java @@ -15,7 +15,6 @@ */ package ghidra.program.database.data; -import java.io.File; import java.io.IOException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -29,15 +28,12 @@ import org.apache.commons.lang3.StringUtils; import db.*; import db.util.ErrorHandler; -import generic.jar.ResourceFile; import generic.stl.Pair; import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive; import ghidra.docking.settings.*; import ghidra.framework.Application; import ghidra.framework.data.OpenMode; import ghidra.framework.model.RuntimeIOException; -import ghidra.framework.store.db.PackedDBHandle; -import ghidra.framework.store.db.PackedDatabase; import ghidra.graph.*; import ghidra.graph.algo.GraphNavigator; import ghidra.program.database.*; @@ -79,9 +75,10 @@ abstract public class DataTypeManagerDB implements DataTypeManager { * mode must always be allowed when opening older versions. * - version 1 - Legacy prior to overall DTM versioning (not stored) * 12-Jan-2022 - version 2 - Introduced DataTypeManager data map table and overall DTM version. - * Also added typedef flags and auto-naming support. + * Also added typedef flags and auto-naming support + * 22-Jun-2026 - version 3 - standardized on use of DtArchiveDB Domain Object for all archives. */ - static final int DB_VERSION = 2; + static final int DB_VERSION = 3; static long ROOT_CATEGORY_ID = 0; @@ -133,7 +130,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager { protected final boolean readOnlyMode; protected final DBHandle dbHandle; protected final String tablePrefix; - protected final ErrorHandler errHandler; + + protected ErrorHandler errHandler; + protected Lock lock; private DataTypeConflictHandler currentHandler; @@ -177,195 +176,16 @@ abstract public class DataTypeManagerDB implements DataTypeManager { private ThreadLocal equivalenceCache = new ThreadLocal<>(); private IdentityHashMap resolveCache; - private TreeSet resolveQueue; // Note: is TreeSet really needed? + private TreeSet resolveQueue; // Note: is TreeSet really needed? private LinkedList conflictQueue = new LinkedList<>(); private boolean isBulkRemoving; - protected AddressMap addrMap; - private DataOrganization dataOrganization; private ProgramArchitecture programArchitecture; private VariableStorageManager variableStorageMgr; - protected final Lock lock; - - private static class ResolvePair implements Comparable { - - private final DataTypeDB resolvedDt; - private final DataType definitionDt; - - ResolvePair(DataTypeDB resolvedDt, DataType definitionDt) { - this.resolvedDt = resolvedDt; - this.definitionDt = definitionDt; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof ResolvePair)) { - return false; - } - return resolvedDt.getKey() == ((ResolvePair) obj).resolvedDt.getKey(); - } - - @Override - public int hashCode() { - long value = resolvedDt.getKey(); - return (int) (value ^ (value >>> 32)); - } - - @Override - public int compareTo(ResolvePair o) { - long r = resolvedDt.getKey() - o.resolvedDt.getKey(); - if (r == 0) { - return 0; - } - if (r < 0) { - return -1; - } - return 1; - } - } - - /** - * Construct a new temporary data-type manager. Note that this manager does not - * support the save or saveAs operation. No Language is associated with instance. - * - * @param dataOrganization applicable data organization - * @throws RuntimeIOException if database error occurs during creation - */ - protected DataTypeManagerDB(DataOrganization dataOrganization) throws RuntimeIOException { - this.lock = new Lock("DataTypeManagerDB"); - this.errHandler = new DbErrorHandler(); - this.dataOrganization = dataOrganization; - this.tablePrefix = ""; - - try { - dbHandle = new DBHandle(); - readOnlyMode = false; - long txId = dbHandle.startTransaction(); - try { - init(OpenMode.CREATE, TaskMonitor.DUMMY); - } - catch (VersionException | CancelledException e) { - throw new AssertException(e); // unexpected - } - finally { - dbHandle.endTransaction(txId, true); - } - } - catch (IOException e) { - throw new RuntimeIOException(e); - } - } - - /** - * Constructor for a data-type manager backed by a packed database file. When - * opening for UPDATE an automatic upgrade will be performed if required. - * NOTE: Default DataOrganization will be used for new archive. - * - * @param packedDBfile packed datatype archive file (i.e., *.gdt resource). - * @param openMode open mode CREATE, READ_ONLY or UPDATE - * @param monitor task monitor - * @throws IOException a low-level IO error. This exception may also be thrown - * when a version error occurs (cause is VersionException). - * @throws CancelledException if task cancelled - */ - protected DataTypeManagerDB(ResourceFile packedDBfile, OpenMode openMode, TaskMonitor monitor) - throws IOException, CancelledException { - - this.errHandler = new DbErrorHandler(); - this.lock = new Lock("DataTypeManagerDB"); - this.tablePrefix = ""; - this.readOnlyMode = (openMode == OpenMode.IMMUTABLE); - - File file = packedDBfile.getFile(false); - if (file == null && openMode != OpenMode.IMMUTABLE) { - throw new IOException("Unsupported mode (" + openMode + - ") for read-only Datatype Archive: " + packedDBfile.getAbsolutePath()); - } - - // Open packed database archive - boolean openSuccess = false; - PackedDatabase pdb = null; - try { - if (openMode == OpenMode.CREATE) { - dbHandle = new PackedDBHandle( - DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE); - } - else { - pdb = PackedDatabase.getPackedDatabase(packedDBfile, false, monitor); - - if (openMode == OpenMode.IMMUTABLE) { - dbHandle = pdb.open(monitor); - } - else { // UPDATE mode (allows upgrade use) - dbHandle = pdb.openForUpdate(monitor); - } - } - openSuccess = true; - } - finally { - if (!openSuccess && pdb != null) { - pdb.dispose(); // dispose on error - } - } - - // Initialize datatype manager and save new archive on CREATE - boolean initSuccess = false; - try { - initPackedDatabase(packedDBfile, openMode, monitor); // performs upgrade if needed - if (openMode == OpenMode.CREATE) { - // preserve UniversalID if it has been established - Long uid = universalID != null ? universalID.getValue() : null; - ((PackedDBHandle) dbHandle).saveAs("Archive", file.getParentFile(), - packedDBfile.getName(), uid, monitor); - } - initSuccess = true; - } - finally { - if (!initSuccess) { - dbHandle.close(); // close on error (packed database will also be disposed) - } - } - } - - private void initPackedDatabase(ResourceFile packedDBfile, OpenMode openMode, - TaskMonitor monitor) throws CancelledException, IOException { - Long txId = dbHandle.startTransaction(); - try { - init(openMode, monitor); - - if (openMode != OpenMode.CREATE && hasDataOrganizationChange(true)) { - // check for data organization change with possible upgrade - handleDataOrganizationChange(openMode, monitor); - } - - if (openMode == OpenMode.UPGRADE) { - migrateOldFlexArrayComponentsIfRequired(monitor); - - Msg.showInfo(this, null, "Archive Upgraded", - "Data type archive has been upgraded: " + packedDBfile.getName()); - } - } - catch (VersionException e) { - if (openMode == OpenMode.UPDATE && e.isUpgradable()) { - // Try again with UPGRADE mode - dbHandle.endTransaction(txId, true); - txId = null; - initPackedDatabase(packedDBfile, OpenMode.UPGRADE, monitor); - } - else { - // Unable to handle required upgrade - throw new IOException(e); - } - } - finally { - if (txId != null) { - dbHandle.endTransaction(txId, true); - } - } - } + protected AddressMap addrMap; /** * Constructor for a database-backed DataTypeManagerDB extension. @@ -387,18 +207,56 @@ abstract public class DataTypeManagerDB implements DataTypeManager { * @throws VersionException if any database handle's version doesn't match the expected version. * This exception will never be thrown in READ_ONLY mode. */ - protected DataTypeManagerDB(DBHandle handle, AddressMap addrMap, OpenMode openMode, + protected DataTypeManagerDB(DBHandle handle, OpenMode openMode, AddressMap addrMap, String tablePrefix, ErrorHandler errHandler, Lock lock, TaskMonitor monitor) throws CancelledException, IOException, VersionException { + this.addrMap = addrMap; this.tablePrefix = tablePrefix != null ? tablePrefix : ""; this.dbHandle = handle; this.readOnlyMode = (openMode == OpenMode.IMMUTABLE); - this.addrMap = addrMap; this.errHandler = errHandler; this.lock = lock; init(openMode, monitor); } + /** + * Constructor for a transient database-backed DataTypeManagerDB instance using + * a new {@link DBHandle}. + * + * @throws RuntimeIOException if there is a problem creating the database + */ + protected DataTypeManagerDB() + throws RuntimeIOException { + try { + this.tablePrefix = ""; + this.dbHandle = new DBHandle(); + this.readOnlyMode = false; + this.errHandler = new DbErrorHandler(); + this.lock = new Lock("DTMLock"); + long txId = dbHandle.startTransaction(); + try { + init(OpenMode.CREATE, TaskMonitor.DUMMY); + } + finally { + dbHandle.endTransaction(txId, true); + } + } + catch (CancelledException | VersionException e) { + throw new AssertException(e); + } + catch (IOException e) { + throw new RuntimeIOException(e); + } + } + + private static class DbErrorHandler implements ErrorHandler { + + @Override + public void dbError(IOException e) throws RuntimeIOException { + throw new RuntimeIOException(e); + } + } + private void init(OpenMode openMode, TaskMonitor monitor) throws CancelledException, IOException, VersionException { updateID(); @@ -831,6 +689,11 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } } + protected void setDataOrganization(DataOrganization dataOrganization) { + this.dataOrganization = dataOrganization != null ? dataOrganization + : DataOrganizationImpl.getDefaultOrganization(); + } + /** * Perform updates related to a compiler spec change, including: *

    @@ -914,21 +777,10 @@ abstract public class DataTypeManagerDB implements DataTypeManager { return programArchitecture; } - protected static String getProgramArchitectureSummary(LanguageID languageId, - int languageVersion, CompilerSpecID compilerSpecId) { - StringBuilder buf = new StringBuilder(); - buf.append(languageId.getIdAsString()); - buf.append(" / "); - buf.append(compilerSpecId.getIdAsString()); - return buf.toString(); - } - @Override public String getProgramArchitectureSummary() { if (programArchitecture != null) { - return getProgramArchitectureSummary(programArchitecture.getLanguage().getLanguageID(), - programArchitecture.getLanguage().getVersion(), - programArchitecture.getCompilerSpec().getCompilerSpecID()); + return programArchitecture.getSummary(); } return null; } @@ -960,10 +812,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager { defaultListener.restored(this); } + /** + * {@return the ID associated this datatype manager's associated domain file storage if + * applicable, or null} + */ abstract protected String getDomainFileID(); - abstract protected String getPath(); - private List getSortedDataTypes() { List local = lazySortedDataTypes; if (local == null) { @@ -3462,11 +3316,6 @@ abstract public class DataTypeManagerDB implements DataTypeManager { enumValueMap = null; } - @Override - public void close() { - dispose(); - } - /** * Invalidates the cache. */ @@ -3878,11 +3727,6 @@ abstract public class DataTypeManagerDB implements DataTypeManager { return null; } - @Override - public AddressMap getAddressMap() { - return addrMap; - } - @Override public final DataOrganization getDataOrganization() { if (dataOrganization == null) { @@ -4120,7 +3964,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { replaceSourceArchive(sourceArchive, mappedSourceArchive); } } - BuiltInDataTypeManager builtInDTM = BuiltInDataTypeManager.getDataTypeManager(); + DataTypeManager builtInDTM = BuiltInDataTypeManager.getDataTypeManager(); for (String name : SourceArchiveUpgradeMap.getTypedefReplacements()) { monitor.checkCancelled(); DataType dataType = getDataType(CategoryPath.ROOT, name); @@ -4457,7 +4301,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { if (resolveQueue == null) { resolveQueue = new TreeSet<>(); } - resolveQueue.add(new ResolvePair(resolvedDt, definitionDt)); + resolveQueue.add(new ResolvedDt(resolvedDt, definitionDt)); } /** @@ -4500,7 +4344,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { if (resolveQueue != null) { DataTypeConflictHandler handler = getDependencyConflictHandler(); while (!resolveQueue.isEmpty()) { - ResolvePair resolvePair = resolveQueue.pollFirst(); + ResolvedDt resolvePair = resolveQueue.pollFirst(); DataTypeDB resolvedDt = resolvePair.resolvedDt; try { if (!resolvedDt.isDeleted()) { @@ -4776,21 +4620,6 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } } - private class DbErrorHandler implements ErrorHandler { - - @Override - public void dbError(IOException e) throws RuntimeIOException { - - String message = e.getMessage(); - if (e instanceof ClosedException) { - message = "Data type archive is closed: " + getName(); - Msg.showError(this, null, "IO ERROR", message, e); - } - - throw new RuntimeIOException(e); - } - } - /** * Diagnostic method to determine actual number of datatype records which exist. This * may differ from the total number of datatypes reported via {@link DataTypeManager#getAllDataTypes()} @@ -4972,4 +4801,33 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } } + private static record ResolvedDt(DataTypeDB resolvedDt, DataType definitionDt) + implements Comparable { + + @Override + public int hashCode() { + long value = resolvedDt.getKey(); + return (int) (value ^ (value >>> 32)); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ResolvedDt other = (ResolvedDt) obj; + return resolvedDt.getKey() == other.resolvedDt.getKey(); + } + + @Override + public int compareTo(ResolvedDt o) { + return Long.compare(resolvedDt.getKey(), o.resolvedDt.getKey()); + } + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FileDataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FileDataTypeManagerDB.java new file mode 100644 index 0000000000..c38cdc5374 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FileDataTypeManagerDB.java @@ -0,0 +1,187 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.database.data; + +import static ghidra.program.model.dtarchive.FileDataTypeArchive.*; + +import java.io.File; +import java.io.IOException; + +import db.DBHandle; +import db.util.ErrorHandler; +import ghidra.framework.data.OpenMode; +import ghidra.framework.store.db.PackedDatabase; +import ghidra.program.database.dtarchive.FileDtArchiveDB; +import ghidra.program.model.data.*; +import ghidra.util.Lock; +import ghidra.util.UniversalID; +import ghidra.util.exception.*; +import ghidra.util.task.TaskMonitor; + +public class FileDataTypeManagerDB extends ArchiveDataTypeManagerDB + implements FileDataTypeManager { + + final static String OLD_EXTENSION = "dtf"; + final static String OLD_SUFFIX = "." + OLD_EXTENSION; + + /** + * Construct a new DataTypeFileManager using the default data organization. + *

    + * NOTE: it may be appropriate to {@link #getWarning() check for warnings} after + * opening an existing archive file prior to use. While an archive will remain useable + * with a warning condition, architecture-specific data may not be available or up-to-date. + * @param handle the open database handle + * @param openMode CREATE, READ_ONLY or UPDATE + * @param errHandler the object to report errors + * @param lock the database lock. + * @param monitor the progress monitor + * @throws IOException if an IO error occurs + * @throws CancelledException if task cancelled + * @throws VersionException if the datatype database is not the correct version + */ + public FileDataTypeManagerDB(DBHandle handle, OpenMode openMode, + ErrorHandler errHandler, Lock lock, TaskMonitor monitor) + throws IOException, CancelledException, VersionException { + super(handle, openMode, errHandler, lock, monitor); + } + + @Override + public FileDtArchiveDB getDataStore() { + return (FileDtArchiveDB) super.getDataStore(); + } + + /** + * Saves the data type manager to the given file with a specific databaseId. + * NOTE: This method is intended for use in transforming one archive database to + * match another existing archive database. + * @param saveFile the file to save + * @param newUniversalId the new id to use + * @throws DuplicateFileException if save file already exists + * @throws IOException if IO error occurs + */ + @Override + public void saveAs(File saveFile, UniversalID newUniversalId) + throws DuplicateFileException, IOException { + getDataStore().saveAs(saveFile, newUniversalId); + + } + + /** + * Saves the data type manager to the given file + * @param saveFile the file to save + * @throws DuplicateFileException if save file already exists + * @throws IOException if IO error occurs + */ + @Override + public void saveAs(File saveFile) throws DuplicateFileException, IOException { + try { + getDataStore().saveAs(saveFile, TaskMonitor.DUMMY); + } + catch (CancelledException e) { + // can't happen since we used a DUMMY monitor + } + } + + /** + * Save the category to source file. + * @throws IOException if IO error occurs + */ + @Override + public void save() throws IOException { + try { + getDataStore().save(null, TaskMonitor.DUMMY); + } + catch (CancelledException e) { + // can't happen since we are using a DUMMY TaskMonitor + } + } + + /** + * Convert the filename for the given file to have the packed database + * file extension. + * @param file file whose name is to be converted + * @return file if the filename already ends in the packed database + * file extension, or a new File object that has the packed database + * file extension + */ + public static File convertFilename(File file) { + String fname = file.getName(); + if (file.getName().endsWith(SUFFIX)) { + return file; + } + int pos = fname.indexOf(OLD_SUFFIX); + if (pos > 0) { + fname = fname.substring(0, pos); + } + fname = fname + SUFFIX; + return new File(file.getParentFile(), fname); + } + + public void notifyNameChange() { + defaultListener.categoryRenamed(this, CategoryPath.ROOT, CategoryPath.ROOT); + } + + @Override + public boolean isClosed() { + return archive.isClosed(); + } + + @Override + public void finalize() { + close(); + } + + public void delete() throws IOException { + getDataStore().delete(); + } + + public static void delete(File packedDbfile) throws IOException { + if (packedDbfile == null) { + return; + } + String filename = packedDbfile.getAbsolutePath(); + if (filename.endsWith(OLD_SUFFIX)) { + packedDbfile.delete(); + } + else { + PackedDatabase.delete(packedDbfile); + } + } + + @Override + public String getPath() { + return archive.getPath(); + } + + @Override + public ArchiveType getType() { + return ArchiveType.FILE; + } + + @Override + @Deprecated(since = "12.2", forRemoval = true) + public void close() { + if (!archive.isClosed()) { + getDataStore().releaseDefaultConsumers(); + } + } + + @Override + public String toString() { + return getClass().getSimpleName() + " - " + getName(); + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramBasedDataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramBasedDataTypeManagerDB.java index 100fb4f4f0..1bdeeecea2 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramBasedDataTypeManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramBasedDataTypeManagerDB.java @@ -64,7 +64,7 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB protected ProgramBasedDataTypeManagerDB(DBHandle handle, AddressMap addrMap, OpenMode openMode, String tablePrefix, ErrorHandler errHandler, Lock lock, TaskMonitor monitor) throws CancelledException, VersionException, IOException { - super(handle, addrMap, openMode, tablePrefix, errHandler, lock, monitor); + super(handle, openMode, addrMap, tablePrefix, errHandler, lock, monitor); } @Override @@ -192,6 +192,15 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB return false; } + /** + * Returns the associated AddressMap used by this datatype manager. + * @return the AddressMap used by this datatype manager or null if + * one has not be established. + */ + public AddressMap getAddressMap() { + return addrMap; + } + @Override public void clearAllSettings(Data data) { if (instanceSettingsAdapter == null) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java index c8c922cc82..606bb9b499 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java @@ -25,21 +25,24 @@ import db.util.ErrorHandler; import ghidra.framework.data.OpenMode; import ghidra.framework.model.DomainFile; import ghidra.framework.options.Options; -import ghidra.program.database.ManagerDB; import ghidra.program.database.ProgramDB; +import ghidra.program.database.ProgramDBModule; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.DataTypeStore; import ghidra.program.model.listing.Program; import ghidra.program.util.ProgramEvent; -import ghidra.util.*; +import ghidra.util.Lock; +import ghidra.util.UniversalID; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; /** * Class for managing data types in a program */ -public class ProgramDataTypeManager extends ProgramBasedDataTypeManagerDB implements ManagerDB { +public class ProgramDataTypeManager extends ProgramBasedDataTypeManagerDB + implements ProgramDBModule { private static final String OLD_DT_ARCHIVE_FILENAMES = "DataTypeArchiveFilenames"; // eliminated with Ghidra 4.3 @@ -65,11 +68,16 @@ public class ProgramDataTypeManager extends ProgramBasedDataTypeManagerDB implem upgrade = (openMode == OpenMode.UPGRADE); } + @Override + public DataTypeStore getDataStore() { + return program; + } + /** * Save the current data organization to facilitate future change detection and * upgrades. This method must be invoked by {@link ProgramDB} during the final * stage of program creation (i.e., openMode == CREATE). - * @throws IOException if failure occured while saving data organization. + * @throws IOException if failure occurred while saving data organization. */ @Override public void saveDataOrganization() throws IOException { @@ -87,7 +95,7 @@ public class ProgramDataTypeManager extends ProgramBasedDataTypeManagerDB implem } @Override - public void setProgram(ProgramDB p) { + public void setDomainObject(ProgramDB p) { this.program = p; try { setProgramArchitecture(p, p.getSymbolTable().getVariableStorageManager(), false, @@ -121,7 +129,7 @@ public class ProgramDataTypeManager extends ProgramBasedDataTypeManagerDB implem } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { if (openMode == OpenMode.UPGRADE) { doSourceArchiveUpdates(monitor); @@ -145,17 +153,6 @@ public class ProgramDataTypeManager extends ProgramBasedDataTypeManagerDB implem return program.getName(); } - @Override - public void setName(String name) throws InvalidNameException { - if (name == null || name.length() == 0) { - throw new InvalidNameException("Name is invalid: " + name); - } - - program.setName(name); - Category root = getRootCategory(); - categoryRenamed(CategoryPath.ROOT, root); - } - @Override public void sourceArchiveChanged(UniversalID sourceArchiveID) { super.sourceArchiveChanged(sourceArchiveID); @@ -316,22 +313,11 @@ public class ProgramDataTypeManager extends ProgramBasedDataTypeManagerDB implem } @Override - public DomainFile getDomainFile() { - return program.getDomainFile(); - } - - @Override - public String getDomainFileID() { + protected String getDomainFileID() { DomainFile domainFile = program.getDomainFile(); // Can be null if it has never been saved. return (domainFile != null) ? domainFile.getFileID() : null; } - @Override - public String getPath() { - DomainFile domainFile = program.getDomainFile(); // Can be null if it has never been saved. - return (domainFile != null) ? domainFile.getPathname() : null; - } - @Override public ArchiveType getType() { return ArchiveType.PROGRAM; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManagerDB.java new file mode 100644 index 0000000000..0815687d56 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManagerDB.java @@ -0,0 +1,107 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.database.data; + +import java.io.IOException; + +import db.DBHandle; +import db.util.ErrorHandler; +import ghidra.framework.data.OpenMode; +import ghidra.framework.model.DomainFile; +import ghidra.program.database.dtarchive.ProjectDtArchiveDB; +import ghidra.program.model.data.ArchiveType; +import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.lang.CompilerSpec; +import ghidra.program.model.lang.Language; +import ghidra.util.Lock; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; + +/** + * Class for managing data types in a project archive + * NOTE: default data organization is used. + */ +public class ProjectDataTypeManagerDB extends ArchiveDataTypeManagerDB { + + /** + * Constructor for a data-type manager using a specified DBHandle. + *

    + * NOTE: If archive has an assigned architecture, issues may arise due to a revised or + * missing {@link Language}/{@link CompilerSpec} which will result in a warning but not + * prevent the archive from being opened. Such a warning condition will ne logged and may + * result in missing or stale information for existing datatypes which have architecture related + * data. In some case it may be appropriate to + * {@link FileDataTypeManager#getWarning() check for warnings} on the returned archive + * object prior to its use. + * + * @param handle open database handle + * @param openMode the program open mode + * @param errHandler the database I/O error handler + * @param lock the program synchronization lock + * @param monitor the progress monitor + * @throws CancelledException if the user cancels an upgrade + * @throws VersionException if the database does not match the expected version. + * @throws IOException if a database I/O error occurs. + */ + public ProjectDataTypeManagerDB(DBHandle handle, + OpenMode openMode, + ErrorHandler errHandler, Lock lock, TaskMonitor monitor) + throws CancelledException, VersionException, IOException { + super(handle, openMode, errHandler, lock, monitor); + } + + @Override + public ProjectDtArchiveDB getDataStore() { + return (ProjectDtArchiveDB) super.getDataStore(); + } + + @Override + public String getName() { + return archive.getDomainFile().getName(); + } + + /** + * Get the domain file for the associated project archive. + * @return the associated domain file + */ + public DomainFile getDomainFile() { + return archive.getDomainFile(); + } + + @Override + protected String getDomainFileID() { + DomainFile domainFile = getDomainFile(); // Can be null if it has never been saved. + return (domainFile != null) ? domainFile.getFileID() : null; + } + + @Override + public String getPath() { + return archive.getPath(); + } + + @Override + public ArchiveType getType() { + return ArchiveType.PROJECT; + } + + @Override + public synchronized void close() { + // do nothing - cannot close a project data type manager + // dispose should be invoked by the owner of the instance + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveDB.java index b4df771f49..98e3475d30 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveDB.java @@ -37,8 +37,7 @@ public class SourceArchiveDB extends DbObject implements SourceArchive { * @param adapter the source archive database adapter * @param record the source archive record */ - SourceArchiveDB(DataTypeManagerDB dtMgr, SourceArchiveAdapter adapter, - DBRecord record) { + SourceArchiveDB(DataTypeManagerDB dtMgr, SourceArchiveAdapter adapter, DBRecord record) { super(record.getKey()); this.dtMgr = dtMgr; this.adapter = adapter; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TransientDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TransientDataTypeManager.java new file mode 100644 index 0000000000..5971dcb1cc --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TransientDataTypeManager.java @@ -0,0 +1,141 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.database.data; + +import java.io.IOException; + +import javax.help.UnsupportedOperationException; + +import ghidra.framework.model.RuntimeIOException; +import ghidra.framework.store.LockException; +import ghidra.program.database.dtarchive.TransientDtArchiveDB; +import ghidra.program.model.data.ArchiveType; +import ghidra.program.model.data.DataOrganization; +import ghidra.program.model.dtarchive.DataTypeArchive.LanguageUpdateOption; +import ghidra.program.model.dtarchive.TransientDataTypeArchive; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.IncompatibleLanguageException; +import ghidra.program.util.DefaultLanguageService; +import ghidra.util.exception.AssertException; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * {@link TransientDataTypeManager} provides a transient/temporary datatype manager that is meant to + * be used as a temporary scratch pad for resolving datatype relationships. + */ +public class TransientDataTypeManager extends ArchiveDataTypeManagerDB { + + /** + * Create a transient/temporary datatype manager using a specified data organization. + * + * @param name archive and datatype manager name + * @param dataOrganization data organization + * @throws RuntimeIOException if database error occurs during creation + */ + public TransientDataTypeManager(String name, DataOrganization dataOrganization) + throws RuntimeIOException { + super(); + try { + // Unusual code alert: Normally DataTypeManagers are created by the archives, but + // for convenience of being able to just create a TransientDataTypeManager by + // constructing it, we internally construct the containing archive here and pass our + // instance to the archive constructor. + archive = new TransientDtArchiveDB(dbHandle, name, this); + } + catch (IOException e) { + throw new RuntimeIOException(e); + } + setDataOrganization(dataOrganization); // may be null + this.errHandler = archive; + this.lock = archive.getLock(); + } + + /** + * Create a transient/temporary datatype manager using a default data organization. + * + * @param name archive and datatype manager name + * @throws RuntimeIOException if database error occurs during creation + */ + public TransientDataTypeManager(String name) + throws RuntimeIOException { + this(name, null); + } + + /** + * Create a transient/temporary datatype manager using a specified program architecture. + * + * @param name archive and datatype manager name + * @param language processor language + * @param compilerSpecId valid compiler spec ID which corresponds to the language ID. + * @throws CompilerSpecNotFoundException if invalid compilerSpecId specified for language + * @throws LanguageNotFoundException if current language is not found (if required for data transition) + * @throws IOException if an IO error occurs + */ + public TransientDataTypeManager(String name, Language language, CompilerSpecID compilerSpecId) + throws CompilerSpecNotFoundException, LanguageNotFoundException, IOException { + this(name, null); + try { + archive.setProgramArchitecture(language, compilerSpecId, LanguageUpdateOption.CLEAR, + TaskMonitor.DUMMY); + } + catch (CancelledException | LockException | UnsupportedOperationException + | IncompatibleLanguageException e) { + throw new AssertException(e); + } + catch (RuntimeIOException e) { + e.throwIOException(); + } + } + + /** + * Create a transient/temporary datatype manager using a specified program architecture. + * + * @param name archive and datatype manager name + * @param languageId valid language ID (see appropriate *.ldefs file for defined IDs) + * @param compilerSpecId valid compiler spec ID which corresponds to the language ID. + * @throws CompilerSpecNotFoundException if invalid compilerSpecId specified for language + * @throws LanguageNotFoundException if current language is not found (if required for data transition) + * @throws IOException if an IO error occurs + */ + public TransientDataTypeManager(String name, LanguageID languageId, + CompilerSpecID compilerSpecId) + throws CompilerSpecNotFoundException, LanguageNotFoundException, IOException { + + // Verify that the specified language and compiler spec are valid + LanguageService languageService = DefaultLanguageService.getLanguageService(); + Language language = languageService.getLanguage(languageId); + language.getCompilerSpecByID(compilerSpecId); // performs check only + + this(name, language, compilerSpecId); + } + + @Override + public TransientDataTypeArchive getDataStore() { + return (TransientDataTypeArchive) super.getDataStore(); + } + + @Override + public ArchiveType getType() { + return ArchiveType.TEMPORARY; + } + + @Override + public void close() { + ((TransientDtArchiveDB) archive).close(); + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java index 62a5e04c8e..dee75f2bd7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java @@ -410,7 +410,7 @@ class TypedefDB extends DataTypeDB implements TypeDef { @Override protected Settings doGetDefaultSettings() { DataTypeSettingsDB settings = new DataTypeSettingsDB(dataMgr, this, key); - settings.setLock(dataMgr instanceof BuiltInDataTypeManager); + settings.setLock(dataMgr instanceof BuiltInDataTypeManagerDB); settings.setAllowedSettingPredicate(n -> isAllowedSetting(n)); settings.setDefaultSettings(getDataType().getDefaultSettings()); return settings; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/BuiltInDtArchiveDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/BuiltInDtArchiveDB.java new file mode 100644 index 0000000000..636f5afd5a --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/BuiltInDtArchiveDB.java @@ -0,0 +1,173 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.database.dtarchive; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import db.DBHandle; +import ghidra.framework.ShutdownHookRegistry; +import ghidra.framework.ShutdownPriority; +import ghidra.framework.data.OpenMode; +import ghidra.framework.model.RuntimeIOException; +import ghidra.program.database.data.BuiltInDataTypeManagerDB; +import ghidra.program.model.data.*; +import ghidra.util.Msg; +import ghidra.util.classfinder.ClassFilter; +import ghidra.util.classfinder.ClassSearcher; +import ghidra.util.exception.*; +import ghidra.util.task.TaskMonitor; + +/** + * DataTypeArchive containing all found built-in datatypes. + */ +public class BuiltInDtArchiveDB extends DataTypeArchiveDB { + private static volatile BuiltInDtArchiveDB INSTANCE; + + private BuiltInDtArchiveDB() throws IOException { + super(new DBHandle(), DataTypeManager.BUILT_IN_DATA_TYPES_NAME, new Object()); + initialize(); + setImmutable(); + } + + @Override + protected BuiltInDataTypeManagerDB createDataTypeManager(DBHandle handle, OpenMode openMode, + TaskMonitor monitor) + throws VersionException, IOException, CancelledException { + return new BuiltInDataTypeManagerDB(handle, openMode, this, lock, monitor); + } + + private void initialize() { + try { + populateBuiltInTypes(); + } + catch (Throwable t) { + Msg.showError(this, null, "Error", "Error populating Built In Data Types", t); + } + } + + @Override + public boolean isTemporary() { + return true; + } + + @Override + public boolean isUpdatable() { + return false; + } + + @Override + public String getDescription() { + return "Buit-in Data Type Archive"; + } + + @Override + protected boolean isArchitectureChangeAllowed() { + return false; + } + + /** + * Add the built in data types to the default built in folder if they + * were not found in any other category. + */ + protected void populateBuiltInTypes() { + withTransaction("Populate Built-in Datatypes", () -> { + ClassFilter filter = new BuiltInDataTypeClassExclusionFilter(); + List datatypes = + ClassSearcher.getInstances(BuiltInDataType.class, filter); + if (!resolveBuiltInDataTypes(datatypes)) { + Msg.showError(this, null, "Built-in Datatype Name Collision", + "One or more Built-in datatypes were discovered with the same name. \n" + "" + + "See the console or log for the list of duplicate datatype names"); + } + }); + } + + private boolean resolveBuiltInDataTypes(List datatypes) { + BuiltInDataTypeManager dtm = getDataTypeManager(); + List list = new ArrayList<>(); + boolean hadNameCollisions = false; + + for (BuiltInDataType datatype : datatypes) { + list.clear(); + dtm.findDataTypes(datatype.getName(), list); + if (list.size() > 1) { + // This can't happen since we don't add in built-ins that collide, so at most + // there can only ever be one in the list. + throw new AssertException( + "Found multiple resolved built-in datatypes with the same name!"); + } + if (!list.isEmpty()) { + DataType resolvedDt = list.get(0); + if (!resolvedDt.isEquivalent(resolvedDt)) { + Msg.error(this, + "Found duplicate built-in datatype with name " + datatype.getName()); + hadNameCollisions = true; + } + // At this point we have a dupe, either way we don't resolve it. It is either + // the same datatype but already resolved(dtm assigned) or is is an incompatible + // duplicate that we just reported. + continue; + } + dtm.resolve(datatype, null); + } + return !hadNameCollisions; + } + + private void closeStaticInstance() { + close(); + } + + @Override + protected void finalize() throws Throwable { + close(); + } + + @Override + public BuiltInDataTypeManagerDB getDataTypeManager() { + return (BuiltInDataTypeManagerDB) super.getDataTypeManager(); + } + + @Override + public ArchiveType getArchiveType() { + return ArchiveType.BUILT_IN; + } + + /** + * Returns shared instance of built-in data type archive. + * @return the built-in data type archive + */ + public static synchronized BuiltInDtArchiveDB getDataTypeArchive() { + if (INSTANCE == null) { + try { + INSTANCE = new BuiltInDtArchiveDB(); + } + catch (IOException e) { + throw new RuntimeIOException(e); + } + Runnable cleanupTask = () -> { + if (INSTANCE != null) { + INSTANCE.closeStaticInstance(); + INSTANCE = null; + } + }; + ShutdownHookRegistry.addShutdownHook(cleanupTask, + ShutdownPriority.DISPOSE_DATABASES.before()); + } + return INSTANCE; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveContentHandler.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeArchiveContentHandler.java similarity index 83% rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveContentHandler.java rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeArchiveContentHandler.java index fc607114ce..7866f50805 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveContentHandler.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeArchiveContentHandler.java @@ -4,16 +4,16 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.program.database; +package ghidra.program.database.dtarchive; import java.io.IOException; @@ -27,6 +27,8 @@ import ghidra.framework.model.ChangeSet; import ghidra.framework.model.DomainObject; import ghidra.framework.store.*; import ghidra.framework.store.local.LocalDatabaseItem; +import ghidra.program.database.data.DataTypeArchiveMergeManagerFactory; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; import ghidra.util.InvalidNameException; import ghidra.util.Msg; import ghidra.util.exception.CancelledException; @@ -38,14 +40,14 @@ import ghidra.util.task.TaskMonitor; * and FolderItem storage. This class also produces the appropriate Icon for * DataTypeArchive files. */ -public class DataTypeArchiveContentHandler extends DBContentHandler { +public class DataTypeArchiveContentHandler extends DBContentHandler { static Icon DATA_TYPE_ARCHIVE_ICON = new GIcon("icon.content.handler.archive.dt"); public final static String DATA_TYPE_ARCHIVE_CONTENT_TYPE = "Archive"; - final static Class DATA_TYPE_ARCHIVE_DOMAIN_OBJECT_CLASS = - DataTypeArchiveDB.class; + final static Class DATA_TYPE_ARCHIVE_DOMAIN_OBJECT_CLASS = + ProjectDtArchiveDB.class; final static String DATA_TYPE_ARCHIVE_CONTENT_DEFAULT_TOOL = "CodeBrowser"; private static final DataTypeArchiveLinkContentHandler linkHandler = @@ -56,15 +58,16 @@ public class DataTypeArchiveContentHandler extends DBContentHandler getDomainObjectClass() { + public Class getDomainObjectClass() { return DATA_TYPE_ARCHIVE_DOMAIN_OBJECT_CLASS; } @@ -358,8 +368,9 @@ public class DataTypeArchiveContentHandler extends DBContentHandler + *

  • name
  • + *
  • languageName
  • + *
  • languageVersion
  • + *
  • LanguageMinorVersion
  • + *
+ * @param openMode program open mode + * @return version exception if the current version is out of date and can be upgraded. + * @throws IOException if an error occurs reading/writing to the database + * @throws VersionException if the data is newer than this version of Ghidra and can not be + * upgraded or opened. + */ + private VersionException initArchiveInfo(OpenMode openMode) + throws IOException, VersionException { + + // Update will always trigger upgrade situation if archive data map + // does not exist, to ensure that it does exist during any update use. + + archiveDataMap = getArchiveDataMap(openMode == OpenMode.UPGRADE); + + if (archiveDataMap == null || getStoredVersion() < 5) { + if (openMode == OpenMode.UPDATE) { + throw new VersionException(true); + } + return null; + } + + int storedVersion = archiveDataMap.getInt(ARCHIVE_DB_VERSION, 1); + + if (storedVersion > DB_VERSION) { + throw new VersionException(VersionException.NEWER_VERSION, false); + } + if (openMode != OpenMode.UPGRADE && storedVersion < UPGRADE_REQUIRED_BEFORE_VERSION) { + return new VersionException(true); + } + if (openMode == OpenMode.UPDATE && storedVersion < DB_VERSION) { + return new VersionException(true); + } + return null; + } + + private void upgradeDatabase() throws IOException { + archiveDataMap.put(ARCHIVE_DB_VERSION, Integer.toString(DB_VERSION)); + } + + private void createArchiveInfo() throws IOException { + archiveDataMap = getArchiveDataMap(true); + archiveDataMap.put(ARCHIVE_DB_VERSION, Integer.toString(DB_VERSION)); + } + + private int getStoredVersion() throws IOException { + if (archiveDataMap != null) { + return archiveDataMap.getInt(ARCHIVE_DB_VERSION, 1); + } + return 1; + } + + private void initManagers(OpenMode openMode, TaskMonitor monitor) + throws IOException, CancelledException { + monitor.checkCancelled(); + dataTypeManager.setDomainObject(this); + + warning = ArchiveWarning.NONE; + if (openMode == OpenMode.CREATE) { + getDataTypeManager().saveDataOrganization(); + } + + dataTypeManager.domainObjectReady(openMode, getStoredVersion(), monitor); + } + + private void propertiesRestore() { + Options pl = getOptions(ARCHIVE_INFO); + boolean origChangeState = changed; + pl.registerOption(CREATED_WITH_GHIDRA_VERSION, "4.3", null, + "Version of Ghidra used to create this program."); + pl.registerOption(DATE_CREATED, JANUARY_1_1970, null, "Date this program was created"); + changed = origChangeState; + } + + private void propertiesCreate() { + Options pl = getOptions(ARCHIVE_INFO); + boolean origChangeState = changed; + pl.setString(CREATED_WITH_GHIDRA_VERSION, Application.getApplicationVersion()); + pl.setDate(DATE_CREATED, new Date()); + changed = origChangeState; + } + + /** + * Get the archive string data map. + * @param createIfNeeded if true map will be created if it does not exist + * @return manager string data map or null + * @throws IOException if an IO error occurs + */ + private DBStringMapAdapter getArchiveDataMap(boolean createIfNeeded) throws IOException { + DBStringMapAdapter mapAdapter = null; + boolean exists = (dbh.getTable(DATA_MAP_TABLE_NAME) != null); + if (exists) { + mapAdapter = new DBStringMapAdapter(dbh, DATA_MAP_TABLE_NAME, false); + } + else if (createIfNeeded) { + mapAdapter = new DBStringMapAdapter(dbh, DATA_MAP_TABLE_NAME, true); + } + return mapAdapter; + } + + protected VersionException createManagers(OpenMode openMode, TaskMonitor monitor) + throws CancelledException, IOException { + + VersionException versionExc = null; + try { + dataTypeManager = createDataTypeManager(dbh, openMode, monitor); + if (dataTypeManager.hasReadOnlyDataOrgChange()) { + warning = ArchiveWarning.DATA_ORG_CHANGED; + } + + // + // NOTE: logWarning() must be invoked immediately after instantiating a + // StandAloneDataTypeManagerDB for an existing database after which + // getName() and getPath() can be invoked safely. In addition, it + // may be appropriate to use getWarning(), to check for warnings, + // prior to use. + // + + logWarning(); + } + catch (VersionException e) { + versionExc = e.combine(versionExc); + } + return versionExc; + } + + /** + * Set change set during testing. + * @param changeSet archive DB change set + */ + void setChangeSet(DataTypeArchiveDBChangeSet changeSet) { + this.changeSet = changeSet; + } + + @Override + public String getName() { + return getDomainFile().getName(); + } + + @Override + public boolean isChangeable() { + return !immutable; + } + + @Override + protected void setImmutable() { + super.setImmutable(); + immutable = true; + changed = false; + } + + @Override + public boolean isUpdatable() { + return dbh.canUpdate(); + } + + @Override + protected void domainObjectRestored() { + super.domainObjectRestored(); + dataTypeManager.notifyRestored(); + } + + @Override + protected void clearCache(boolean all) { + try (@SuppressWarnings("unused") + Closeable c = lock.write()) { + super.clearCache(all); + dataTypeManager.invalidateCache(); + } + } + + @Override + protected void close() { + closeManagers(); + super.close(); + } + + private void closeManagers() { + if (dataTypeManager != null) { + dataTypeManager.dispose(); + dataTypeManager = null; + } + } + + @Override + public ArchiveDataTypeManagerDB getDataTypeManager() { + return dataTypeManager; + } + + @Override + public ProgramArchitecture getProgramArchitecture() { + return programArchitecture; + } + + public VariableStorageManager getVariableStorageManager() { + return variableStorageMgr; + } + + /** + * Get the program architecture information which has been associated with this + * datatype archive. If {@link #getProgramArchitecture()} returns null this method + * may still return information if the program architecture was set on an archive + * and either {@link #isProgramArchitectureMissing()} or + * {@link #isProgramArchitectureUpgradeRequired()} returns true. + * @return program architecture summary if it has been set + */ + @Override + public String getProgramArchitectureSummary() { + if (programArchitectureSummary != null) { + return programArchitectureSummary; + } + if (programArchitecture != null) { + return programArchitecture.getSummary(); + } + return null; + } + + /** + * Determine if a program architecture change is permitted + * @return true if change allowed else false if disallowed + */ + protected boolean isArchitectureChangeAllowed() { + return true; + } + + @Override + public void setProgramArchitecture(Language language, CompilerSpecID compilerSpecId, + LanguageUpdateOption updateOption, TaskMonitor monitor) + throws CompilerSpecNotFoundException, LanguageNotFoundException, IOException, + CancelledException, LockException, UnsupportedOperationException, + IncompatibleLanguageException { + + lock.writeLock().lock(); + try { + checkConditionsForSettingArchitecture(language, compilerSpecId); + CompilerSpec compilerSpec = language.getCompilerSpecByID(compilerSpecId); + int txId = startTransaction("Set Program Architecture"); + try { + ProgramArchitectureTranslator translator = null; + ProgramArchitecture oldArch = getProgramArchitecture(); + if (oldArch != null || isProgramArchitectureMissing()) { + doSetArchiture(language, compilerSpecId, updateOption, monitor, translator, + oldArch); + } + + programArchitecture = new ArchiveProgramArchitecture(language, compilerSpec); + updateVariableStorageManager(); + saveLanguageInfo(language.getLanguageID(), compilerSpecId, language.getVersion()); + warning = ArchiveWarning.NONE; + dataTypeManager.programArchitectureChanged(monitor); + } + finally { + // TODO: ensure state is restored if transaction rollback/cancel occurs + endTransaction(txId, !monitor.isCancelled()); + } + + setChanged(DtArchiveEvent.ARCHIVE_ARCHITECTURE_CHANGED, null, null); + + } + finally { + clearCache(true); + lock.writeLock().unlock(); + } + } + + private void updateVariableStorageManager() throws IOException { + if (variableStorageMgr == null) { + try { + variableStorageMgr = + new VariableStorageManagerDB(dbh, null, OpenMode.CREATE, + this, lock, TaskMonitor.DUMMY); + } + catch (VersionException | CancelledException e) { + throw new AssertException(e); // unexpected + } + } + variableStorageMgr.setProgramArchitecture(programArchitecture); + } + + private void doSetArchiture(Language language, CompilerSpecID compilerSpecId, + LanguageUpdateOption updateOption, TaskMonitor monitor, + ProgramArchitectureTranslator translator, ProgramArchitecture oldArch) + throws IOException, CancelledException, LanguageNotFoundException, + CompilerSpecNotFoundException, IncompatibleLanguageException { + + if (updateOption == LanguageUpdateOption.CLEAR) { + deleteAllProgramArchitectureData(monitor); + variableStorageMgr = null; + } + else if (isProgramArchitectureMissing()) { + + assert (variableStorageMgr == null); + + if (updateOption == LanguageUpdateOption.TRANSLATE) { + // Go out on a limb and use any version of old language if available + LanguageID oldLanguageId = + new LanguageID(archiveDataMap.get(LANGUAGE_ID)); + CompilerSpecID oldCompilerSpecId = + new CompilerSpecID(archiveDataMap.get(COMPILER_SPEC_ID)); + translator = new ProgramArchitectureTranslator(oldLanguageId, -1, + oldCompilerSpecId, language, compilerSpecId); + } + + if (VariableStorageManagerDB.exists(dbh)) { + try { + variableStorageMgr = new VariableStorageManagerDB(dbh, null, + OpenMode.UPDATE, this, lock, monitor); + } + catch (VersionException e) { + throw new IOException( + "Unexpected version error for VariableStorageManagerDB"); + } + } + } + else if (updateOption == LanguageUpdateOption.TRANSLATE) { + translator = new ProgramArchitectureTranslator(oldArch.getLanguage(), + oldArch.getCompilerSpec().getCompilerSpecID(), language, + compilerSpecId); + } + + if (translator != null && variableStorageMgr != null) { + variableStorageMgr.setLanguage(translator, monitor); + } + } + + private void checkConditionsForSettingArchitecture(Language language, + CompilerSpecID compilerSpecId) throws ReadOnlyException, LockException { + + if (!isArchitectureChangeAllowed()) { + throw new UnsupportedOperationException( + "Program-architecture change not permitted"); + } + + if (immutable) { + throw new ReadOnlyException("Read-only Archive: " + getName()); + } + + if (!hasExclusiveAccess()) { + throw new LockException("Exclusive access required for architecture change"); + } + + Msg.info(this, + "Updating program-architecture for Archive: " + getName() + "\n Language: " + + language.getLanguageID() + " version " + language.getVersion() + ".x" + + ", CompilerSpec: " + compilerSpecId); + + } + + @Override + public void setProgramArchitecture(LanguageID languageId, CompilerSpecID compilerSpecId, + LanguageUpdateOption updateOption, TaskMonitor monitor) + throws CompilerSpecNotFoundException, LanguageNotFoundException, IOException, + CancelledException, LockException, UnsupportedOperationException, + IncompatibleLanguageException { + + // Verify that the specified language and compiler spec are valid + LanguageService languageService = DefaultLanguageService.getLanguageService(); + Language language = languageService.getLanguage(languageId); + language.getCompilerSpecByID(compilerSpecId); + + setProgramArchitecture(language, compilerSpecId, updateOption, monitor); + } + + /** + * Delete all program architecture related data in response to an + * architecture change when all related data should be removed. + * @throws IOException if IO error occurs + * @throws CancelledException if task cancelled + */ + private void deleteAllProgramArchitectureData(TaskMonitor monitor) + throws IOException, CancelledException { + + archiveDataMap.delete(LANGUAGE_ID); + archiveDataMap.delete(LANGUAGE_VERSION); + archiveDataMap.delete(COMPILER_SPEC_ID); + + VariableStorageManagerDB.delete(dbh); + + warning = ArchiveWarning.NONE; + programArchitecture = null; + } + + @Override + public void clearProgramArchitecture(TaskMonitor monitor) + throws CancelledException, IOException, LockException { + lock.writeLock().lock(); + try { + + if (!isArchitectureChangeAllowed()) { + throw new UnsupportedOperationException( + "Program-architecture change not permitted"); + } + + if (!dbh.canUpdate()) { + throw new ReadOnlyException("Read-only Archive: " + getName()); + } + + if (getProgramArchitecture() == null && !isProgramArchitectureMissing()) { + return; + } + + Msg.info(this, "Removing program-architecture for Archive: " + getName()); + + int txId = startTransaction("Remove Program Architecture"); + try { + if (!isArchitectureChangeAllowed()) { + throw new UnsupportedOperationException( + "Program-architecture change not permitted"); + } + dataTypeManager.clearCustomStorageUse(monitor); + deleteAllProgramArchitectureData(monitor); + dataTypeManager.programArchitectureChanged(monitor); + } + finally { + // TODO: ensure state is restored if transaction rollback/cancel occurs + endTransaction(txId, !monitor.isCancelled()); + + } + + setChanged(DtArchiveEvent.ARCHIVE_ARCHITECTURE_CHANGED, null, null); + } + finally { + clearCache(true); + lock.writeLock().unlock(); + } + } + + @Override + public boolean isProgramArchitectureUpgradeRequired() { + return warning == ArchiveWarning.LANGUAGE_UPGRADE_REQURED; + } + + @Override + public boolean isProgramArchitectureMissing() { + return warning == ArchiveWarning.LANGUAGE_NOT_FOUND || + warning == ArchiveWarning.COMPILER_SPEC_NOT_FOUND; + } + + @Override + public ArchiveWarning getWarning() { + return warning; + } + + @Override + public Exception getWarningDetail() { + return warningDetail; + } + + @Override + public String getWarningMessage(boolean includeDetails) { + String msg = null; + switch (warning) { + case LANGUAGE_NOT_FOUND: + msg = "Language not found for Archive"; + if (includeDetails) { + msg += " '" + getName() + "': " + warningDetail.getMessage(); + } + break; + case COMPILER_SPEC_NOT_FOUND: + msg = "Compiler specification not found for Archive"; + if (includeDetails) { + msg += " '" + getName() + "': " + warningDetail.getMessage(); + } + break; + case LANGUAGE_UPGRADE_REQURED: + msg = "Language upgrade required for Archive"; + if (includeDetails) { + msg += " '" + getName() + "': " + getProgramArchitectureSummary(); + } + break; + case UPGRADED_LANGUAGE_VERSION: + msg = "Upgraded program-architecture for Archive"; + if (includeDetails) { + ProgramArchitecture arch = getProgramArchitecture(); + LanguageDescription languageDescription = + arch.getLanguage().getLanguageDescription(); + msg += " '" + getName() + "'\n Language: " + + languageDescription.getLanguageID() + " Version " + + languageDescription.getVersion() + ".x" + ", CompilerSpec: " + + arch.getCompilerSpec().getCompilerSpecID(); + } + break; + case DATA_ORG_CHANGED: + msg = "Data organization upgrade required for Archive"; + if (includeDetails) { + msg += " '" + getName() + "': " + getProgramArchitectureSummary(); + } + break; + default: + break; + } + return msg; + } + + /** + * Due to the suppression of error and warning conditions during instantiation this method should + * be invoked at the end of instantiation when {@link #getName()} and {@link #getPath()} are + * ready to be invoked safely. Logging will be performed via {@link Msg}. + */ + public void logWarning() { + String msg = getWarningMessage(true); + if (msg == null) { + return; + } + switch (warning.level) { + case ERROR: + Msg.error(this, msg); + break; + case WARN: + Msg.warn(this, msg); + break; + default: + Msg.info(this, msg); + break; + } + + } + + /** + * Get the appropriate {@link ArchiveDataTypeManagerDB} implementation during initialization + * of this archive. + * @param handle database handle + * @param openMode open mode + * @param monitor task monitor + * @return datatype manager instance + * @throws IOException if an error accessing the database occurs. + * @throws VersionException if database version does not match implementation, + * If open mode is UPDATE, an UPGRADE may be possible as indicated by exception. + * @throws CancelledException if instantiation is canceled by monitor + */ + protected abstract ArchiveDataTypeManagerDB createDataTypeManager(DBHandle handle, + OpenMode openMode, TaskMonitor monitor) + throws VersionException, IOException, CancelledException; + + @Override + public int startTransaction(String description, AbortedTransactionListener listener) + throws TerminatedTransactionException { + // NOTE: All public start and open transaction invocations pass through this method + if (immutable) { + throw new TerminatedTransactionException("Transaction not permitted: read-only"); + } + return super.startTransaction(description, listener); + } + + /** + * notification the a data type has changed + * @param dataTypeID the id of the data type that changed. + * @param eventType the type of the change (moved, renamed, etc.) + * @param isAutoResponseChange true if change is an auto-response change caused by + * another datatype's change (e.g., size, alignment), else false in which case this + * change will be added to archive change-set to aid merge conflict detection. + * @param oldValue the old data type. + * @param newValue the new data type. + */ + public void dataTypeChanged(long dataTypeID, ProgramEvent eventType, + boolean isAutoResponseChange, Object oldValue, Object newValue) { + changed = true; + fireEvent(new ProgramChangeRecord(eventType, oldValue, newValue)); + } + + /** + * Notification that a data type was added. + * @param dataTypeID the id if the data type that was added. + * @param eventType should always be DATATYPE_ADDED + * @param oldValue always null + * @param newValue the data type added. + */ + public void dataTypeAdded(long dataTypeID, ProgramEvent eventType, Object oldValue, + Object newValue) { + changed = true; + fireEvent(new ProgramChangeRecord(eventType, oldValue, newValue)); + } + + /** + * Notification that a category was changed. + * @param categoryID the id of the data type that was added. + * @param eventType the type of change + * @param oldValue old value depends on the type. + * @param newValue new value depends on the type. + */ + public void categoryChanged(long categoryID, ProgramEvent eventType, Object oldValue, + Object newValue) { + changed = true; + fireEvent(new ProgramChangeRecord(eventType, oldValue, newValue)); + } + + /** + * Notification that a category was added. + * @param categoryID the id of the data type that was added. + * @param eventType the type of change (should always be CATEGORY_ADDED) + * @param oldValue always null + * @param newValue new value depends on the type. + */ + public void categoryAdded(long categoryID, ProgramEvent eventType, Object oldValue, + Object newValue) { + changed = true; + fireEvent(new ProgramChangeRecord(eventType, oldValue, newValue)); + } + + /** + * Mark the state this Data Type Archive as having changed and generate + * the event. Any or all parameters may be null. + * @param eventType event type + * @param oldValue original value + * @param newValue new value + */ + public void setChanged(DtArchiveEvent eventType, Object oldValue, Object newValue) { + changed = true; + fireEvent(new DtArchiveChangeRecord(this, eventType, oldValue, newValue)); + } + + private static class ArchiveProgramArchitecture implements ProgramArchitecture { + private Language language; + private CompilerSpec cspec; + private AddressFactory addressFactory; + + ArchiveProgramArchitecture(Language language, CompilerSpec cspec) { + this.language = language; + this.cspec = cspec; + this.addressFactory = new ProgramAddressFactory(language, cspec, null); + } + + @Override + public Language getLanguage() { + return language; + } + + @Override + public AddressFactory getAddressFactory() { + return addressFactory; + } + + @Override + public CompilerSpec getCompilerSpec() { + return cspec; + } + } + + private static record ArchitectureInfo(LanguageID languageId, CompilerSpecID compilerID, + int languageVersion) {} + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDBChangeSet.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeArchiveDBChangeSet.java similarity index 99% rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDBChangeSet.java rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeArchiveDBChangeSet.java index 2169f66237..b063e29f6e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDBChangeSet.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeArchiveDBChangeSet.java @@ -4,16 +4,16 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.program.database; +package ghidra.program.database.dtarchive; import java.io.IOException; import java.util.*; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeArchiveFactory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeArchiveFactory.java new file mode 100644 index 0000000000..a167f692e2 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeArchiveFactory.java @@ -0,0 +1,311 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.database.dtarchive; + +import java.io.File; +import java.io.IOException; + +import db.DBHandle; +import generic.jar.ResourceFile; +import ghidra.framework.data.OpenMode; +import ghidra.framework.model.DomainFile; +import ghidra.framework.model.DomainFolder; +import ghidra.framework.store.db.PackedDatabase; +import ghidra.program.model.data.BuiltInDataTypeManager; +import ghidra.program.model.dtarchive.*; +import ghidra.program.model.lang.CompilerSpecID; +import ghidra.program.model.lang.LanguageID; +import ghidra.util.InvalidNameException; +import ghidra.util.ReadOnlyException; +import ghidra.util.exception.*; +import ghidra.util.task.TaskMonitor; + +/** + * Provides all the static methods for creating and opening {@link PersistentDataTypeArchive}s. + */ +public class DataTypeArchiveFactory { + /** + * Creates a new FileDtArchive and stores it into the given output file. + * @param file the file to write the new FileDtArchive data to when saved + * @param consumer The object that is using this potentially shared archive. The caller of + * this method is expected to call {@link PersistentDataTypeArchive#release(Object)} using this same + * consumer when done using this archive. + * @return the newly created FileDtArchive + * @throws IOException if an I/O error occurs writing to the output file + */ + public static FileDataTypeArchive createFileArchive(File file, Object consumer) + throws IOException { + return FileDtArchiveDB.create(file, consumer); + } + + /** + * Creates a new {@link FileDataTypeArchive} and stores it into the given output file. + * @param file the file to write the new FileDtArchive data to when saved + * @param languageId valid language ID (see appropriate *.ldefs file for defined IDs). If null + * invocation will be deferred to {@link #createFileArchive(File, Object)}. + * @param compilerSpecId valid compiler spec ID which corresponds to the language ID. + * @param consumer The object that is using this potentially shared archive. The caller of + * this method is expected to call {@link PersistentDataTypeArchive#release(Object)} using this same + * consumer when done using this archive. + * @return the newly created FileDtArchive + * @throws IOException if an I/O error occurs writing to the output file + */ + public static FileDataTypeArchive createFileArchive(File file, String languageId, + String compilerSpecId, Object consumer) throws IOException { + return FileDtArchiveDB.create(file, languageId, compilerSpecId, consumer); + } + + /** + * Creates a new {@link FileDataTypeArchive} and stores it into the given output file. + * @param file the file to write the new FileDtArchive data to when saved + * @param languageId the id of the language to use for determining datatype organization details + * @param compilerSpecId the id of the compilerSpec to use for determining datatype organization + * details + * @param consumer The object that is using this potentially shared archive. The caller of + * this method is expected to call {@link PersistentDataTypeArchive#release(Object)} using this same + * consumer when done using this archive. + * @return the newly created FileDtArchive + * @throws IOException if an I/O error occurs writing to the output file + */ + public static FileDataTypeArchive createFileArchive(File file, LanguageID languageId, + CompilerSpecID compilerSpecId, Object consumer) throws IOException { + return FileDtArchiveDB.create(file, languageId, compilerSpecId, consumer); + } + + /** + * Creates a new {@link ProjectDataTypeArchive} in the given folder with the given name. + * @param folder the DomainFolder to store the project datatype archive + * @param name the name of the new project datatype archive + * @param consumer the consumer that is using the project datatype archive. The archive is + * closed when the last consumer is released. + * @return a new ProjectDataTypeArchive that is stored in the given domain folder with the + * given name. + * @throws DuplicateNameException if a DomainFile already exists in the given folder with the + * same name. + * @throws InvalidNameException if the given name is not a valid DomainFile name + * @throws IOException If an error occurs creating the database files for the new archive. + */ + public static ProjectDataTypeArchive createProjectArchive(DomainFolder folder, String name, + Object consumer) throws DuplicateNameException, InvalidNameException, IOException { + + return new ProjectDtArchiveDB(folder, name, consumer); + } + + /** + * Opens a {@link FileDataTypeArchive} that is stored in the given packed database file. + * @param file the file storing the archive's data as a packed database file + * @param consumer The object that is using this potentially shared archive. The caller of + * this method is expected to call {@link PersistentDataTypeArchive#release(Object)} using this same + * consumer when done using this archive. + * @param monitor the task monitor to use while opening the archive + * @return the newly opened FileDtArchive + * @throws CancelledException if the task to open the archive was cancelled + * @throws VersionException If the okToUpgrade is false and the file version is not the current + * version. If the okToUpgrade is true, then a version exception is only thrown if the file is + * an older version and there is no upgrade path to the current version. + * @throws IOException If an I/O error occurs reading from the database file + */ + public static FileDataTypeArchive openReadOnly(File file, Object consumer, TaskMonitor monitor) + throws CancelledException, VersionException, IOException { + return openReadOnly(new ResourceFile(file), consumer, monitor); + } + + /** + * Opens a {@link FileDataTypeArchive} that is stored in the given packed database file. + * @param file the file storing the archive's data as a packed database file + * @param consumer The object that is using this potentially shared archive. The caller of + * this method is expected to call {@link PersistentDataTypeArchive#release(Object)} using this same + * consumer when done using this archive. + * @param monitor the task monitor to use while opening the archive + * @return the newly opened FileDtArchive + * @throws CancelledException if the task to open the archive was cancelled + * @throws VersionException If the okToUpgrade is false and the file version is not the current + * version. If the okToUpgrade is true, then a version exception is only thrown if the file is + * an older version and there is no upgrade path to the current version. + * @throws IOException If an I/O error occurs reading from the database file + */ + public static FileDataTypeArchive openReadOnly(ResourceFile file, Object consumer, + TaskMonitor monitor) throws CancelledException, VersionException, IOException { + return FileDtArchiveDB.openReadOnly(file, consumer, monitor); + } + + /** + * Opens a {@link FileDataTypeArchive} that is stored in the given packed database file. + * @param packedDbFile the file storing the archive's data as a packed database file + * @param okToUpgrade true if the archive can be allowed to upgrade to a newer version + * @param consumer The object that is using this potentially shared archive. The caller of + * this method is expected to call {@link PersistentDataTypeArchive#release(Object)} using this same + * consumer when done using this archive. + * @param monitor the task monitor to use while opening the archive + * @return the newly opened FileDtArchive + * @throws CancelledException if the task to open the archive was cancelled + * @throws VersionException If the okToUpgrade is false and the file version is not the current + * version. If the okToUpgrade is true, then a version exception is only thrown if the file is + * an older version and there is no upgrade path to the current version. + * @throws IOException If an I/O error occurs reading from the database file + */ + public static FileDataTypeArchive openForUpdate(File packedDbFile, boolean okToUpgrade, + Object consumer, TaskMonitor monitor) + throws CancelledException, VersionException, IOException { + return openForUpdate(new ResourceFile(packedDbFile), okToUpgrade, consumer, monitor); + } + + /** + * Opens a {@link FileDataTypeArchive} that is stored in the given packed database file. + * @param packedDbFile the packed database resource file storing the archive's data + * @param okToUpgrade true if the archive can be allowed to upgrade to a newer version + * @param consumer The object that is using this potentially shared archive. The caller of + * this method is expected to call {@link PersistentDataTypeArchive#release(Object)} using this same + * consumer when done using this archive. + * @param monitor the task monitor to use while opening the archive + * @return the newly opened FileDtArchive + * @throws CancelledException if the task to open the archive was cancelled + * @throws VersionException If the okToUpgrade is false and the file version is not the current + * version. If the okToUpgrade is true, then a version exception is only thrown if the file is + * an older version and there is no upgrade path to the current version. + * @throws IOException If an I/O error occurs reading from the database file + */ + public static FileDataTypeArchive openForUpdate(ResourceFile packedDbFile, boolean okToUpgrade, + Object consumer, TaskMonitor monitor) + throws CancelledException, VersionException, IOException { + return FileDtArchiveDB.openForUpdate(packedDbFile, okToUpgrade, consumer, monitor); + } + + /** + * Opens a {@link ProjectDataTypeArchive} for updating that is stored in a project domain file. + * @param domainFile the project domain file containing the stored archive + * @param consumer the object used to mark the archive is in use by that object. Project + * archives don't have a close method, instead they are closed when the last consumer is + * released (using the {@link ProjectDataTypeArchive#release(Object)} method. + * @param okToUpgrade if true, the archive can be allowed to upgrade to a newer version + * @param okToRecover if true, then any crash unsaved changes can be recovered when the + * archive is opened + * @param monitor the task monitor to use while opening the archive + * @return the newly opened project data type archive + * @throws CancelledException if the task to open the archive was cancelled + * @throws VersionException If the okToUpgrade is false and the file version is not the current + * version. If the okToUpgrade is true, then a version exception is only thrown if the file is + * an older version and there is no upgrade path to the current version. + * @throws ReadOnlyException if domainFile is not checked-out or marked as read-only + * @throws IOException If an I/O error occurs reading from the database file + */ + public static ProjectDataTypeArchive openForUpdate(DomainFile domainFile, + Object consumer, boolean okToUpgrade, boolean okToRecover, TaskMonitor monitor) + throws VersionException, CancelledException, ReadOnlyException, IOException { + if (domainFile.isReadOnly()) { + throw new ReadOnlyException(); + } + return (ProjectDataTypeArchive) domainFile.getDomainObject(consumer, okToUpgrade, + okToRecover, monitor); + } + + /** + * Opens a {@link ProjectDataTypeArchive} in a read-only mode that is stored in a project domain + * file. + * @param domainFile the project domain file containing the stored archive + * @param consumer the object used to mark the archive is in use by that object. Project + * archives don't have a close method, instead they are closed when the last consumer is + * released (using the {@link ProjectDataTypeArchive#release(Object)} method. + * @param monitor the task monitor to use while opening the archive + * @return the newly opened project data type archive + * @throws CancelledException if the task to open the archive was cancelled + * @throws VersionException If the okToUpgrade is false and the file version is not the current + * version. If the okToUpgrade is true, then a version exception is only thrown if the file is + * an older version and there is no upgrade path to the current version. + * @throws IOException If an I/O error occurs reading from the database file + */ + public static ProjectDataTypeArchive openReadOnly(DomainFile domainFile, + Object consumer, TaskMonitor monitor) + throws VersionException, CancelledException, IOException { + return openReadOnly(domainFile, DomainFile.DEFAULT_VERSION, consumer, monitor); + } + + /** + * Opens a specific version of a {@link ProjectDataTypeArchive} in a read-only mode. + * @param domainFile the project domain file containing the stored archive + * @param version the specific version to open (This only makes sense if the archive has + * been added to version control (Or use {@link DomainFile#DEFAULT_VERSION} to specify + * the current version)). + * @param consumer the object used to mark the archive is in use by that object. Project + * archives don't have a close method, instead they are closed when the last consumer is + * released (using the {@link ProjectDataTypeArchive#release(Object)} method. + * @param monitor the task monitor to use while opening the archive + * @return the newly opened project data type archive + * @throws CancelledException if the task to open the archive was cancelled + * @throws VersionException If the okToUpgrade is false and the file version is not the current + * version. If the okToUpgrade is true, then a version exception is only thrown if the file is + * an older version and there is no upgrade path to the current version. + * @throws IOException If an I/O error occurs reading from the database file + */ + public static ProjectDataTypeArchive openReadOnly(DomainFile domainFile, int version, + Object consumer, TaskMonitor monitor) + throws VersionException, CancelledException, IOException { + // NOTE: use of getReadOnlyDomainObject allows any upgrade to be performed if needed + return (ProjectDataTypeArchive) domainFile.getReadOnlyDomainObject(consumer, version, + monitor); + } + + /** + * Returns shared instance of built-in data type manager. + * @return the built-in data type manager + */ + public static BuiltInDataTypeManager getBuiltInDataTypeManager() { + BuiltInDtArchiveDB builtInArchive = BuiltInDtArchiveDB.getDataTypeArchive(); + return builtInArchive.getDataTypeManager(); + } + + /** + * Creates an unsaved ProjectDataTypeArchive from the datatypes stored in a + * {@link FileDataTypeArchive} packed database file. + * @param file the file that contains a packed database file for a FileDataTypeArchive + * @param name the name for the new project datatype archive + * @param consumer the consumer keeping the project archive open. Project archives are closed + * when the last consumer is released using the {@link ProjectDataTypeArchive#release(Object)} + * method + * @param monitor that task monitor + * @return a new unsaved ProjectDataTypeArchive. + * @throws IOException if an I/O error occurs creating the project database + * @throws VersionException if the file contains a version that can't be upgraded. + * @throws CancelledException if the monitor is cancelled before the operation is complete + */ + public static ProjectDataTypeArchive importProjectArchive(File file, String name, + Object consumer, TaskMonitor monitor) + throws IOException, VersionException, CancelledException { + + PackedDatabase packedDatabase = PackedDatabase.getPackedDatabase(file, true, monitor); + if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE + .equals(packedDatabase.getContentType())) { + throw new IOException("Given file is not a Datatype Archive: " + name); + } + + monitor.setMessage("Loading " + name); + DBHandle dbh = null; + try { + dbh = packedDatabase.open(monitor); + return new ProjectDtArchiveDB(dbh, name, OpenMode.UPGRADE, monitor, consumer); + } + catch (CancelledException | VersionException | IOException e) { + if (dbh != null) { + dbh.close(); + } + else { + packedDatabase.dispose(); + } + throw e; + } + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveLinkContentHandler.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeArchiveLinkContentHandler.java similarity index 91% rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveLinkContentHandler.java rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeArchiveLinkContentHandler.java index bebd6b64f0..95acbdea08 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveLinkContentHandler.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeArchiveLinkContentHandler.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.program.database; +package ghidra.program.database.dtarchive; import javax.swing.Icon; import ghidra.framework.data.LinkHandler; -public class DataTypeArchiveLinkContentHandler extends LinkHandler { +public class DataTypeArchiveLinkContentHandler extends LinkHandler { public static DataTypeArchiveLinkContentHandler INSTANCE = new DataTypeArchiveLinkContentHandler(); @@ -37,7 +37,7 @@ public class DataTypeArchiveLinkContentHandler extends LinkHandler getDomainObjectClass() { + public Class getDomainObjectClass() { // return linked content class return DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_DOMAIN_OBJECT_CLASS; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileBasedDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeStoreDBModule.java similarity index 68% rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileBasedDataTypeManager.java rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeStoreDBModule.java index ec515f40fe..b1f3319680 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileBasedDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DataTypeStoreDBModule.java @@ -1,26 +1,25 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.program.model.data; +package ghidra.program.database.dtarchive; + +import ghidra.framework.data.DomainObjectDbModule; /** - * Extends DataTypeManager to provide methods specific to a data type manager that is file based. + * Interface that all {@link DataTypeArchiveDB} module managers must implement. */ -public interface FileBasedDataTypeManager extends DataTypeManager { - - public String getPath(); +public interface DataTypeStoreDBModule extends DomainObjectDbModule { } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DefaultConsumer.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DefaultConsumer.java new file mode 100644 index 0000000000..3702d87752 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/DefaultConsumer.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.database.dtarchive; + +import ghidra.program.model.data.FileDataTypeManager; + +/** + * Objects used as a consumer for legacy FileDataTypeArchives where they didn't take a consumer + * and could be closed via a close method in the {@link FileDataTypeManager}. The close method + * can specifically look for DefaultConsumer and safely close them. This class can be removed + * when the deprecated FileDataTypeManager interface is deleted. + */ +@Deprecated(since = "12.2", forRemoval = true) +public class DefaultConsumer { + // marker class +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/FileDtArchiveDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/FileDtArchiveDB.java new file mode 100644 index 0000000000..68750f0d6a --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/FileDtArchiveDB.java @@ -0,0 +1,429 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.database.dtarchive; + +import static ghidra.program.database.dtarchive.DataTypeArchiveContentHandler.*; + +import java.io.File; +import java.io.IOException; +import java.util.Objects; + +import javax.help.UnsupportedOperationException; + +import db.DBHandle; +import generic.jar.ResourceFile; +import ghidra.framework.data.DomainFileProxy; +import ghidra.framework.data.OpenMode; +import ghidra.framework.model.DomainObjectChangeRecord; +import ghidra.framework.model.DomainObjectEvent; +import ghidra.framework.store.LockException; +import ghidra.framework.store.db.PackedDBHandle; +import ghidra.framework.store.db.PackedDatabase; +import ghidra.program.database.data.FileDataTypeManagerDB; +import ghidra.program.model.data.ArchiveType; +import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.program.model.dtarchive.FileDataTypeArchive; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.IncompatibleLanguageException; +import ghidra.program.util.DefaultLanguageService; +import ghidra.util.UniversalID; +import ghidra.util.exception.*; +import ghidra.util.task.TaskMonitor; + +/** + * Datatype archives that are stored in stand-alone files (packed database files). + */ +public class FileDtArchiveDB extends DataTypeArchiveDB implements FileDataTypeArchive { + private FileDtArchiveDB(DBHandle dbh, String name, Object consumer) + throws IOException { + super(dbh, name, consumer); + } + + private FileDtArchiveDB(DBHandle dbh, String name, OpenMode openMode, Object consumer, + TaskMonitor monitor) throws IOException, VersionException, CancelledException { + super(dbh, name, openMode, monitor, consumer); + } + + @Override + protected FileDataTypeManagerDB createDataTypeManager(DBHandle handle, OpenMode openMode, + TaskMonitor monitor) + throws VersionException, IOException, CancelledException { + return new FileDataTypeManagerDB(handle, openMode, this, lock, monitor); + } + + /** + * Used for backwards compatibility so the the deprecated {@link FileDataTypeManager#close()} + * can still work and close an archive regardless of its consumers. This method does not + * play well with others and will be removed in a future release. + * @deprecated this should be removed when the deprecated close() method is removed. + */ + @Deprecated + public void releaseDefaultConsumers() { + for (Object consumer : getConsumerList()) { + if (consumer instanceof DefaultConsumer) { + release(consumer); + } + } + } + + @Override + public boolean isTemporary() { + return true; + } + + @Override + public ArchiveType getArchiveType() { + return ArchiveType.FILE; + } + + @Override + public FileDataTypeManagerDB getDataTypeManager() { + return (FileDataTypeManagerDB) super.getDataTypeManager(); + } + + @Override + public UniversalID getUniversalID() { + return dataTypeManager.getUniversalID(); + } + + @Override + public String getDescription() { + return "File Based Data Type Archive"; + } + + /** + * Saves the data type manager to the given file + * @param saveFile the file to save + * @throws DuplicateFileException if save file already exists + * @throws IOException if IO error occurs + * @throws CancelledException if cancelled + */ + @Override + public void saveAs(File saveFile, TaskMonitor monitor) + throws DuplicateFileException, IOException, CancelledException { + ResourceFile resourceSaveFile = new ResourceFile(saveFile); + validateFilename(resourceSaveFile); + try { + ((PackedDBHandle) dbh).saveAs("DTArchive", saveFile.getParentFile(), + saveFile.getName(), monitor); + String fileName = saveFile.getName(); + if (fileName.endsWith(SUFFIX)) { + fileName = fileName.substring(0, fileName.length() - SUFFIX.length()); + } + setName(fileName); + dataTypeManager.updateID(); + } + finally { + clearUndo(); + } + } + + /** + * Saves the data type archive to the given file with a specific databaseId. + * NOTE: This method is intended for use in transforming one archive database to + * match another existing archive database. + * @param saveFile the file to save + * @param newUniversalId the new id to use + * @throws DuplicateFileException if save file already exists + * @throws IOException if IO error occurs + */ + @Override + public void saveAs(File saveFile, UniversalID newUniversalId) + throws DuplicateFileException, IOException { + ResourceFile resourceSaveFile = new ResourceFile(saveFile); + validateFilename(resourceSaveFile); + try { + ((PackedDBHandle) dbh).saveAs("DTArchive", saveFile.getParentFile(), + saveFile.getName(), newUniversalId.getValue(), TaskMonitor.DUMMY); + String fileName = saveFile.getName(); + setName(fileName); + dataTypeManager.updateID(); + } + catch (CancelledException e) { + // Cancel can't happen because we are using a dummy monitor + } + finally { + clearUndo(); + } + + } + + @Override + public void save(String comment, TaskMonitor monitor) throws IOException, CancelledException { + if (!lock("save")) { + throw new IOException("Unable to lock due to active transaction"); + } + boolean wasSaved = false; + try { + ((PackedDBHandle) dbh).save(monitor); + } + catch (CancelledException e) { + // Cancel can't happen because we are using a dummy monitor + } + finally { + clearUndo(); + unlock(); + } + + if (wasSaved) { + fireEvent(new DomainObjectChangeRecord(DomainObjectEvent.SAVED)); + } + } + + @Override + public synchronized boolean canSave() { + return isChangeable() && isChanged(); + } + + public String getFileName() { + PackedDatabase packedDatabase = ((PackedDBHandle) dbh).getPackedDatabase(); + ResourceFile packedFile = packedDatabase.getPackedFile(); + return packedFile.getName(); + } + + @Override + public ResourceFile getFile() { + PackedDatabase packedDatabase = ((PackedDBHandle) dbh).getPackedDatabase(); + return packedDatabase.getPackedFile(); + } + + @Override + public String getPath() { + return getFile().getAbsolutePath(); + } + + @Override + public void delete() throws IOException { + PackedDatabase packedDB = ((PackedDBHandle) dbh).getPackedDatabase(); + super.close(); + if (packedDB != null) { + packedDB.delete(); + packedDB = null; + } + } + + @Override + public DomainFileProxy getDomainFile() { + return (DomainFileProxy) super.getDomainFile(); + } + + @Override + public void setName(String newName) { + getDomainFile().setName(newName); + } + +//================================================================================================== +// Static methods +//================================================================================================== + /** + * Opens the FileDtArchive stored in the given packed database file. + * @param packedDbFile the packed database resource file storing the archive's data + * @param consumer The object that is using this potentially shared archive. The caller of + * this method is expected to call {@link PersistentDataTypeArchive#release(Object)} using this same + * consumer when done using this archive. + * @param monitor the task monitor to use while opening the archive + * @return the newly opened FileDtArchive + * @throws CancelledException if the task to open the archive was cancelled + * @throws IOException If an I/O error occurs reading from the database file + * @throws VersionException if the file is not the current version and doesn't support read + * only mode for that file's stored version, or if the file's version is newer than the + * expected current version for the software running. + */ + static FileDtArchiveDB openReadOnly(ResourceFile packedDbFile, Object consumer, + TaskMonitor monitor) throws CancelledException, IOException, VersionException { + DBHandle dbh = openDb(packedDbFile, false, monitor); + String name = getRootName(packedDbFile.getName()); + + return new FileDtArchiveDB(dbh, name, OpenMode.IMMUTABLE, consumer, monitor); + } + + /** + * Opens the FileDtArchive stored in the given packed database file. + * @param packedDbFile the packed database resource file storing the archive's data + * @param okToUpgrade If true, the file will be upgraded to the current version if it is not + * the current version and can be upgraded. + * @param consumer The object that is using this potentially shared archive. The caller of + * this method is expected to call {@link PersistentDataTypeArchive#release(Object)} using this same + * consumer when done using this archive. + * @param monitor the task monitor to use while opening the archive + * @return the newly opened FileDtArchive + * @throws CancelledException if the task to open the archive was cancelled + * @throws IOException If an I/O error occurs reading from the database file + * @throws VersionException If the okToUpgrade is false and the file version is not the current + * version. If the okToUpgrade is true, then a version exception is only thrown if the file is + * an older version and there is no upgrade path to the current version. + */ + static FileDtArchiveDB openForUpdate(ResourceFile packedDbFile, boolean okToUpgrade, + Object consumer, TaskMonitor monitor) + throws CancelledException, IOException, VersionException { + DBHandle dbh = openDb(packedDbFile, true, monitor); + String name = getRootName(packedDbFile.getName()); + + OpenMode openMode = okToUpgrade ? OpenMode.UPGRADE : OpenMode.UPDATE; + return new FileDtArchiveDB(dbh, name, openMode, consumer, monitor); + } + + /** + * Creates a new FileDtArchive and stores it into the given output file. + * @param outputFile the file to write the new FileDtArchive data to when saved + * @param consumer The object that is using this potentially shared archive. The caller of + * this method is expected to call {@link PersistentDataTypeArchive#release(Object)} using this same + * consumer when done using this archive. + * @return the newly created FileDtArchive + * @throws IOException if an I/O error occurs writing to the output file + */ + static FileDtArchiveDB create(File outputFile, Object consumer) throws IOException { + if (outputFile.exists()) { + throw new DuplicateFileException("File already exists: " + outputFile); + } + + PackedDBHandle dbh = new PackedDBHandle(DATA_TYPE_ARCHIVE_CONTENT_TYPE); + FileDtArchiveDB archive = + new FileDtArchiveDB(dbh, getRootName(outputFile.getName()), consumer); + try { + dbh.saveAs("Archive", outputFile.getParentFile(), + outputFile.getName(), null, TaskMonitor.DUMMY); + } + catch (CancelledException e) { + // can't happen with a DUMMY task monitor + } + return archive; + } + + /** + * Creates a new FileDtArchive and stores it into the given output file. + * @param outputFile the file to write the new FileDtArchive data to when saved + * @param languageId the id of the language to use for determining datatype organization details + * @param compilerSpecId the id of the compilerSpec to use for determining datatype organization + * details + * @param consumer The object that is using this potentially shared archive. The caller of + * this method is expected to call {@link PersistentDataTypeArchive#release(Object)} using this same + * consumer when done using this archive. + * @return the newly created FileDtArchive + * @throws IOException if an I/O error occurs writing to the output file + */ + static FileDtArchiveDB create(File outputFile, LanguageID languageId, + CompilerSpecID compilerSpecId, Object consumer) throws IOException { + Objects.requireNonNull(languageId, "missing required languageId"); + Objects.requireNonNull(compilerSpecId, "missing required compilerSpecId"); + if (outputFile.exists()) { + throw new DuplicateFileException("File already exists: " + outputFile); + } + FileDtArchiveDB archive = create(outputFile, consumer); + try { + // Verify that the specified language and compiler spec are valid + LanguageService languageService = DefaultLanguageService.getLanguageService(); + Language language = languageService.getLanguage(languageId); + language.getCompilerSpecByID(compilerSpecId); + + archive.setProgramArchitecture(language, compilerSpecId, LanguageUpdateOption.CLEAR, + TaskMonitor.DUMMY); + } + catch (CancelledException e) { + // can't happen with a DUMMY task monitor + } + catch (LockException | IncompatibleLanguageException | UnsupportedOperationException e) { + throw new RuntimeException(e); // unexpected for new archive + } + + return archive; + } + + /** + * Create a new data-type file archive using the specified language/compiler spec + * @param packedDbfile archive file (filename must end with DataTypeFileManager.SUFFIX) + * @param languageId valid language ID (see appropriate *.ldefs file for defined IDs). If null + * invocation will be deferred to {@link #create(File)}. + * @param compilerSpecId valid compiler spec ID which corresponds to the language ID. + * @param consumer The object that is using this potentially shared archive. The caller of + * this method is expected to call {@link PersistentDataTypeArchive#release(Object)} using this same + * consumer when done using this archive. + * @return data-type manager backed by the specified packedDbFile + * @throws LanguageNotFoundException if specified {@code languageId} not defined. + * @throws CompilerSpecNotFoundException if specified {@code compilerSpecId} is not defined + * for the specified language. + * @throws IOException if an IO error occurs + */ + public static FileDataTypeArchive create(File packedDbfile, String languageId, + String compilerSpecId, Object consumer) throws IOException { + + if (languageId == null && compilerSpecId != null) { + throw new IllegalArgumentException("compilerSpecId specified without languageId"); + } + if (languageId != null && compilerSpecId == null) { + throw new IllegalArgumentException("languageId specified without compilerSpecId"); + } + + if (languageId == null) { + return create(packedDbfile, consumer); + } + return create(packedDbfile, new LanguageID(languageId), + new CompilerSpecID(compilerSpecId), consumer); + } + + private static DBHandle openDb(ResourceFile packedDbFile, boolean openForUpdate, + TaskMonitor monitor) throws IOException, CancelledException { + validateFilename(packedDbFile); + File file = packedDbFile.getFile(false); + if (file == null && openForUpdate) { + throw new IOException("Attempted to open for update a read-only Datatype Archive: " + + packedDbFile.getAbsolutePath()); + } + + // Open packed database archive + PackedDatabase pdb = null; + DBHandle dbh = null; + try { + + pdb = PackedDatabase.getPackedDatabase(packedDbFile, false, monitor); + + if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE + .equals(pdb.getContentType())) { + throw new IOException("File is not a Datatype Archive: " + packedDbFile.getName()); + } + + if (openForUpdate) { + dbh = pdb.openForUpdate(monitor); + } + else { + dbh = pdb.open(monitor); + } + } + finally { + if (dbh == null && pdb != null) { + pdb.dispose(); // dispose on error + } + } + return dbh; + } + + private static String getRootName(String filename) { + int pos = filename.lastIndexOf(SUFFIX); + if (pos > 0) { + filename = filename.substring(0, pos); + } + return filename; + } + + private static ResourceFile validateFilename(ResourceFile packedDbfile) { + if (!packedDbfile.getName().endsWith(SUFFIX)) { + throw new IllegalArgumentException("Archive files must end with " + SUFFIX); + } + return packedDbfile; + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/ProjectDtArchiveDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/ProjectDtArchiveDB.java new file mode 100644 index 0000000000..f955e6753b --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/ProjectDtArchiveDB.java @@ -0,0 +1,302 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.database.dtarchive; + +import java.io.IOException; +import java.util.*; + +import javax.help.UnsupportedOperationException; + +import db.DBHandle; +import ghidra.framework.data.OpenMode; +import ghidra.framework.model.DomainFile; +import ghidra.framework.model.DomainFolder; +import ghidra.framework.options.Options; +import ghidra.framework.store.LockException; +import ghidra.program.database.data.ProjectDataTypeManagerDB; +import ghidra.program.model.data.ArchiveType; +import ghidra.program.model.data.PointerDataType; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.IncompatibleLanguageException; +import ghidra.program.model.listing.Program; +import ghidra.program.util.ProgramEvent; +import ghidra.util.InvalidNameException; +import ghidra.util.Lock.Closeable; +import ghidra.util.exception.*; +import ghidra.util.task.TaskMonitor; + +/** + * DataTypeArchives that are stored as an item in a Ghidra project. + */ +public class ProjectDtArchiveDB extends DataTypeArchiveDB + implements ProjectDataTypeArchive { + + /** Name of data type archive settings property list */ + public static final String ARCHIVE_SETTINGS = "Data Type Archive Settings"; + + private static final String DEFAULT_POINTER_SIZE = "Default Pointer Size"; + + private boolean recordChanges; + + /** + * Constructs a new DataTypeArchiveDB within a project folder. + * @param folder folder within which the project archive will be created + * @param name the name of the data type archive + * @param consumer the object that is using this data type archive. + * @throws IOException if there is an error accessing the database. + * @throws InvalidNameException if the name contains invalid characters. + * @throws DuplicateNameException if a DomainFile already exists in the given folder + * with the given name. + */ + ProjectDtArchiveDB(DomainFolder folder, String name, Object consumer) + throws IOException, DuplicateNameException, InvalidNameException { + super(new DBHandle(), name, consumer); + + recordChanges = false; + + if (!dbh.isClosed() && folder != null) { + try { + folder.createFile(name, this, TaskMonitor.DUMMY); + } + catch (CancelledException e) { + throw new AssertException(); + } + } + } + + /** + * Constructs a new DataTypeArchiveDB + * @param handle a handle to an open data type archive database. + * @param name the name of the archive + * @param openMode one of: + * READ_ONLY: the original database will not be modified + * UPDATE: the database can be written to. + * UPGRADE: the database is upgraded to the latest schema as it is opened. + * @param monitor TaskMonitor that allows the open to be canceled. + * @param consumer the object that keeping the program open. + * @throws IOException if an error accessing the database occurs. + * @throws VersionException if database version does not match implementation, UPGRADE may be possible. + * @throws CancelledException if instantiation is canceled by monitor + */ + ProjectDtArchiveDB(DBHandle handle, String name, OpenMode openMode, + TaskMonitor monitor, Object consumer) + throws IOException, VersionException, CancelledException { + + super(handle, name, openMode, monitor, consumer); + recordChanges = true; + } + + @Override + protected ProjectDataTypeManagerDB createDataTypeManager(DBHandle handle, OpenMode openMode, + TaskMonitor monitor) + throws VersionException, IOException, CancelledException { + return new ProjectDataTypeManagerDB(handle, openMode, this, lock, monitor); + } + + @Override + protected void setDomainFile(DomainFile df) { + super.setDomainFile(df); + recordChanges = true; + } + + @Override + protected boolean propertyChanged(String propertyName, Object oldValue, Object newValue) { + if (propertyName.endsWith(DEFAULT_POINTER_SIZE) && (newValue instanceof Integer)) { + if (!isValidDefaultpointerSize((Integer) newValue)) { + return false; + } + } + return super.propertyChanged(propertyName, oldValue, newValue); + } + + private boolean isValidDefaultpointerSize(int pointerSize) { + return pointerSize > 0 && pointerSize <= PointerDataType.MAX_POINTER_SIZE_BYTES; + } + + @Override + public ProjectDataTypeManagerDB getDataTypeManager() { + return (ProjectDataTypeManagerDB) dataTypeManager; + } + + public Date getCreationDate() { + Options pl = getOptions(ARCHIVE_INFO); + return pl.getDate(DATE_CREATED, new Date(0)); + } + + public int getDefaultPointerSize() { + // Not sure what size this should be so use 4 for now. + // Maybe this should prompt the user when it hasn't been set yet. + Options pl = getOptions(ARCHIVE_SETTINGS); + return pl.getInt(DEFAULT_POINTER_SIZE, 4); + } + + @Override + public void clearProgramArchitecture(TaskMonitor monitor) + throws CancelledException, IOException, LockException { + checkExclusiveAccess(); + super.clearProgramArchitecture(monitor); + } + + @Override + public void setProgramArchitecture(Language language, CompilerSpecID compilerSpecId, + LanguageUpdateOption updateOption, TaskMonitor monitor) + throws CompilerSpecNotFoundException, LanguageNotFoundException, IOException, + LockException, UnsupportedOperationException, IncompatibleLanguageException, + CancelledException { + checkExclusiveAccess(); + super.setProgramArchitecture(language, compilerSpecId, updateOption, monitor); + } + + @Override + public void setProgramArchitecture(LanguageID languageId, CompilerSpecID compilerSpecId, + LanguageUpdateOption updateOption, TaskMonitor monitor) + throws CompilerSpecNotFoundException, LanguageNotFoundException, IOException, + CancelledException, LockException, UnsupportedOperationException, + IncompatibleLanguageException { + checkExclusiveAccess(); + super.setProgramArchitecture(languageId, compilerSpecId, updateOption, monitor); + } + + @Override + public DataTypeArchiveDBChangeSet getChanges() { + return (DataTypeArchiveDBChangeSet) changeSet; + } + + /** + * notification the a data type has changed + * @param dataTypeID the id of the data type that changed. + * @param eventType the type of the change (moved, renamed, etc.) + * @param isAutoResponseChange true if change is an auto-response change caused by + * another datatype's change (e.g., size, alignment), else false in which case this + * change will be added to archive change-set to aid merge conflict detection. + * @param oldValue the old data type. + * @param newValue the new data type. + */ + @Override + public void dataTypeChanged(long dataTypeID, ProgramEvent eventType, + boolean isAutoResponseChange, Object oldValue, Object newValue) { + if (recordChanges && !isAutoResponseChange) { + getChanges().dataTypeChanged(dataTypeID); + } + super.dataTypeChanged(dataTypeID, eventType, isAutoResponseChange, oldValue, newValue); + } + + /** + * Notification that a data type was added. + * @param dataTypeID the id if the data type that was added. + * @param eventType should always be DATATYPE_ADDED + * @param oldValue always null + * @param newValue the data type added. + */ + @Override + public void dataTypeAdded(long dataTypeID, ProgramEvent eventType, Object oldValue, + Object newValue) { + if (recordChanges) { + ((DataTypeArchiveDBChangeSet) changeSet).dataTypeAdded(dataTypeID); + } + super.dataTypeAdded(dataTypeID, eventType, oldValue, newValue); + } + + /** + * Notification that a category was changed. + * @param categoryID the id of the data type that was added. + * @param eventType the type of change + * @param oldValue old value depends on the type. + * @param newValue new value depends on the type. + */ + @Override + public void categoryChanged(long categoryID, ProgramEvent eventType, Object oldValue, + Object newValue) { + if (recordChanges) { + ((DataTypeArchiveDBChangeSet) changeSet).categoryChanged(categoryID); + } + super.categoryChanged(categoryID, eventType, oldValue, newValue); + } + + /** + * Notification that a category was added. + * @param categoryID the id of the data type that was added. + * @param eventType the type of change (should always be CATEGORY_ADDED) + * @param oldValue always null + * @param newValue new value depends on the type. + */ + @Override + public void categoryAdded(long categoryID, ProgramEvent eventType, Object oldValue, + Object newValue) { + if (recordChanges) { + ((DataTypeArchiveDBChangeSet) changeSet).categoryAdded(categoryID); + } + super.categoryAdded(categoryID, eventType, oldValue, newValue); + } + + @Override + public void setName(String newName) { + // Can't change the name of a program archive - it comes from the program + } + + @Override + public String getDescription() { + return "Project Based Data Type Archive"; + } + + @Override + protected void clearCache(boolean all) { + try (Closeable c = lock.write()) { + super.clearCache(all); + dataTypeManager.invalidateCache(); + } + } + + @Override + protected void setChanged(boolean b) { + super.setChanged(b); + } + + @Override + public Map getMetadata() { + + metadata.clear(); + metadata.put("Data Type Archive Name", getName()); + metadata.put("# of Data Types", "" + getDataTypeManager().getDataTypeCount(true)); + metadata.put("# of Data Type Categories", "" + getDataTypeManager().getCategoryCount()); + + Options propList = getOptions(Program.PROGRAM_INFO); + List propNames = propList.getOptionNames(); + Collections.sort(propNames); + for (String propName : propNames) { + if (propName.indexOf(Options.DELIMITER) >= 0) { + continue; // ignore second tier options + } + String valueAsString = propList.getValueAsString(propName); + if (valueAsString != null) { + metadata.put(propName, propList.getValueAsString(propName)); + } + } + return metadata; + } + + @Override + protected void updateMetadata() throws IOException { + getMetadata(); // updates metadata map + super.updateMetadata(); + } + + @Override + public ArchiveType getArchiveType() { + return ArchiveType.PROJECT; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/TransientDtArchiveDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/TransientDtArchiveDB.java new file mode 100644 index 0000000000..79eec6ed69 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/dtarchive/TransientDtArchiveDB.java @@ -0,0 +1,80 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.database.dtarchive; + +import java.io.IOException; + +import db.DBHandle; +import ghidra.framework.data.OpenMode; +import ghidra.program.database.data.TransientDataTypeManager; +import ghidra.program.model.data.ArchiveType; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.program.model.dtarchive.TransientDataTypeArchive; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; + +/** + * {@link PersistentDataTypeArchive} that is meant to be used by clients as a temporary scratch pad for + * exploring datatype relationships without polluting the program's data type manager or any + * persistent datatype archive. + */ +public class TransientDtArchiveDB extends DataTypeArchiveDB implements TransientDataTypeArchive { + + private TransientDataTypeManager dtm; + + public TransientDtArchiveDB(DBHandle dbHandle, String name, TransientDataTypeManager dtm) + throws IOException { + this.dtm = dtm; + super(dbHandle, name, new Object()); + } + + @Override + protected TransientDataTypeManager createDataTypeManager(DBHandle handle, OpenMode openMode, + TaskMonitor monitor) throws VersionException, IOException, CancelledException { + if (openMode != OpenMode.CREATE) { + throw new IllegalArgumentException("only create is supported"); + } + return dtm; // use instance specified with TransientDtArchiveDB constructor + } + + @Override + public TransientDataTypeManager getDataTypeManager() { + return (TransientDataTypeManager) super.getDataTypeManager(); + } + + @Override + public String getDescription() { + return "Transient Data Type Archive"; + } + + @Override + public void close() { + // Expose method to TransientDataTypeManager + super.close(); + } + + @Override + public boolean isTemporary() { + return true; + } + + @Override + public ArchiveType getArchiveType() { + return ArchiveType.TEMPORARY; + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/external/ExternalManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/external/ExternalManagerDB.java index 5bec8b448f..ba7a4267cb 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/external/ExternalManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/external/ExternalManagerDB.java @@ -22,7 +22,7 @@ import org.apache.commons.lang3.StringUtils; import db.*; import ghidra.framework.data.OpenMode; -import ghidra.program.database.ManagerDB; +import ghidra.program.database.ProgramDBModule; import ghidra.program.database.ProgramDB; import ghidra.program.database.function.FunctionDB; import ghidra.program.database.function.FunctionManagerDB; @@ -43,7 +43,7 @@ import ghidra.util.task.TaskMonitor; /** * Manages the database for external references. */ -public class ExternalManagerDB implements ManagerDB, ExternalManager { +public class ExternalManagerDB implements ProgramDBModule, ExternalManager { private AddressMap addrMap; private SymbolManager symbolMgr; @@ -90,14 +90,14 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager { } @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { this.program = program; symbolMgr = program.getSymbolTable(); functionMgr = program.getFunctionManager(); } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { if (openMode != OpenMode.UPGRADE) { return; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java index f4e7abea04..916beb909a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java @@ -590,7 +590,7 @@ public class FunctionManagerDB implements FunctionManager { } @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { this.program = program; namespaceMgr = program.getNamespaceManager(); codeMgr = program.getCodeManager(); @@ -601,7 +601,7 @@ public class FunctionManagerDB implements FunctionManager { } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { if (openMode == OpenMode.UPGRADE) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java index 39bf839059..1060f2892c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java @@ -41,7 +41,7 @@ import ghidra.util.task.TaskMonitor; /** * The database memory map manager. */ -public class MemoryMapDB implements Memory, ManagerDB { +public class MemoryMapDB implements Memory, ProgramDBModule { private ProgramDB program; private AddressMapDB addrMap; @@ -215,7 +215,7 @@ public class MemoryMapDB implements Memory, ManagerDB { * Set the program. */ @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { this.program = program; try { reloadAll(); @@ -226,7 +226,7 @@ public class MemoryMapDB implements Memory, ManagerDB { } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { if (openMode == OpenMode.UPGRADE) { // Ensure that the key has been generated for the end address of each block diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/TreeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/TreeManager.java index db7e3acb40..5e66b08404 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/TreeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/TreeManager.java @@ -21,7 +21,7 @@ import java.util.*; import db.*; import db.util.ErrorHandler; import ghidra.framework.data.OpenMode; -import ghidra.program.database.ManagerDB; +import ghidra.program.database.ProgramDBModule; import ghidra.program.database.ProgramDB; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.*; @@ -40,7 +40,7 @@ import ghidra.util.task.TaskMonitor; * * */ -public class TreeManager implements ManagerDB { +public class TreeManager implements ProgramDBModule { /** * The name of the default tree that is created when a program is created. */ @@ -83,7 +83,7 @@ public class TreeManager implements ManagerDB { } @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { this.program = program; if (treeMap.isEmpty()) { @@ -92,7 +92,7 @@ public class TreeManager implements ManagerDB { } @Override - public void programReady(OpenMode openMode1, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode1, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { // Nothing to do } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java index 2f032b30d9..6709bb803e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java @@ -24,7 +24,7 @@ import java.util.concurrent.ConcurrentHashMap; import db.*; import ghidra.framework.data.OpenMode; -import ghidra.program.database.ManagerDB; +import ghidra.program.database.ProgramDBModule; import ghidra.program.database.ProgramDB; import ghidra.program.database.bookmark.OldBookmark; import ghidra.program.database.map.AddressMap; @@ -43,7 +43,7 @@ import ghidra.util.task.TaskMonitor; /** * Manages generic address keyed properties. */ -public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { +public class DBPropertyMapManager implements PropertyMapManager, ProgramDBModule { private DBHandle dbHandle; private ProgramDB program; @@ -104,12 +104,12 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { } @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { this.program = program; } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { // Nothing to do } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ReferenceDBManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ReferenceDBManager.java index d50389c9cd..07ec455d86 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ReferenceDBManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ReferenceDBManager.java @@ -43,7 +43,7 @@ import ghidra.util.task.TaskMonitor; /** * Reference manager implementation for the database. */ -public class ReferenceDBManager implements ReferenceManager, ManagerDB, ErrorHandler { +public class ReferenceDBManager implements ReferenceManager, ProgramDBModule, ErrorHandler { private static final Reference[] NO_REFS = new Reference[0]; private LRUMap variablesCache = new LRUMap<>(10); @@ -126,13 +126,13 @@ public class ReferenceDBManager implements ReferenceManager, ManagerDB, ErrorHan } @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { this.program = program; symbolMgr = program.getSymbolTable(); } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { if (openMode == OpenMode.UPGRADE) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/OldProgramContextDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/OldProgramContextDB.java index 324633519f..7ff3ecf54f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/OldProgramContextDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/OldProgramContextDB.java @@ -22,7 +22,7 @@ import java.util.*; import db.*; import db.util.ErrorHandler; import ghidra.framework.data.OpenMode; -import ghidra.program.database.ManagerDB; +import ghidra.program.database.ProgramDBModule; import ghidra.program.database.ProgramDB; import ghidra.program.database.map.AddressMap; import ghidra.program.database.util.AddressRangeMapDB; @@ -41,7 +41,7 @@ import ghidra.util.task.TaskMonitor; * ProgramContextDB defines a processor context over an address * space using database range maps for storage. */ -public class OldProgramContextDB implements ProgramContext, DefaultProgramContext, ManagerDB { +public class OldProgramContextDB implements ProgramContext, DefaultProgramContext, ProgramDBModule { private final static UndefinedValueException UNDEFINED_VALUE_EXCEPTION = new UndefinedValueException(); @@ -323,12 +323,12 @@ public class OldProgramContextDB implements ProgramContext, DefaultProgramContex } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { } @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/ProgramRegisterContextDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/ProgramRegisterContextDB.java index 47d65b3090..be54c06b5a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/ProgramRegisterContextDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/ProgramRegisterContextDB.java @@ -23,7 +23,7 @@ import db.DBHandle; import db.Table; import db.util.ErrorHandler; import ghidra.framework.data.OpenMode; -import ghidra.program.database.ManagerDB; +import ghidra.program.database.ProgramDBModule; import ghidra.program.database.ProgramDB; import ghidra.program.database.code.CodeManager; import ghidra.program.database.map.AddressMap; @@ -38,7 +38,7 @@ import ghidra.util.Msg; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; -public class ProgramRegisterContextDB extends AbstractStoredProgramContext implements ManagerDB { +public class ProgramRegisterContextDB extends AbstractStoredProgramContext implements ProgramDBModule { private DBHandle dbHandle; private ErrorHandler errorHandler; @@ -190,12 +190,12 @@ public class ProgramRegisterContextDB extends AbstractStoredProgramContext imple } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { } @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { this.program = program; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/reloc/RelocationManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/reloc/RelocationManager.java index d80d2be06c..2f13fae80b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/reloc/RelocationManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/reloc/RelocationManager.java @@ -21,7 +21,7 @@ import java.util.*; import db.*; import ghidra.framework.data.OpenMode; import ghidra.framework.options.Options; -import ghidra.program.database.ManagerDB; +import ghidra.program.database.ProgramDBModule; import ghidra.program.database.ProgramDB; import ghidra.program.database.map.AddressMap; import ghidra.program.database.mem.AddressSourceInfo; @@ -44,7 +44,7 @@ import ghidra.util.task.TaskMonitor; * * */ -public class RelocationManager implements RelocationTable, ManagerDB { +public class RelocationManager implements RelocationTable, ProgramDBModule { private ProgramDB program; private AddressMap addrMap; @@ -80,12 +80,12 @@ public class RelocationManager implements RelocationTable, ManagerDB { } @Override - public void setProgram(ProgramDB p) { + public void setDomainObject(ProgramDB p) { this.program = p; } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { if (openMode == OpenMode.UPGRADE && diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/sourcemap/SourceFileManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/sourcemap/SourceFileManagerDB.java index 2ddf2f8a56..bfa6fc2a70 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/sourcemap/SourceFileManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/sourcemap/SourceFileManagerDB.java @@ -22,7 +22,7 @@ import db.*; import db.util.ErrorHandler; import ghidra.framework.data.OpenMode; import ghidra.framework.store.LockException; -import ghidra.program.database.ManagerDB; +import ghidra.program.database.ProgramDBModule; import ghidra.program.database.ProgramDB; import ghidra.program.database.map.AddressMapDB; import ghidra.program.model.address.*; @@ -38,7 +38,7 @@ import ghidra.util.task.TaskMonitor; /** * Database Manager for managing source files and source map information. */ -public class SourceFileManagerDB implements SourceFileManager, ManagerDB, ErrorHandler { +public class SourceFileManagerDB implements SourceFileManager, ProgramDBModule, ErrorHandler { private ProgramDB program; @@ -69,12 +69,12 @@ public class SourceFileManagerDB implements SourceFileManager, ManagerDB, ErrorH } @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { this.program = program; } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { // nothing to do } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java index 7c066cc646..021ccaa0fa 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java @@ -37,7 +37,7 @@ import ghidra.util.task.TaskMonitor; /** * Implementation of the Equate Table */ -public class EquateManager implements EquateTable, ErrorHandler, ManagerDB { +public class EquateManager implements EquateTable, ErrorHandler, ProgramDBModule { private AddressMap addrMap; private DbCache refCache; @@ -95,12 +95,12 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB { } @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { this.program = program; } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { // Nothing to do } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/NamespaceManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/NamespaceManager.java index 27bd5ff133..1c3179543a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/NamespaceManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/NamespaceManager.java @@ -23,7 +23,7 @@ import org.apache.commons.collections4.map.LRUMap; import db.*; import db.util.ErrorHandler; import ghidra.framework.data.OpenMode; -import ghidra.program.database.ManagerDB; +import ghidra.program.database.ProgramDBModule; import ghidra.program.database.ProgramDB; import ghidra.program.database.map.AddressMap; import ghidra.program.database.util.AddressRangeMapDB; @@ -39,7 +39,7 @@ import ghidra.util.task.TaskMonitor; * Class to manage namespaces. */ -public class NamespaceManager implements ManagerDB { +public class NamespaceManager implements ProgramDBModule { private static final String NAMESPACE_MAP_NAME = "SCOPE ADDRESSES"; private AddressRangeMapDB namespaceMap; private ErrorHandler errHandler; @@ -94,13 +94,13 @@ public class NamespaceManager implements ManagerDB { } @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { this.symbolMgr = program.getSymbolTable(); globalNamespace = program.getGlobalNamespace(); } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { // Nothing to do } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV0.java index bd61333d08..06742454ff 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV0.java @@ -79,7 +79,7 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter { * Stores local symbols information in a temporary database table because this version * is so old, we don't have enough information in the record to upgrade during the normal * upgrade time. So we store off the information and will complete this upgrade when - * {@link SymbolManager#programReady(OpenMode, int, TaskMonitor)} is called + * {@link SymbolManager#domainObjectReady(OpenMode, int, TaskMonitor)} is called * * @param handle handle to temporary database * @param monitor the {@link TaskMonitor} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java index df71b74623..5a1dbee784 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java @@ -45,7 +45,7 @@ import ghidra.util.Lock.Closeable; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; -public class SymbolManager implements SymbolTable, ManagerDB { +public class SymbolManager implements SymbolTable, ProgramDBModule { private static final String OLD_LOCAL_SYMBOLS_TABLE = "OldLocalSymbols"; private static final int OLD_SYMBOL_ADDR_COL = 0; @@ -146,7 +146,7 @@ public class SymbolManager implements SymbolTable, ManagerDB { } @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { this.program = program; refManager = program.getReferenceManager(); namespaceMgr = program.getNamespaceManager(); @@ -154,7 +154,7 @@ public class SymbolManager implements SymbolTable, ManagerDB { } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { if (openMode == OpenMode.UPGRADE) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArchiveDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArchiveDataTypeManager.java new file mode 100644 index 0000000000..017ce98301 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArchiveDataTypeManager.java @@ -0,0 +1,130 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.data; + +import java.io.IOException; +import java.util.List; + +import javax.help.UnsupportedOperationException; + +import ghidra.framework.store.LockException; +import ghidra.program.model.dtarchive.*; +import ghidra.program.model.dtarchive.DataTypeArchive.LanguageUpdateOption; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.IncompatibleLanguageException; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * {@link ArchiveDataTypeManager} provides datatype management interface for + * {@link PersistentDataTypeArchive stand-alone datatype archives}. + */ +public interface ArchiveDataTypeManager extends DataTypeManager { + + @Override + public DataTypeArchive getDataStore(); + + /** + * Get the path name associated with the storage of this datatype manager's archive. + * @return path name or null if not applicable + * @deprecated use {@link DataTypeStore#getPath()} instead. (After calling getDataStore()) + */ + @Deprecated(since = "12.2", forRemoval = true) + public default String getPath() { + return null; + } + + /** + * Establish the program architecture for this datatype manager. The current setting can be + * determined from {@link #getProgramArchitecture()}. Archive must be open for update for + * this method to be used. + * @param language language + * @param compilerSpecId compiler specification ID defined by the language. + * @param updateOption indicates how variable storage data should be transitioned. If {@link #isProgramArchitectureMissing()} + * is true and {@link LanguageUpdateOption#TRANSLATE} specified, the translator will be based on whatever language version can + * be found. In this situation it may be best to force a {@link LanguageUpdateOption#CLEAR}. + * @param monitor task monitor (cancel not permitted to avoid corrupt state) + * @throws CompilerSpecNotFoundException if invalid compilerSpecId specified for language + * @throws LanguageNotFoundException if current language is not found (if required for data transition) + * @throws IOException if IO error occurs + * @throws CancelledException if task cancelled. If thrown, this data type manager is no longer + * stable and should be closed without saving. + * @throws LockException failure if exclusive access is required + * @throws UnsupportedOperationException if architecture change is not permitted + * @throws IncompatibleLanguageException if translation requested but not possible due to incompatible language architectures + * @deprecated use {@link PersistentDataTypeArchive#setProgramArchitecture(Language, CompilerSpecID, LanguageUpdateOption, TaskMonitor)} + * on the associated archive. + */ + @Deprecated(since = "12.2", forRemoval = true) + public default void setProgramArchitecture(Language language, CompilerSpecID compilerSpecId, + LanguageUpdateOption updateOption, TaskMonitor monitor) + throws CompilerSpecNotFoundException, LanguageNotFoundException, IOException, + CancelledException, LockException, UnsupportedOperationException, + IncompatibleLanguageException { + getDataStore().setProgramArchitecture(language, compilerSpecId, updateOption, monitor); + } + + /** + * Undo the last transaction if available + */ + public void undo(); + + /** + * Redo the previous undo if available + */ + public void redo(); + + /** + * Determine if there is a transaction previously undone (see {@link #undo()}) that can be + * redone (see {@link #redo()}). + * + * @return true if there is a transaction previously undone that can be redone, else false + */ + public boolean canRedo(); + + /** + * Determine if there is a previous transaction that can be reverted/undone (see {@link #undo()}). + * + * @return true if there is a previous transaction that can be reverted/undone, else false. + */ + public boolean canUndo(); + + /** + * Get the transaction name that is available for {@link #redo()} (see {@link #canRedo()}). + * @return transaction name that is available for {@link #redo()} or empty String. + */ + public String getRedoName(); + + /** + * Get the transaction name that is available for {@link #undo()} (see {@link #canUndo()}). + * @return transaction name that is available for {@link #undo()} or empty String. + */ + public String getUndoName(); + + /** + * Get all transaction names that are available within the {@link #undo()} stack. + * + * @return all transaction names that are available within the {@link #undo()} stack. + */ + public List getAllUndoNames(); + + /** + * Get all transaction names that are available within the {@link #redo()} stack. + * + * @return all transaction names that are available within the {@link #redo()} stack. + */ + public List getAllRedoNames(); +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArchiveType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArchiveType.java index 398de44892..cd56c6d0b5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArchiveType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArchiveType.java @@ -15,15 +15,25 @@ */ package ghidra.program.model.data; +/** + * Enum used to indicate what type of DataTypeStore is associated with a SourceArchive or + * DataTypeStore + */ public enum ArchiveType { //@formatter:off - BUILT_IN, - FILE, - PROJECT, - PROGRAM, - TEMPORARY; + BUILT_IN(0), + FILE(3), + PROJECT(2), + PROGRAM(1), + TEMPORARY(4); //@formatter:on + public final int sortOrder; + + private ArchiveType(int sortOrder) { + this.sortOrder = sortOrder; + } + public boolean isBuiltIn() { return this == BUILT_IN; } @@ -34,4 +44,5 @@ public enum ArchiveType { public boolean isValidSourceArchive() { return this == FILE || this == PROJECT; } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BuiltInDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BuiltInDataTypeManager.java index b9c878d059..ebaa98aaa1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BuiltInDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BuiltInDataTypeManager.java @@ -15,213 +15,19 @@ */ package ghidra.program.model.data; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.help.UnsupportedOperationException; -import javax.swing.event.ChangeListener; - -import ghidra.framework.ShutdownHookRegistry; -import ghidra.framework.ShutdownPriority; -import ghidra.program.database.symbol.VariableStorageManager; -import ghidra.program.model.lang.ProgramArchitecture; -import ghidra.util.*; -import ghidra.util.classfinder.ClassFilter; -import ghidra.util.classfinder.ClassSearcher; -import ghidra.util.exception.AssertException; -import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitor; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; /** * Data type manager for built in types that do not live anywhere except * in memory. */ -public final class BuiltInDataTypeManager extends StandAloneDataTypeManager { - - // TODO: There appear to be many public methods in DataTypeManagerDB which could potentially modify the - // underlying database - these methods should probably be overridden - - private static BuiltInDataTypeManager manager; - private ChangeListener classSearcherListener = e -> refresh(); +public interface BuiltInDataTypeManager extends DataTypeManager { /** * Returns shared instance of built-in data type manager. * @return the manager */ - public static synchronized BuiltInDataTypeManager getDataTypeManager() { - if (manager == null) { - manager = new BuiltInDataTypeManager(); - Runnable cleanupTask = () -> { - if (manager != null) { - manager.closeStaticInstance(); - manager = null; - } - }; - ShutdownHookRegistry.addShutdownHook(cleanupTask, - ShutdownPriority.DISPOSE_DATABASES.before()); - } - return manager; - } - - private BuiltInDataTypeManager() { - super(BUILT_IN_DATA_TYPES_NAME); - initialize(); - setImmutable(); - } - - @Override - protected final void setProgramArchitecture(ProgramArchitecture programArchitecture, - VariableStorageManager variableStorageMgr, boolean force, TaskMonitor monitor) - throws IOException, CancelledException { - throw new UnsupportedOperationException("program architecture change not permitted"); - } - - @Override - protected final boolean isArchitectureChangeAllowed() { - return false; - } - - @Override - public synchronized int startTransaction(String description) { - if (manager != null) { - throw new UnsupportedOperationException( - "Built-in datatype manager may not be modified"); - } - return super.startTransaction(description); - } - - @Override - public synchronized boolean endTransaction(int transactionID, boolean commit) { - if (manager != null) { - throw new UnsupportedOperationException(); - } - return super.endTransaction(transactionID, commit); - } - - @Override - public synchronized boolean canUndo() { - return false; - } - - @Override - public synchronized boolean canRedo() { - return false; - } - - @Override - public Category createCategory(CategoryPath path) { - if (path != CategoryPath.ROOT) { - throw new UnsupportedOperationException( - "Built-in category limited to root category only"); - } - return super.createCategory(path); - } - - private synchronized void closeStaticInstance() { - ClassSearcher.removeChangeListener(classSearcherListener); - super.close(); - } - - /** - * Refresh the list of Built-In data types found by searching the class path. - */ - private synchronized void refresh() { - populateBuiltInTypes(); - } - - private void initialize() { - try { - populateBuiltInTypes(); - } - catch (Throwable t) { - Msg.showError(this, null, "Error", "Error populating Built In Data Types", t); - } - ClassSearcher.addChangeListener(classSearcherListener); - } - - /** - * Add the built in data types to the default built in folder if they - * were not found in any other category. - */ - protected void populateBuiltInTypes() { - int id = super.startTransaction("Populate"); - try { - - List list = new ArrayList<>(); - ClassFilter filter = new BuiltInDataTypeClassExclusionFilter(); - List datatypes = - ClassSearcher.getInstances(BuiltInDataType.class, filter); - for (BuiltInDataType datatype : datatypes) { - list.clear(); - findDataTypes(datatype.getName(), list); - if (list.size() == 0) { - super.resolve(datatype, null); - } - else if (!list.get(0).isEquivalent(datatype)) { - Msg.showError(this, null, "Invalid BuiltIn Data Type", - "BuiltIn datatype name collision between " + - datatype.getClass().getSimpleName() + " and " + - list.get(0).getClass().getSimpleName() + ", both named '" + - datatype.getName() + "'"); - } - else if (list.size() != 1) { - throw new AssertException("Should be no duplicate named built-in types"); - } - } - } - finally { - super.endTransaction(id, true); - } - - } - - protected UniversalID resolveSourceArchiveID(DataType dataType) { - if (dataType instanceof BuiltInDataType) { - return DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID; - } - throw new IllegalArgumentException("Only Built-in data types can be resolved by the " + - getClass().getSimpleName() + " manager."); - } - - @Override - public ArchiveType getType() { - return ArchiveType.BUILT_IN; - } - - @Override - public DataType resolve(DataType dataType, DataTypeConflictHandler handler) { - return super.resolve(dataType, DataTypeConflictHandler.BUILT_IN_MANAGER_HANDLER); - } - - @Override - public DataType addDataType(DataType originalDataType, DataTypeConflictHandler handler) { - throw new UnsupportedOperationException(); - } - - @Override - public void setName(String name) throws InvalidNameException { - throw new UnsupportedOperationException(); - } - - @Override - public void associateDataTypeWithArchive(DataType datatype, SourceArchive archive) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean remove(DataType dataType) { - throw new UnsupportedOperationException(); - } - - @Override - public DataType replaceDataType(DataType existingDt, DataType replacementDt, - boolean updateCategoryPath) throws DataTypeDependencyException { - throw new UnsupportedOperationException(); - } - - @Override - public void close() { - // cannot close a built-in data type manager; close performed automatically during shutdown + public static BuiltInDataTypeManager getDataTypeManager() { + return DataTypeArchiveFactory.getBuiltInDataTypeManager(); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeArchiveIdDumper.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeArchiveIdDumper.java index d59dc90afd..0b0d99e5ed 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeArchiveIdDumper.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeArchiveIdDumper.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,12 +22,18 @@ import ghidra.GhidraApplicationLayout; import ghidra.GhidraLaunchable; import ghidra.framework.Application; import ghidra.framework.ApplicationConfiguration; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.util.UniversalID; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; public class DataTypeArchiveIdDumper implements GhidraLaunchable { @Override - public void launch(GhidraApplicationLayout layout, String[] args) throws IOException { + public void launch(GhidraApplicationLayout layout, String[] args) + throws IOException, CancelledException, VersionException { if (args.length != 2) { System.out.println("Usage: DataTypeArchiveDumper it = archive.getAllDataTypes(); + Iterator it = archive.getDataTypeManager().getAllDataTypes(); while (it.hasNext()) { DataType dt = it.next(); UniversalID universalID = dt.getUniversalID(); @@ -59,5 +65,6 @@ public class DataTypeArchiveIdDumper implements GhidraLaunchable { } } writer.close(); + archive.release(this); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeConflictHandler.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeConflictHandler.java index 7ac6c6f3b7..940bad04e0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeConflictHandler.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeConflictHandler.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -222,24 +222,6 @@ public abstract class DataTypeConflictHandler { } }; - final static DataTypeConflictHandler BUILT_IN_MANAGER_HANDLER = new DataTypeConflictHandler() { - @Override - public ConflictResult resolveConflict(DataType addedDataType, DataType existingDataType) { - throw new UnsupportedOperationException( - "Built-in data-types may not be substantially changed while Ghidra is running"); - } - - @Override - public boolean shouldUpdate(DataType sourceDataType, DataType localDataType) { - return false; - } - - @Override - public DataTypeConflictHandler getSubsequentHandler() { - return this; - } - }; - /** * Callback to handle conflicts in a datatype manager when new datatypes are added that * have the same name as an existing datatype. The implementer of this interface should do diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java index da20223d02..51e2f04801 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java @@ -20,11 +20,11 @@ import java.util.*; import db.Transaction; import ghidra.framework.model.DomainObject; import ghidra.program.database.SpecExtension; -import ghidra.program.database.map.AddressMap; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.program.model.dtarchive.DataTypeStore; import ghidra.program.model.lang.*; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; -import ghidra.util.InvalidNameException; import ghidra.util.UniversalID; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -33,6 +33,16 @@ import utility.function.ExceptionalSupplier; /** * Interface for Managing data types. + *

+ * DatatypeManagers have different implementations depending on where the datatype manager resides. + * Currently, data type managers exist in either a {@link Program} or a {@link PersistentDataTypeArchive}. + * (There are also some debugger flavors as well.) + *

+ * There are different types of data type archives (project, file, built-in, and + * transient). Each of these returns a variant subclass of DataTypeManager. This is mostly for + * legacy reasons where non-program datatype managers where stand-alone and not part of some + * larger concept such as a data type archive. + * */ public interface DataTypeManager { @@ -372,13 +382,6 @@ public interface DataTypeManager { */ public String getName(); - /** - * Sets this data type manager's name - * @param name the new name - * @throws InvalidNameException if the given name is invalid (such as when null or empty) - */ - public void setName(String name) throws InvalidNameException; - /** * Returns true if this DataTypeManager can be modified. * @return true if this DataTypeMangaer can be modified. @@ -501,7 +504,18 @@ public interface DataTypeManager { /** * Closes this dataType manager + * @deprecated In previous versions, this was only supported for DataTypeManagers that came + * from stand-alone file datatype archives. This was problematic for archives that were + * opened via the service since using the service means that the archive may be shared with + * other clients and it would be bad to close it out from under them. We have moved to the + * consumer approach where the archive is not closed until all consumers are released. + *

+ * If the datatype archive is opened using the service, the service will add its own consumer + * and it can be closed by calling the service to close it. Any clients that don't want the + * archive closed out from under it, can add a consumer to the archive which then would need + * to be released when done with it. */ + @Deprecated(since = "12.2", forRemoval = true) public void close(); /** @@ -665,13 +679,6 @@ public interface DataTypeManager { */ public DataOrganization getDataOrganization(); - /** - * Returns the associated AddressMap used by this datatype manager. - * @return the AddressMap used by this datatype manager or null if - * one has not be established. - */ - public AddressMap getAddressMap(); - /** * Returns a list of source archives not including the builtin or the program's archive. * @return a list of source archives not including the builtin or the program's archive. @@ -763,4 +770,9 @@ public interface DataTypeManager { */ public PrototypeModel getCallingConvention(String name); + /** + * {@return the archive this DataTypeManager belongs to} + */ + public DataTypeStore getDataStore(); + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerDomainObject.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerDomainObject.java deleted file mode 100644 index 069f01c453..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerDomainObject.java +++ /dev/null @@ -1,22 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.program.model.data; - -import ghidra.app.merge.DataTypeManagerOwner; -import ghidra.framework.model.DomainObject; - -public interface DataTypeManagerDomainObject extends DomainObject, DataTypeManagerOwner { -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java index f58dc7c846..a2d50c18ca 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java @@ -17,91 +17,46 @@ package ghidra.program.model.data; import java.io.File; import java.io.IOException; -import java.util.Objects; import generic.jar.ResourceFile; -import ghidra.framework.data.OpenMode; -import ghidra.framework.store.LockException; -import ghidra.framework.store.db.PackedDBHandle; -import ghidra.framework.store.db.PackedDatabase; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; +import ghidra.program.database.dtarchive.DefaultConsumer; +import ghidra.program.model.dtarchive.PersistentDataTypeArchive; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.program.model.lang.*; -import ghidra.program.model.listing.IncompatibleLanguageException; -import ghidra.program.util.DefaultLanguageService; -import ghidra.util.InvalidNameException; import ghidra.util.UniversalID; import ghidra.util.exception.*; -import ghidra.util.filechooser.ExtensionFileFilter; -import ghidra.util.filechooser.GhidraFileFilter; import ghidra.util.task.TaskMonitor; /** * DataTypeManager for a file. Can import categories from a file, or export * categories to a packed database. + * @deprecated There is no longer any need to work with a specialized version of DataTypeManager. + * Create or open a {@link FileDataTypeArchive}, then call + * {@link PersistentDataTypeArchive#getDataTypeManager()}. */ -public class FileDataTypeManager extends StandAloneDataTypeManager - implements FileArchiveBasedDataTypeManager { - - public final static String EXTENSION = "gdt"; // Ghidra Data Types - public static final GhidraFileFilter GDT_FILEFILTER = - ExtensionFileFilter.forExtensions("Ghidra Data Type Files", EXTENSION); - - /** - * Suffix for an archive file. - */ - public final static String SUFFIX = "." + EXTENSION; +@Deprecated(since = "12.2", forRemoval = true) +public interface FileDataTypeManager extends ArchiveDataTypeManager, AutoCloseable { + @Deprecated(since = "12.2", forRemoval = true) final static String OLD_EXTENSION = "dtf"; + + @Deprecated(since = "12.2", forRemoval = true) final static String OLD_SUFFIX = "." + OLD_EXTENSION; - private ResourceFile file; - private PackedDatabase packedDB; - - /** - * Construct a new DataTypeFileManager using the default data organization. - *

- * NOTE: it may be appropriate to {@link #getWarning() check for warnings} after - * opening an existing archive file prior to use. While an archive will remain useable - * with a warning condition, architecture-specific data may not be available or up-to-date. - * - * @param packedDbfile file to load or create based upon openMode - * @param openMode CREATE, READ_ONLY or UPDATE - * @param monitor the progress monitor - * @throws IOException if an IO error occurs - * @throws CancelledException if task cancelled - */ - private FileDataTypeManager(ResourceFile packedDbfile, OpenMode openMode, TaskMonitor monitor) - throws IOException, CancelledException { - super(validateFilename(packedDbfile), openMode, monitor); - file = packedDbfile; - name = getRootName(file.getName()); - packedDB = ((PackedDBHandle) dbHandle).getPackedDatabase(); - logWarning(); - if (openMode == OpenMode.IMMUTABLE) { - setImmutable(); - } - } - - private static ResourceFile validateFilename(ResourceFile packedDbfile) { - if (!packedDbfile.getName().endsWith(SUFFIX)) { - throw new IllegalArgumentException("Archive files must end with " + SUFFIX); - } - return packedDbfile; - } - /** * Create a new data-type file archive using the default data organization * @param packedDbfile archive file (filename must end with DataTypeFileManager.SUFFIX) * @return data-type manager backed by specified packedDbFile * @throws IOException if an IO error occurs + * @deprecated Use {@link DataTypeArchiveFactory#createFileArchive(File)} to create a file + * base archive, then call {@link PersistentDataTypeArchive#getDataTypeManager()} */ + @Deprecated(since = "12.2", forRemoval = true) public static FileDataTypeManager createFileArchive(File packedDbfile) throws IOException { - try { - return new FileDataTypeManager(new ResourceFile(packedDbfile), OpenMode.CREATE, - TaskMonitor.DUMMY); - } - catch (CancelledException e) { - throw new AssertException(e); // unexpected without task monitor use - } + FileDataTypeArchive archive = + DataTypeArchiveFactory.createFileArchive(packedDbfile, new DefaultConsumer()); + return archive != null ? (FileDataTypeManager) archive.getDataTypeManager() : null; } /** @@ -115,41 +70,24 @@ public class FileDataTypeManager extends StandAloneDataTypeManager * @throws CompilerSpecNotFoundException if specified {@code compilerSpecId} is not defined * for the specified language. * @throws IOException if an IO error occurs + * @deprecated Use + * {@link DataTypeArchiveFactory#createFileArchive(File, LanguageID, CompilerSpecID)} to create + * a file based archive, then call {@link PersistentDataTypeArchive#getDataTypeManager()} */ + @Deprecated(since = "12.2", forRemoval = true) public static FileDataTypeManager createFileArchive(File packedDbFile, LanguageID languageId, CompilerSpecID compilerSpecId) throws LanguageNotFoundException, CompilerSpecNotFoundException, IOException { - Objects.requireNonNull(languageId, "missing required languageId"); - Objects.requireNonNull(compilerSpecId, "missing required compilerSpecId"); - try { - if (packedDbFile.exists()) { - throw new DuplicateFileException("File already exists: " + packedDbFile); - } - // Verify that the specified language and compiler spec are valid - LanguageService languageService = DefaultLanguageService.getLanguageService(); - Language language = languageService.getLanguage(languageId); - language.getCompilerSpecByID(compilerSpecId); + FileDataTypeArchive archive = + DataTypeArchiveFactory.createFileArchive(packedDbFile, languageId, compilerSpecId, + new DefaultConsumer()); - FileDataTypeManager dtm = - new FileDataTypeManager(new ResourceFile(packedDbFile), OpenMode.CREATE, - TaskMonitor.DUMMY); - - dtm.setProgramArchitecture(language, compilerSpecId, LanguageUpdateOption.CLEAR, - TaskMonitor.DUMMY); - - return dtm; - } - catch (CancelledException e) { - throw new AssertException(e); // unexpected without task monitor use - } - catch (LockException | IncompatibleLanguageException | UnsupportedOperationException e) { - throw new RuntimeException(e); // unexpected for new archive - } + return archive != null ? (FileDataTypeManager) archive.getDataTypeManager() : null; } /** - * Create a new data-type file archive using the default data organization. + * Create a new data-type file archive using the specified language/compiler spec * @param packedDbfile archive file (filename must end with DataTypeFileManager.SUFFIX) * @param languageId valid language ID (see appropriate *.ldefs file for defined IDs). If null * invocation will be deferred to {@link #createFileArchive(File)}. @@ -159,20 +97,17 @@ public class FileDataTypeManager extends StandAloneDataTypeManager * @throws CompilerSpecNotFoundException if specified {@code compilerSpecId} is not defined * for the specified language. * @throws IOException if an IO error occurs + * @deprecated Use + * {@link DataTypeArchiveFactory#createFileArchive(File, String, String)} to create + * a file based archive, then call {@link PersistentDataTypeArchive#getDataTypeManager()} */ + @Deprecated(since = "12.2", forRemoval = true) public static FileDataTypeManager createFileArchive(File packedDbfile, String languageId, String compilerSpecId) throws IOException { - if (languageId == null) { - if (compilerSpecId != null) { - throw new IllegalArgumentException("compilerSpecId specified without languageId"); - } - return createFileArchive(packedDbfile); - } - if (compilerSpecId == null) { - throw new IllegalArgumentException("languageId specified without compilerSpecId"); - } - return createFileArchive(packedDbfile, new LanguageID(languageId), - new CompilerSpecID(compilerSpecId)); + FileDataTypeArchive archive = + DataTypeArchiveFactory.createFileArchive(packedDbfile, languageId, compilerSpecId, + new DefaultConsumer()); + return archive != null ? (FileDataTypeManager) archive.getDataTypeManager() : null; } /** @@ -180,20 +115,22 @@ public class FileDataTypeManager extends StandAloneDataTypeManager *

* NOTE: If archive has an assigned architecture, issues may arise due to a revised or * missing {@link Language}/{@link CompilerSpec} which will result in a warning but not - * prevent the archive from being opened. Such a warning condition will ne logged and may + * prevent the archive from being opened. Such a warning condition will be logged and may * result in missing or stale information for existing datatypes which have architecture related - * data. In some case it may be appropriate to - * {@link FileDataTypeManager#getWarning() check for warnings} on the returned archive - * object prior to its use. + * data. Warnings may be checked via the associated archive object. * - * @param packedDbfile archive file (filename must end with DataTypeFileManager.SUFFIX) + * @param file archive file (filename must end with DataTypeFileManager.SUFFIX) * @param openForUpdate if true archive will be open for update * @return data-type manager backed by specified packedDbFile * @throws IOException if an IO error occurs + * @deprecated Use {@link DataTypeArchiveFactory#openReadOnly(File, TaskMonitor)} or + * {@link DataTypeArchiveFactory#openForUpdate(File, boolean, TaskMonitor)} to open + * a file based archive, then call {@link PersistentDataTypeArchive#getDataTypeManager()} */ - public static FileDataTypeManager openFileArchive(File packedDbfile, boolean openForUpdate) + @Deprecated(since = "12.2", forRemoval = true) + public static FileDataTypeManager openFileArchive(File file, boolean openForUpdate) throws IOException { - return openFileArchive(new ResourceFile(packedDbfile), openForUpdate); + return openFileArchive(new ResourceFile(file), openForUpdate); } /** @@ -201,208 +138,92 @@ public class FileDataTypeManager extends StandAloneDataTypeManager *

* NOTE: If archive has an assigned architecture, issues may arise due to a revised or * missing {@link Language}/{@link CompilerSpec} which will result in a warning but not - * prevent the archive from being opened. Such a warning condition will ne logged and may + * prevent the archive from being opened. Such a warning condition will be logged and may * result in missing or stale information for existing datatypes which have architecture related - * data. In some case it may be appropriate to - * {@link FileDataTypeManager#getWarning() check for warnings} on the returned archive - * object prior to its use. + * data . Warnings may be checked via the associated archive object. * - * @param packedDbfile archive file (filename must end with DataTypeFileManager.SUFFIX) + * @param file archive file (filename must end with DataTypeFileManager.SUFFIX) * @param openForUpdate if true archive will be open for update * @return data-type manager backed by specified packedDbFile * @throws IOException if an IO error occurs + * @deprecated Use {@link DataTypeArchiveFactory#openReadOnly(ResourceFile, TaskMonitor)} or + * {@link DataTypeArchiveFactory#openForUpdate(ResourceFile, boolean, TaskMonitor)} to open + * a file based archive, then call {@link PersistentDataTypeArchive#getDataTypeManager()} */ - public static FileDataTypeManager openFileArchive(ResourceFile packedDbfile, + @Deprecated(since = "12.2", forRemoval = true) + public static FileDataTypeManager openFileArchive(ResourceFile file, boolean openForUpdate) throws IOException { - OpenMode mode = openForUpdate ? OpenMode.UPDATE : OpenMode.IMMUTABLE; + FileDataTypeArchive archive; try { - return new FileDataTypeManager(packedDbfile, mode, TaskMonitor.DUMMY); + archive = openArchive(file, openForUpdate); + return (FileDataTypeManager) archive.getDataTypeManager(); } - catch (CancelledException e) { - throw new AssertException(e); // unexpected without task monitor use + catch (CancelledException | VersionException e) { + throw new IOException(e); + } + } + + private static FileDataTypeArchive openArchive(ResourceFile file, boolean openForUpdate) + throws CancelledException, VersionException, IOException { + Object consumer = new DefaultConsumer(); + if (!openForUpdate) { + return DataTypeArchiveFactory.openReadOnly(file, consumer, TaskMonitor.DUMMY); + } + try { + return DataTypeArchiveFactory.openForUpdate(file, false, consumer, TaskMonitor.DUMMY); + } + catch (VersionException e) { + if (e.isUpgradable()) { + return DataTypeArchiveFactory.openForUpdate(file, true, consumer, + TaskMonitor.DUMMY); + } + throw e; } } /** - * Saves the data type manager to the given file with a specific databaseId. - * NOTE: This method is intended for use in transforming one archive database to - * match another existing archive database. - * @param saveFile the file to save - * @param newUniversalId the new id to use - * @throws DuplicateFileException if save file already exists + * {@return true if the archive has changed} + * @deprecated Use {@link PersistentDataTypeArchive#isChanged} + */ + @Deprecated(since = "12.2", forRemoval = true) + public boolean isChanged(); + + /** + * {@return true if the archive containing this datatype manager has been closed} + * @deprecated Use {@link PersistentDataTypeArchive#isClosed()} + */ + @Deprecated(since = "12.2", forRemoval = true) + public boolean isClosed(); + + /** + * Save the archive back to its original file. * @throws IOException if IO error occurs + * @deprecated Use {@link FileDataTypeArchive#save()} */ - public void saveAs(File saveFile, UniversalID newUniversalId) - throws DuplicateFileException, IOException { - ResourceFile resourceSaveFile = new ResourceFile(saveFile); - validateFilename(resourceSaveFile); - try { - universalID = newUniversalId; - packedDB = ((PackedDBHandle) dbHandle).saveAs("DTArchive", saveFile.getParentFile(), - saveFile.getName(), newUniversalId.getValue(), TaskMonitor.DUMMY); - file = resourceSaveFile; - updateRootCategoryName(resourceSaveFile, getRootCategory()); - } - catch (CancelledException e) { - // Cancel can't happen because we are using a dummy monitor - } - finally { - clearUndo(); - } - } + @Deprecated(since = "12.2", forRemoval = true) + public void save() throws IOException; /** - * Saves the data type manager to the given file - * @param saveFile the file to save - * @throws DuplicateFileException if save file already exists + * Save the archive to a new file. + * @param outFile the file to write to + * @param newFileID the Universal id to use for the file. (Specialty use for creating an + * new version of an archive while maintaining the same old id.) + * @throws DuplicateFileException if a file with that name already exists. * @throws IOException if IO error occurs + * @deprecated Use {@link FileDataTypeArchive#saveAs(File, UniversalID)} */ - public void saveAs(File saveFile) throws DuplicateFileException, IOException { - ResourceFile resourceSaveFile = new ResourceFile(saveFile); - validateFilename(resourceSaveFile); - try { - packedDB = ((PackedDBHandle) dbHandle).saveAs("DTArchive", saveFile.getParentFile(), - saveFile.getName(), TaskMonitor.DUMMY); - file = resourceSaveFile; - updateRootCategoryName(resourceSaveFile, getRootCategory()); - } - catch (CancelledException e) { - // Cancel can't happen because we are using a dummy monitor - } - finally { - clearUndo(); - } - } + @Deprecated(since = "12.2", forRemoval = true) + public void saveAs(File outFile, UniversalID newFileID) + throws DuplicateFileException, IOException; /** - * Save the category to source file. + * Save the archive to a new file. + * @param outFile the file to write to * @throws IOException if IO error occurs + * @throws DuplicateFileException if a file with that name already exists. + * @deprecated Use {@link FileDataTypeArchive#saveAs(File)} */ - public void save() throws IOException { + @Deprecated(since = "12.2", forRemoval = true) + public void saveAs(File outFile) throws DuplicateFileException, IOException; - if (file == null) { - throw new IllegalStateException("Output File was not specified: call saveAs(String)"); - } - - try { - ((PackedDBHandle) dbHandle).save(TaskMonitor.DUMMY); - } - catch (CancelledException e) { - // Cancel can't happen because we are using a dummy monitor - } - finally { - clearUndo(); - } - } - - /** - * Get the filename for the current file. - * - * @return String filename, or null if there is no current file. - */ - public String getFilename() { - if (file != null) { - return file.getAbsolutePath(); - } - return null; - } - - /** - * Convert the filename for the given file to have the packed database - * file extension. - * @param file file whose name is to be converted - * @return file if the filename already ends in the packed database - * file extension, or a new File object that has the packed database - * file extension - */ - public static File convertFilename(File file) { - String fname = file.getName(); - if (file.getName().endsWith(SUFFIX)) { - return file; - } - int pos = fname.indexOf(OLD_SUFFIX); - if (pos > 0) { - fname = fname.substring(0, pos); - } - fname = fname + SUFFIX; - return new File(file.getParentFile(), fname); - } - - private void updateRootCategoryName(ResourceFile newFile, Category root) { - String newName = getRootName(newFile.getName()); - - if (root.getName().equals(newName)) { - return; - } - - try { - root.setName(newName); - } - catch (InvalidNameException | DuplicateNameException e) { - // do nothing - } - } - - private String getRootName(String newName) { - int pos = newName.lastIndexOf(SUFFIX); - if (pos > 0) { - newName = newName.substring(0, pos); - } - return newName; - } - - public void delete() throws IOException { - super.close(); - if (packedDB != null) { - packedDB.delete(); - packedDB = null; - } - } - - @Override - public synchronized void close() { - if (packedDB != null) { - super.close(); - packedDB.dispose(); - packedDB = null; - } - } - - public boolean isClosed() { - return packedDB == null; - } - - @Override - public void finalize() { - close(); - } - - public static void delete(File packedDbfile) throws IOException { - if (packedDbfile == null) { - return; - } - String filename = packedDbfile.getAbsolutePath(); - if (filename.endsWith(OLD_SUFFIX)) { - packedDbfile.delete(); - } - else { - PackedDatabase.delete(packedDbfile); - } - } - - @Override - public String getPath() { - return (file != null) ? file.getAbsolutePath() : null; // TODO Is this correct? - } - - @Override - public ArchiveType getType() { - return ArchiveType.FILE; - } - - @Override - public String toString() { - return getClass().getSimpleName() + " - " + getName(); - } } - diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ProgramBasedDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ProgramBasedDataTypeManager.java index 1fc1eda8f3..b15bf94397 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ProgramBasedDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ProgramBasedDataTypeManager.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,11 +23,11 @@ import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; /** - * Extends DataTypeManager to include methods specific to a data type manager for + * Extends DataTypeManager to include methods specific to a datatype manager for * a program. */ -public interface ProgramBasedDataTypeManager extends DomainFileBasedDataTypeManager { - +public interface ProgramBasedDataTypeManager extends DataTypeManager { + /** * Get the program instance associated with this datatype manager * @return program instance associated with this datatype manager diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ProjectArchiveBasedDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ProjectArchiveBasedDataTypeManager.java deleted file mode 100644 index 46a9c422e3..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ProjectArchiveBasedDataTypeManager.java +++ /dev/null @@ -1,24 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.program.model.data; - -/** - * Extends DataTypeManager to provide methods specific to project data type archives. - */ -public interface ProjectArchiveBasedDataTypeManager extends DomainFileBasedDataTypeManager { - // TODO -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java index e6f98f78b6..32c7036156 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java @@ -15,1102 +15,41 @@ */ package ghidra.program.model.data; -import java.io.Closeable; -import java.io.IOException; -import java.util.*; - -import javax.help.UnsupportedOperationException; - -import com.google.common.collect.ImmutableList; - -import db.*; -import db.buffers.BufferMgr; -import db.util.ErrorHandler; -import generic.jar.ResourceFile; -import ghidra.framework.data.OpenMode; import ghidra.framework.model.RuntimeIOException; -import ghidra.framework.store.LockException; -import ghidra.program.database.DBStringMapAdapter; -import ghidra.program.database.ProgramAddressFactory; -import ghidra.program.database.data.DataTypeManagerDB; -import ghidra.program.database.symbol.VariableStorageManager; -import ghidra.program.database.symbol.VariableStorageManagerDB; -import ghidra.program.model.address.AddressFactory; -import ghidra.program.model.lang.*; -import ghidra.program.model.listing.IncompatibleLanguageException; -import ghidra.program.util.DefaultLanguageService; -import ghidra.program.util.LanguageTranslator; -import ghidra.util.*; -import ghidra.util.exception.*; -import ghidra.util.task.TaskMonitor; +import ghidra.program.database.data.TransientDataTypeManager; /** - * Basic implementation of the DataTypeManger interface + * Basic implementation of the DataTypeManger interface for those data type managers that + * originate in an archive and not a program. + * + * @deprecated use {@link TransientDataTypeManager} */ -public class StandAloneDataTypeManager extends DataTypeManagerDB implements Closeable { - - private static final String LANGUAGE_VERSION = "Language Version"; // major version only - private static final String LANGUAGE_ID = "Language ID"; - private static final String COMPILER_SPEC_ID = "Compiler Spec ID"; - - private static final int NUM_UNDOS = 50; - - private LinkedList undoList = new LinkedList<>(); - private LinkedList redoList = new LinkedList<>(); - - private int transactionCount; - private Long transaction; - private boolean commitTransaction; - private String transactionName; - - private boolean isImmutable; - - private LanguageTranslator languageUpgradeTranslator; - private String programArchitectureSummary; // summary of expected program architecture - - protected String name; - - public static enum ArchiveWarningLevel { - INFO, WARN, ERROR; - } - - public static enum ArchiveWarning { - - /** - * {@link #NONE} indicates a normal archive condition - */ - NONE(ArchiveWarningLevel.INFO), - - /** - * {@link #UPGRADED_LANGUAGE_VERSION} indicates an archive which has been open for update - * was upgraded to a newer language version. This is expected when the {@link Language} - * required by the associated {@link ProgramArchitecture} has a major version change - * which involves significant {@link Register} changes. Sharing an upgraded archive - * may impact others who do not have access to the updated {@link Language} module. - */ - UPGRADED_LANGUAGE_VERSION(ArchiveWarningLevel.INFO), - - // programArchitectureSummary must be set for the warnings below - - /** - * {@link #LANGUAGE_NOT_FOUND} indicates the {@link Language} or its appropriate version, - * required by the associated {@link ProgramArchitecture}, was not found or encountered - * a problem being loaded. The {@link FileDataTypeManager#getWarningDetail()} may provide - * additional insight to the underlying cause. - */ - LANGUAGE_NOT_FOUND(ArchiveWarningLevel.ERROR), - - /** - * {@link #COMPILER_SPEC_NOT_FOUND} indicates the {@link CompilerSpec}, - * required by the associated {@link ProgramArchitecture}, was not found or encountered - * a problem being loaded. The {@link FileDataTypeManager#getWarningDetail()} may provide - * additional insight to the underlying cause. This condition can only occur if the - * required {@link Language} was found. - */ - COMPILER_SPEC_NOT_FOUND(ArchiveWarningLevel.ERROR), - - /** - * {@link #LANGUAGE_UPGRADE_REQURED} indicates an archive which has been open read-only - * requires an upgraded to a newer language version. This is expected when the - * {@link Language} required by the associated {@link ProgramArchitecture} has a major - * version change within the current installation. Major version changes for a - * {@link Language} rarely occur but are required when significant {@link Register} - * or addressing changes have been made. Upgrading a shared archive may impact others - * who do not have access to the updated {@link Language} module and should be - * coordinated with others who may be affected. - */ - LANGUAGE_UPGRADE_REQURED(ArchiveWarningLevel.WARN), - - /** - * {@link #DATA_ORG_CHANGED} indicates an archive which has been open read-only - * requires an upgraded to adjust for changes in the associated data organization. - */ - DATA_ORG_CHANGED(ArchiveWarningLevel.WARN); - - final ArchiveWarningLevel level; - - ArchiveWarning(ArchiveWarningLevel level) { - this.level = level; - } - - /** - * Get the warning level - * @return warning level - */ - public ArchiveWarningLevel level() { - return level; - } - } - - private ArchiveWarning warning; - private Exception warningDetail; +@Deprecated(forRemoval = true, since = "12.2") +public class StandAloneDataTypeManager extends TransientDataTypeManager { /** * Constructor for new temporary data-type manager using the default DataOrganization. * Note that this manager does not support the save or saveAs operation. - * @param rootName Name of the root category. + * @param name temporary archive and datatype manager name * @throws RuntimeIOException if database error occurs during creation + * @deprecated use {@link TransientDataTypeManager} */ - public StandAloneDataTypeManager(String rootName) throws RuntimeIOException { - super(DataOrganizationImpl.getDefaultOrganization()); - this.name = rootName; - initTransactionState(); + @Deprecated(forRemoval = true, since = "12.2") + public StandAloneDataTypeManager(String name) throws RuntimeIOException { + super(name); } /** * Constructor for new temporary data-type manager using a specified DataOrganization. * Note that this manager does not support the save or saveAs operation. - * @param rootName Name of the root category. + * @param name temporary archive and datatype manager name * @param dataOrganzation applicable data organization * @throws RuntimeIOException if database error occurs during creation + * @deprecated use {@link TransientDataTypeManager} */ - public StandAloneDataTypeManager(String rootName, DataOrganization dataOrganzation) + @Deprecated(forRemoval = true, since = "12.2") + public StandAloneDataTypeManager(String name, DataOrganization dataOrganzation) throws RuntimeIOException { - super(dataOrganzation); - this.name = rootName; - initTransactionState(); - } - - /** - * Constructor for a data-type manager backed by a packed database file. - * When opening for UPDATE an automatic upgrade will be performed if required. - *

- * NOTE: {@link #logWarning()} should be invoked immediately after - * instantiating a {@link StandAloneDataTypeManager} for an existing database after - * {@link #getName()} and {@link #getPath()} can be invoked safely. In addition, it - * may be appropriate to use {@link #getWarning() check for warnings} prior to use. - * - * @param packedDbfile packed datatype archive file (i.e., *.gdt resource). - * @param openMode open mode CREATE, READ_ONLY or UPDATE - * @param monitor the progress monitor - * @throws IOException a low-level IO error. This exception may also be thrown - * when a version error occurs (cause is VersionException). - * @throws CancelledException if task cancelled - */ - protected StandAloneDataTypeManager(ResourceFile packedDbfile, OpenMode openMode, - TaskMonitor monitor) throws IOException, CancelledException { - super(packedDbfile, openMode, monitor); - initTransactionState(); - } - - /** - * Constructor for a data-type manager using a specified DBHandle. - *
- * NOTE: {@link #logWarning()} should be invoked immediately after - * instantiating a {@link StandAloneDataTypeManager} for an existing database after - * {@link #getName()} and {@link #getPath()} can be invoked safely. In addition, it - * may be appropriate to use {@link #getWarning() check for warnings} prior to use. - * - * @param handle open database handle - * @param openMode open mode CREATE, READ_ONLY or UPDATE - * @param errHandler the database I/O error handler - * @param lock the program synchronization lock - * @param monitor the progress monitor - * @throws CancelledException if task cancelled - * @throws VersionException if the database does not match the expected version. - * @throws IOException if a database I/O error occurs. - */ - protected StandAloneDataTypeManager(DBHandle handle, OpenMode openMode, ErrorHandler errHandler, - Lock lock, TaskMonitor monitor) - throws CancelledException, VersionException, IOException { - super(handle, null, openMode, null, errHandler, lock, monitor); - if (openMode != OpenMode.CREATE && hasDataOrganizationChange(true)) { - handleDataOrganizationChange(openMode, monitor); - } - initTransactionState(); - } - - /** - * Set instance as immutable by disabling use of transactions. Attempts to start a transaction - * will result in a {@link TerminatedTransactionException}. - */ - protected void setImmutable() { - isImmutable = true; - } - - /** - * Get the {@link ArchiveWarning} which may have occured immediately following - * instatiation of this {@link StandAloneDataTypeManager}. {@link ArchiveWarning#NONE} - * will be returned if not warning condition. - * @return warning type. - */ - public ArchiveWarning getWarning() { - return warning; - } - - /** - * Get the detail exception associated with {@link ArchiveWarning#LANGUAGE_NOT_FOUND} or - * {@link ArchiveWarning#COMPILER_SPEC_NOT_FOUND} warning (see {@link #getWarning()}) - * immediately following instatiation of this {@link StandAloneDataTypeManager}. - * @return warning detail exception or null - */ - public Exception getWarningDetail() { - return warningDetail; - } - - /** - * Get a suitable warning message. See {@link #getWarning()} for type and its severity level - * {@link ArchiveWarning#level()}. - * @param includeDetails if false simple message returned, otherwise more details are included. - * @return warning message or null if {@link #getWarning()} is {@link ArchiveWarning#NONE}. - */ - public String getWarningMessage(boolean includeDetails) { - String msg = null; - switch (warning) { - case LANGUAGE_NOT_FOUND: - msg = "Language not found for Archive"; - if (includeDetails) { - msg += " '" + getName() + "': " + warningDetail.getMessage(); - } - break; - case COMPILER_SPEC_NOT_FOUND: - msg = "Compiler specification not found for Archive"; - if (includeDetails) { - msg += " '" + getName() + "': " + warningDetail.getMessage(); - } - break; - case LANGUAGE_UPGRADE_REQURED: - msg = "Language upgrade required for Archive"; - if (includeDetails) { - msg += " '" + getName() + "': " + programArchitectureSummary; - } - break; - case UPGRADED_LANGUAGE_VERSION: - msg = "Upgraded program-architecture for Archive"; - if (includeDetails) { - ProgramArchitecture arch = getProgramArchitecture(); - LanguageDescription languageDescription = - arch.getLanguage().getLanguageDescription(); - msg += " '" + getName() + "'\n Language: " + - languageDescription.getLanguageID() + " Version " + - languageDescription.getVersion() + ".x" + ", CompilerSpec: " + - arch.getCompilerSpec().getCompilerSpecID(); - } - break; - case DATA_ORG_CHANGED: - msg = "Data organization upgrade required for Archive"; - if (includeDetails) { - msg += " '" + getName() + "': " + programArchitectureSummary; - } - break; - default: - break; - } - return msg; - } - - /** - * Due to the supression of error and warning conditions during instantiation this method should - * be invoked at the end of instatiation when {@link #getName()} and {@link #getPath()} are - * ready to be invoked safely. Logging will be performed via {@link Msg}. - */ - protected void logWarning() { - String msg = getWarningMessage(true); - if (msg == null) { - return; - } - switch (warning.level) { - case ERROR: - Msg.error(this, msg); - break; - case WARN: - Msg.warn(this, msg); - break; - default: - Msg.info(this, msg); - break; - } - - } - - @Override - protected void initializeOtherAdapters(OpenMode openMode, TaskMonitor monitor) - throws CancelledException, IOException, VersionException { - - warning = ArchiveWarning.NONE; - if (openMode == OpenMode.CREATE) { - saveDataOrganization(); // save default dataOrg - return; // optional program architecture is set after initialization is complete - } - - // Check for optional program architecture data (LanguageID, etc.) - // The DB data map is also used by the base implementation to store the data organization - DBStringMapAdapter dataMap = getDataMap(false); - if (dataMap == null) { - return; - } - - String languageIdStr = dataMap.get(LANGUAGE_ID); - if (languageIdStr == null) { - return; // assume architecture info is missing - } - LanguageID languageId = new LanguageID(languageIdStr); - CompilerSpecID compilerSpecId = new CompilerSpecID(dataMap.get(COMPILER_SPEC_ID)); - int languageVersion = 1; - try { - languageVersion = Integer.parseInt(dataMap.get(LANGUAGE_VERSION)); - } - catch (Exception e) { - // Ignore - } - - VariableStorageManagerDB variableStorageMgr = null; - if (VariableStorageManagerDB.exists(dbHandle)) { - variableStorageMgr = - new VariableStorageManagerDB(dbHandle, null, openMode, errHandler, lock, monitor); - } - - programArchitectureSummary = - getProgramArchitectureSummary(languageId, languageVersion, compilerSpecId); - - Language language = null; - LanguageVersionException languageVersionExc = null; - try { - language = DefaultLanguageService.getLanguageService().getLanguage(languageId); - languageVersionExc = LanguageVersionException.check(language, languageVersion, -1); // don't care about minor version - } - catch (LanguageNotFoundException e) { - warning = ArchiveWarning.LANGUAGE_NOT_FOUND; - warningDetail = e; - try { - languageVersionExc = - LanguageVersionException.checkForLanguageChange(e, languageId, languageVersion); - } - catch (LanguageNotFoundException e2) { - // Missing language or language translation - warningDetail = e2; - return; // allow archive to open without error - } - } - - if (languageVersionExc != null && !languageVersionExc.isUpgradable()) { - // Inability to translate language treated like language-not-found - warning = ArchiveWarning.LANGUAGE_NOT_FOUND; - warningDetail = languageVersionExc; - } - else if (languageVersionExc != null) { - warning = ArchiveWarning.LANGUAGE_UPGRADE_REQURED; - languageUpgradeTranslator = languageVersionExc.getLanguageTranslator(); - - // language upgrade required - if (openMode == OpenMode.IMMUTABLE) { - // read-only mode - do not set program architecture - upgrade flag has been set - return; - } - - if (openMode == OpenMode.UPDATE) { - throw languageVersionExc; - } - - // else UPGRADE mode falls-through - language = languageUpgradeTranslator.getNewLanguage(); - compilerSpecId = languageUpgradeTranslator.getNewCompilerSpecID(compilerSpecId); - } - - assert (language != null); - - CompilerSpec compilerSpec; - try { - compilerSpec = language.getCompilerSpecByID(compilerSpecId); - } - catch (CompilerSpecNotFoundException e) { - warning = ArchiveWarning.COMPILER_SPEC_NOT_FOUND; - warningDetail = e; - return; // allow archive to open without error - } - - if (warning == ArchiveWarning.LANGUAGE_UPGRADE_REQURED) { - - if (variableStorageMgr != null) { - variableStorageMgr.setLanguage(languageUpgradeTranslator, monitor); - } - - // update data map with language upgrade info - dataMap.put(LANGUAGE_ID, language.getLanguageID().getIdAsString()); - dataMap.put(LANGUAGE_VERSION, Integer.toString(language.getVersion())); - dataMap.put(COMPILER_SPEC_ID, compilerSpecId.getIdAsString()); - - warning = ArchiveWarning.UPGRADED_LANGUAGE_VERSION; - } - - programArchitectureSummary = null; // not needed - - final Language lang = language; - final CompilerSpec cspec = compilerSpec; - - // Use ProgramAddressFactory to provide stack space - final AddressFactory addrFactory = new ProgramAddressFactory(lang, cspec, s -> null); - - super.setProgramArchitecture(new ProgramArchitecture() { - - @Override - public Language getLanguage() { - return lang; - } - - @Override - public CompilerSpec getCompilerSpec() { - return cspec; - } - - @Override - public AddressFactory getAddressFactory() { - return addrFactory; - } - }, variableStorageMgr, false, monitor); - - if (variableStorageMgr != null) { - variableStorageMgr.setProgramArchitecture(getProgramArchitecture()); - } - } - - @Override - protected void handleDataOrganizationChange(OpenMode openMode, TaskMonitor monitor) - throws LanguageVersionException, CancelledException, IOException { - if (openMode == OpenMode.IMMUTABLE) { - warning = ArchiveWarning.DATA_ORG_CHANGED; - } - super.handleDataOrganizationChange(openMode, monitor); - } - - /** - * Get the program architecture information which has been associated with this - * datatype manager. If {@link #getProgramArchitecture()} returns null this method - * may still return information if the program architecture was set on an archive - * and either {@link #isProgramArchitectureMissing()} or - * {@link #isProgramArchitectureUpgradeRequired()} returns true. - * @return program architecture summary if it has been set - */ - @Override - public String getProgramArchitectureSummary() { - if (programArchitectureSummary != null) { - return programArchitectureSummary; - } - return super.getProgramArchitectureSummary(); - } - - /** - * Delete all program architecture related data in response to an - * architecture change when all related data should be removed. - * @throws IOException if IO error occurs - * @throws CancelledException if task cancelled - */ - private void deleteAllProgramArchitectureData(TaskMonitor monitor) - throws IOException, CancelledException { - - clearCustomStorageUse(monitor); - - DBStringMapAdapter dataMap = getDataMap(false); - if (dataMap != null) { - dataMap.delete(LANGUAGE_ID); - dataMap.delete(LANGUAGE_VERSION); - dataMap.delete(COMPILER_SPEC_ID); - } - - VariableStorageManagerDB.delete(dbHandle); - - warning = ArchiveWarning.NONE; - programArchitectureSummary = null; - } - - private VariableStorageManagerDB createProgramArchitectureData( - ProgramArchitecture programArchitecture, VariableStorageManagerDB variableStorageMgr) - throws IOException { - Language newLanguage = programArchitecture.getLanguage(); - LanguageID newLanguageId = newLanguage.getLanguageID(); - CompilerSpec newCompilerSpec = programArchitecture.getCompilerSpec(); - CompilerSpecID newCmpilerSpecID = newCompilerSpec.getCompilerSpecID(); - - DBStringMapAdapter dataMap = getDataMap(true); - dataMap.put(LANGUAGE_ID, newLanguageId.getIdAsString()); - dataMap.put(COMPILER_SPEC_ID, newCmpilerSpecID.getIdAsString()); - dataMap.put(LANGUAGE_VERSION, Integer.toString(newLanguage.getVersion())); // major version only - - if (variableStorageMgr == null) { // TODO: may re-use if translation performed - try { - variableStorageMgr = new VariableStorageManagerDB(dbHandle, null, OpenMode.CREATE, - errHandler, lock, TaskMonitor.DUMMY); - variableStorageMgr.setProgramArchitecture(programArchitecture); - } - catch (VersionException | CancelledException e) { - throw new AssertException(e); // unexpected - } - } - - variableStorageMgr.setProgramArchitecture(programArchitecture); - - warning = ArchiveWarning.NONE; - programArchitectureSummary = null; - - return variableStorageMgr; - } - - /** - * Indicates that an program architecture upgrade is required in order - * to constitute associated data. If true, the associated archive - * must be open for update to allow the upgrade to complete, or a new - * program architecture may be set/cleared if such an operation is supported. - * @return true if a program architecture upgrade is required, else false - */ - public boolean isProgramArchitectureUpgradeRequired() { - return warning == ArchiveWarning.LANGUAGE_UPGRADE_REQURED; - } - - /** - * Indicates that a failure occured establishing the program architecture - * for the associated archive. - * @return true if a failure occured establishing the program architecture - */ - public boolean isProgramArchitectureMissing() { - return warning == ArchiveWarning.LANGUAGE_NOT_FOUND || - warning == ArchiveWarning.COMPILER_SPEC_NOT_FOUND; - } - - /** - * Clear the program architecture setting and all architecture-specific data from this archive. - * Archive will revert to using the default {@link DataOrganization}. - * Archive must be open for update for this method to be used. - * @param monitor task monitor - * @throws CancelledException if task cancelled. If thrown, this data type manager is no longer - * stable and should be closed without saving. - * @throws IOException if IO error occurs - * @throws LockException failure if exclusive access is required - * @throws UnsupportedOperationException if architecture change is not permitted by - * implementation (e.g., {@link BuiltInDataTypeManager}). - */ - public void clearProgramArchitecture(TaskMonitor monitor) - throws CancelledException, IOException, LockException { - try (Lock.Closeable c = lock.write()) { - - if (!isArchitectureChangeAllowed()) { - throw new UnsupportedOperationException( - "Program-architecture change not permitted"); - } - - if (!dbHandle.canUpdate()) { - throw new ReadOnlyException("Read-only Archive: " + getName()); - } - - if (getProgramArchitecture() == null && !isProgramArchitectureMissing()) { - return; - } - - Msg.info(this, "Removing program-architecture for Archive: " + getName()); - - int txId = startTransaction("Remove Program Architecture"); - try { - if (!isArchitectureChangeAllowed()) { - throw new UnsupportedOperationException( - "Program-architecture change not permitted"); - } - deleteAllProgramArchitectureData(monitor); - - super.setProgramArchitecture((ProgramArchitecture) null, null, true, monitor); - } - finally { - // TODO: ensure state is restored if transaction rollback/cancel occurs - endTransaction(txId, !monitor.isCancelled()); - } - - defaultListener.programArchitectureChanged(this); - invalidateCache(); - } - } - - public static enum LanguageUpdateOption { - /** - * All existing storage data should be cleared - */ - CLEAR, - /** - * An attempt should be made to translate from old-to-new language. - * This has limitations (i.e., similar architecture) and may result in - * poor register mappings. - */ - TRANSLATE, - /** - * Variable storage data will be retained as-is but may not de-serialize - * properly when used. - */ - UNCHANGED // TODO: Need to test to see if this option is safe and useable - } - - /** - * Establish the program architecture for this datatype manager. The current setting can be - * determined from {@link #getProgramArchitecture()}. Archive must be open for update for - * this method to be used. - * @param language language - * @param compilerSpecId compiler specification ID defined by the language. - * @param updateOption indicates how variable storage data should be transitioned. If {@link #isProgramArchitectureMissing()} - * is true and {@link LanguageUpdateOption#TRANSLATE} specified, the translator will be based on whatever language version can - * be found. In this situation it may be best to force a {@link LanguageUpdateOption#CLEAR}. - * @param monitor task monitor (cancel not permitted to avoid corrupt state) - * @throws CompilerSpecNotFoundException if invalid compilerSpecId specified for language - * @throws LanguageNotFoundException if current language is not found (if required for data transition) - * @throws IOException if IO error occurs - * @throws CancelledException if task cancelled. If thrown, this data type manager is no longer - * stable and should be closed without saving. - * @throws LockException failure if exclusive access is required - * @throws UnsupportedOperationException if architecture change is not permitted - * @throws IncompatibleLanguageException if translation requested but not possible due to incompatible language architectures - */ - public void setProgramArchitecture(Language language, CompilerSpecID compilerSpecId, - LanguageUpdateOption updateOption, TaskMonitor monitor) - throws CompilerSpecNotFoundException, LanguageNotFoundException, IOException, - CancelledException, LockException, UnsupportedOperationException, - IncompatibleLanguageException { - - try (Lock.Closeable c = lock.write()) { - - if (!isArchitectureChangeAllowed()) { - throw new UnsupportedOperationException( - "Program-architecture change not permitted"); - } - - if (readOnlyMode) { - throw new ReadOnlyException("Read-only Archive: " + getName()); - } - - Msg.info(this, - "Updating program-architecture for Archive: " + getName() + "\n Language: " + - language.getLanguageID() + " version " + language.getVersion() + ".x" + - ", CompilerSpec: " + compilerSpecId); - - CompilerSpec compilerSpec = language.getCompilerSpecByID(compilerSpecId); - - // This type of datatype manager only uses VariableStorageManagerDB - VariableStorageManagerDB variableStorageMgr = - (VariableStorageManagerDB) getVariableStorageManager(); - - int txId = startTransaction("Set Program Architecture"); - try { - ProgramArchitectureTranslator translator = null; - - ProgramArchitecture oldArch = getProgramArchitecture(); - if (oldArch != null || isProgramArchitectureMissing()) { - - if (updateOption == LanguageUpdateOption.CLEAR) { - deleteAllProgramArchitectureData(monitor); - variableStorageMgr = null; - } - else if (isProgramArchitectureMissing()) { - - assert (variableStorageMgr == null); - - if (updateOption == LanguageUpdateOption.TRANSLATE) { - // Go out on a limb and use any version of old language if available - DBStringMapAdapter dataMap = getDataMap(false); - LanguageID oldLanguageId = - new LanguageID(getDataMap(false).get(LANGUAGE_ID)); - CompilerSpecID oldCompilerSpecId = - new CompilerSpecID(dataMap.get(COMPILER_SPEC_ID)); - translator = new ProgramArchitectureTranslator(oldLanguageId, -1, - oldCompilerSpecId, language, compilerSpecId); - } - - if (VariableStorageManagerDB.exists(dbHandle)) { - try { - variableStorageMgr = new VariableStorageManagerDB(dbHandle, null, - OpenMode.UPDATE, errHandler, lock, monitor); - } - catch (VersionException e) { - throw new IOException( - "Unexpected version error for VariableStorageManagerDB"); - } - } - } - else if (updateOption == LanguageUpdateOption.TRANSLATE) { - translator = new ProgramArchitectureTranslator(oldArch.getLanguage(), - oldArch.getCompilerSpec().getCompilerSpecID(), language, - compilerSpecId); - } - - if (translator != null && variableStorageMgr != null) { - variableStorageMgr.setLanguage(translator, monitor); - } - } - - // Use ProgramAddressFactory to provide stack space - final AddressFactory addrFactory = - new ProgramAddressFactory(language, compilerSpec, s -> null); - - ProgramArchitecture programArchitecture = new ProgramArchitecture() { - - @Override - public Language getLanguage() { - return language; - } - - @Override - public CompilerSpec getCompilerSpec() { - return compilerSpec; - } - - @Override - public AddressFactory getAddressFactory() { - return addrFactory; - } - }; - - variableStorageMgr = - createProgramArchitectureData(programArchitecture, variableStorageMgr); - - super.setProgramArchitecture(programArchitecture, variableStorageMgr, true, - monitor); - } - finally { - // TODO: ensure state is restored if transaction rollback/cancel occurs - endTransaction(txId, !monitor.isCancelled()); - } - - defaultListener.programArchitectureChanged(this); - } - finally { - invalidateCache(); - } - } - - /** - * Set the architecture-specific details associated with a new datatype manager. - * This method is intended to be used during instantiation of derived implementations. - * @param programArchitecture program architecture details (required) - * @param variableStorageMgr variable storage manager. Must be null. - * @param store if true database update will occur and datatypes will be updated if - * any change to the data organization is detected (a stored copy may be used to - * detect this condition). This should never be passed as true if opened read-only. - * If true and no variable storage is specified it will be created. - * @param monitor task monitor - * @throws IOException if IO error occurs - * @throws CancelledException if task cancelled - * @throws UnsupportedOperationException if language was previously set - */ - @Override - protected void setProgramArchitecture(ProgramArchitecture programArchitecture, - VariableStorageManager variableStorageMgr, boolean store, TaskMonitor monitor) - throws IOException, CancelledException { - - // TODO: Determine if cancelling can leave in bad state - - if (programArchitecture == null) { - throw new IllegalArgumentException("ProgramArchitecture must be specified"); - } - if (variableStorageMgr != null) { - throw new IllegalArgumentException("VariableStorageManager may not be specified"); - } - - if (getProgramArchitecture() != null || isProgramArchitectureUpgradeRequired() || - isProgramArchitectureMissing()) { - throw new UnsupportedOperationException( - "Program-architecture change not permitted with this method"); - } - - if (store) { - variableStorageMgr = createProgramArchitectureData(programArchitecture, null); - } - - // super handles possible change to data organization and update if store is true - super.setProgramArchitecture(programArchitecture, variableStorageMgr, store, monitor); - } - - /** - * Determine if a program architecture change is permitted - * @return true if change allowed else false if disallowed - */ - protected boolean isArchitectureChangeAllowed() { - return true; - } - - @Override - public String getName() { - return name; - } - - @Override - public void setName(String name) throws InvalidNameException { - if (name == null || name.length() == 0) { - throw new InvalidNameException("Name is invalid: " + name); - } - this.name = name; - - defaultListener.categoryRenamed(this, CategoryPath.ROOT, CategoryPath.ROOT); - } - - protected void initTransactionState() { - clearUndo(); - } - - @Override - public Transaction openTransaction(String description) throws IllegalStateException { - return new Transaction() { - - private int txId = startTransaction(description); - - @Override - protected boolean endTransaction(boolean commit) { - StandAloneDataTypeManager.this.endTransaction(txId, commit); - return commitTransaction; - } - - @Override - public boolean isSubTransaction() { - return true; - } - }; - } - - @Override - public synchronized int startTransaction(String description) { - if (isImmutable) { - throw new TerminatedTransactionException("Transaction not permitted: read-only"); - } - if (transaction == null) { - transaction = dbHandle.startTransaction(); - transactionName = description; - commitTransaction = true; - } - transactionCount++; - return transaction.intValue(); - } - - /** - * Get the number of active datatype manager transactions - * @return number of active datatype manager transactions - */ - protected int getTransactionCount() { - return transactionCount; - } - - @Override - public boolean endTransaction(int transactionID, boolean commit) { - boolean restored = false; - synchronized (this) { - if (transaction == null) { - throw new IllegalStateException("No Transaction Open"); - } - if (transaction.intValue() != transactionID) { - throw new IllegalArgumentException( - "Transaction id does not match current transaction"); - } - if (!commit) { - commitTransaction = false; - } - if (--transactionCount == 0) { - try { - if (dbHandle.endTransaction(transaction.longValue(), commitTransaction)) { - redoList.clear(); - undoList.addLast(transactionName); - if (undoList.size() > NUM_UNDOS) { - undoList.removeFirst(); - } - } - else if (!commitTransaction) { - restored = true; - } - transaction = null; - } - catch (IOException e) { - dbError(e); - } - } - } - if (restored) { - invalidateCache(); - notifyRestored(); - } - return transaction == null && !restored; - } - - public void undo() { - synchronized (this) { - if (!canUndo()) { - return; - } - try { - dbHandle.undo(); - redoList.addLast(undoList.removeLast()); - } - catch (IOException e) { - dbError(e); - } - } - invalidateCache(); - notifyRestored(); - } - - public void redo() { - synchronized (this) { - if (!canRedo()) { - return; - } - try { - dbHandle.redo(); - undoList.addLast(redoList.removeLast()); - } - catch (IOException e) { - dbError(e); - } - } - invalidateCache(); - notifyRestored(); - } - - /** - * Clear undo/redo stack. - *
- * NOTE: It is important that this always be invoked following any save operation that - * compacts the checkpoints within the database {@link BufferMgr}. - */ - protected synchronized void clearUndo() { - undoList.clear(); - redoList.clear(); - - // Flatten all checkpoints then restore undo stack size - dbHandle.setMaxUndos(0); - dbHandle.setMaxUndos(NUM_UNDOS); - } - - /** - * Determine if there is a transaction previously undone (see {@link #undo()}) that can be - * redone (see {@link #redo()}). - * - * @return true if there is a transaction previously undone that can be redone, else false - */ - public synchronized boolean canRedo() { - return transaction == null && !redoList.isEmpty(); - } - - /** - * Determine if there is a previous transaction that can be reverted/undone (see {@link #undo()}). - * - * @return true if there is a previous transaction that can be reverted/undone, else false. - */ - public synchronized boolean canUndo() { - return transaction == null && !undoList.isEmpty(); - } - - /** - * Get the transaction name that is available for {@link #redo()} (see {@link #canRedo()}). - * @return transaction name that is available for {@link #redo()} or empty String. - */ - public synchronized String getRedoName() { - if (canRedo()) { - return redoList.getLast(); - } - return ""; - } - - /** - * Get the transaction name that is available for {@link #undo()} (see {@link #canUndo()}). - * @return transaction name that is available for {@link #undo()} or empty String. - */ - public synchronized String getUndoName() { - if (canUndo()) { - return undoList.getLast(); - } - return ""; - } - - /** - * Get all transaction names that are available within the {@link #undo()} stack. - * - * @return all transaction names that are available within the {@link #undo()} stack. - */ - public synchronized List getAllUndoNames() { - if (canUndo()) { - return new ArrayList<>(undoList); - } - return List.of(); - } - - /** - * Get all transaction names that are available within the {@link #redo()} stack. - * - * @return all transaction names that are available within the {@link #redo()} stack. - */ - public synchronized List getAllRedoNames() { - if (canRedo()) { - return new ArrayList<>(redoList); - } - return List.of(); - } - - @Override - public void flushEvents() { - // do nothing - } - - @Override - protected void replaceDataTypesUsed(Map dataTypeReplacementMap) { - // do nothing - } - - @Override - protected void deleteDataTypesUsed(Set deletedIds) { - // do nothing - } - - @Override - public synchronized void close() { - if (dbHandle.isTransactionActive()) { - Msg.error(this, "DTM closed with active transaction", - new RuntimeException("DTM closed with active transaction")); - } - undoList.clear(); - redoList.clear(); - if (!dbHandle.isClosed()) { - dbHandle.close(); - } - super.close(); - } - - @Override - public void finalize() { - close(); - } - - @Override - protected String getDomainFileID() { - return null; - } - - /** - * Get the path name associated with the storage of this stand alone - * datatype manager. - * @return path name or null if not applicable - */ - @Override - public String getPath() { - return null; - } - - @Override - public ArchiveType getType() { - return ArchiveType.TEMPORARY; - } - - /** - * Update custom storage for function definitions to be unassigned. - * @param monitor task monitor - * @throws CancelledException if task cancelled - */ - private void clearCustomStorageUse(TaskMonitor monitor) throws CancelledException { - - // Get copy of all function defs to avoid concurrent modification of underlaying table - ImmutableList defs = ImmutableList.copyOf(getAllFunctionDefinitions()); - - monitor.initialize(defs.size()); - monitor.setMessage("Clear custom storage use..."); - -// for (FunctionDefinition def : ImmutableList.copyOf(getAllFunctionDefinitions())) { - monitor.checkCancelled(); -// monitor.incrementProgress(1); -// -// // TODO: update function definition -// if (def.hasCustomStorage()) { -// -// -// } -// } - + super(name, dataOrganzation); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/ArchiveWarning.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/ArchiveWarning.java new file mode 100644 index 0000000000..b7d8d0bb95 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/ArchiveWarning.java @@ -0,0 +1,88 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.dtarchive; + +import ghidra.program.model.lang.*; + +public enum ArchiveWarning { + + /** + * {@link #NONE} indicates a normal archive condition + */ + NONE(ArchiveWarningLevel.INFO), + + /** + * {@link #UPGRADED_LANGUAGE_VERSION} indicates an archive which has been open for update + * was upgraded to a newer language version. This is expected when the {@link Language} + * required by the associated {@link ProgramArchitecture} has a major version change + * which involves significant {@link Register} changes. Sharing an upgraded archive + * may impact others who do not have access to the updated {@link Language} module. + */ + UPGRADED_LANGUAGE_VERSION(ArchiveWarningLevel.INFO), + + /** + * {@link #LANGUAGE_NOT_FOUND} indicates the {@link Language} or its appropriate version, + * required by the associated {@link ProgramArchitecture}, was not found or encountered + * a problem being loaded. The {@link StandAloneDataTypeArchive#getWarningDetail()} may provide + * additional insight to the underlying cause. + */ + LANGUAGE_NOT_FOUND(ArchiveWarningLevel.ERROR), + + /** + * {@link #COMPILER_SPEC_NOT_FOUND} indicates the {@link CompilerSpec}, + * required by the associated {@link ProgramArchitecture}, was not found or encountered + * a problem being loaded. The {@link StandAloneDataTypeArchive#getWarningDetail()} may provide + * additional insight to the underlying cause. This condition can only occur if the + * required {@link Language} was found. + */ + COMPILER_SPEC_NOT_FOUND(ArchiveWarningLevel.ERROR), + + /** + * {@link #LANGUAGE_UPGRADE_REQURED} indicates an archive which has been open read-only + * requires an upgraded to a newer language version. This is expected when the + * {@link Language} required by the associated {@link ProgramArchitecture} has a major + * version change within the current installation. Major version changes for a + * {@link Language} rarely occur but are required when significant {@link Register} + * or addressing changes have been made. Upgrading a shared archive may impact others + * who do not have access to the updated {@link Language} module and should be + * coordinated with others who may be affected. + */ + LANGUAGE_UPGRADE_REQURED(ArchiveWarningLevel.WARN), + + /** + * {@link #DATA_ORG_CHANGED} indicates an archive which has been open read-only + * requires an upgraded to adjust for changes in the associated data organization. + */ + DATA_ORG_CHANGED(ArchiveWarningLevel.WARN); + + public final ArchiveWarningLevel level; + + private ArchiveWarning(ArchiveWarningLevel level) { + this.level = level; + } + + /** + * Get the warning level + * @return warning level + */ + public ArchiveWarningLevel level() { + return level; + } + + public static enum ArchiveWarningLevel { + INFO, WARN, ERROR; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/DataTypeArchive.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/DataTypeArchive.java new file mode 100644 index 0000000000..b86e58d93d --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/DataTypeArchive.java @@ -0,0 +1,176 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.dtarchive; + +import java.io.IOException; + +import ghidra.framework.store.LockException; +import ghidra.program.model.data.*; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.IncompatibleLanguageException; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * Common interface of {@link DataTypeStore}s that are not a Program. + *

+ * The main difference between a DataTypeStore that is a program and those that are archives is the + * idea of a {@link ProgramArchitecture}. Programs naturally have a program architecture since they + * always have an underlying processor and compiler spec. Archives, on the other hand, have + * methods to handle when their associated program architecture is changed. Such a change + * may require internal upgrading and bulk changes to the archive's data type store. + */ +public interface DataTypeArchive extends DataTypeStore { + + /** + * Returns true if this DataTypeManager can be modified. + * @return true if this DataTypeMangaer can be modified. + */ + public boolean isUpdatable(); + + /** + * Indicates that a program architecture upgrade is required in order + * to constitute associated data. If true, the associated archive + * must be open for update to allow the upgrade to complete, or a new + * program architecture may be set/cleared if such an operation is supported. + * @return true if a program architecture upgrade is required, else false + */ + public boolean isProgramArchitectureUpgradeRequired(); + + /** + * Indicates that a failure occurred establishing the program architecture + * for the associated archive. + * @return true if a failure occurred establishing the program architecture + */ + public boolean isProgramArchitectureMissing(); + + /** + * Establish the program architecture for this archive. The current setting can be + * determined from {@link #getProgramArchitecture()}. Archive must be open for update for + * this method to be used. + *

+ * If there is no program architecture set, then a failure occurred. If the updateOption is + * {@link LanguageUpdateOption#TRANSLATE}, the translator will be based on whatever language + * version can be found. In this situation it may be best to force a + * {@link LanguageUpdateOption#CLEAR}. + * + * @param language processor language + * @param compilerSpecId compiler specification ID defined by the language. + * @param updateOption indicates how variable storage data should be transitioned. + * @param monitor task monitor (cancel not permitted to avoid corrupt state) + * @throws CompilerSpecNotFoundException if invalid compilerSpecId specified for language + * @throws LanguageNotFoundException if current language is not found (if required for data + * transition) + * @throws IOException if IO error occurs + * @throws CancelledException if task cancelled. If thrown, this data type manager is no longer + * stable and should be closed without saving. + * @throws LockException failure if exclusive access is required + * @throws UnsupportedOperationException if architecture change is not permitted + * @throws IncompatibleLanguageException if translation requested but not possible due to + * incompatible language architectures + */ + public void setProgramArchitecture(Language language, CompilerSpecID compilerSpecId, + LanguageUpdateOption updateOption, TaskMonitor monitor) + throws CompilerSpecNotFoundException, LanguageNotFoundException, IOException, + CancelledException, LockException, UnsupportedOperationException, + IncompatibleLanguageException; + + /** + * Establish the program architecture for this archive. The current setting can be + * determined from {@link #getProgramArchitecture()}. Archive must be open for update for + * this method to be used. + * @param languageId valid processor language ID (see appropriate *.ldefs file for defined IDs) + * @param compilerSpecId valid compiler spec ID which corresponds to the language ID. + * @param updateOption indicates how variable storage data should be transitioned. If {@link #isProgramArchitectureMissing()} + * is true and {@link LanguageUpdateOption#TRANSLATE} specified, the translator will be based on whatever language version can + * be found. In this situation it may be best to force a {@link LanguageUpdateOption#CLEAR}. + * @param monitor task monitor (cancel not permitted to avoid corrupt state) + * @throws CompilerSpecNotFoundException if invalid compilerSpecId specified for language + * @throws LanguageNotFoundException if current language is not found (if required for data transition) + * @throws IOException if IO error occurs + * @throws CancelledException if task cancelled. If thrown, this data type manager is no longer + * stable and should be closed without saving. + * @throws LockException failure if exclusive access is required + * @throws UnsupportedOperationException if architecture change is not permitted + * @throws IncompatibleLanguageException if translation requested but not possible due to incompatible language architectures + */ + public void setProgramArchitecture(LanguageID languageId, CompilerSpecID compilerSpecId, + LanguageUpdateOption updateOption, TaskMonitor monitor) + throws CompilerSpecNotFoundException, LanguageNotFoundException, IOException, + CancelledException, LockException, UnsupportedOperationException, + IncompatibleLanguageException; + + /** + * Clear the program architecture setting and all architecture-specific data from this archive. + * Archive will revert to using the default {@link DataOrganization}. + * Archive must be open for update for this method to be used. + * @param monitor task monitor + * @throws CancelledException if task cancelled. If thrown, this data type manager is no longer + * stable and should be closed without saving. + * @throws IOException if IO error occurs + * @throws LockException failure if exclusive access is required + * @throws UnsupportedOperationException if architecture change is not permitted by + * implementation (e.g., {@link BuiltInDataTypeManager}). + */ + public void clearProgramArchitecture(TaskMonitor monitor) + throws CancelledException, IOException, LockException; + + /** + * Get the {@link ArchiveWarning} which may have occured immediately following + * instantiation of this {@link StandAloneDataTypeManager}. {@link ArchiveWarning#NONE} + * will be returned if not warning condition. + * @return warning type. + */ + public ArchiveWarning getWarning(); + + /** + * Get the detail exception associated with {@link ArchiveWarning#LANGUAGE_NOT_FOUND} or + * {@link ArchiveWarning#COMPILER_SPEC_NOT_FOUND} warning (see {@link #getWarning()}) + * immediately following instantiation of this {@link StandAloneDataTypeManager}. + * @return warning detail exception or null + */ + public Exception getWarningDetail(); + + /** + * Get a suitable warning message. See {@link #getWarning()} for type and its severity level + * {@link ArchiveWarning#level()}. + * @param includeDetails if false simple message returned, otherwise more details are included. + * @return warning message or null if {@link #getWarning()} is {@link ArchiveWarning#NONE}. + */ + public String getWarningMessage(boolean includeDetails); + + /** + * Specifies how to handle a language update when setting the program architecture. + */ + public static enum LanguageUpdateOption { + /** + * All existing storage data should be cleared + */ + CLEAR, + /** + * An attempt should be made to translate from old-to-new language. + * This has limitations (i.e., similar architecture) and may result in + * poor register mappings. + */ + TRANSLATE, + /** + * Variable storage data will be retained as-is but may not de-serialize + * properly when used. + */ + UNCHANGED // Note: Is this option safe? + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/DataTypeStore.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/DataTypeStore.java new file mode 100644 index 0000000000..d31b184864 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/DataTypeStore.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.dtarchive; + +import ghidra.framework.model.DomainObject; +import ghidra.program.model.data.*; +import ghidra.program.model.lang.ProgramArchitecture; + +/** + * A DatatypeStore provides a collection of {@link DataType}s and related + * resources. The associated {@link DataTypeManager} instance is provided to facilitate + * queries and management of the {@link DataType}s. + */ +public interface DataTypeStore extends DomainObject { + + /** + * {@return the data type manager for this archive} + */ + public DataTypeManager getDataTypeManager(); + + /** + * {@return the type of the archive (or if it is a program)} + */ + public ArchiveType getArchiveType(); + + /** + * Get the optional program architecture details associated with this archive + * @return program architecture details or null if none + */ + public ProgramArchitecture getProgramArchitecture(); + + /** + * Get the program architecture information which has been associated with this + * datatype manager. If {@link #getProgramArchitecture()} returns null this method + * may still return information if the program architecture was set on an archive + * and conditions exist which prevent architecture from taking affect. + * @return program architecture summary if it has been set, else null + */ + public String getProgramArchitectureSummary(); + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/FileDataTypeArchive.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/FileDataTypeArchive.java new file mode 100644 index 0000000000..c5a0f83621 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/FileDataTypeArchive.java @@ -0,0 +1,90 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.dtarchive; + +import java.io.File; +import java.io.IOException; + +import generic.jar.ResourceFile; +import ghidra.util.UniversalID; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.DuplicateFileException; +import ghidra.util.filechooser.ExtensionFileFilter; +import ghidra.util.filechooser.GhidraFileFilter; +import ghidra.util.task.TaskMonitor; + +/** + * {@link FileDataTypeArchive} corresponds to a stand-alone datatype archive that is stored + * within a packed {@link File} on a native filesystem. Read-only archives may also be accessed + * as a {@link ResourceFile} contained within a Java Archive (jar) resource. + */ +public interface FileDataTypeArchive extends PersistentDataTypeArchive { + + /** + * File extension for a datatype archive file. "gdt" + */ + public final static String EXTENSION = "gdt"; + + /** + * Filename suffix for a datatype archive file. ".gdt" + */ + public final static String SUFFIX = "." + EXTENSION; + + /** + * File archive chooser filter + */ + public static final GhidraFileFilter GDT_FILEFILTER = + ExtensionFileFilter.forExtensions("Ghidra Data Type Files", EXTENSION); + + /** + * Saves the data type manager to the given file + * @param outputFile the new output file + * @param monitor the TaskMonitor that can be used to cancel the operation + * @throws DuplicateFileException if output file already exists + * @throws IOException if IO error occurs + */ + public void saveAs(File outputFile, TaskMonitor monitor) + throws DuplicateFileException, IOException, CancelledException; + + /** + * Saves the data type manager to the given file with a specific databaseId. + * NOTE: This method is intended for use in transforming one archive database to + * match another existing archive database. + * @param outputFile the new output file + * @param newUniversalId the new id to use + * @throws DuplicateFileException if output file already exists + * @throws IOException if IO error occurs + */ + public void saveAs(File outputFile, UniversalID newUniversalId) + throws DuplicateFileException, IOException; + + /** + * {@return the {@link ResourceFile} that is where the data for this archive is stored} + */ + public ResourceFile getFile(); + + /** + * Deletes this archive (deletes the underlying storage database file) + * @throws IOException if the file can't be deleted + */ + public void delete() throws IOException; + + /** + * {@return the UniversalId associated with this archive} + */ + public UniversalID getUniversalID(); + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/PersistentDataTypeArchive.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/PersistentDataTypeArchive.java new file mode 100644 index 0000000000..84c636167a --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/PersistentDataTypeArchive.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.dtarchive; + +import java.io.IOException; + +import ghidra.program.model.data.ArchiveDataTypeManager; +import ghidra.program.model.data.DataType; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * {@link PersistentDataTypeArchive} provides a collection of {@link DataType}s independent of any + * Program and have their own storage mechanism to persist the collection. The associated + * {@link ArchiveDataTypeManager} instance is provided to facilitate queries and management of + * the {@link DataType}s. + */ +public interface PersistentDataTypeArchive extends DataTypeArchive { + + @Override + public void release(Object consumer); + + @Override + public boolean addConsumer(Object consumer); + + /** + * Save the category to source file. + * @param monitor the TaskMonitor to use to monitor/cancel the operation + * @throws IOException if IO error occurs + */ + @Override + public void save(String comment, TaskMonitor monitor) throws IOException, CancelledException; +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/ProjectDataTypeArchive.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/ProjectDataTypeArchive.java new file mode 100644 index 0000000000..f615a8aa6e --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/ProjectDataTypeArchive.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.dtarchive; + +import ghidra.program.model.listing.DataTypeArchiveChangeSet; + +/** + * A {@link ProjectDataTypeArchive} is a DataTypeArchive that is stored within a + * Ghidra project. + */ +public interface ProjectDataTypeArchive extends PersistentDataTypeArchive { + + /** + * Get the project datatype archive changes since the last save as a set of addresses. + * @return set of changed addresses within program. + */ + public DataTypeArchiveChangeSet getChanges(); + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/merge/DataTypeManagerOwner.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/TransientDataTypeArchive.java similarity index 59% rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/merge/DataTypeManagerOwner.java rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/TransientDataTypeArchive.java index 66aa35082b..62181ffa45 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/merge/DataTypeManagerOwner.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/dtarchive/TransientDataTypeArchive.java @@ -1,28 +1,34 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.merge; +package ghidra.program.model.dtarchive; -import ghidra.program.model.data.DataTypeManager; +import ghidra.program.database.data.TransientDataTypeManager; -public interface DataTypeManagerOwner { +/** + * DataTypeStores that are are for temporary (scratch-pad) storing of datatypes and their + * relationships. + */ +public interface TransientDataTypeArchive extends DataTypeArchive { + + @Override + public TransientDataTypeManager getDataTypeManager(); /** - * Gets the associated data type manager. - * @return the data type manager. + * Closes this archive. */ - public DataTypeManager getDataTypeManager(); + public void close(); + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ProgramArchitecture.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ProgramArchitecture.java index e38ebb7139..1a40e9d849 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ProgramArchitecture.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ProgramArchitecture.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -54,4 +54,32 @@ public interface ProgramArchitecture { getCompilerSpec().getCompilerSpecID()); } + /** + * {@return a summary of this program architecture} + */ + public default String getSummary() { + Language language = getLanguage(); + CompilerSpec compiler = getCompilerSpec(); + LanguageID languageId = language.getLanguageID(); + int version = language.getVersion(); + CompilerSpecID compilerId = compiler.getCompilerSpecID(); + return getSummary(languageId, version, compilerId); + } + + /** + * {@return a summary of the program architecture for the given parameters} + * @param languageId the the processor language ID + * @param languageVersion the processor language version + * @param compilerSpecId the compiler specification ID + */ + public static String getSummary(LanguageID languageId, int languageVersion, + CompilerSpecID compilerSpecId) { + StringBuilder buf = new StringBuilder(); + buf.append(languageId.getIdAsString()); + buf.append(" / "); + buf.append(compilerSpecId.getIdAsString()); + return buf.toString(); + + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataTypeArchive.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataTypeArchive.java deleted file mode 100644 index ef347c544f..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataTypeArchive.java +++ /dev/null @@ -1,68 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.program.model.listing; - -import java.util.Date; - -import ghidra.program.model.data.DataTypeManagerDomainObject; -import ghidra.program.model.data.StandAloneDataTypeManager; - -/** - * This interface represents the main entry point into an object which - * stores all information relating to a single data type archive. - */ -public interface DataTypeArchive extends DataTypeManagerDomainObject { - - /** Name of data type archive information property list */ - public static final String DATA_TYPE_ARCHIVE_INFO = "Data Type Archive Information"; - /** Name of data type archive settings property list */ - public static final String DATA_TYPE_ARCHIVE_SETTINGS = "Data Type Archive Settings"; - /** Name of date created property */ - public static final String DATE_CREATED = "Date Created"; - /** Name of ghidra version property */ - public static final String CREATED_WITH_GHIDRA_VERSION = "Created With Ghidra Version"; - /** A date from January 1, 1970 */ - public static final Date JANUARY_1_1970 = new Date(0); - - /** - * {@return the associated standalone data type manager.} - */ - @Override - public StandAloneDataTypeManager getDataTypeManager(); - - /** - * {@return the default pointer size as it may be stored within the data type archive.} - */ - public int getDefaultPointerSize(); - - /** - * {@return the creation date of this data type archive or Jan 1, 1970 if unknown.} - */ - public Date getCreationDate(); - - /** - * Get the data type archive changes since the last save as a set of addresses. - * @return set of changed addresses within program. - */ - public DataTypeArchiveChangeSet getChanges(); - - /** - * Invalidates any caching in a data type archive. - * NOTE: Over-using this method can adversely affect system performance. - */ - public void invalidate(); - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionManager.java index f70442ca70..848bf902ad 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionManager.java @@ -18,7 +18,7 @@ package ghidra.program.model.listing; import java.util.Collection; import java.util.Iterator; -import ghidra.program.database.ManagerDB; +import ghidra.program.database.ProgramDBModule; import ghidra.program.database.function.OverlappingFunctionException; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; @@ -33,7 +33,7 @@ import ghidra.util.task.TaskMonitor; /** * The manager for functions */ -public interface FunctionManager extends ManagerDB { +public interface FunctionManager extends ProgramDBModule { /** * Returns this manager's program diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java index bb969dc78c..4542a0721f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java @@ -19,6 +19,7 @@ import java.util.Date; import java.util.Set; import ghidra.app.util.sourcelanguage.SourceLanguageID; +import ghidra.framework.model.DomainObject; import ghidra.framework.store.LockException; import ghidra.program.database.IntRangeMap; import ghidra.program.database.ProgramOverlayAddressSpace; @@ -26,6 +27,7 @@ import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.*; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.DataTypeStore; import ghidra.program.model.lang.*; import ghidra.program.model.mem.Memory; import ghidra.program.model.pcode.Varnode; @@ -38,6 +40,8 @@ import ghidra.util.InvalidNameException; import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.NotFoundException; import ghidra.util.task.TaskMonitor; +import utility.function.ExceptionalCallback; +import utility.function.ExceptionalSupplier; /** * This interface represents the main entry point into an object which @@ -52,7 +56,7 @@ import ghidra.util.task.TaskMonitor; * For example, the createCodeUnit() method of listing will fail if memory is * undefined at the address where the codeUnit is to be created. */ -public interface Program extends DataTypeManagerDomainObject, ProgramArchitecture { +public interface Program extends DomainObject, ProgramArchitecture, DataTypeStore { public static final String ANALYSIS_PROPERTIES = "Analyzers"; public static final String DISASSEMBLER_PROPERTIES = "Disassembler"; @@ -87,6 +91,20 @@ public interface Program extends DataTypeManagerDomainObject, ProgramArchitectur /** Name of cold entry point addresses property map **/ public static final String COLD_ENTRY_MAP_NAME = "ColdEntries"; + @Override + default ProgramArchitecture getProgramArchitecture() { + return this; + } + + @Override + default String getProgramArchitectureSummary() { + StringBuilder buf = new StringBuilder(); + buf.append(getLanguage().getLanguageID().getIdAsString()); + buf.append(" / "); + buf.append(getCompilerSpec().getCompilerSpecID().getIdAsString()); + return buf.toString(); + } + /** * Get the listing object. * @return the Listing interface to the listing object. @@ -104,6 +122,7 @@ public interface Program extends DataTypeManagerDomainObject, ProgramArchitectur /** * Returns the program's datatype manager. + * @return the datatypeManager */ @Override public ProgramBasedDataTypeManager getDataTypeManager(); @@ -594,4 +613,36 @@ public interface Program extends DataTypeManagerDomainObject, ProgramArchitectur */ public long getUniqueProgramID(); + @Override + public default void withTransaction(String description, + ExceptionalCallback callback) throws E { + int id = startTransaction(description); + try { + callback.call(); + } + finally { + endTransaction(id, true); + } + } + + @Override + public default T withTransaction(String description, + ExceptionalSupplier supplier) throws E { + T t = null; + boolean success = false; + int id = startTransaction(description); + try { + t = supplier.get(); + success = true; + } + finally { + endTransaction(id, success); + } + return t; + } + + @Override + public default String getPath() { + return getDomainFile().getPathname(); + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/DtArchiveChangeRecord.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/DtArchiveChangeRecord.java new file mode 100644 index 0000000000..e3f737efdf --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/DtArchiveChangeRecord.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.util; + +import ghidra.framework.model.DomainObjectChangeRecord; +import ghidra.program.model.dtarchive.DataTypeArchive; + +/** + * Event data for a DomainObjectChangeEvent generated by a DataTypeArchive. + */ +public class DtArchiveChangeRecord extends DomainObjectChangeRecord { + + private DataTypeArchive dataTypeStore; + + public DtArchiveChangeRecord(DataTypeArchive store, DtArchiveEvent eventType, + Object oldValue, + Object newValue) { + super(eventType, oldValue, newValue); + this.dataTypeStore = store; + } + + /** + * Return the archive that is the subject of this change record. + * @return the object affected or null if not applicable + */ + public DataTypeArchive getDataStore() { + return dataTypeStore; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(super.toString()); + if (dataTypeStore != null) { + builder.append(", archive = " + dataTypeStore.getName()); + } + return builder.toString(); + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DomainFileBasedDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/DtArchiveEvent.java similarity index 59% rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DomainFileBasedDataTypeManager.java rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/DtArchiveEvent.java index d75a73b150..bfbd063da8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DomainFileBasedDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/DtArchiveEvent.java @@ -1,28 +1,33 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.program.model.data; +package ghidra.program.util; -import ghidra.framework.model.DomainFile; +import ghidra.framework.model.DomainObjectEventIdGenerator; +import ghidra.framework.model.EventType; /** - * Extends DataTypeManager to provide methods specific to a data type manager stored as a domain file. + * {@link EventType} for when the architecture is changed in a DataTypeArchive. */ -public interface DomainFileBasedDataTypeManager extends FileBasedDataTypeManager { +public enum DtArchiveEvent implements EventType { + ARCHIVE_ARCHITECTURE_CHANGED; - public DomainFile getDomainFile(); + private final int id = DomainObjectEventIdGenerator.next(); + @Override + public int getId() { + return id; + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/database/data/DataTypeUtilitiesTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/database/data/DataTypeUtilitiesTest.java index 13dbbe4585..a0700aaa89 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/database/data/DataTypeUtilitiesTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/database/data/DataTypeUtilitiesTest.java @@ -38,8 +38,8 @@ public class DataTypeUtilitiesTest extends AbstractGenericTest { @Before public void setUp() throws Exception { - dataTypeManager = new StandAloneDataTypeManager("Test"); - dataTypeManager.startTransaction("Test"); + dataTypeManager = new TransientDataTypeManager("Test"); + txID = dataTypeManager.startTransaction("Test"); } @After @@ -47,6 +47,7 @@ public class DataTypeUtilitiesTest extends AbstractGenericTest { if (txID != 0) { dataTypeManager.endTransaction(txID, true); } + dataTypeManager.close(); } @Test diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/EnumTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/EnumTest.java index 5bedbfd670..b50c6bef8e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/EnumTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/EnumTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,6 +22,7 @@ import java.util.NoSuchElementException; import org.junit.*; import generic.test.AbstractGenericTest; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.util.task.TaskMonitor; /** @@ -30,11 +31,18 @@ import ghidra.util.task.TaskMonitor; public class EnumTest extends AbstractGenericTest { private DataTypeManager dataMgr; + private int txId; @Before public void setUp() throws Exception { - dataMgr = new StandAloneDataTypeManager("Test"); - dataMgr.startTransaction(""); + dataMgr = new TransientDataTypeManager("Test"); + txId = dataMgr.startTransaction(""); + } + + @After + public void tearDown() { + dataMgr.endTransaction(txId, false); + dataMgr.close(); } @Test @@ -129,7 +137,7 @@ public class EnumTest extends AbstractGenericTest { Category c = root.createCategory("enumms"); Enum enummDT = (Enum) c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); - Enum copyDT = (Enum) enummDT.clone(null); + Enum copyDT = enummDT.clone(null); assertNotNull(copyDT); Enum c2 = (Enum) root.addDataType(copyDT, DataTypeConflictHandler.DEFAULT_HANDLER); diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FileDataTypeManagerTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FileDataTypeManagerTest.java index 5db5c46761..03b90b7ae4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FileDataTypeManagerTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FileDataTypeManagerTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,7 +24,10 @@ import java.util.ArrayList; import org.junit.*; import generic.test.AbstractGenericTest; -import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; +import ghidra.program.model.dtarchive.ArchiveWarning; +import ghidra.program.model.dtarchive.FileDataTypeArchive; +import ghidra.util.task.TaskMonitor; public class FileDataTypeManagerTest extends AbstractGenericTest { @@ -43,33 +46,37 @@ public class FileDataTypeManagerTest extends AbstractGenericTest { } @Test - public void testCreateAndOpenArchive() { - - FileDataTypeManager dtMgr = null; + public void testCreateAndOpenArchive() throws Exception { + FileDataTypeArchive archive = null; + DataTypeManager dtMgr = null; try { - dtMgr = FileDataTypeManager.createFileArchive(testArchiveFile); + archive = DataTypeArchiveFactory.createFileArchive(testArchiveFile, this); + dtMgr = archive.getDataTypeManager(); assertTrue(dtMgr.isUpdatable()); DataType dt1, dt2; int txId = dtMgr.startTransaction("Add Types"); try { dt1 = - dtMgr.addDataType(new TypedefDataType("T1", ByteDataType.dataType), null).clone( - null); + dtMgr.addDataType(new TypedefDataType("T1", ByteDataType.dataType), null) + .clone( + null); dt2 = - dtMgr.addDataType(new TypedefDataType("T2", ByteDataType.dataType), null).clone( - null); + dtMgr.addDataType(new TypedefDataType("T2", ByteDataType.dataType), null) + .clone( + null); } finally { dtMgr.endTransaction(txId, true); } - assertTrue(dtMgr.isChanged()); - dtMgr.save(); - dtMgr.close(); + assertTrue(archive.isChanged()); + archive.save(null, TaskMonitor.DUMMY); + archive.release(this); dtMgr = null; - dtMgr = FileDataTypeManager.openFileArchive(testArchiveFile, false); - assertEquals(ArchiveWarning.NONE, dtMgr.getWarning()); + archive = DataTypeArchiveFactory.openReadOnly(testArchiveFile, this, TaskMonitor.DUMMY); + dtMgr = archive.getDataTypeManager(); + assertEquals(ArchiveWarning.NONE, archive.getWarning()); assertFalse(dtMgr.isUpdatable()); ArrayList list = new ArrayList<>(); @@ -80,8 +87,11 @@ public class FileDataTypeManagerTest extends AbstractGenericTest { StringBuilder buffy = new StringBuilder(); for (DataType dt : list) { - buffy.append(dt.getName()).append(" - ").append(dt.getDescription()).append( - "\n"); + buffy.append(dt.getName()) + .append(" - ") + .append(dt.getDescription()) + .append( + "\n"); } Assert.fail( @@ -92,76 +102,79 @@ public class FileDataTypeManagerTest extends AbstractGenericTest { assertTrue(dt1.isEquivalent(dtMgr.getDataType(CategoryPath.ROOT, "T1"))); assertTrue(dt2.isEquivalent(dtMgr.getDataType(CategoryPath.ROOT, "T2"))); - dtMgr.close(); - dtMgr = null; + archive.release(this); + archive = null; } catch (IOException e) { failWithException("Unexpected exception", e); } finally { - if (dtMgr != null) { - dtMgr.close(); + if (archive != null) { + archive.release(this); } } } @Test - public void testModifyArchive() { + public void testModifyArchive() throws Exception { testCreateAndOpenArchive(); // establish archive for (int i = 0; i < 10; i++) { - FileDataTypeManager dtMgr = null; + FileDataTypeArchive archive = null; try { - dtMgr = FileDataTypeManager.openFileArchive(testArchiveFile, true); - assertEquals(ArchiveWarning.NONE, dtMgr.getWarning()); - assertTrue("Archive not updateable, i=" + i, dtMgr.isUpdatable()); + archive = DataTypeArchiveFactory.openForUpdate(testArchiveFile, true, this, + TaskMonitor.DUMMY); + assertEquals(ArchiveWarning.NONE, archive.getWarning()); + assertTrue("Archive not updateable, i=" + i, archive.isChangeable()); - int txId = dtMgr.startTransaction("Add Type"); + int txId = archive.startTransaction("Add Type"); try { - dtMgr.addDataType(new TypedefDataType("X" + i, ByteDataType.dataType), null); + DataTypeManager dtm = archive.getDataTypeManager(); + dtm.addDataType(new TypedefDataType("X" + i, ByteDataType.dataType), null); } finally { - dtMgr.endTransaction(txId, true); + archive.endTransaction(txId, true); } - dtMgr.save(); - dtMgr.close(); - dtMgr = null; + archive.save(null, TaskMonitor.DUMMY); + archive.release(this); + archive = null; } catch (IOException e) { Assert.fail("Unexpected Exception"); } finally { - if (dtMgr != null) { - dtMgr.close(); + if (archive != null) { + archive.release(this); } } } - FileDataTypeManager dtMgr = null; + FileDataTypeArchive archive = null; try { - dtMgr = FileDataTypeManager.openFileArchive(testArchiveFile, false); - assertEquals(ArchiveWarning.NONE, dtMgr.getWarning()); - assertFalse(dtMgr.isUpdatable()); + archive = DataTypeArchiveFactory.openReadOnly(testArchiveFile, this, TaskMonitor.DUMMY); + assertEquals(ArchiveWarning.NONE, archive.getWarning()); + assertFalse(archive.isChangeable()); + DataTypeManager dtm = archive.getDataTypeManager(); ArrayList list = new ArrayList<>(); - dtMgr.getAllDataTypes(list); + dtm.getAllDataTypes(list); assertEquals(13, list.size()); - dtMgr.close(); - dtMgr = null; + archive.release(this); + archive = null; } catch (IOException e) { Assert.fail("Unexpected Exception"); } finally { - if (dtMgr != null) { - dtMgr.close(); + if (archive != null) { + archive.release(this); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FunctionDefinitionDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FunctionDefinitionDataTypeTest.java index ee01baebde..9064e9c79f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FunctionDefinitionDataTypeTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FunctionDefinitionDataTypeTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,9 +20,11 @@ import static org.junit.Assert.*; import org.junit.*; import generic.test.AbstractGenericTest; +import ghidra.program.database.data.TransientDataTypeManager; public class FunctionDefinitionDataTypeTest extends AbstractGenericTest { - private StandAloneDataTypeManager dtm; + + private DataTypeManager dtm; private FunctionDefinition functionDt; private FunctionDefinition createFunctionDefinition(String name) { @@ -31,11 +33,16 @@ public class FunctionDefinitionDataTypeTest extends AbstractGenericTest { @Before public void setUp() throws Exception { - dtm = new StandAloneDataTypeManager("dummyDTM"); + dtm = new TransientDataTypeManager("dummyDTM"); dtm.startTransaction(""); functionDt = createFunctionDefinition("Test"); } + @After + public void tearDown() { + dtm.close(); + } + @Test public void testConstructor_WithName() { FunctionDefinitionDataType impl; diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/BitFieldDBDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/BitFieldDBDataTypeTest.java index 0aa7334fb6..02cc641af1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/BitFieldDBDataTypeTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/BitFieldDBDataTypeTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,20 +17,28 @@ package ghidra.program.database.data; import static org.junit.Assert.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.*; import generic.test.AbstractGenericTest; import ghidra.program.model.data.*; public class BitFieldDBDataTypeTest extends AbstractGenericTest { - private DataTypeManagerDB dataMgr; + private TransientDataTypeManager dataMgr; + private int txId; @Before public void setup() throws Exception { - dataMgr = new StandAloneDataTypeManager("dummyDTM"); - dataMgr.startTransaction("Test"); + dataMgr = new TransientDataTypeManager("dummyArchive"); + txId = dataMgr.startTransaction("Test"); + } + + @After + public void tearDown() { + if (dataMgr != null) { + dataMgr.endTransaction(txId, true); + dataMgr.close(); + } } @Test diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/DataTypeDBReplaceTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/DataTypeDBReplaceTest.java index 58a5934486..f54c972612 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/DataTypeDBReplaceTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/DataTypeDBReplaceTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,20 +19,26 @@ import static org.junit.Assert.*; import java.util.Iterator; -import org.junit.Before; -import org.junit.Test; +import org.junit.*; import generic.test.AbstractGenericTest; import ghidra.program.model.data.*; public class DataTypeDBReplaceTest extends AbstractGenericTest { - private DataTypeManagerDB dataMgr; + private TransientDataTypeManager dataMgr; + private int txId; @Before public void setUp() throws Exception { - dataMgr = new StandAloneDataTypeManager("dummyDTM"); - dataMgr.startTransaction("Test"); + dataMgr = new TransientDataTypeManager("dummyArchive"); + txId = dataMgr.startTransaction("Test"); + } + + @After + public void tearDown() { + dataMgr.endTransaction(txId, false); + dataMgr.close(); } @Test @@ -257,7 +263,8 @@ public class DataTypeDBReplaceTest extends AbstractGenericTest { // Compare count with actual record count to ensure both proper maps updates and // potential datatype duplication not reflected in count above. - assertEquals("Incomplete datatype manager update", cnt, dataMgr.getDataTypeRecordCount()); + assertEquals("Incomplete datatype manager update", cnt, + dataMgr.getDataTypeRecordCount()); return cnt; } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java index c4f12021cc..713e52f4be 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java @@ -31,13 +31,13 @@ import ghidra.util.exception.DuplicateNameException; public class StructureDBTest extends AbstractGenericTest { private StructureDB struct; - private StandAloneDataTypeManager dataMgr; + private TransientDataTypeManager dataMgr; private int txId; @Before public void setUp() throws Exception { - dataMgr = new StandAloneDataTypeManager("dummyDTM"); + dataMgr = new TransientDataTypeManager("dummyDTM"); // default data organization is little-endian // default BitFieldPackingImpl uses gcc conventions with type alignment enabled @@ -592,7 +592,7 @@ public class StructureDBTest extends AbstractGenericTest { // Clear structure and set length to 8 with only zero-length components struct.setLength(0); struct.setLength(8); - + struct.insertAtOffset(2, zeroArray, -1, "z4", null); struct.insertAtOffset(0, zeroArray, -1, "z1", null); struct.insertAtOffset(0, zeroArray, -1, "z2", null); @@ -1799,7 +1799,7 @@ public class StructureDBTest extends AbstractGenericTest { } @Test - public void testDeleteMany() { + public void testDeleteMany() throws Exception { struct.growStructure(20); struct.insertAtOffset(12, WordDataType.dataType, -1, "A", null); @@ -1822,8 +1822,8 @@ public class StructureDBTest extends AbstractGenericTest { // Verify that records were properly updated by comitting and performing an undo/redo dataMgr.endTransaction(txId, true); - dataMgr.undo(); - dataMgr.redo(); + dataMgr.getDataStore().undo(); + dataMgr.getDataStore().redo(); txId = dataMgr.startTransaction("Continue Test"); assertEquals(28, struct.getLength()); diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/TypedefDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/TypedefDBTest.java index ff3a5dc935..acff14b6b0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/TypedefDBTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/TypedefDBTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,16 +25,16 @@ import ghidra.program.model.data.*; public class TypedefDBTest extends AbstractGenericTest { private final String NAME = "Test"; - - private DataTypeManagerDB dataMgr; + + private DataTypeManager dataMgr; private int txId; @Before public void setUp() throws Exception { - dataMgr = new StandAloneDataTypeManager("dummyDTM"); + dataMgr = new TransientDataTypeManager("dummyDTM"); txId = dataMgr.startTransaction("Test"); } - + @After public void tearDown() { if (txId > 0) { @@ -42,28 +42,28 @@ public class TypedefDBTest extends AbstractGenericTest { dataMgr.close(); } } - + @Test public void testDuplicateNameResolve() throws Exception { - + Structure struct = new StructureDataType(NAME, 0); struct.add(new ByteDataType(), "field1", "Comment1"); struct.add(new WordDataType(), null, "Comment2"); struct.add(new DWordDataType(), "field3", null); struct.add(new ByteDataType(), "field4", "Comment4"); - + Pointer structPtr = new PointerDataType(struct); - + TypeDef typeDef = new TypedefDataType(NAME, structPtr); - + TypeDef td = (TypeDef) dataMgr.resolve(typeDef, null); assertNotNull(td); assertEquals(NAME + ".conflict", td.getName()); - + assertTrue(td.isEquivalent(typeDef)); - + assertEquals("typedef Test.conflict Test *", td.toString()); - + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java index f773f889b2..4f74a0ffba 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java @@ -26,19 +26,18 @@ import ghidra.program.model.data.*; public class UnionDBTest extends AbstractGenericTest { - private StandAloneDataTypeManager dataMgr; + private TransientDataTypeManager dtm; private UnionDB union; private int txId; @Before public void setUp() throws Exception { - dataMgr = new StandAloneDataTypeManager("dummydataMgr"); - + dtm = new TransientDataTypeManager("dummyDTM"); // default data organization is little-endian // default BitFieldPackingImpl uses gcc conventions - txId = dataMgr.startTransaction("Test"); + txId = dtm.startTransaction("Test"); union = createUnion("TestUnion"); union.add(new ByteDataType(), "field1", "Comment1"); @@ -49,31 +48,31 @@ public class UnionDBTest extends AbstractGenericTest { @After public void tearDown() { - if (dataMgr != null) { - dataMgr.endTransaction(txId, true); - dataMgr.close(); + if (dtm != null) { + dtm.endTransaction(txId, true); + dtm.close(); } } private void transitionToBigEndian() { Union unionClone = union.clone(null); - dataMgr.remove(union); + dtm.remove(union); - DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); + DataOrganizationImpl dataOrg = (DataOrganizationImpl) dtm.getDataOrganization(); dataOrg.setBigEndian(true); // re-resolve with modified endianness - union = (UnionDB) dataMgr.resolve(unionClone, null); + union = (UnionDB) dtm.resolve(unionClone, null); } private UnionDB createUnion(String name) { Union unionDt = new UnionDataType(name); - return (UnionDB) dataMgr.addDataType(unionDt, DataTypeConflictHandler.DEFAULT_HANDLER); + return (UnionDB) dtm.addDataType(unionDt, DataTypeConflictHandler.DEFAULT_HANDLER); } private Structure createStructure(String name, int size) { - return (Structure) dataMgr.addDataType(new StructureDataType(name, size), + return (Structure) dtm.addDataType(new StructureDataType(name, size), DataTypeConflictHandler.DEFAULT_HANDLER); } @@ -252,7 +251,7 @@ public class UnionDBTest extends AbstractGenericTest { public void testDeleteBitFieldDependency() throws InvalidDataTypeException { TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType); - td = (TypeDef) dataMgr.resolve(td, null); + td = (TypeDef) dtm.resolve(td, null); union.insertBitField(2, td, 4, "bf1", "bf1Comment"); union.insertBitField(3, td, 4, "bf2", "bf2Comment"); @@ -271,7 +270,7 @@ public class UnionDBTest extends AbstractGenericTest { "Length: 4 Alignment: 1", union); //@formatter:on - dataMgr.remove(td); + dtm.remove(td); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + @@ -293,7 +292,7 @@ public class UnionDBTest extends AbstractGenericTest { throws InvalidDataTypeException, DataTypeDependencyException { TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType); - td = (TypeDef) dataMgr.resolve(td, null); + td = (TypeDef) dtm.resolve(td, null); union.insertBitField(2, td, 4, "bf1", "bf1Comment"); union.insertBitField(3, td, 4, "bf2", "bf2Comment"); @@ -312,7 +311,7 @@ public class UnionDBTest extends AbstractGenericTest { "Length: 4 Alignment: 1", union); //@formatter:on - dataMgr.replaceDataType(td, CharDataType.dataType, false); + dtm.replaceDataType(td, CharDataType.dataType, false); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + @@ -404,10 +403,10 @@ public class UnionDBTest extends AbstractGenericTest { assertEquals(2, comps[2].getOrdinal()); // Verify that records were properly updated by comitting and performing an undo/redo - dataMgr.endTransaction(txId, true); - dataMgr.undo(); - dataMgr.redo(); - txId = dataMgr.startTransaction("Continue Test"); + dtm.endTransaction(txId, true); + dtm.getDataStore().undo(); + dtm.getDataStore().redo(); + txId = dtm.startTransaction("Continue Test"); assertEquals(2, union.getLength()); @@ -752,7 +751,7 @@ public class UnionDBTest extends AbstractGenericTest { DataTypeComponent component = newUnion.add(new ByteDataType(), " name with spaces", null); assertEquals("name_with_spaces", component.getFieldName()); - union = (UnionDB) dataMgr.resolve(newUnion, null); + union = (UnionDB) dtm.resolve(newUnion, null); component = union.getComponent(0); component.setFieldName(" name in db with spaces "); @@ -777,7 +776,7 @@ public class UnionDBTest extends AbstractGenericTest { assertNull(component.getFieldName()); assertEquals("field0", component.getDefaultFieldName()); - union = (UnionDB) dataMgr.resolve(newUnion, null); + union = (UnionDB) dtm.resolve(newUnion, null); component = union.add(new ByteDataType(), null, null); assertNull(component.getFieldName()); diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/merge/EnumMergerTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/merge/EnumMergerTest.java index 6d3cbb9491..a21c818d2b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/merge/EnumMergerTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/merge/EnumMergerTest.java @@ -20,24 +20,28 @@ import static org.junit.Assert.*; import org.junit.*; import generic.test.AbstractGenericTest; -import ghidra.program.model.data.*; +import ghidra.program.database.data.TransientDataTypeManager; +import ghidra.program.model.data.CategoryPath; import ghidra.program.model.data.Enum; +import ghidra.program.model.data.EnumDataType; import ghidra.util.UniversalIdGenerator; public class EnumMergerTest extends AbstractGenericTest { - private StandAloneDataTypeManager dataTypeManager; + + private TransientDataTypeManager dataTypeManager; private int txId; @Before public void setUp() throws Exception { UniversalIdGenerator.initialize(); - dataTypeManager = new StandAloneDataTypeManager("Test"); + dataTypeManager = new TransientDataTypeManager("test"); txId = dataTypeManager.startTransaction("Test"); } @After public void tearDown() { dataTypeManager.endTransaction(txId, false); + dataTypeManager.close(); } @Test diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/merge/StructureMergerTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/merge/StructureMergerTest.java index bf54a3e70d..51663b4017 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/merge/StructureMergerTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/merge/StructureMergerTest.java @@ -22,6 +22,7 @@ import java.util.List; import org.junit.*; import generic.test.AbstractGenericTest; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.program.model.data.*; import ghidra.util.UniversalIdGenerator; @@ -30,7 +31,7 @@ public class StructureMergerTest extends AbstractGenericTest { private DataType dwordDt; private IntegerDataType intDt; private DataType zeroArray; - private StandAloneDataTypeManager dtm; + private TransientDataTypeManager dtm; private int txId; @Before @@ -40,13 +41,14 @@ public class StructureMergerTest extends AbstractGenericTest { dwordDt = new DWordDataType(); intDt = new IntegerDataType(); zeroArray = new ArrayDataType(intDt, 0); - dtm = new StandAloneDataTypeManager("Test"); + dtm = new TransientDataTypeManager("Test"); txId = dtm.startTransaction("Test"); } @After public void tearDown() { dtm.endTransaction(txId, false); + dtm.close(); } @Test diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/merge/UnionMergerTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/merge/UnionMergerTest.java index afc6d57dde..1914d569e5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/merge/UnionMergerTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/merge/UnionMergerTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.*; import org.junit.*; import generic.test.AbstractGenericTest; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.program.model.data.*; import ghidra.util.UniversalIdGenerator; @@ -27,7 +28,7 @@ public class UnionMergerTest extends AbstractGenericTest { private DataType wordDt; private DataType dwordDt; private IntegerDataType intDt; - private StandAloneDataTypeManager dataTypeManager; + private DataTypeManager dataTypeManager; private int txId; @Before @@ -36,13 +37,14 @@ public class UnionMergerTest extends AbstractGenericTest { wordDt = new WordDataType(); dwordDt = new DWordDataType(); intDt = new IntegerDataType(); - dataTypeManager = new StandAloneDataTypeManager("Test"); + dataTypeManager = new TransientDataTypeManager("Test"); txId = dataTypeManager.startTransaction("Test"); } @After public void tearDown() { dataTypeManager.endTransaction(txId, false); + dataTypeManager.close(); } @Test diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java index 21bf3a36b6..b6cce5a928 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -63,7 +63,7 @@ public class MemBlockDBTest extends AbstractGenericTest { FileBytesAdapter fileBytesAdapter = new FileBytesAdapterV0(handle, true); mem.init(adapter, fileBytesAdapter); - mem.setProgram(program); + mem.setDomainObject(program); } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubFunctionManager.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubFunctionManager.java index d186c06b23..3170ecf9b6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubFunctionManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubFunctionManager.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -147,12 +147,12 @@ public class StubFunctionManager implements FunctionManager { } @Override - public void setProgram(ProgramDB program) { + public void setDomainObject(ProgramDB program) { throw new UnsupportedOperationException(); } @Override - public void programReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) + public void domainObjectReady(OpenMode openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { throw new UnsupportedOperationException(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubProgram.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubProgram.java index 82b6581e90..d67bbbc017 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubProgram.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubProgram.java @@ -29,6 +29,7 @@ import ghidra.program.database.ProgramOverlayAddressSpace; import ghidra.program.database.data.ProgramDataTypeManager; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.*; +import ghidra.program.model.data.ArchiveType; import ghidra.program.model.data.CategoryPath; import ghidra.program.model.lang.*; import ghidra.program.model.listing.*; @@ -41,6 +42,8 @@ import ghidra.program.model.util.PropertyMapManager; import ghidra.util.exception.CancelledException; import ghidra.util.exception.DuplicateNameException; import ghidra.util.task.TaskMonitor; +import utility.function.ExceptionalCallback; +import utility.function.ExceptionalSupplier; public class StubProgram implements Program { @@ -625,4 +628,36 @@ public class StubProgram implements Program { throw new UnsupportedOperationException(); } + @Override + public String getPath() { + return null; + } + + @Override + public void withTransaction(String description, + ExceptionalCallback callback) throws E { + throw new UnsupportedOperationException(); + } + + @Override + public T withTransaction(String description, + ExceptionalSupplier supplier) throws E { + throw new UnsupportedOperationException(); + } + + @Override + public ArchiveType getArchiveType() { + return ArchiveType.PROGRAM; + } + + @Override + public ProgramArchitecture getProgramArchitecture() { + return this; + } + + @Override + public String getProgramArchitectureSummary() { + return null; + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java index 22cdd00ac1..10d8465e1c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java @@ -18,10 +18,11 @@ package ghidra.program.model.data; import java.util.*; import db.Transaction; -import ghidra.program.database.map.AddressMap; +import ghidra.program.model.dtarchive.DataTypeStore; import ghidra.program.model.lang.ProgramArchitecture; import ghidra.program.model.lang.PrototypeModel; -import ghidra.util.*; +import ghidra.util.UniversalID; +import ghidra.util.UniversalIdGenerator; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -42,11 +43,6 @@ public class TestDoubleDataTypeManager implements DataTypeManager { return id; } - @Override - public AddressMap getAddressMap() { - throw new UnsupportedOperationException(); - } - @Override public ProgramArchitecture getProgramArchitecture() { throw new UnsupportedOperationException(); @@ -220,11 +216,6 @@ public class TestDoubleDataTypeManager implements DataTypeManager { throw new UnsupportedOperationException(); } - @Override - public void setName(String name) throws InvalidNameException { - throw new UnsupportedOperationException(); - } - @Override public Transaction openTransaction(String description) throws IllegalStateException { throw new UnsupportedOperationException(); @@ -404,4 +395,9 @@ public class TestDoubleDataTypeManager implements DataTypeManager { public PrototypeModel getCallingConvention(String name) { throw new UnsupportedOperationException(); } + + @Override + public DataTypeStore getDataStore() { + throw new UnsupportedOperationException(); + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java index eababfc72a..ef210464e7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java @@ -18,10 +18,9 @@ package ghidra.program.model.data; import java.util.*; import db.Transaction; -import ghidra.program.database.map.AddressMap; +import ghidra.program.model.dtarchive.DataTypeStore; import ghidra.program.model.lang.ProgramArchitecture; import ghidra.program.model.lang.PrototypeModel; -import ghidra.util.InvalidNameException; import ghidra.util.UniversalID; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -37,6 +36,12 @@ public class TestDummyDataTypeManager implements DataTypeManager { return null; } + @Override + public DataTypeStore getDataStore() { + // stub + return null; + } + @Override public boolean containsCategory(CategoryPath path) { // stub @@ -224,12 +229,6 @@ public class TestDummyDataTypeManager implements DataTypeManager { return null; } - @Override - public void setName(String name) throws InvalidNameException { - // stub - - } - @Override public Transaction openTransaction(String description) throws IllegalStateException { throw new UnsupportedOperationException(); @@ -385,12 +384,6 @@ public class TestDummyDataTypeManager implements DataTypeManager { return false; } - @Override - public AddressMap getAddressMap() { - // stub - return null; - } - @Override public ProgramArchitecture getProgramArchitecture() { // stub diff --git a/Ghidra/Processors/Atmel/ghidra_scripts/CreateAVR8GDTArchiveScript.java b/Ghidra/Processors/Atmel/ghidra_scripts/CreateAVR8GDTArchiveScript.java index 0274a5855a..93745a2e38 100644 --- a/Ghidra/Processors/Atmel/ghidra_scripts/CreateAVR8GDTArchiveScript.java +++ b/Ghidra/Processors/Atmel/ghidra_scripts/CreateAVR8GDTArchiveScript.java @@ -34,9 +34,11 @@ import ghidra.app.script.GhidraScript; import ghidra.app.util.cparser.C.CParserUtils; import ghidra.app.util.cparser.C.CParserUtils.CParseResults; import ghidra.app.util.cparser.CPP.*; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.dtarchive.FileDataTypeArchive; import ghidra.util.Msg; +import ghidra.util.task.TaskMonitor; public class CreateAVR8GDTArchiveScript extends GhidraScript { @@ -115,7 +117,9 @@ public class CreateAVR8GDTArchiveScript extends GhidraScript { // ResourceFile clib64ArchiveFile = DataTypeArchiveUtility.findArchiveFile("generic_clib.gdt"); File file = new File(clib64ArchiveFile.getAbsolutePath()); - DataTypeManager vsDTMgr = FileDataTypeManager.openFileArchive(file, false); + FileDataTypeArchive clibArchive = + DataTypeArchiveFactory.openReadOnly(file, this, TaskMonitor.DUMMY); + DataTypeManager vsDTMgr = clibArchive.getDataTypeManager(); DataTypeManager openTypes[] = { vsDTMgr }; // by defaults, don't want to be dependent on other archives if have all necessary definitions // comment out if missing data types @@ -125,19 +129,20 @@ public class CreateAVR8GDTArchiveScript extends GhidraScript { File f = getArchiveFile(dataTypeFile); - FileDataTypeManager dtMgr = - FileDataTypeManager.createFileArchive(f, "avr8:LE:16:atmega256", "gcc"); + FileDataTypeArchive archive = + DataTypeArchiveFactory.createFileArchive(f, "avr8:LE:16:atmega256", "gcc", this); + DataTypeManager dtm = archive.getDataTypeManager(); // Parse each processor variant as an individual parse that gets added to the data // type manager. If all header files were parsed at once, there are conflicting // macro definitions that will cause the parse to fail. // for (String variantName : processorVariants) { - parseProcessorDefs(variantName, dtMgr, openTypes); + parseProcessorDefs(variantName, dtm, openTypes); } - - dtMgr.save(); - dtMgr.close(); + clibArchive.release(this); + archive.save(null, TaskMonitor.DUMMY); + archive.release(this); } /** @@ -170,7 +175,7 @@ public class CreateAVR8GDTArchiveScript extends GhidraScript { * @throws ghidra.app.util.cparser.C.ParseException * @throws IOException io exception */ - private void parseProcessorDefs(String procName, FileDataTypeManager dtMgr, + private void parseProcessorDefs(String procName, DataTypeManager dtMgr, DataTypeManager[] openTypes) throws ParseException, ghidra.app.util.cparser.C.ParseException, IOException { @@ -191,7 +196,7 @@ public class CreateAVR8GDTArchiveScript extends GhidraScript { * @param dtMgr add data types to dtMgr * @param cpp pre-processor holds macros/defines from parsing */ - private void storeExtraDefinitions(String procName, FileDataTypeManager dtMgr, + private void storeExtraDefinitions(String procName, DataTypeManager dtMgr, DataTypeManager[] openTypes, PreProcessor cpp) { int transactionID = dtMgr.startTransaction("Add Extra Equates"); try { diff --git a/Ghidra/Processors/JVM/ghidra_scripts/CreateJNIGDTArchivesScript.java b/Ghidra/Processors/JVM/ghidra_scripts/CreateJNIGDTArchivesScript.java index 902e89c12a..3f86191fb2 100644 --- a/Ghidra/Processors/JVM/ghidra_scripts/CreateJNIGDTArchivesScript.java +++ b/Ghidra/Processors/JVM/ghidra_scripts/CreateJNIGDTArchivesScript.java @@ -34,43 +34,46 @@ import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility; import ghidra.app.script.GhidraScript; import ghidra.app.util.cparser.C.CParserUtils; import ghidra.app.util.cparser.C.ParseException; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.dtarchive.FileDataTypeArchive; +import ghidra.util.task.TaskMonitor; public class CreateJNIGDTArchivesScript extends GhidraScript { private File outputDirectory; - + // location of header files base directory private static String headerFilePath = "/data/HeaderFiles"; @Override protected void run() throws Exception { outputDirectory = askDirectory("Select Directory for GDT files", "Select GDT Output Dir"); - + parseGDT_Linux_JNI(); parseGDT_Windows_JNI(); } - private void parseHeaderFilesToGDT(DataTypeManager openTypes[], File outputDir, String gdtName, String languageID, String compiler, String[] filenames, String[] args) + private void parseHeaderFilesToGDT(DataTypeManager openTypes[], File outputDir, String gdtName, + String languageID, String compiler, String[] filenames, String[] args) throws ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { - + String dataTypeFile = outputDir + File.separator + gdtName + ".gdt"; - + File f = getArchiveFile(dataTypeFile); - - FileDataTypeManager dtMgr = CParserUtils.parseHeaderFiles(openTypes, filenames, null, args, - f.getAbsolutePath(), languageID, compiler, monitor); - - dtMgr.close(); + + FileDataTypeArchive archive = CParserUtils.parseHeaderFiles(filenames, null, args, + f.getAbsolutePath(), languageID, compiler, openTypes, this, monitor); + + archive.release(this); } - + /** * Turn string into a file, delete old archive/lock file if it exists * - * @param dataTypeFile + * @param dataTypeFile the file containing the stored datatypes * * @return file */ @@ -87,36 +90,34 @@ public class CreateJNIGDTArchivesScript extends GhidraScript { return f; } - - public void parseGDT_Linux_JNI() throws Exception { + public void parseGDT_Linux_JNI() throws Exception { String filenames[] = { - "jni.h", - "jawt.h", - "jdwpTransport.h", - "jvmti.h", - "jvmticmlr.h", - "classfile_constants.h", - }; - - String args[] = { - "-I"+headerFilePath+"/jni/linux", - "-I"+headerFilePath+"/jni/linux/linux", - "-D_X86_", - "-D__STDC__", - "-D_GNU_SOURCE", - "-D__WORDSIZE=64", - "-D__builtin_va_list=void *", - "-D__DO_NOT_DEFINE_COMPILE", - "-D_Complex", - "-D__NO_STRING_INLINES", - "-D__signed__", - "-D__extension__=", - "-D__GLIBC_HAVE_LONG_LONG=1", - "-D__need_sigset_t", - "-Daligned_u64=uint64_t", + "jni.h", + "jawt.h", + "jdwpTransport.h", + "jvmti.h", + "jvmticmlr.h", + "classfile_constants.h", + }; + + String args[] = { + "-I" + headerFilePath + "/jni/linux", + "-I" + headerFilePath + "/jni/linux/linux", + "-D_X86_", + "-D__STDC__", + "-D_GNU_SOURCE", + "-D__WORDSIZE=64", + "-D__builtin_va_list=void *", + "-D__DO_NOT_DEFINE_COMPILE", + "-D_Complex", + "-D__NO_STRING_INLINES", + "-D__signed__", + "-D__extension__=", + "-D__GLIBC_HAVE_LONG_LONG=1", + "-D__need_sigset_t", + "-Daligned_u64=uint64_t", }; - // Using another archive while parsing will cause: // - a dependence on the other archive // - any missing data types while parsing are supplied if present from existingDTMgr @@ -125,40 +126,45 @@ public class CreateJNIGDTArchivesScript extends GhidraScript { // // NOTE: This will only occur if the data type from the exisitngDTMgr is equivalent. // - ResourceFile clib64ArchiveFile = DataTypeArchiveUtility.findArchiveFile("generic_clib_64.gdt"); - File file = new File(clib64ArchiveFile.getAbsolutePath()); - DataTypeManager existingDTMgr = FileDataTypeManager.openFileArchive(file, false); + ResourceFile clib64ArchiveFile = + DataTypeArchiveUtility.findArchiveFile("generic_clib_64.gdt"); + FileDataTypeArchive archive = + DataTypeArchiveFactory.openReadOnly(clib64ArchiveFile, this, TaskMonitor.DUMMY); + + DataTypeManager existingDTMgr = archive.getDataTypeManager(); DataTypeManager openTypes[] = { existingDTMgr }; - - parseHeaderFilesToGDT(openTypes, outputDirectory, "jni_linux", "x86:LE:64:default", "gcc", filenames, args); + + parseHeaderFilesToGDT(openTypes, outputDirectory, "jni_linux", "x86:LE:64:default", "gcc", + filenames, args); + archive.release(this); } - - public void parseGDT_Windows_JNI() throws Exception { + + public void parseGDT_Windows_JNI() throws Exception { String filenames[] = { - "jni.h", - "jawt.h", - "jdwpTransport.h", - "jvmti.h", - "jvmticmlr.h", - "classfile_constants.h", + "jni.h", + "jawt.h", + "jdwpTransport.h", + "jvmti.h", + "jvmticmlr.h", + "classfile_constants.h", }; - + String args[] = { - "-I"+headerFilePath+"/jni/win32", - "-I"+headerFilePath+"/jni/win32/win32", - "-D_X86_", - "-D__STDC__", - "-D_GNU_SOURCE", - "-D__WORDSIZE=64", - "-D__builtin_va_list=void *", - "-D__DO_NOT_DEFINE_COMPILE", - "-D_Complex", - "-D__NO_STRING_INLINES", - "-D__signed__", - "-D__extension__=", - "-D__GLIBC_HAVE_LONG_LONG=1", - "-D__need_sigset_t", - "-Daligned_u64=uint64_t", + "-I" + headerFilePath + "/jni/win32", + "-I" + headerFilePath + "/jni/win32/win32", + "-D_X86_", + "-D__STDC__", + "-D_GNU_SOURCE", + "-D__WORDSIZE=64", + "-D__builtin_va_list=void *", + "-D__DO_NOT_DEFINE_COMPILE", + "-D_Complex", + "-D__NO_STRING_INLINES", + "-D__signed__", + "-D__extension__=", + "-D__GLIBC_HAVE_LONG_LONG=1", + "-D__need_sigset_t", + "-Daligned_u64=uint64_t", }; // Using another archive while parsing will cause: @@ -169,11 +175,17 @@ public class CreateJNIGDTArchivesScript extends GhidraScript { // // NOTE: This will only occur if the data type from the exisitngDTMgr is equivalent. // - ResourceFile clib64ArchiveFile = DataTypeArchiveUtility.findArchiveFile("windows_vs12_64.gdt"); - File file = new File(clib64ArchiveFile.getAbsolutePath()); - DataTypeManager existingDTMgr = FileDataTypeManager.openFileArchive(file, false); + + ResourceFile clib64ArchiveFile = + DataTypeArchiveUtility.findArchiveFile("windows_vs12_64.gdt"); + FileDataTypeArchive archive = + DataTypeArchiveFactory.openReadOnly(clib64ArchiveFile, this, TaskMonitor.DUMMY); + + DataTypeManager existingDTMgr = archive.getDataTypeManager(); DataTypeManager openTypes[] = { existingDTMgr }; - - parseHeaderFilesToGDT(openTypes, outputDirectory, "jni_windows", "x86:LE:64:default", "windows", filenames, args); + + parseHeaderFilesToGDT(openTypes, outputDirectory, "jni_windows", "x86:LE:64:default", + "windows", filenames, args); + archive.release(this); } } diff --git a/Ghidra/Processors/JVM/src/test/java/ghidra/javaclass/test/DescriptorDecoderTest.java b/Ghidra/Processors/JVM/src/test/java/ghidra/javaclass/test/DescriptorDecoderTest.java index 66095dd2d2..d6ef1df8a0 100644 --- a/Ghidra/Processors/JVM/src/test/java/ghidra/javaclass/test/DescriptorDecoderTest.java +++ b/Ghidra/Processors/JVM/src/test/java/ghidra/javaclass/test/DescriptorDecoderTest.java @@ -21,30 +21,40 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.junit.Before; -import org.junit.Test; +import org.junit.*; import generic.test.AbstractGenericTest; import ghidra.app.util.pcodeInject.*; import ghidra.javaclass.format.DescriptorDecoder; import ghidra.javaclass.format.constantpool.AbstractConstantPoolInfoJava; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.program.model.data.*; public class DescriptorDecoderTest extends AbstractGenericTest { - DataTypeManager dtm; - DataType dtInteger; + + private DataTypeManager dtm; + private DataType dtInteger; @Before public void setUp() { - dtm = new StandAloneDataTypeManager(""); - int transactionID = dtm.startTransaction(null); - dtInteger = DescriptorDecoder.resolveClassForString("java/lang/Integer", dtm, - DWordDataType.dataType); - DescriptorDecoder.resolveClassForString("JVM_primitives/byte", dtm, - SignedByteDataType.dataType); - DescriptorDecoder.resolveClassForString("JVM_primitives/boolean", dtm, - BooleanDataType.dataType); - dtm.endTransaction(transactionID, true); + dtm = new TransientDataTypeManager("test"); + int transactionID = dtm.startTransaction("test"); + try { + dtInteger = DescriptorDecoder.resolveClassForString("java/lang/Integer", dtm, + DWordDataType.dataType); + DescriptorDecoder.resolveClassForString("JVM_primitives/byte", dtm, + SignedByteDataType.dataType); + DescriptorDecoder.resolveClassForString("JVM_primitives/boolean", dtm, + BooleanDataType.dataType); + } + finally { + dtm.endTransaction(transactionID, true); + } + } + + @After + public void tearDown() { + dtm.close(); } @Test diff --git a/Ghidra/Processors/eBPF/src/main/java/ghidra/app/plugin/core/analysis/eBPFHelperDataTypes.java b/Ghidra/Processors/eBPF/src/main/java/ghidra/app/plugin/core/analysis/eBPFHelperDataTypes.java index 73bd63a94c..32333005c0 100644 --- a/Ghidra/Processors/eBPF/src/main/java/ghidra/app/plugin/core/analysis/eBPFHelperDataTypes.java +++ b/Ghidra/Processors/eBPF/src/main/java/ghidra/app/plugin/core/analysis/eBPFHelperDataTypes.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,6 +23,7 @@ import ghidra.app.util.cparser.C.CParser; import ghidra.app.util.cparser.C.ParseException; import ghidra.app.util.importer.MessageLog; import ghidra.docking.settings.Settings; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.program.model.data.*; import ghidra.program.model.listing.Program; import ghidra.util.Msg; @@ -368,8 +369,9 @@ public class eBPFHelperDataTypes implements Closeable { DataType be32; boolean success = false; - DataTypeManager dtm = - new StandAloneDataTypeManager("BPF", DataOrganizationImpl.getDefaultOrganization()); + + DataTypeManager dtm = new TransientDataTypeManager("BPF", + DataOrganizationImpl.getDefaultOrganization()); int txId = dtm.startTransaction("Parse Types"); try { diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeManagerPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeManagerPluginScreenShots.java index 5dc020278d..d0b0175f1c 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeManagerPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeManagerPluginScreenShots.java @@ -33,11 +33,13 @@ import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; import ghidra.app.plugin.core.datamgr.*; import ghidra.app.plugin.core.datamgr.actions.FindStructuresBySizeAction; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; -import ghidra.app.plugin.core.datamgr.archive.InvalidFileArchive; +import ghidra.app.plugin.core.datamgr.archive.InvalidArchive; +import ghidra.framework.model.DomainFolder; import ghidra.framework.preferences.Preferences; import ghidra.program.database.data.merge.StructureBuilder; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; import ghidra.util.UniversalID; import ghidra.util.table.GhidraTable; @@ -104,7 +106,7 @@ public class DataTypeManagerPluginScreenShots extends GhidraScreenShotGenerator @Test public void testEditPaths() { - Preferences.setProperty(DataTypeManagerHandler.DATA_TYPE_ARCHIVE_PATH_KEY, + Preferences.setProperty(ArchiveManager.DATA_TYPE_ARCHIVE_PATH_KEY, "/archives/subPath" + File.pathSeparator + "/otherArchives/subpath"); performAction("Edit Archive Paths", "DataTypeManagerPlugin", false); @@ -306,7 +308,7 @@ public class DataTypeManagerPluginScreenShots extends GhidraScreenShotGenerator DataTypeManagerPlugin plugin = getPlugin(tool, DataTypeManagerPlugin.class); List list = new ArrayList<>(); Set set = new HashSet<>(); - StandAloneDataTypeManager sourceDtm = createChangedDatatypesFromArchive(list, set); + DataTypeManager sourceDtm = createChangedDatatypesFromArchive(list, set); DataType dt1 = sourceDtm.getDataType("/MyDataType1"); @@ -343,19 +345,19 @@ public class DataTypeManagerPluginScreenShots extends GhidraScreenShotGenerator private void removeInvalidArchives() { DataTypeManagerPlugin plugin = env.getPlugin(DataTypeManagerPlugin.class); - DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler(); + ArchiveManager archiveManager = plugin.getArchiveManager(); @SuppressWarnings("unchecked") - Map invalid = - (Map) getInstanceField("invalidArchives", handler); - Collection values = invalid.values(); - for (InvalidFileArchive invalidFileArchive : values) { - removeArchive(handler, invalidFileArchive); + Map invalid = + (Map) getInstanceField("invalidArchives", + archiveManager); + Collection values = invalid.values(); + for (InvalidArchive invalidFileArchive : values) { + removeInvalidArchive(archiveManager, invalidFileArchive); } } - private void removeArchive(final DataTypeManagerHandler handler, - final InvalidFileArchive archive) { - runSwing(() -> handler.removeInvalidArchive(archive)); + private void removeInvalidArchive(ArchiveManager archiveManager, InvalidArchive archive) { + runSwing(() -> archiveManager.closeInvalidArchive(archive)); } private DataTypesProvider getVisibleProvider() { @@ -370,18 +372,20 @@ public class DataTypeManagerPluginScreenShots extends GhidraScreenShotGenerator return null;// cannot get here } - private StandAloneDataTypeManager createChangedDatatypesFromArchive(List list, + private DataTypeManager createChangedDatatypesFromArchive(List list, Set set) { DataTypeManager dtm = program.getDataTypeManager(); - StandAloneDataTypeManager sourceDtm = new StandAloneDataTypeManager("MyArhcive") { - @Override - public ArchiveType getType() { - // Need to not be the default TEMPORARY type so that the universal ID of our data - // types will be maintained. This is needed to associate the types. - return ArchiveType.FILE; - } - }; + DomainFolder rootFolder = env.getGhidraProject().getRootFolder(); + ProjectDataTypeArchive archive; + try { + archive = DataTypeArchiveFactory.createProjectArchive(rootFolder, "MyArchive", this); + } + catch (Exception e) { + throw new RuntimeException(e); + } + DataTypeManager sourceDtm = archive.getDataTypeManager(); + StructureDataType sdt1 = new StructureDataType("MyDataType1", 0); sdt1.add(new PointerDataType(new StringDataType()), "name", null); sdt1.add(new IntegerDataType(), "age", null); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/RepositoryScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/RepositoryScreenShots.java index a86727d2b4..f10dd9e7c8 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/RepositoryScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/RepositoryScreenShots.java @@ -45,6 +45,7 @@ import ghidra.program.database.external.ExternalManagerDB; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSet; import ghidra.program.model.data.*; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; import ghidra.program.model.lang.Register; import ghidra.program.model.listing.*; import ghidra.program.model.mem.MemoryBlock; @@ -1902,9 +1903,8 @@ public class RepositoryScreenShots extends AbstractListingMergeManagerTest { FrontEndTool frontEndTool = env.showFrontEndTool(); DomainFolder rootFolder = frontEndTool.getProject().getProjectData().getRootFolder(); - TaskMonitor dummyMonitor = TaskMonitor.DUMMY; DomainFile myTestArchiveDF = env.restoreDataTypeArchive("MyTestArchive.gdt", rootFolder); - final DataTypeArchive myTestArchive = (DataTypeArchiveDB) myTestArchiveDF + final ProjectDataTypeArchive myTestArchive = (ProjectDataTypeArchive) myTestArchiveDF .getDomainObject(this, true, false, TaskMonitor.DUMMY); final CategoryPath sourceCatPath = new CategoryPath("/Category1/Category2/Category5"); diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/datatree/ProjectLinkFileStatusTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/datatree/ProjectLinkFileStatusTest.java index 95fa029c86..635a73ce30 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/datatree/ProjectLinkFileStatusTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/datatree/ProjectLinkFileStatusTest.java @@ -27,8 +27,9 @@ import ghidra.framework.data.LinkHandler; import ghidra.framework.data.LinkHandler.LinkStatus; import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainFolder; -import ghidra.program.database.DataTypeArchiveDB; import ghidra.program.database.ProgramLinkContentHandler; +import ghidra.program.database.dtarchive.DataTypeArchiveFactory; +import ghidra.program.model.dtarchive.ProjectDataTypeArchive; import ghidra.program.model.listing.Program; import ghidra.server.remote.ServerTestUtil; import ghidra.test.*; @@ -545,9 +546,10 @@ public class ProjectLinkFileStatusTest extends AbstractGhidraHeadedIntegrationTe // // Create DataTypeArchive project file /abc/foo // - DataTypeArchiveDB dtm = new DataTypeArchiveDB(abcFolder, "foo", this); - dtm.save(null, TaskMonitor.DUMMY); - dtm.release(this); + ProjectDataTypeArchive archive = + DataTypeArchiveFactory.createProjectArchive(abcFolder, "foo", this); + archive.save(null, TaskMonitor.DUMMY); + archive.release(this); env.waitForTree(); // give time for ChangeManager to update diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/AbstractProtoModelTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/AbstractProtoModelTest.java index 413fc59a72..0b33a62623 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/AbstractProtoModelTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/AbstractProtoModelTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,12 +17,14 @@ package ghidra.program.model.lang; import java.util.ArrayList; +import org.junit.After; import org.junit.Assert; import generic.test.AbstractGenericTest; import ghidra.app.plugin.processors.sleigh.SleighLanguageProvider; import ghidra.app.util.cparser.C.ParseException; import ghidra.app.util.parser.FunctionSignatureParser; +import ghidra.program.database.data.TransientDataTypeManager; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.data.*; @@ -39,6 +41,13 @@ public class AbstractProtoModelTest extends AbstractGenericTest { protected FunctionSignatureParser parser; protected DataTypeParser dataTypeParser; + @After + public void tearDown() { + if (dtManager != null) { + dtManager.close(); + } + } + protected void buildParsers() { parser = new FunctionSignatureParser(dtManager, null); dataTypeParser = @@ -77,7 +86,7 @@ public class AbstractProtoModelTest extends AbstractGenericTest { } protected void buildDataTypeManager(String name) { - dtManager = new StandAloneDataTypeManager(name, cspec.getDataOrganization()); + dtManager = new TransientDataTypeManager(name, cspec.getDataOrganization()); int txID = dtManager.startTransaction("Add core types"); try { @@ -256,10 +265,15 @@ public class AbstractProtoModelTest extends AbstractGenericTest { buffer.append(cspec.getCompilerSpecID()); buffer.append(' ').append(model.getName()).append(' '); if (i == 0) { - buffer.append("Output ").append("@"+toString(resPiece)).append(" does not match for "); + buffer.append("Output ") + .append("@" + toString(resPiece)) + .append(" does not match for "); } else { - buffer.append("Parameter ").append(i - 1).append(" @"+toString(resPiece)+" ").append(" does not match for: "); + buffer.append("Parameter ") + .append(i - 1) + .append(" @" + toString(resPiece) + " ") + .append(" does not match for: "); } buffer.append(signature); message = buffer.toString(); @@ -272,13 +286,13 @@ public class AbstractProtoModelTest extends AbstractGenericTest { Varnode[] joinPieces = resPiece.joinPieces; if (joinPieces != null) { StringBuilder buffer = new StringBuilder("join "); - + for (Varnode varnode : joinPieces) { buffer.append(toString(varnode)).append(" "); } return buffer.toString(); } - + Address addr = resPiece.address; resPiece.type.getLength(); if (addr != null) {