├── .github └── workflows │ └── maven.yaml ├── .gitignore ├── .plugin_config ├── codestyle.xml ├── codestyle_suppressions.xml ├── formatter.xml └── spotbugs_exclude.xml ├── LICENSE ├── README.md ├── bumpVersion.sh ├── decompiler_agent ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── org │ │ └── jrd │ │ ├── agent │ │ ├── AgentActionWorker.java │ │ ├── AgentLogger.java │ │ ├── ClassFilter.java │ │ ├── ConnectionDelegator.java │ │ ├── InstrumentationProvider.java │ │ ├── Main.java │ │ ├── ReceivedType.java │ │ ├── Transformer.java │ │ └── api │ │ │ ├── AbstractMasterKeyMap.java │ │ │ ├── ClassClassLoaderMap.java │ │ │ ├── UnsafeVariables.java │ │ │ └── Variables.java │ │ └── backend │ │ └── data │ │ ├── MetadataProperties.java │ │ └── package-info.java │ └── resources │ └── org │ └── jrd │ └── backend │ └── data │ └── metadata.prop ├── images ├── hex.sh ├── image.sh └── pom.xml ├── pom.xml ├── runtime-decompiler ├── .plugin_config ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ ├── fife │ │ │ └── ui │ │ │ │ └── hex │ │ │ │ ├── ByteBuffer.java │ │ │ │ ├── event │ │ │ │ ├── HexEditorEvent.java │ │ │ │ ├── HexEditorListener.java │ │ │ │ ├── HexSearchActionListener.java │ │ │ │ └── HexSearchDocumentListener.java │ │ │ │ ├── package-info.java │ │ │ │ └── swing │ │ │ │ ├── ByteArrayTransferable.java │ │ │ │ ├── HexEditor.java │ │ │ │ ├── HexEditorRowHeader.java │ │ │ │ ├── HexEditorTransferHandler.java │ │ │ │ ├── HexSearch.java │ │ │ │ ├── HexSearchParseException.java │ │ │ │ ├── HexTable.java │ │ │ │ ├── HexTableModel.java │ │ │ │ └── SearchState.java │ │ │ ├── jrd │ │ │ ├── backend │ │ │ │ ├── communication │ │ │ │ │ ├── CallDecompilerAgent.java │ │ │ │ │ ├── Communicate.java │ │ │ │ │ ├── DelegatingJrdAgent.java │ │ │ │ │ ├── ErrorCandidate.java │ │ │ │ │ ├── FsAgent.java │ │ │ │ │ ├── InstallDecompilerAgentImpl.java │ │ │ │ │ ├── JrdAgent.java │ │ │ │ │ ├── RuntimeCompilerConnector.java │ │ │ │ │ └── TopLevelErrorCandidate.java │ │ │ │ ├── completion │ │ │ │ │ ├── ClassesAndMethodsProvider.java │ │ │ │ │ └── JrdCompletionSettings.java │ │ │ │ ├── core │ │ │ │ │ ├── AgentAttachManager.java │ │ │ │ │ ├── AgentLoader.java │ │ │ │ │ ├── AgentRequestAction.java │ │ │ │ │ ├── ClassInfo.java │ │ │ │ │ ├── DecompilerRequestReceiver.java │ │ │ │ │ ├── Logger.java │ │ │ │ │ ├── VmDecompilerStatus.java │ │ │ │ │ └── agentstore │ │ │ │ │ │ ├── AgentLiveliness.java │ │ │ │ │ │ ├── AgentLoneliness.java │ │ │ │ │ │ ├── KnownAgent.java │ │ │ │ │ │ └── KnownAgents.java │ │ │ │ ├── data │ │ │ │ │ ├── ArchiveManager.java │ │ │ │ │ ├── ArchiveManagerOptions.java │ │ │ │ │ ├── BytemanCompanion.java │ │ │ │ │ ├── Config.java │ │ │ │ │ ├── DependenciesReader.java │ │ │ │ │ ├── Directories.java │ │ │ │ │ ├── Main.java │ │ │ │ │ ├── MetadataProperties.java │ │ │ │ │ ├── Model.java │ │ │ │ │ ├── VmInfo.java │ │ │ │ │ ├── VmManager.java │ │ │ │ │ └── cli │ │ │ │ │ │ ├── Cli.java │ │ │ │ │ │ ├── CliSwitches.java │ │ │ │ │ │ ├── CliUtils.java │ │ │ │ │ │ ├── Help.java │ │ │ │ │ │ ├── InMemoryJar.java │ │ │ │ │ │ ├── Lib.java │ │ │ │ │ │ ├── utils │ │ │ │ │ │ ├── AgentConfig.java │ │ │ │ │ │ ├── BytecodeSorter.java │ │ │ │ │ │ ├── CompileArguments.java │ │ │ │ │ │ ├── FqnAndClassToJar.java │ │ │ │ │ │ ├── ObtainedCodeWithNameAndBytecode.java │ │ │ │ │ │ ├── PluginWithOptions.java │ │ │ │ │ │ ├── PluginWrapperWithMetaInfo.java │ │ │ │ │ │ ├── ReceivedType.java │ │ │ │ │ │ └── Saving.java │ │ │ │ │ │ └── workers │ │ │ │ │ │ ├── AddClasses.java │ │ │ │ │ │ ├── Api.java │ │ │ │ │ │ ├── AttachDetach.java │ │ │ │ │ │ ├── Classes.java │ │ │ │ │ │ ├── Compile.java │ │ │ │ │ │ ├── Decompile.java │ │ │ │ │ │ ├── InitClass.java │ │ │ │ │ │ ├── ListAgents.java │ │ │ │ │ │ ├── ListJvms.java │ │ │ │ │ │ ├── ListPlugins.java │ │ │ │ │ │ ├── Overrides.java │ │ │ │ │ │ ├── OverwriteAndUpload.java │ │ │ │ │ │ ├── Patch.java │ │ │ │ │ │ ├── PrintBytes.java │ │ │ │ │ │ └── Shared.java │ │ │ │ └── decompiling │ │ │ │ │ ├── DecompilerWrapper.java │ │ │ │ │ ├── DecompilerWrapperDeserializer.java │ │ │ │ │ ├── DecompilerWrapperSerializer.java │ │ │ │ │ ├── ExpandableUrl.java │ │ │ │ │ ├── ImportUtils.java │ │ │ │ │ ├── JavapDisassemblerWrapper.java │ │ │ │ │ └── PluginManager.java │ │ │ └── frontend │ │ │ │ ├── frame │ │ │ │ ├── about │ │ │ │ │ └── AboutView.java │ │ │ │ ├── filesystem │ │ │ │ │ ├── NewFsVmController.java │ │ │ │ │ └── NewFsVmView.java │ │ │ │ ├── hex │ │ │ │ │ ├── FeatureFullHex.java │ │ │ │ │ └── StandaloneHex.java │ │ │ │ ├── license │ │ │ │ │ └── LicenseView.java │ │ │ │ ├── main │ │ │ │ │ ├── AgentsManager.java │ │ │ │ │ ├── GlobalConsole.java │ │ │ │ │ ├── LoadingDialog.java │ │ │ │ │ ├── LoadingDialogProvider.java │ │ │ │ │ ├── MainFrameView.java │ │ │ │ │ ├── ModelProvider.java │ │ │ │ │ ├── NewAgentDialog.java │ │ │ │ │ ├── OverridesManager.java │ │ │ │ │ ├── decompilerview │ │ │ │ │ │ ├── BytecodeDecompilerView.java │ │ │ │ │ │ ├── ClassOverwriter.java │ │ │ │ │ │ ├── CompileActionListener.java │ │ │ │ │ │ ├── CompileAndUploadActionListener.java │ │ │ │ │ │ ├── CompletionSettingsDialogue.java │ │ │ │ │ │ ├── DecompilationController.java │ │ │ │ │ │ ├── HexWithControls.java │ │ │ │ │ │ ├── InitAddClassDialog.java │ │ │ │ │ │ ├── InitAddClassJar.java │ │ │ │ │ │ ├── LinesProvider.java │ │ │ │ │ │ ├── OverwriteActionListener.java │ │ │ │ │ │ ├── QuickCompiler.java │ │ │ │ │ │ ├── SearchControlsPanel.java │ │ │ │ │ │ ├── SupportedKeySets.java │ │ │ │ │ │ ├── TextWithControls.java │ │ │ │ │ │ ├── UndoRedoKeyAdapter.java │ │ │ │ │ │ ├── dummycompiler │ │ │ │ │ │ │ ├── AbstractCompileAction.java │ │ │ │ │ │ │ ├── AbstractCompileAndRunAction.java │ │ │ │ │ │ │ ├── BytemanCompileAction.java │ │ │ │ │ │ │ ├── CanCompile.java │ │ │ │ │ │ │ ├── ClassesAndMethodsProviderBasedClassesProvider.java │ │ │ │ │ │ │ ├── JasmCompileAction.java │ │ │ │ │ │ │ ├── JavacCompileAction.java │ │ │ │ │ │ │ ├── JustBearerAction.java │ │ │ │ │ │ │ ├── NullClassesProvider.java │ │ │ │ │ │ │ ├── providers │ │ │ │ │ │ │ │ ├── ClasspathProvider.java │ │ │ │ │ │ │ │ ├── ExecuteMethodProvider.java │ │ │ │ │ │ │ │ ├── MainProviders.java │ │ │ │ │ │ │ │ ├── SaveProvider.java │ │ │ │ │ │ │ │ └── UploadProvider.java │ │ │ │ │ │ │ └── templates │ │ │ │ │ │ │ │ ├── BytemanSkeletonTemplateMenuItem.java │ │ │ │ │ │ │ │ ├── BytemanTemplateMenuItem.java │ │ │ │ │ │ │ │ ├── Jasm2TemplateMenuItem.java │ │ │ │ │ │ │ │ ├── JasmTemplateMenuItem.java │ │ │ │ │ │ │ │ └── JavaTemplateMenuItem.java │ │ │ │ │ │ └── verifiers │ │ │ │ │ │ │ ├── ClassVerifier.java │ │ │ │ │ │ │ ├── FileVerifier.java │ │ │ │ │ │ │ ├── GetSetText.java │ │ │ │ │ │ │ └── JarVerifier.java │ │ │ │ │ ├── popup │ │ │ │ │ │ ├── ClassListPopupMenu.java │ │ │ │ │ │ ├── DiffPopup.java │ │ │ │ │ │ ├── JListPopupMenu.java │ │ │ │ │ │ └── SingleFilePatch.java │ │ │ │ │ └── renderer │ │ │ │ │ │ ├── ClassListRenderer.java │ │ │ │ │ │ └── VmListRenderer.java │ │ │ │ ├── overwrite │ │ │ │ │ ├── FileToClassValidator.java │ │ │ │ │ ├── LatestPaths.java │ │ │ │ │ └── OverwriteClassDialog.java │ │ │ │ ├── plugins │ │ │ │ │ ├── ConfigPanel.java │ │ │ │ │ ├── FileSelectorArrayAddRow.java │ │ │ │ │ ├── FileSelectorArrayPanel.java │ │ │ │ │ ├── FileSelectorArrayRow.java │ │ │ │ │ ├── FileSelectorPanel.java │ │ │ │ │ ├── MessagePanel.java │ │ │ │ │ ├── OkCancelPanel.java │ │ │ │ │ ├── PluginConfigurationEditorController.java │ │ │ │ │ ├── PluginConfigurationEditorView.java │ │ │ │ │ ├── PluginListPanel.java │ │ │ │ │ ├── PluginTopOptionPanel.java │ │ │ │ │ └── TextInputPanel.java │ │ │ │ ├── remote │ │ │ │ │ ├── NewConnectionController.java │ │ │ │ │ └── NewConnectionView.java │ │ │ │ └── settings │ │ │ │ │ ├── AgentSettingsPanel.java │ │ │ │ │ ├── ChangeReporter.java │ │ │ │ │ ├── CompilationSettingsPanel.java │ │ │ │ │ ├── MiscellaneousSettingsPanel.java │ │ │ │ │ ├── NestedJarsSettingsPanel.java │ │ │ │ │ └── SettingsView.java │ │ │ │ └── utility │ │ │ │ ├── AgentApiGenerator.java │ │ │ │ ├── CommonUtils.java │ │ │ │ ├── ImageButtonFactory.java │ │ │ │ ├── ScreenFinder.java │ │ │ │ └── TeeOutputStream.java │ │ │ └── kcc │ │ │ ├── CompletionItem.java │ │ │ ├── CompletionSettings.java │ │ │ ├── ContextSuggestionsNarrower.java │ │ │ ├── KeywordBasedCodeCompletion.java │ │ │ ├── KeywordBasedCodeCompletionMain.java │ │ │ └── wordsets │ │ │ ├── BytecodeKeywordsWithHelp.java │ │ │ ├── BytemanKeywords.java │ │ │ ├── ConnectedKeywords.java │ │ │ ├── JavaKeywordsWithHelp.java │ │ │ └── JrdApiKeywords.java │ └── resources │ │ ├── icons │ │ ├── add_24dp.png │ │ ├── attach_24dp.png │ │ ├── compile_24dp.png │ │ ├── detach_24dp.png │ │ ├── init_24dp.png │ │ ├── insert_24dp.png │ │ ├── main-icon.png │ │ ├── main-icon.svg │ │ ├── overwrite_24dp.png │ │ ├── redo_24dp.png │ │ ├── refresh_24dp.png │ │ ├── remove_24dp.png │ │ ├── trash_24dp.png │ │ ├── undo_24dp.png │ │ └── upload_24dp.png │ │ └── org │ │ └── jrd │ │ └── backend │ │ └── data │ │ └── metadata.prop │ ├── plugins │ ├── CfrDecompilerWrapper.java │ ├── CfrDecompilerWrapper.json │ ├── FernflowerDecompilerWrapper.java │ ├── FernflowerDecompilerWrapper.json │ ├── Jasm7DecompilerWrapper.java │ ├── Jasm7DecompilerWrapper.json │ ├── JasmDecompilerWrapper.java │ ├── JasmDecompilerWrapper.json │ ├── JasmG7DecompilerWrapper.java │ ├── JasmG7DecompilerWrapper.json │ ├── JasmGDecompilerWrapper.java │ ├── JasmGDecompilerWrapper.json │ ├── Jcoder7DecompilerWrapper.java │ ├── Jcoder7DecompilerWrapper.json │ ├── JcoderDecompilerWrapper.java │ ├── JcoderDecompilerWrapper.json │ ├── JcoderG7DecompilerWrapper.java │ ├── JcoderG7DecompilerWrapper.json │ ├── JcoderGDecompilerWrapper.java │ ├── JcoderGDecompilerWrapper.json │ ├── JdDecompilerWrapper.java │ ├── JdDecompilerWrapper.json │ ├── ProcyonAssemblerDecompilerWrapper.java │ ├── ProcyonAssemblerDecompilerWrapper.json │ ├── ProcyonDecompilerWrapper.java │ └── ProcyonDecompilerWrapper.json │ └── test │ ├── java │ └── org │ │ ├── jrd │ │ ├── backend │ │ │ ├── data │ │ │ │ └── cli │ │ │ │ │ ├── AbstractAgentNeedingTest.java │ │ │ │ │ ├── AbstractSourceTestClass.java │ │ │ │ │ ├── ArchiveManagerOptionsTest.java │ │ │ │ │ ├── CliTest.java │ │ │ │ │ ├── CompileUploadCliTest.java │ │ │ │ │ ├── InMemoryJarTest.java │ │ │ │ │ ├── LibTest.java │ │ │ │ │ ├── ModifiableDummyTestingHelper.java │ │ │ │ │ └── TestingDummyHelper.java │ │ │ └── decompiling │ │ │ │ └── ExpandableUrlTest.java │ │ └── frontend │ │ │ ├── HexSearch │ │ │ └── HexSearchTest.java │ │ │ ├── frame │ │ │ ├── main │ │ │ │ └── popup │ │ │ │ │ └── DiffPopupTest.java │ │ │ ├── overwrite │ │ │ │ └── OverwriteClassDialogTest.java │ │ │ └── plugins │ │ │ │ └── FileSelectorArrayRowTest.java │ │ │ └── utility │ │ │ └── AgentApiGeneratorTest.java │ │ └── kcc │ │ └── KeywordBasedCodeCompletionTest.java │ └── resources │ └── org │ └── jrd │ └── frontend │ └── frame │ └── main │ └── popup │ ├── testPatch1 │ └── testPatch2 ├── start.bat └── start.sh /.github/workflows/maven.yaml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | workflow_dispatch: 11 | 12 | jobs: 13 | build: 14 | runs-on: ${{ matrix.os }} 15 | 16 | strategy: 17 | matrix: 18 | os: [ubuntu-latest, windows-latest] 19 | java-version: [ 11, 17 ] 20 | fail-fast: false 21 | 22 | name: ${{ matrix.os }} - Java ${{ matrix.java-version }} - Maven 23 | steps: 24 | - name: Checkout JRD repo 25 | uses: actions/checkout@v2 26 | 27 | - name: Set up JDK ${{ matrix.java-version }} 28 | uses: actions/setup-java@v2 29 | with: 30 | java-version: ${{ matrix.java-version }} 31 | distribution: 'adopt' 32 | cache: maven 33 | 34 | - name: clone and build procyon devel we depeond on Linux 35 | if: matrix.os == 'ubuntu-latest' 36 | run: | 37 | set -x 38 | git clone https://github.com/mstrobel/procyon.git 39 | cd procyon 40 | git checkout develop 41 | sed "s/6.9/7.3/g" -i gradle/wrapper/gradle-wrapper.properties 42 | sed "s/.*.*//g" -i Procyon.Reflection/src/main/java/com/strobel/reflection/emit/CodeStream.java 43 | ./gradlew install 44 | shell: bash 45 | 46 | - name: clone and build procyon devel we depeond on Windows 47 | if: matrix.os == 'windows-latest' 48 | run: | 49 | @echo on 50 | git clone https://github.com/mstrobel/procyon.git 51 | cd procyon 52 | git checkout develop 53 | ./gradlew.bat install 54 | shell: cmd 55 | 56 | - name: Build JRD with Maven 57 | run: mvn --batch-mode --update-snapshots clean install 58 | 59 | - name: Create and populate config file on Linux 60 | if: matrix.os == 'ubuntu-latest' 61 | run: | 62 | set -x 63 | CONFIG_HOME="$HOME/.config/java-runtime-decompiler/conf" 64 | mkdir -p -v "$CONFIG_HOME" 65 | AGENT_PATH=$(find "${{ github.workspace }}" -name "decompiler-agent-*.jar" | head -n 1) 66 | echo "{\"AGENT_PATH\":\"${AGENT_PATH}\"}" > "$CONFIG_HOME/config.json" 67 | cat "$CONFIG_HOME/config.json" 68 | shell: bash 69 | 70 | - name: Create and populate config file on Windows 71 | if: matrix.os == 'windows-latest' 72 | run: | 73 | @echo on 74 | setLocal EnableDelayedExpansion 75 | set "CONFIG_HOME=%UserProfile%\.config\java-runtime-decompiler\conf" 76 | md %CONFIG_HOME% 77 | for /f "delims=" %%i in ('dir *decompiler-agent-*.jar /B /S') do set "AGENT_PATH=%%i" 78 | set "AGENT_PATH=%AGENT_PATH:\=/%" 79 | echo {"AGENT_PATH":"%AGENT_PATH%"} >"%CONFIG_HOME%\config.json" 80 | type "%CONFIG_HOME%\config.json" 81 | shell: cmd 82 | 83 | - name: Test Cli 84 | timeout-minutes: 10 85 | run: mvn --batch-mode test -Dtest=*CliTest -DfailIfNoTests=false "-Dsurefire.reportFormat=plain" 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #image creation output 2 | build.log 3 | images/build.log 4 | 5 | # Compiled class file 6 | *.class 7 | 8 | # Log file 9 | *.log 10 | 11 | # BlueJ files 12 | *.ctxt 13 | 14 | # Mobile Tools for Java (J2ME) 15 | .mtj.tmp/ 16 | 17 | #IDEA 18 | .idea/ 19 | *.iml 20 | target/ 21 | 22 | # Package Files # 23 | *.jar 24 | *.war 25 | *.ear 26 | *.zip 27 | *.tar.gz 28 | *.rar 29 | 30 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 31 | hs_err_pid* 32 | -------------------------------------------------------------------------------- /.plugin_config/codestyle_suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /.plugin_config/spotbugs_exclude.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /bumpVersion.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## resolve folder of this script, following all symlinks, 4 | ## http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in 5 | SCRIPT_SOURCE="${BASH_SOURCE[0]}" 6 | while [ -h "$SCRIPT_SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink 7 | SCRIPT_DIR="$( cd -P "$( dirname "$SCRIPT_SOURCE" )" && pwd )" 8 | SCRIPT_SOURCE="$(readlink "$SCRIPT_SOURCE")" 9 | # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located 10 | [[ $SCRIPT_SOURCE != /* ]] && SCRIPT_SOURCE="$SCRIPT_DIR/$SCRIPT_SOURCE" 11 | done 12 | readonly SCRIPT_DIR="$( cd -P "$( dirname "$SCRIPT_SOURCE" )" && pwd )" 13 | 14 | set -e 15 | 16 | current=`head ${SCRIPT_DIR}/pom.xml -n 20 | grep "" | sed "s;./\?version.;;g" | sed "s/\s\+//g"` 17 | echo "current version: $current" 18 | echo -n "type future one: " 19 | read future 20 | echo $current "->" $future 21 | echo "ok? y/n" 22 | read yn 23 | if [ ! $yn == y ] ; then 24 | echo aborted 25 | exit 0 26 | fi 27 | poms=`find ${SCRIPT_DIR}| grep "/pom.xml"` 28 | for pom in $poms ; do 29 | echo " * $pom * " 30 | cat $pom | grep --color "$current" -A 1 -B 3 31 | done 32 | 33 | echo "ok? y/n" 34 | read yn 35 | if [ ! $yn == y ] ; then 36 | echo aborted 37 | exit 0 38 | fi 39 | 40 | poms=`find ${SCRIPT_DIR}| grep "/pom.xml"` 41 | for pom in $poms ; do 42 | echo " * $pom * " 43 | sed -i "s;$current;$future;g" ${pom} 44 | cat $pom | grep --color "$future" -A 1 -B 3 45 | done 46 | 47 | echo "ok? y/n" 48 | read yn 49 | if [ ! $yn == y ] ; then 50 | echo "run 'git reset --hard' in ${SCRIPT_DIR}" 51 | exit 0 52 | fi 53 | pushd ${SCRIPT_DIR} 54 | git diff 55 | popd 56 | -------------------------------------------------------------------------------- /decompiler_agent/README.md: -------------------------------------------------------------------------------- 1 | # thermostat-decompiler-agent -------------------------------------------------------------------------------- /decompiler_agent/src/main/java/org/jrd/agent/AgentLogger.java: -------------------------------------------------------------------------------- 1 | package org.jrd.agent; 2 | 3 | /** 4 | * Class for logging exceptions in the Agent. 5 | */ 6 | public class AgentLogger { 7 | 8 | private static final String NULL_OBJECT_MESSAGE = "Trying to log null object"; 9 | 10 | private static class AgentLoggerHolder { 11 | // https://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom 12 | // https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java 13 | private static final AgentLogger INSTANCE = new AgentLogger(); 14 | } 15 | 16 | public static AgentLogger getLogger() { 17 | return AgentLoggerHolder.INSTANCE; 18 | } 19 | 20 | public void log(Object o) { 21 | String s = ""; 22 | 23 | if (o == null) { 24 | s = NULL_OBJECT_MESSAGE; 25 | } else if (o instanceof Throwable) { 26 | ((Throwable) o).printStackTrace(); 27 | return; 28 | } else { 29 | s = o.toString(); 30 | } 31 | 32 | System.err.println(s); 33 | } 34 | 35 | public static String classLoaderId(ClassLoader cl) { 36 | if (cl == null) { 37 | return "unknown"; 38 | } else { 39 | return cl.toString(); 40 | } 41 | } 42 | 43 | public static String moduleId(Module cl) { 44 | if (cl == null) { 45 | return "unknown"; 46 | } else { 47 | return cl.getName(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /decompiler_agent/src/main/java/org/jrd/agent/ClassFilter.java: -------------------------------------------------------------------------------- 1 | package org.jrd.agent; 2 | 3 | import java.nio.charset.StandardCharsets; 4 | import java.util.Optional; 5 | import java.util.regex.Pattern; 6 | 7 | public class ClassFilter { 8 | 9 | private final Pattern nameFilter; 10 | private final String bodySubstring; 11 | 12 | public ClassFilter(Optional nameFilter, Optional bodySubstring) { 13 | this.nameFilter = Pattern.compile(nameFilter.orElseGet(() -> ".*")); 14 | this.bodySubstring = bodySubstring.orElseGet(() -> ""); 15 | } 16 | 17 | public static Optional create(String substringAndRegexLine) { 18 | if (substringAndRegexLine == null) { 19 | return Optional.empty(); 20 | } 21 | final String[] substringAndRegex = substringAndRegexLine.trim().split("\\s+"); 22 | if (substringAndRegex.length == 0) { 23 | return Optional.empty(); 24 | } 25 | if (substringAndRegex.length == 1) { 26 | return Optional.of(new ClassFilter(Optional.of(substringAndRegex[0]), Optional.empty())); 27 | } 28 | return Optional 29 | .of(new ClassFilter(Optional.of(substringAndRegex[0]), Optional.of(AgentActionWorker.base64toSring(substringAndRegex[1])))); 30 | } 31 | 32 | public boolean match(InstrumentationProvider instrumentationProvider, Class loadedClass, String classLoader) { 33 | try { 34 | if (nameFilter.matcher(loadedClass.getName()).matches()) { 35 | byte[] b = instrumentationProvider.getClassBody(loadedClass, classLoader); 36 | String ascii = new String(b, StandardCharsets.US_ASCII); 37 | if (ascii.contains(bodySubstring)) { 38 | return true; 39 | } else { 40 | String utf8 = new String(b, StandardCharsets.UTF_8); 41 | if (utf8.contains(bodySubstring)) { 42 | return true; 43 | } 44 | } 45 | } 46 | return false; 47 | } catch (Exception ex) { 48 | return false; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /decompiler_agent/src/main/java/org/jrd/agent/ReceivedType.java: -------------------------------------------------------------------------------- 1 | package org.jrd.agent; 2 | 3 | public enum ReceivedType { 4 | OVERWRITE_CLASS, 5 | ADD_CLASS, 6 | ADD_JAR 7 | } 8 | -------------------------------------------------------------------------------- /decompiler_agent/src/main/java/org/jrd/agent/api/ClassClassLoaderMap.java: -------------------------------------------------------------------------------- 1 | package org.jrd.agent.api; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.stream.Collectors; 8 | 9 | public class ClassClassLoaderMap { 10 | // class->classlaoder->byte[] 11 | private Map> map = new HashMap<>(); 12 | 13 | public void reset() { 14 | map = new HashMap<>(); 15 | } 16 | 17 | public void remove(String clazz) { 18 | map.remove(clazz); 19 | } 20 | 21 | public void remove(String clazz, String classloader) { 22 | Map submap = map.get(clazz); 23 | if (submap == null) { 24 | //FIXME is this correct? 25 | return; 26 | } 27 | submap.remove(classloader); 28 | if (submap.isEmpty()) { 29 | map.remove(clazz); 30 | } 31 | } 32 | 33 | public byte[] get(String classname) { 34 | Map classes = map.get(classname); 35 | if (classes == null || classes.isEmpty()) { 36 | return null; 37 | } 38 | byte[] nullLoader = classes.get(null); 39 | if (nullLoader != null) { 40 | return nullLoader; 41 | } 42 | return new ArrayList(classes.values()).get(0); 43 | } 44 | 45 | public byte[] get(String classname, String classlaoder) { 46 | Map classes = map.get(classname); 47 | if (classes == null || classes.isEmpty()) { 48 | return null; 49 | } 50 | byte[] targettedReturn = classes.get(classlaoder); 51 | if (targettedReturn == null && !classes.isEmpty()) { 52 | return new ArrayList<>(classes.values()).get(0); 53 | } else { 54 | return targettedReturn; 55 | } 56 | } 57 | 58 | public byte[] getStrict(String classname, String classlaoder) { 59 | Map classes = map.get(classname); 60 | if (classes == null || classes.isEmpty()) { 61 | return null; 62 | } 63 | return classes.get(classlaoder); 64 | } 65 | 66 | public void put(String nameWithoutSlashes, byte[] classfileBuffer, String loader) { 67 | Map classes = map.get(nameWithoutSlashes); 68 | if (classes == null) { 69 | classes = new HashMap<>(); 70 | map.put(nameWithoutSlashes, classes); 71 | } 72 | classes.put(loader, classfileBuffer); 73 | } 74 | 75 | public List keySet() { 76 | return keySetPairs().stream().map(a -> a[0] + ":" + a[1]).collect(Collectors.toList()); 77 | } 78 | 79 | public List keySetPairs() { 80 | List r = new ArrayList<>(); 81 | for (Map.Entry> fqn : map.entrySet()) { 82 | for (String classloader : fqn.getValue().keySet()) { 83 | r.add(new String[]{fqn.getKey(), nullClassloaderToUnknown(classloader)}); 84 | } 85 | } 86 | return r; 87 | } 88 | 89 | public static String nullClassloaderToUnknown(String classloader) { 90 | return classloader == null ? "unknown" : classloader; 91 | } 92 | 93 | public static String unknownToNullClasslaoder(String unknown) { 94 | return "unknown".equals(unknown) ? null : unknown; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /decompiler_agent/src/main/java/org/jrd/backend/data/package-info.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data; 2 | 3 | /** 4 | * This package contains copypasted code from runtime-decompiler 5 | * This should remain as it is without excuses. Any developmen should happen in runtime-decompiler and then copypasted here. 6 | * 7 | * Thsi duplication have to remain minimal. Currently the only one file here should be 8 | * - MetadataProperties.java 9 | * - metadata.prop 10 | * which ensures handshake between runtime-decompiler and agent 11 | * Both should remain identical for ever. 12 | * 13 | * Once windows support symlinks, just link them and we are done... 14 | */ 15 | -------------------------------------------------------------------------------- /decompiler_agent/src/main/resources/org/jrd/backend/data/metadata.prop: -------------------------------------------------------------------------------- 1 | groupId=${project.groupId} 2 | name=${project.parent.name} 3 | timestamp=${timestamp} 4 | version=${project.version} 5 | -------------------------------------------------------------------------------- /images/hex.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## resolve folder of this script, following all symlinks: 4 | ## http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in 5 | SCRIPT_SOURCE="${BASH_SOURCE[0]}" 6 | while [ -h "$SCRIPT_SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink 7 | SCRIPT_DIR="$( cd -P "$( dirname "$SCRIPT_SOURCE" )" && pwd )" 8 | SCRIPT_SOURCE="$(readlink "$SCRIPT_SOURCE")" 9 | # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located 10 | [[ $SCRIPT_SOURCE != /* ]] && SCRIPT_SOURCE="$SCRIPT_DIR/$SCRIPT_SOURCE" 11 | done 12 | readonly THIS_SCRIPT_DIR="$( cd -P "$( dirname "$SCRIPT_SOURCE" )" && pwd )" 13 | readonly SCRIPT_DIR=`readlink -f "$THIS_SCRIPT_DIR/../"` 14 | 15 | sh $THIS_SCRIPT_DIR/start.sh -hex "$@" -------------------------------------------------------------------------------- /images/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | java-runtime-decompiler 7 | java-runtime-decompiler 8 | 9.1.1 9 | ../ 10 | 11 | 12 | java-runtime-decompiler-images 13 | Images 14 | Helper to create release images 15 | pom 16 | 17 | 18 | ${project.parent.basedir} 19 | 20 | 21 | 22 | 23 | java-runtime-decompiler 24 | runtime-decompiler 25 | 9.1.1 26 | 27 | 28 | java-runtime-decompiler 29 | decompiler-agent 30 | 9.1.1 31 | 32 | 33 | 34 | 35 | 36 | images 37 | 38 | 39 | 40 | exec-maven-plugin 41 | org.codehaus.mojo 42 | 1.6.0 43 | 44 | 45 | build-image 46 | install 47 | 48 | exec 49 | 50 | 51 | bash 52 | ${basedir}/image.sh ${project.version} 53 | ${basedir}/build.log 54 | 55 | 56 | 57 | clean-image 58 | clean 59 | 60 | exec 61 | 62 | 63 | rm 64 | -rvf ${basedir}/target 65 | ${basedir}/build.log 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /runtime-decompiler/.plugin_config: -------------------------------------------------------------------------------- 1 | ../.plugin_config/ -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/fife/ui/hex/event/HexEditorEvent.java: -------------------------------------------------------------------------------- 1 | // 2 | // Decompiled by Procyon v0.5.36 3 | // 4 | 5 | package org.fife.ui.hex.event; 6 | 7 | import org.fife.ui.hex.swing.HexEditor; 8 | 9 | import java.util.EventObject; 10 | 11 | public class HexEditorEvent extends EventObject { 12 | private static final long serialVersionUID = 1L; 13 | private int offset; 14 | private int added; 15 | private int removed; 16 | 17 | public HexEditorEvent(final HexEditor editor, final int offs, final int added, final int removed) { 18 | super(editor); 19 | this.offset = offs; 20 | this.added = added; 21 | this.removed = removed; 22 | } 23 | 24 | public int getAddedCount() { 25 | return this.added; 26 | } 27 | 28 | public HexEditor getHexEditor() { 29 | return (HexEditor) this.getSource(); 30 | } 31 | 32 | public int getOffset() { 33 | return this.offset; 34 | } 35 | 36 | public int getRemovedCount() { 37 | return this.removed; 38 | } 39 | 40 | public boolean isModification() { 41 | return this.getAddedCount() == this.getRemovedCount(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/fife/ui/hex/event/HexEditorListener.java: -------------------------------------------------------------------------------- 1 | // 2 | // Decompiled by Procyon v0.5.36 3 | // 4 | 5 | package org.fife.ui.hex.event; 6 | 7 | import java.util.EventListener; 8 | 9 | public interface HexEditorListener extends EventListener { 10 | void hexBytesChanged(HexEditorEvent p0); 11 | } 12 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/fife/ui/hex/event/HexSearchActionListener.java: -------------------------------------------------------------------------------- 1 | package org.fife.ui.hex.event; 2 | 3 | import org.fife.ui.hex.swing.HexSearch; 4 | import org.jrd.backend.core.Logger; 5 | 6 | import javax.swing.JComboBox; 7 | import javax.swing.JTextField; 8 | import java.awt.event.ActionEvent; 9 | import java.awt.event.ActionListener; 10 | 11 | public class HexSearchActionListener implements ActionListener { 12 | 13 | private final HexSearch hexSearchEngine; 14 | private final JTextField hexSearch; 15 | private final JComboBox hexSearchType; 16 | private final Method method; 17 | 18 | public enum Method { 19 | NEXT, 20 | PREV 21 | } 22 | 23 | public HexSearchActionListener( 24 | HexSearch hexSearchEngine, JTextField hexSearch, JComboBox hexSearchType, Method method 25 | ) { 26 | this.hexSearchEngine = hexSearchEngine; 27 | this.hexSearch = hexSearch; 28 | this.hexSearchType = hexSearchType; 29 | this.method = method; 30 | } 31 | 32 | @Override 33 | public void actionPerformed(ActionEvent actionEvent) { 34 | try { 35 | switch (method) { 36 | case NEXT: 37 | hexSearchEngine.next(hexSearch.getText(), (HexSearch.HexSearchOptions) hexSearchType.getSelectedItem()); 38 | break; 39 | case PREV: 40 | hexSearchEngine.previous(hexSearch.getText(), (HexSearch.HexSearchOptions) hexSearchType.getSelectedItem()); 41 | break; 42 | default: 43 | break; 44 | } 45 | } catch (Exception e) { 46 | Logger.getLogger().log(e); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/fife/ui/hex/event/HexSearchDocumentListener.java: -------------------------------------------------------------------------------- 1 | package org.fife.ui.hex.event; 2 | 3 | import org.fife.ui.hex.swing.HexSearch; 4 | import org.jrd.backend.core.Logger; 5 | 6 | import javax.swing.JComboBox; 7 | import javax.swing.JTextField; 8 | import javax.swing.event.DocumentEvent; 9 | import javax.swing.event.DocumentListener; 10 | import java.awt.event.ActionListener; 11 | 12 | public class HexSearchDocumentListener implements DocumentListener { 13 | 14 | private final HexSearch hexSearchEngine; 15 | private final JTextField hexSearch; 16 | private final JComboBox hexSearchType; 17 | private ActionListener wasNotFoundListener; 18 | 19 | public HexSearchDocumentListener(HexSearch hexSearchEngine, JTextField hexSearch, JComboBox hexSearchType) { 20 | this.hexSearchEngine = hexSearchEngine; 21 | this.hexSearch = hexSearch; 22 | this.hexSearchType = hexSearchType; 23 | } 24 | 25 | public HexSearchDocumentListener( 26 | HexSearch hexSearchEngine, JTextField hexSearch, JComboBox hexSearchType, 27 | ActionListener wasNotFoundListener 28 | ) { 29 | this(hexSearchEngine, hexSearch, hexSearchType); 30 | this.wasNotFoundListener = wasNotFoundListener; 31 | } 32 | 33 | @Override 34 | public void insertUpdate(DocumentEvent documentEvent) { 35 | find(); 36 | } 37 | 38 | @Override 39 | public void removeUpdate(DocumentEvent documentEvent) { 40 | find(); 41 | } 42 | 43 | @Override 44 | public void changedUpdate(DocumentEvent documentEvent) { 45 | find(); 46 | } 47 | 48 | private void find() { 49 | try { 50 | boolean wasFound = 51 | hexSearchEngine.searchHexCode(hexSearch.getText(), (HexSearch.HexSearchOptions) hexSearchType.getSelectedItem()); 52 | 53 | if (!wasFound && wasNotFoundListener != null) { 54 | wasNotFoundListener.actionPerformed(null); 55 | } 56 | } catch (Exception e) { 57 | Logger.getLogger().log(e); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/fife/ui/hex/package-info.java: -------------------------------------------------------------------------------- 1 | package org.fife.ui.hex; 2 | /* 3 | * Reincarnation - by decompilation of the only found binary - of http://fifesoft.com/#/hexeditor 4 | */ 5 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/fife/ui/hex/swing/ByteArrayTransferable.java: -------------------------------------------------------------------------------- 1 | // 2 | // Decompiled by Procyon v0.5.36 3 | // 4 | 5 | package org.fife.ui.hex.swing; 6 | 7 | import java.awt.datatransfer.DataFlavor; 8 | import java.awt.datatransfer.Transferable; 9 | import java.awt.datatransfer.UnsupportedFlavorException; 10 | import java.io.IOException; 11 | import java.io.StringReader; 12 | import java.nio.charset.StandardCharsets; 13 | 14 | public class ByteArrayTransferable implements Transferable { 15 | private static final DataFlavor[] FLAVORS; 16 | 17 | static { 18 | FLAVORS = new DataFlavor[]{DataFlavor.stringFlavor, DataFlavor.plainTextFlavor}; 19 | } 20 | 21 | private int offset; 22 | private byte[] bytes; 23 | 24 | public ByteArrayTransferable(final int offset, final byte[] bytes) { 25 | this.offset = offset; 26 | if (bytes != null) { 27 | this.bytes = bytes.clone(); 28 | } else { 29 | this.bytes = new byte[0]; 30 | } 31 | } 32 | 33 | public int getLength() { 34 | return this.bytes.length; 35 | } 36 | 37 | public int getOffset() { 38 | return this.offset; 39 | } 40 | 41 | public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException, IOException { 42 | if (flavor.equals(ByteArrayTransferable.FLAVORS[0])) { 43 | return new String(this.bytes, StandardCharsets.UTF_8); 44 | } 45 | if (flavor.equals(ByteArrayTransferable.FLAVORS[1])) { 46 | return new StringReader(new String(this.bytes, StandardCharsets.UTF_8)); 47 | } 48 | throw new UnsupportedFlavorException(flavor); 49 | } 50 | 51 | public DataFlavor[] getTransferDataFlavors() { 52 | return ByteArrayTransferable.FLAVORS.clone(); 53 | } 54 | 55 | public boolean isDataFlavorSupported(final DataFlavor flavor) { 56 | for (int i = 0; i < ByteArrayTransferable.FLAVORS.length; ++i) { 57 | if (flavor.equals(ByteArrayTransferable.FLAVORS[i])) { 58 | return true; 59 | } 60 | } 61 | return false; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/fife/ui/hex/swing/HexEditorTransferHandler.java: -------------------------------------------------------------------------------- 1 | // 2 | // Decompiled by Procyon v0.5.36 3 | // 4 | 5 | package org.fife.ui.hex.swing; 6 | 7 | import javax.swing.JComponent; 8 | import javax.swing.TransferHandler; 9 | import java.awt.datatransfer.DataFlavor; 10 | import java.awt.datatransfer.Transferable; 11 | import java.awt.datatransfer.UnsupportedFlavorException; 12 | import java.io.IOException; 13 | import java.nio.charset.StandardCharsets; 14 | 15 | class HexEditorTransferHandler extends TransferHandler { 16 | private static final long serialVersionUID = 1L; 17 | 18 | @Override 19 | public boolean canImport(final JComponent comp, final DataFlavor[] flavors) { 20 | final HexEditor editor = (HexEditor) comp; 21 | return editor.isEnabled() && this.getImportFlavor(flavors, editor) != null; 22 | } 23 | 24 | @Override 25 | protected Transferable createTransferable(final JComponent c) { 26 | final HexEditor e = (HexEditor) c; 27 | final int start = e.getSmallestSelectionIndex(); 28 | final int end = e.getLargestSelectionIndex(); 29 | final byte[] array = new byte[end - start + 1]; 30 | for (int i = end; i >= start; --i) { 31 | array[i - start] = e.getByte(i); 32 | } 33 | final ByteArrayTransferable bat = new ByteArrayTransferable(start, array); 34 | return bat; 35 | } 36 | 37 | @Override 38 | protected void exportDone(final JComponent source, final Transferable data, final int action) { 39 | if (action == 2) { 40 | final ByteArrayTransferable bat = (ByteArrayTransferable) data; 41 | final int offs = bat.getOffset(); 42 | final HexEditor e = (HexEditor) source; 43 | e.removeBytes(offs, bat.getLength()); 44 | } 45 | } 46 | 47 | private DataFlavor getImportFlavor(final DataFlavor[] flavors, final HexEditor e) { 48 | for (int i = 0; i < flavors.length; ++i) { 49 | if (flavors[i].equals(DataFlavor.stringFlavor)) { 50 | return flavors[i]; 51 | } 52 | } 53 | return null; 54 | } 55 | 56 | @Override 57 | public int getSourceActions(final JComponent c) { 58 | final HexEditor e = (HexEditor) c; 59 | return e.isEnabled() ? 3 : 1; 60 | } 61 | 62 | @Override 63 | public boolean importData(final JComponent c, final Transferable t) { 64 | final HexEditor e = (HexEditor) c; 65 | final boolean imported = false; 66 | final DataFlavor flavor = this.getImportFlavor(t.getTransferDataFlavors(), e); 67 | if (flavor != null) { 68 | try { 69 | final Object data = t.getTransferData(flavor); 70 | if (flavor.equals(DataFlavor.stringFlavor)) { 71 | final String text = (String) data; 72 | final byte[] bytes = text.getBytes(StandardCharsets.UTF_8); 73 | e.replaceSelection(bytes); 74 | } 75 | } catch (UnsupportedFlavorException ufe) { 76 | ufe.printStackTrace(); 77 | } catch (IOException ioe) { 78 | ioe.printStackTrace(); 79 | } 80 | } 81 | return imported; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/fife/ui/hex/swing/HexSearchParseException.java: -------------------------------------------------------------------------------- 1 | package org.fife.ui.hex.swing; 2 | 3 | public class HexSearchParseException extends RuntimeException { 4 | public HexSearchParseException(String exceptionMessage) { 5 | super(exceptionMessage); 6 | } 7 | 8 | public String getName() { 9 | return "HexSearch parse error"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/fife/ui/hex/swing/SearchState.java: -------------------------------------------------------------------------------- 1 | package org.fife.ui.hex.swing; 2 | 3 | public final class SearchState { 4 | private final int start; 5 | private final int end; 6 | private final boolean found; 7 | 8 | public SearchState(int start, int end, boolean found) { 9 | this.start = start; 10 | this.end = end; 11 | this.found = found; 12 | } 13 | 14 | public int getStart() { 15 | return start; 16 | } 17 | 18 | public int getEnd() { 19 | return end; 20 | } 21 | 22 | public boolean isFound() { 23 | return found; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/communication/CallDecompilerAgent.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.communication; 2 | 3 | import org.jrd.backend.core.Logger; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * This class is handling opening of communication socket and request submitting. 9 | */ 10 | public class CallDecompilerAgent implements DelegatingJrdAgent { 11 | 12 | public static final String DEFAULT_ADDRESS = "localhost"; 13 | public static final int DEFAULT_PORT = 5395; 14 | 15 | private final int port; 16 | private final String address; 17 | private final DelegatingHelper delegationCandidates = new DelegatingHelper(); 18 | 19 | /** 20 | * Constructor of the object 21 | * @param port port where to open socket 22 | * @param host socket host 23 | */ 24 | public CallDecompilerAgent(int port, String host) { 25 | if (host == null) { 26 | host = DEFAULT_ADDRESS; 27 | } 28 | 29 | if (port <= 0) { 30 | port = DEFAULT_PORT; 31 | } 32 | 33 | this.address = host; 34 | this.port = port; 35 | } 36 | 37 | /** 38 | * Opens a socket and sends the request to the agent via socket. 39 | * @param request either "CLASSES" or "BYTES \n className", other formats 40 | * are refused 41 | * @return agents response or null 42 | */ 43 | @Override 44 | public String submitRequest(final String request) { 45 | final Communicate comm = new Communicate(this.address, this.port); 46 | try { 47 | comm.println(request); 48 | return comm.readResponse(); 49 | } catch (IOException ex) { 50 | Logger.getLogger().log(Logger.Level.DEBUG, ex); 51 | return null; 52 | } finally { 53 | comm.close(); 54 | } 55 | } 56 | 57 | @Override 58 | public JrdAgent addDelegatingAgent(JrdAgent agent) { 59 | return delegationCandidates.addDelegatingAgent(agent); 60 | } 61 | 62 | @Override 63 | public JrdAgent removeDelegatingAgent(JrdAgent agent) { 64 | return delegationCandidates.removeDelegatingAgent(agent); 65 | } 66 | 67 | @Override 68 | public int cleanDelegatingAgents() { 69 | return delegationCandidates.cleanDelegatingAgents(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/communication/DelegatingJrdAgent.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.communication; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public interface DelegatingJrdAgent extends JrdAgent { 7 | 8 | enum CommandDelegationOptions { 9 | FIRST_OK /*eg getBytecode*/, 10 | ALL /*eg init, and most likely also get classes, but that needs set form all without duplications*/, 11 | MAIN_ONLY /*eg overwrite */ 12 | } 13 | 14 | String submitRequest(String request); 15 | 16 | JrdAgent addDelegatingAgent(JrdAgent agent); 17 | 18 | JrdAgent removeDelegatingAgent(JrdAgent agent); 19 | 20 | int cleanDelegatingAgents(); 21 | 22 | class DelegatingHelper { 23 | private final List delegationCandidates = new ArrayList<>(1); 24 | 25 | public JrdAgent addDelegatingAgent(JrdAgent agent) { 26 | if (!delegationCandidates.contains(agent)) { 27 | delegationCandidates.add(agent); 28 | return agent; 29 | } else { 30 | return null; 31 | } 32 | } 33 | 34 | public JrdAgent removeDelegatingAgent(JrdAgent agent) { 35 | if (delegationCandidates.contains(agent)) { 36 | delegationCandidates.remove(agent); 37 | return agent; 38 | } else { 39 | return null; 40 | } 41 | } 42 | 43 | public int cleanDelegatingAgents() { 44 | int r = delegationCandidates.size(); 45 | delegationCandidates.clear(); 46 | return r; 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/communication/ErrorCandidate.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.communication; 2 | 3 | public class ErrorCandidate { 4 | 5 | private static final String ERROR_ID = "ERROR"; 6 | private final String line; 7 | 8 | public ErrorCandidate(String line) { 9 | this.line = line; 10 | } 11 | 12 | protected String getIdentifier() { 13 | return ERROR_ID; 14 | } 15 | 16 | public String getLine() { 17 | return line; 18 | } 19 | 20 | public boolean isError() { 21 | return isErrorImpl(getLine()) != null; 22 | } 23 | 24 | public String getErrorMessage() { 25 | if (isError()) { 26 | return isErrorImpl(getLine()); 27 | } else { 28 | return "This was not error - " + getLine(); 29 | } 30 | } 31 | 32 | public static String toError(String message) { 33 | return ERROR_ID + " " + message; 34 | } 35 | 36 | public static String toError(Exception ex) { 37 | return toError(ex.toString()); 38 | } 39 | 40 | /** 41 | * @return null if line is not error, empty string or message otherwise 42 | */ 43 | private String isErrorImpl(String linex) { 44 | if (linex.startsWith(getIdentifier())) { 45 | String message = linex.replaceAll("^" + getIdentifier(), "").trim(); 46 | return message; 47 | } else { 48 | return null; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/communication/JrdAgent.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.communication; 2 | 3 | public interface JrdAgent { 4 | 5 | String submitRequest(String request); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/communication/TopLevelErrorCandidate.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.communication; 2 | 3 | public class TopLevelErrorCandidate extends ErrorCandidate { 4 | private static final String ERROR_RESPONSE = "error"; 5 | 6 | public TopLevelErrorCandidate(String line) { 7 | super(line); 8 | } 9 | 10 | public static String toError(String message) { 11 | return ERROR_RESPONSE + " " + message; 12 | } 13 | 14 | public static String toError(Exception ex) { 15 | return toError(ex.toString()); 16 | } 17 | 18 | @Override 19 | protected String getIdentifier() { 20 | return ERROR_RESPONSE; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/completion/JrdCompletionSettings.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.completion; 2 | 3 | import org.jrd.frontend.frame.main.decompilerview.SupportedKeySets; 4 | import org.kcc.CompletionItem; 5 | import org.kcc.CompletionSettings; 6 | 7 | public class JrdCompletionSettings extends CompletionSettings { 8 | 9 | private final boolean dynamicClasses; 10 | private final boolean configAdditionalClasses; 11 | private final boolean methodNames; 12 | private final boolean methodFullSignatures; 13 | 14 | public static JrdCompletionSettings getDefault(ClassesAndMethodsProvider isDynamic) { 15 | return getDefault(isDynamic(isDynamic)); 16 | } 17 | 18 | public static JrdCompletionSettings getDefault(boolean dynamicClasses) { 19 | if (dynamicClasses) { 20 | return new JrdCompletionSettings( 21 | SupportedKeySets.getDefaultSet(), CompletionSettings.OP.SPARSE, false, true, true, false, true, true 22 | ); 23 | } else { 24 | return new JrdCompletionSettings( 25 | SupportedKeySets.getDefaultSet(), CompletionSettings.OP.SPARSE, false, true, false, true, true, true 26 | ); 27 | } 28 | } 29 | 30 | public JrdCompletionSettings( 31 | CompletionItem.CompletionItemSet set, CompletionSettings.OP op, boolean caseSensitive, boolean showHelp, boolean dynamicClasses, 32 | boolean configAdditionalClasses, boolean methodNames, boolean methodFullSignatures 33 | ) { 34 | super(set, op, caseSensitive, showHelp); 35 | this.dynamicClasses = dynamicClasses; 36 | this.configAdditionalClasses = configAdditionalClasses; 37 | this.methodNames = methodNames; 38 | this.methodFullSignatures = methodFullSignatures; 39 | } 40 | 41 | public static boolean isDynamic(ClassesAndMethodsProvider isDynamic) { 42 | return !(isDynamic instanceof ClassesAndMethodsProvider.SettingsClassesAndMethodsProvider); 43 | } 44 | 45 | public boolean isDynamicClasses() { 46 | return dynamicClasses; 47 | } 48 | 49 | public boolean isConfigAdditionalClasses() { 50 | return configAdditionalClasses; 51 | } 52 | 53 | public boolean isMethodNames() { 54 | return methodNames; 55 | } 56 | 57 | public boolean isMethodFullSignatures() { 58 | return methodFullSignatures; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/core/AgentAttachManager.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.core; 2 | 3 | import org.jrd.backend.communication.CallDecompilerAgent; 4 | import org.jrd.backend.core.agentstore.KnownAgent; 5 | import org.jrd.backend.core.agentstore.KnownAgents; 6 | import org.jrd.backend.data.VmManager; 7 | import org.jrd.backend.data.cli.utils.AgentConfig; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Attach manager for agent contains utility methods and information about 13 | * attach. 14 | */ 15 | public class AgentAttachManager { 16 | 17 | private AgentLoader loader; 18 | private VmManager vmManager; 19 | 20 | public AgentAttachManager(VmManager vmManager) { 21 | this.vmManager = vmManager; 22 | this.loader = new AgentLoader(); 23 | } 24 | 25 | public VmDecompilerStatus attachAgentToVm(String vmId, int vmPid, AgentConfig agntConfig) { 26 | List knownAgentsOnPid = KnownAgents.getInstance().findAgents(vmPid); 27 | if (knownAgentsOnPid.size() > 0) { 28 | if (knownAgentsOnPid.size() > 1) { 29 | Logger.getLogger().log( 30 | Logger.Level.DEBUG, 31 | "warning, but more then one agent found " + vmId + " Taking " + "first one - " + knownAgentsOnPid.get(0).toString() 32 | ); 33 | } 34 | Logger.getLogger().log("reusing " + knownAgentsOnPid.get(0).toString()); 35 | VmDecompilerStatus status = new VmDecompilerStatus(true); 36 | status.setHostname(knownAgentsOnPid.get(0).getHost()); 37 | status.setListenPort(knownAgentsOnPid.get(0).getPort()); 38 | status.setVmId(vmId); 39 | status.setBytemanCompanion(knownAgentsOnPid.get(0).getBytemanCompanion()); 40 | vmManager.getVmInfoByID(vmId).replaceVmDecompilerStatus(status); 41 | return status; 42 | } else { 43 | Logger.getLogger().log(Logger.Level.DEBUG, "Attaching agent to VM '" + vmPid + "'"); 44 | int attachedPort = AgentLoader.INVALID_PORT; 45 | try { 46 | attachedPort = loader.attach(vmPid, agntConfig); 47 | } catch (Exception ex) { 48 | Logger.getLogger().log(Logger.Level.ALL, ex); 49 | } 50 | if (attachedPort == AgentLoader.INVALID_PORT) { 51 | Logger.getLogger().log(Logger.Level.DEBUG, "Failed to attach agent for VM '" + vmPid); 52 | return null; 53 | } 54 | VmDecompilerStatus status = new VmDecompilerStatus(); 55 | status.setHostname(CallDecompilerAgent.DEFAULT_ADDRESS); 56 | status.setListenPort(attachedPort); 57 | status.setVmId(vmId); 58 | vmManager.getVmInfoByID(vmId).replaceVmDecompilerStatus(status); 59 | return status; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/core/AgentLoader.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.core; 2 | 3 | import com.sun.tools.attach.AgentInitializationException; 4 | import com.sun.tools.attach.AgentLoadException; 5 | import com.sun.tools.attach.AttachNotSupportedException; 6 | 7 | import org.jrd.backend.communication.CallDecompilerAgent; 8 | import org.jrd.backend.communication.InstallDecompilerAgentImpl; 9 | import org.jrd.backend.data.cli.utils.AgentConfig; 10 | 11 | import java.io.IOException; 12 | import java.net.ServerSocket; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | * This class contains methods for attaching the agent. 18 | */ 19 | public class AgentLoader { 20 | 21 | static final int INVALID_PORT = -1; 22 | private static final int PORT_MIN = 10900; 23 | private static final int MAX_PORT_SLOTS = 200; 24 | private static final int PORT_MAX = PORT_MIN + MAX_PORT_SLOTS; 25 | 26 | private static final String AGENT_PORT_PROPERTY = "com.redhat.decompiler.thermostat.port"; 27 | 28 | AgentLoader() { 29 | } 30 | 31 | /** 32 | * This method handles the attachment of a decompiler agent to given VM. 33 | * 34 | * @param pid PID of the VM 35 | * @return port number if successful, else {@link #INVALID_PORT} 36 | */ 37 | public int attach(int pid, AgentConfig aconf) { 38 | return attachImpl(pid, aconf); 39 | } 40 | 41 | public static int attachImpl(int pid, AgentConfig aconf) { 42 | int port = aconf.getPort().orElse(findPort()); 43 | String[] installProps = createProperties(port); 44 | 45 | Logger.getLogger().log(Logger.Level.DEBUG, "Attempting to attach decompiler agent for VM '" + pid + "' on port '" + port + "'"); 46 | 47 | try { 48 | InstallDecompilerAgentImpl.install( 49 | Integer.toString(pid), false, false, CallDecompilerAgent.DEFAULT_ADDRESS, port, aconf.getLoneliness(), 50 | aconf.getLiveliness(), installProps 51 | ); 52 | } catch (IllegalArgumentException | 53 | IOException | 54 | AttachNotSupportedException | 55 | AgentLoadException | 56 | AgentInitializationException ex) { 57 | Logger.getLogger().log(Logger.Level.ALL, new RuntimeException("Attach failed!! Cause: ", ex)); 58 | return INVALID_PORT; 59 | } 60 | 61 | if (port > 0) { 62 | return port; 63 | } else { 64 | return INVALID_PORT; 65 | } 66 | } 67 | 68 | private static int findPort() { 69 | for (int i = PORT_MIN; i <= PORT_MAX; i++) { 70 | try { 71 | try (ServerSocket s = new ServerSocket(i)) { 72 | s.close(); 73 | return i; 74 | } 75 | } catch (IOException e) { 76 | Logger.getLogger() 77 | .log(Logger.Level.DEBUG, new RuntimeException("Could not open socket on port " + i + ". Trying again.", e)); 78 | } 79 | } 80 | 81 | throw new IllegalStateException("No ports available in range [" + PORT_MIN + "," + PORT_MAX + "]"); 82 | } 83 | 84 | private static String[] createProperties(int port) { 85 | List properties = new ArrayList<>(); 86 | 87 | properties.add(AGENT_PORT_PROPERTY + "=" + port); 88 | 89 | return properties.toArray(new String[]{}); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/core/VmDecompilerStatus.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.core; 2 | 3 | import org.jrd.backend.data.BytemanCompanion; 4 | 5 | import java.util.Arrays; 6 | 7 | /** 8 | * This class stores all information about the state of decompiler plugin on 9 | * each VM. 10 | */ 11 | 12 | public class VmDecompilerStatus { 13 | 14 | private final boolean reused; 15 | private String vmId; 16 | private String hostname; 17 | private int listenPort; 18 | private ClassInfo[] loadedClasses; 19 | private String loadedClassBytes; 20 | private BytemanCompanion bytemanCompanion; 21 | 22 | public VmDecompilerStatus() { 23 | this(false); 24 | } 25 | 26 | public VmDecompilerStatus(boolean reused) { 27 | this.loadedClassBytes = ""; 28 | this.loadedClasses = new ClassInfo[]{}; 29 | this.reused = reused; 30 | } 31 | 32 | public String getVmId() { 33 | return vmId; 34 | } 35 | 36 | public void setVmId(String vmId) { 37 | this.vmId = vmId; 38 | } 39 | 40 | public String getHostname() { 41 | return hostname; 42 | } 43 | 44 | public void setHostname(String hostname) { 45 | this.hostname = hostname; 46 | } 47 | 48 | public void setListenPort(int port) { 49 | this.listenPort = port; 50 | } 51 | 52 | public int getListenPort() { 53 | return listenPort; 54 | } 55 | 56 | public void setLoadedClasses(ClassInfo[] loadedClassNames) { 57 | this.loadedClasses = Arrays.copyOf(loadedClassNames, loadedClassNames.length); 58 | } 59 | 60 | public String getLoadedClassBytes() { 61 | return loadedClassBytes; 62 | } 63 | 64 | public String[] getLoadedClassNames() { 65 | return Arrays.stream(loadedClasses).map(ClassInfo::getName).toArray(String[]::new); 66 | } 67 | 68 | public ClassInfo[] getLoadedClasses() { 69 | return Arrays.copyOf(loadedClasses, loadedClasses.length); 70 | } 71 | 72 | public void setLoadedClassBytes(String value) { 73 | loadedClassBytes = value; 74 | } 75 | 76 | public boolean isReused() { 77 | return reused; 78 | } 79 | 80 | public void setBytemanCompanion(BytemanCompanion bytemanCompanion) { 81 | this.bytemanCompanion = bytemanCompanion; 82 | } 83 | 84 | public BytemanCompanion getBytemanCompanion() { 85 | return bytemanCompanion; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/core/agentstore/AgentLiveliness.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.core.agentstore; 2 | 3 | import java.util.Arrays; 4 | 5 | public enum AgentLiveliness { 6 | ONE_SHOT, 7 | SESSION, 8 | PERMANENT; 9 | 10 | @Override 11 | public String toString() { 12 | return super.toString(); 13 | } 14 | 15 | public String toHelp() { 16 | switch (this) { 17 | case ONE_SHOT: 18 | return "Agent will connect, do its job and disconnect."; 19 | case SESSION: 20 | return "Agent will connect and will remain connected untill end of session."; 21 | case PERMANENT: 22 | return "Agent will attach, and will disconnect only manually or on death of target process"; 23 | default: 24 | throw new RuntimeException("Unknown " + AgentLiveliness.class.getSimpleName() + " value " + this); 25 | } 26 | } 27 | 28 | public String toButton() { 29 | switch (this) { 30 | case ONE_SHOT: 31 | return "1"; 32 | case SESSION: 33 | return "S"; 34 | case PERMANENT: 35 | return "P"; 36 | default: 37 | throw new RuntimeException("Unknown " + AgentLiveliness.class.getSimpleName() + " value " + this); 38 | } 39 | } 40 | 41 | public static AgentLiveliness fromString(String s) throws IllegalArgumentException { 42 | return Arrays.stream(AgentLiveliness.values()).filter(v -> v.toString().equals(s)).findFirst() 43 | .orElseThrow(() -> new IllegalArgumentException("unknown value: " + s)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/core/agentstore/AgentLoneliness.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.core.agentstore; 2 | 3 | import java.util.Arrays; 4 | 5 | public enum AgentLoneliness { 6 | SINGLE_INSTANCE, 7 | ANONYMOUS, 8 | FORCING, 9 | AF; 10 | 11 | @Override 12 | public String toString() { 13 | return super.toString(); 14 | } 15 | 16 | public String toHelp() { 17 | switch (this) { 18 | case SINGLE_INSTANCE: 19 | return "Agent be allowed to attach to each process only once, unless " + FORCING + " is put to " + 20 | "following attachment (default)"; 21 | case ANONYMOUS: 22 | return "Agent will attach, but will not set the flag about its presence. Still, the property will be " + "set. (weird)"; 23 | case FORCING: 24 | return "Agent will attach, but will skip the check for single instance (sometimes useful)"; 25 | case AF: 26 | return "Agent will attach, but will skip the check for single instance" + 27 | "and will not set the flag about its presence. Still, the property will be set. (super weird)"; 28 | default: 29 | throw new RuntimeException("Unknown " + AgentLoneliness.class.getSimpleName() + " value " + this); 30 | } 31 | } 32 | 33 | public String toButton() { 34 | switch (this) { 35 | case SINGLE_INSTANCE: 36 | return "SI"; 37 | case ANONYMOUS: 38 | return "A"; 39 | case FORCING: 40 | return "F"; 41 | case AF: 42 | return "AF"; 43 | default: 44 | throw new RuntimeException("Unknown " + AgentLoneliness.class.getSimpleName() + " value " + this); 45 | } 46 | } 47 | 48 | public static AgentLoneliness fromString(String s) throws IllegalArgumentException { 49 | return Arrays.stream(AgentLoneliness.values()).filter(v -> v.toString().equals(s)).findFirst() 50 | .orElseThrow(() -> new IllegalArgumentException("unknown value: " + s)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/ArchiveManagerOptions.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | public class ArchiveManagerOptions { 8 | 9 | private static class ArchiveManagerOptionsHolder { 10 | private static final ArchiveManagerOptions INSTANCE = new ArchiveManagerOptions(); 11 | } 12 | 13 | public static ArchiveManagerOptions getInstance() { 14 | ArchiveManagerOptionsHolder.INSTANCE.setExtensions(Config.getConfig().getNestedJarExtensions()); 15 | return ArchiveManagerOptionsHolder.INSTANCE; 16 | } 17 | 18 | private List extensions = new ArrayList<>(); 19 | public static final List DEFAULTS = List.of(".zip", ".jar", ".war", ".ear"); 20 | 21 | public void setExtensions(List s) { 22 | extensions = Collections.unmodifiableList(s); 23 | } 24 | 25 | public List getExtensions() { 26 | return Collections.unmodifiableList(extensions); 27 | } 28 | 29 | public static String getExtensionString(String delimiter) { 30 | return String.join(delimiter, DEFAULTS); 31 | } 32 | 33 | public boolean isInner(String n) { 34 | String name = n.toLowerCase(); 35 | 36 | if (areExtensionsEmpty()) { 37 | return oneEnds(DEFAULTS, name); 38 | } else { 39 | return oneEnds(extensions, name); 40 | } 41 | } 42 | 43 | public boolean areExtensionsEmpty() { 44 | return extensions == null || extensions.isEmpty() || extensions.size() == 1 && extensions.get(0).trim().isEmpty(); 45 | } 46 | 47 | private boolean oneEnds(List suffixes, String name) { 48 | for (String suffix : suffixes) { 49 | if (name.endsWith(suffix.toLowerCase())) { 50 | return true; 51 | } 52 | } 53 | 54 | return false; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/BytemanCompanion.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data; 2 | 3 | import java.io.Serializable; 4 | 5 | public class BytemanCompanion implements Serializable { 6 | private final int bytemanPort; 7 | private final String bytemanHost; 8 | private final int postBytemanAgentPort; 9 | private final String postBytemanAgentHost; 10 | 11 | public BytemanCompanion(String bytemanHost, int bytemanPort, String postBytemanAgentHost, int postBytemanAgentPort) { 12 | this.bytemanHost = bytemanHost; 13 | this.bytemanPort = bytemanPort; 14 | this.postBytemanAgentHost = postBytemanAgentHost; 15 | this.postBytemanAgentPort = postBytemanAgentPort; 16 | } 17 | 18 | public BytemanCompanion(String byteman, String companion) { 19 | this( 20 | byteman.split(":")[0], Integer.parseInt(byteman.split(":")[1]), companion.split(":")[0], 21 | Integer.parseInt(companion.split(":")[1]) 22 | ); 23 | } 24 | 25 | public int getBytemanPort() { 26 | return bytemanPort; 27 | } 28 | 29 | public int getPostBytemanAgentPort() { 30 | return postBytemanAgentPort; 31 | } 32 | 33 | public String getBytemanHost() { 34 | return bytemanHost; 35 | } 36 | 37 | public String getPostBytemanAgentHost() { 38 | return postBytemanAgentHost; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/DependenciesReader.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data; 2 | 3 | import org.jrd.frontend.frame.main.LoadingDialogProvider; 4 | import org.jrd.frontend.frame.main.ModelProvider; 5 | 6 | import java.util.Base64; 7 | import java.util.Collection; 8 | 9 | import io.github.mkoncek.classpathless.api.ClassIdentifier; 10 | import io.github.mkoncek.classpathless.api.ClassesProvider; 11 | import io.github.mkoncek.classpathless.api.IdentifiedBytecode; 12 | 13 | public class DependenciesReader { 14 | 15 | private final ModelProvider provider; 16 | private final LoadingDialogProvider gui; 17 | 18 | public DependenciesReader(ModelProvider model, LoadingDialogProvider gui) { 19 | this.provider = model; 20 | this.gui = gui; 21 | } 22 | 23 | public Collection resolve(String clazz, String base64body) { 24 | byte[] bbytes = Base64.getDecoder().decode(base64body); 25 | Collection deps = io.github.mkoncek.classpathless.util.BytecodeExtractor 26 | .extractDependencies(new IdentifiedBytecode(new ClassIdentifier(clazz), bbytes), getClassesProvider()); 27 | return deps; 28 | } 29 | 30 | public VmInfo getVmInfo() { 31 | return provider.getVmInfo(); 32 | } 33 | 34 | public VmManager getVmManager() { 35 | return provider.getVmManager(); 36 | } 37 | 38 | public ClassesProvider getClassesProvider() { 39 | return provider.getClassesProvider(); 40 | } 41 | 42 | public LoadingDialogProvider getGui() { 43 | return gui; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/Main.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data; 2 | 3 | import org.jrd.backend.completion.ClassesAndMethodsProvider; 4 | import org.jrd.backend.core.Logger; 5 | import org.jrd.backend.data.cli.Cli; 6 | import org.jrd.backend.data.cli.Help; 7 | import org.jrd.frontend.frame.hex.StandaloneHex; 8 | import org.jrd.frontend.frame.main.decompilerview.DecompilationController; 9 | import org.jrd.frontend.frame.main.MainFrameView; 10 | 11 | import javax.swing.UIManager; 12 | 13 | public class Main { 14 | 15 | public static void main(String[] allArgs) throws Exception { 16 | Cli cli = new Cli(allArgs); 17 | if (cli.isGui()) { 18 | setLookAndFeel(); 19 | if (!cli.getFilteredArgs().isEmpty()) { 20 | Help.printHelpText(); 21 | StandaloneHex hexview = new StandaloneHex( 22 | cli.getFilteredArgs(), cli.isHex(), new ClassesAndMethodsProvider.SettingsClassesAndMethodsProvider() 23 | ); 24 | hexview.setVisible(true); 25 | } else { 26 | if (cli.isHex() && cli.getFilteredArgs().isEmpty()) { 27 | Help.printHelpText(); 28 | StandaloneHex hexview = new StandaloneHex( 29 | cli.getFilteredArgs(), cli.isHex(), new ClassesAndMethodsProvider.SettingsClassesAndMethodsProvider() 30 | ); 31 | hexview.setVisible(true); 32 | } else { 33 | MainFrameView mainView = new MainFrameView(); 34 | DecompilationController dec = new DecompilationController(mainView, cli.shouldBeVerbose()); 35 | mainView.getBytecodeDecompilerView().setCompletionHelper(dec); 36 | } 37 | } 38 | } else { 39 | cli.consumeCli(); 40 | } 41 | 42 | } 43 | 44 | public static void setLookAndFeel() { 45 | try { 46 | String laf = Config.getConfig().getLaF(); 47 | if (laf == null) { 48 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 49 | } else { 50 | UIManager.setLookAndFeel(laf); 51 | } 52 | } catch (Exception e) { 53 | Logger.getLogger().log(Logger.Level.DEBUG, e); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/Model.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data; 2 | 3 | import org.jrd.backend.decompiling.PluginManager; 4 | 5 | public final class Model { 6 | 7 | private VmManager vmManager = new VmManager(); 8 | private PluginManager pluginManager = new PluginManager(); 9 | 10 | private static final Model MODEL = new Model(); 11 | 12 | private Model() { 13 | } 14 | 15 | public VmManager getVmManager() { 16 | return vmManager; 17 | } 18 | 19 | public PluginManager getPluginManager() { 20 | return pluginManager; 21 | } 22 | 23 | public static Model getModel() { 24 | return MODEL; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/InMemoryJar.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.io.OutputStream; 7 | import java.nio.file.Files; 8 | import java.util.Date; 9 | import java.util.jar.Attributes; 10 | import java.util.jar.JarEntry; 11 | import java.util.jar.JarOutputStream; 12 | import java.util.jar.Manifest; 13 | 14 | public class InMemoryJar { 15 | private Manifest manifest = new Manifest(); 16 | ByteArrayOutputStream bos; 17 | JarOutputStream target; 18 | 19 | public InMemoryJar() { 20 | startManifest(); 21 | } 22 | 23 | public void startManifest() { 24 | manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); 25 | addToManifest("Can-Redefine-Classes", "true"); 26 | } 27 | 28 | public void setMainClass(String mainFqn) { 29 | if (mainFqn != null && !"".equals(mainFqn)) { 30 | addToManifest(Attributes.Name.MAIN_CLASS.toString(), mainFqn); 31 | } 32 | } 33 | 34 | public void addToManifest(String key, String value) { 35 | manifest.getMainAttributes().put(new Attributes.Name(key), value); 36 | } 37 | 38 | public JarOutputStream openJar(OutputStream os) throws IOException { 39 | return new JarOutputStream(os, manifest); 40 | } 41 | 42 | private static void addFile(JarOutputStream target, byte[] body, String fqn) throws IOException { 43 | String name = fqn.replace('.', '/') + ".class"; 44 | JarEntry entry = new JarEntry(name); 45 | entry.setTime(new Date().getTime()); 46 | target.putNextEntry(entry); 47 | target.write(body); 48 | target.closeEntry(); 49 | } 50 | 51 | public void addFile(byte[] body, String fqn) throws IOException { 52 | if (target == null) { 53 | open(); 54 | } 55 | InMemoryJar.addFile(target, body, fqn); 56 | } 57 | 58 | public void open() throws IOException { 59 | bos = new ByteArrayOutputStream(); 60 | target = this.openJar(bos); 61 | } 62 | 63 | public void close() throws IOException { 64 | if (target != null) { 65 | target.flush(); 66 | target.close(); 67 | target = null; 68 | } 69 | } 70 | 71 | public File save() throws IOException { 72 | File f = File.createTempFile("jrd", "test.zip"); 73 | f.deleteOnExit(); 74 | save(f); 75 | return f; 76 | } 77 | 78 | public void save(File f) throws IOException { 79 | if (target != null) { 80 | throw new RuntimeException("Not closed!"); 81 | } 82 | Files.write(f.toPath(), bos.toByteArray()); 83 | } 84 | 85 | public byte[] toBytes() throws IOException { 86 | if (target != null) { 87 | throw new RuntimeException("Not closed!"); 88 | } 89 | return bos.toByteArray(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/utils/BytecodeSorter.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli.utils; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Set; 6 | 7 | //todo, somehow reuse also for both compilations in patch and compile 8 | public interface BytecodeSorter { 9 | 10 | Map> sort(); 11 | 12 | Integer decide(); 13 | 14 | class HexDummySorter implements BytecodeSorter { 15 | 16 | private final Set> entrySet; 17 | 18 | public HexDummySorter(Set> entrySet) { 19 | this.entrySet = entrySet; 20 | } 21 | 22 | @Override 23 | public Map> sort() { 24 | Map> binariesToUpload = new HashMap(); 25 | Map defaultByteCodeMap = new HashMap<>(); 26 | for (Map.Entry singlePatched : entrySet) { 27 | defaultByteCodeMap.put(singlePatched.getKey(), singlePatched.getValue()); 28 | } 29 | binariesToUpload.put(null, defaultByteCodeMap); 30 | return binariesToUpload; 31 | } 32 | 33 | @Override 34 | public Integer decide() { 35 | return null; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/utils/FqnAndClassToJar.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli.utils; 2 | 3 | import java.io.File; 4 | 5 | public class FqnAndClassToJar { 6 | private final String fqn; 7 | private final File file; 8 | 9 | public FqnAndClassToJar(String fqn, File file) { 10 | this.fqn = fqn; 11 | this.file = file; 12 | } 13 | 14 | public String getFqn() { 15 | return fqn; 16 | } 17 | 18 | public File getFile() { 19 | return file; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/utils/ObtainedCodeWithNameAndBytecode.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli.utils; 2 | 3 | public class ObtainedCodeWithNameAndBytecode { 4 | private final String name; 5 | private final String base64Body; 6 | private final Integer bytecodeLevel; 7 | 8 | public ObtainedCodeWithNameAndBytecode(String name, String base64Body, Integer bytecodeLevel) { 9 | this.name = name; 10 | this.base64Body = base64Body; 11 | this.bytecodeLevel = bytecodeLevel; 12 | } 13 | 14 | public String getName() { 15 | return name; 16 | } 17 | 18 | public String getBase64Body() { 19 | return base64Body; 20 | } 21 | 22 | public Integer getBytecodeLevel() { 23 | return bytecodeLevel; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/utils/PluginWithOptions.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli.utils; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | import org.jrd.backend.decompiling.DecompilerWrapper; 5 | 6 | public class PluginWithOptions { 7 | 8 | private final DecompilerWrapper decompiler; 9 | private final String[] options; 10 | 11 | public PluginWithOptions(DecompilerWrapper decompiler, String[] options) { 12 | this.decompiler = decompiler; 13 | this.options = options; 14 | } 15 | 16 | @SuppressFBWarnings("EI_EXPOSE_REP") 17 | public DecompilerWrapper getDecompiler() { 18 | return decompiler; 19 | } 20 | 21 | @SuppressFBWarnings("EI_EXPOSE_REP") 22 | public String[] getOptions() { 23 | return options; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/utils/PluginWrapperWithMetaInfo.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli.utils; 2 | 3 | public class PluginWrapperWithMetaInfo { 4 | private final PluginWithOptions wrapper; 5 | private final boolean haveCompiler; 6 | 7 | public PluginWrapperWithMetaInfo(PluginWithOptions wrapper, boolean haveCompiler) { 8 | this.wrapper = wrapper; 9 | this.haveCompiler = haveCompiler; 10 | } 11 | 12 | public PluginWithOptions getWrapper() { 13 | return wrapper; 14 | } 15 | 16 | public boolean haveCompiler() { 17 | return haveCompiler; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/utils/ReceivedType.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli.utils; 2 | 3 | public enum ReceivedType { 4 | OVERWRITE_CLASS, 5 | ADD_CLASS, 6 | ADD_JAR 7 | } 8 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/utils/Saving.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli.utils; 2 | 3 | import org.jrd.backend.core.Logger; 4 | import org.jrd.frontend.utility.CommonUtils; 5 | 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.io.PrintStream; 9 | 10 | public class Saving implements CommonUtils.StatusKeeper { 11 | public static final String DEFAULT = "default"; 12 | public static final String EXACT = "exact"; 13 | public static final String FQN = "fqn"; 14 | public static final String DIR = "dir"; 15 | private final String as; 16 | private final String like; 17 | 18 | public Saving(String as, String like) { 19 | this.as = as; 20 | if (like == null) { 21 | this.like = DEFAULT; 22 | } else { 23 | this.like = like; 24 | } 25 | } 26 | 27 | public boolean shouldSave() { 28 | return getAs() != null; 29 | } 30 | 31 | @Override 32 | public void setText(String s) { 33 | if (shouldSave()) { 34 | System.out.println(s); 35 | } else { 36 | System.err.println(s); 37 | } 38 | } 39 | 40 | @Override 41 | public void onException(Exception ex) { 42 | Logger.getLogger().log(ex); 43 | } 44 | 45 | @SuppressWarnings("ReturnCount") // returns in switch cases 46 | public int toInt(String suffix) { 47 | switch (getLike()) { 48 | case FQN: 49 | return CommonUtils.FULLY_QUALIFIED_NAME; 50 | case EXACT: 51 | return CommonUtils.CUSTOM_NAME; 52 | case DIR: 53 | return CommonUtils.SRC_SUBDIRS_NAME; 54 | case DEFAULT: 55 | if (".java".equals(suffix)) { 56 | return CommonUtils.FULLY_QUALIFIED_NAME; 57 | } 58 | if (".class".equals(suffix)) { 59 | return CommonUtils.SRC_SUBDIRS_NAME; 60 | } 61 | return CommonUtils.CUSTOM_NAME; 62 | default: 63 | throw new RuntimeException("Unknown saving type: " + getLike() + ". Allowed are: " + FQN + "," + DIR + "," + EXACT); 64 | } 65 | } 66 | 67 | public PrintStream openPrintStream() throws IOException { 68 | return new PrintStream(new FileOutputStream(this.getAs()), true, "UTF-8"); 69 | } 70 | 71 | public String getAs() { 72 | return as; 73 | } 74 | 75 | public String getLike() { 76 | return like; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/workers/Api.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli.workers; 2 | 3 | import org.jrd.backend.data.VmInfo; 4 | import org.jrd.backend.data.VmManager; 5 | import org.jrd.backend.data.cli.CliUtils; 6 | import org.jrd.backend.data.cli.Help; 7 | import org.jrd.backend.data.cli.utils.Saving; 8 | import org.jrd.backend.decompiling.PluginManager; 9 | import org.jrd.frontend.utility.AgentApiGenerator; 10 | 11 | import java.io.PrintStream; 12 | import java.util.List; 13 | 14 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 15 | 16 | public class Api { 17 | 18 | private final List filteredArgs; 19 | private final Saving saving; 20 | private final VmManager vmManager; 21 | private final PluginManager pluginManager; 22 | 23 | public Api(List filteredArgs, Saving saving, VmManager vmManager, PluginManager pluginManager) { 24 | this.filteredArgs = filteredArgs; 25 | this.saving = saving; 26 | this.vmManager = vmManager; 27 | this.pluginManager = pluginManager; 28 | } 29 | 30 | @SuppressFBWarnings(value = "OS_OPEN_STREAM", justification = "The stream is clsoed as conditionally as is created") 31 | public VmInfo api() throws Exception { 32 | PrintStream out = System.out; 33 | try { 34 | if (saving != null && saving.getAs() != null) { 35 | out = saving.openPrintStream(); 36 | } 37 | 38 | if (filteredArgs.size() != 2) { 39 | throw new IllegalArgumentException("Incorrect argument count! Please use '" + Help.API_FORMAT + "'."); 40 | } 41 | 42 | VmInfo vmInfo = CliUtils.getVmInfo(filteredArgs.get(1), vmManager); 43 | AgentApiGenerator.initItems(vmInfo, vmManager, pluginManager); 44 | out.println(AgentApiGenerator.getInterestingHelp()); 45 | out.flush(); 46 | return vmInfo; 47 | } finally { 48 | if (saving != null && saving.getAs() != null) { 49 | out.close(); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/workers/AttachDetach.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli.workers; 2 | 3 | import org.jrd.backend.communication.CallDecompilerAgent; 4 | import org.jrd.backend.core.AgentAttachManager; 5 | import org.jrd.backend.core.VmDecompilerStatus; 6 | import org.jrd.backend.data.VmInfo; 7 | import org.jrd.backend.data.VmManager; 8 | import org.jrd.backend.data.cli.CliUtils; 9 | import org.jrd.backend.data.cli.Help; 10 | import org.jrd.backend.data.cli.Lib; 11 | import org.jrd.backend.data.cli.utils.AgentConfig; 12 | 13 | import java.util.List; 14 | 15 | public class AttachDetach { 16 | 17 | private final List filteredArgs; 18 | private final VmManager vmManager; 19 | 20 | public AttachDetach(List filteredArgs, VmManager vmManager) { 21 | this.filteredArgs = filteredArgs; 22 | this.vmManager = vmManager; 23 | } 24 | 25 | public VmInfo attach(AgentConfig agntConfig) throws Exception { 26 | final int mandatoryParam = 2; 27 | if (filteredArgs.size() < mandatoryParam) { 28 | throw new IllegalArgumentException("Incorrect argument count! Please use '" + Help.ATTACH_FORMAT + "'."); 29 | } 30 | if (CliUtils.guessType(filteredArgs.get(1)) != VmInfo.Type.LOCAL) { 31 | throw new IllegalArgumentException("Sorry, first argument must be running jvm PID, nothing else."); 32 | } 33 | VmInfo vmInfo = CliUtils.getVmInfo(filteredArgs.get(1), vmManager); 34 | VmDecompilerStatus status = new AgentAttachManager(vmManager).attachAgentToVm(vmInfo.getVmId(), vmInfo.getVmPid(), agntConfig); 35 | System.out.println("Attached. Listening on: " + status.getListenPort()); 36 | return vmInfo; 37 | } 38 | 39 | public void detach() { 40 | if (filteredArgs.size() < 2) { 41 | throw new IllegalArgumentException("Incorrect argument count! Please use '" + Help.DETACH_FORMAT + "'."); 42 | } 43 | if (filteredArgs.get(1).contains(":")) { 44 | String[] hostPort = filteredArgs.get(1).split(":"); 45 | Lib.detach(hostPort[0], Integer.parseInt(hostPort[1]), vmManager); 46 | } else { 47 | //TODO is pid? If so, detach its port, else localhost 48 | detach(Integer.parseInt(filteredArgs.get(1))); 49 | } 50 | } 51 | 52 | private void detach(int port) { 53 | detachLocalhost(port, vmManager); 54 | } 55 | 56 | public static void detachLocalhost(int port, VmManager vmManager) { 57 | Lib.detach(CallDecompilerAgent.DEFAULT_ADDRESS, port, vmManager); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/workers/InitClass.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli.workers; 2 | 3 | import org.jrd.backend.data.VmInfo; 4 | import org.jrd.backend.data.VmManager; 5 | import org.jrd.backend.data.cli.CliUtils; 6 | import org.jrd.backend.data.cli.Help; 7 | import org.jrd.backend.data.cli.Lib; 8 | 9 | import java.util.List; 10 | 11 | public class InitClass { 12 | 13 | private final List filteredArgs; 14 | private final VmManager vmManager; 15 | 16 | public InitClass(List filteredArgs, VmManager vmManager) { 17 | this.filteredArgs = filteredArgs; 18 | this.vmManager = vmManager; 19 | } 20 | 21 | public VmInfo init() throws Exception { 22 | if (filteredArgs.size() != 3) { 23 | throw new IllegalArgumentException("Incorrect argument count! Please use '" + Help.INIT_FORMAT + "'."); 24 | } 25 | String fqn = filteredArgs.get(2); 26 | VmInfo vmInfo = CliUtils.getVmInfo(filteredArgs.get(1), vmManager); 27 | Lib.initClass(vmInfo, vmManager, fqn, System.out); 28 | return vmInfo; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/workers/ListAgents.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli.workers; 2 | 3 | import org.jrd.backend.core.agentstore.KnownAgent; 4 | import org.jrd.backend.core.agentstore.KnownAgents; 5 | import org.jrd.backend.data.VmInfo; 6 | import org.jrd.backend.data.VmManager; 7 | import org.jrd.backend.data.cli.CliUtils; 8 | import org.jrd.backend.data.cli.Lib; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | import static org.jrd.backend.data.cli.CliSwitches.VERSIONS; 14 | 15 | public class ListAgents { 16 | 17 | private final VmManager vmManager; 18 | private final boolean verbose; 19 | 20 | public ListAgents(boolean verbose, VmManager vmManager) { 21 | this.verbose = verbose; 22 | this.vmManager = vmManager; 23 | } 24 | 25 | public List listAgents(List params) { 26 | boolean versions = false; 27 | if (params.size() > 1) { 28 | String filteredSecondParam = CliUtils.cleanParameter(params.get(1)); 29 | versions = filteredSecondParam.equals(VERSIONS); 30 | } 31 | return listAgents(versions); 32 | } 33 | 34 | private List listAgents(boolean versions) { 35 | List connections = new ArrayList<>(); 36 | for (KnownAgent agent : KnownAgents.getInstance().getAgents()) { 37 | System.out.println(agent.toPrint()); 38 | if (versions) { 39 | try { 40 | Lib.HandhshakeResult vmInfo = Lib.handshakeAgent(agent, vmManager); 41 | System.out.println(" - " + vmInfo.getAgentVersion()); 42 | System.out.println(" - " + vmInfo.getDiff()); 43 | connections.add(vmInfo.getVmInfo()); 44 | } catch (Exception ex) { 45 | if (verbose) { 46 | ex.printStackTrace(); 47 | } 48 | System.out.println(" - unknown version. Most likely old agent. Should work for most cases though."); 49 | } 50 | } 51 | } 52 | return connections; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/workers/ListJvms.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli.workers; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | import org.jrd.backend.data.VmInfo; 5 | import org.jrd.backend.data.VmManager; 6 | import org.jrd.backend.data.cli.utils.Saving; 7 | 8 | import java.io.IOException; 9 | import java.io.PrintStream; 10 | import java.util.List; 11 | 12 | import static org.jrd.backend.data.cli.CliSwitches.LIST_JVMS; 13 | 14 | public class ListJvms { 15 | 16 | private final List filteredArgs; 17 | private final Saving saving; 18 | private final VmManager vmManager; 19 | 20 | public ListJvms(List filteredArgs, Saving saving, VmManager vmManager) { 21 | this.filteredArgs = filteredArgs; 22 | this.saving = saving; 23 | this.vmManager = vmManager; 24 | } 25 | 26 | @SuppressFBWarnings(value = "OS_OPEN_STREAM", justification = "The stream is clsoed as conditionally as is created") 27 | public void listJvms() throws IOException { 28 | if (filteredArgs.size() != 1) { 29 | throw new RuntimeException(LIST_JVMS + " does not expect arguments."); 30 | } 31 | 32 | PrintStream out = System.out; 33 | try { 34 | if (saving != null && saving.getAs() != null) { 35 | out = saving.openPrintStream(); 36 | } 37 | 38 | for (VmInfo vmInfo : vmManager.getVmInfoSet()) { 39 | out.println(vmInfo.getVmPid() + " " + vmInfo.getVmName()); 40 | } 41 | 42 | out.flush(); 43 | } finally { 44 | if (saving != null && saving.getAs() != null) { 45 | out.close(); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/workers/ListPlugins.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli.workers; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | import org.jrd.backend.data.cli.CliUtils; 5 | import org.jrd.backend.data.cli.utils.Saving; 6 | import org.jrd.backend.decompiling.DecompilerWrapper; 7 | import org.jrd.backend.decompiling.PluginManager; 8 | 9 | import java.io.IOException; 10 | import java.io.PrintStream; 11 | import java.util.List; 12 | 13 | import static org.jrd.backend.data.cli.CliSwitches.LIST_PLUGINS; 14 | 15 | public class ListPlugins { 16 | 17 | private final List filteredArgs; 18 | private final Saving saving; 19 | private final PluginManager pluginManager; 20 | 21 | public ListPlugins(List filteredArgs, Saving saving, PluginManager pluginManager) { 22 | this.filteredArgs = filteredArgs; 23 | this.saving = saving; 24 | this.pluginManager = pluginManager; 25 | } 26 | 27 | @SuppressFBWarnings(value = "OS_OPEN_STREAM", justification = "The stream is clsoed as conditionally as is created") 28 | public void listPlugins() throws IOException { 29 | if (filteredArgs.size() != 1) { 30 | throw new RuntimeException(LIST_PLUGINS + " does not expect arguments."); 31 | } 32 | 33 | PrintStream out = System.out; 34 | try { 35 | if (saving != null && saving.getAs() != null) { 36 | out = saving.openPrintStream(); 37 | } 38 | 39 | for (DecompilerWrapper dw : pluginManager.getWrappers()) { 40 | out.printf( 41 | "%s %s/%s - %s%n", dw.getName(), dw.getScope(), CliUtils.invalidityToString(dw.isInvalidWrapper()), 42 | dw.getFileLocation() 43 | ); 44 | } 45 | 46 | out.flush(); 47 | } finally { 48 | if (saving != null && saving.getAs() != null) { 49 | out.close(); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/workers/Overrides.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli.workers; 2 | 3 | import org.jrd.backend.data.VmInfo; 4 | import org.jrd.backend.data.VmManager; 5 | import org.jrd.backend.data.cli.CliUtils; 6 | import org.jrd.backend.data.cli.Help; 7 | import org.jrd.backend.data.cli.Lib; 8 | 9 | import java.util.List; 10 | 11 | public class Overrides { 12 | 13 | private final List filteredArgs; 14 | private final VmManager vmManager; 15 | 16 | public Overrides(List filteredArgs, VmManager vmManager) { 17 | this.filteredArgs = filteredArgs; 18 | this.vmManager = vmManager; 19 | } 20 | 21 | public VmInfo removeOverrides() { 22 | if (filteredArgs.size() != 3) { 23 | throw new RuntimeException("expected two params: " + Help.REMOVE_OVERRIDES_FORMAT); 24 | } 25 | VmInfo vmInfo = CliUtils.getVmInfo(filteredArgs.get(1), vmManager); 26 | String regex = filteredArgs.get(2); 27 | String[] was = Lib.obtainOverrides(vmInfo, vmManager); 28 | Lib.removeOverrides(vmInfo, vmManager, regex); 29 | String[] is = Lib.obtainOverrides(vmInfo, vmManager); 30 | System.out.println("Removed: " + (was.length - is.length) + " (was: " + was.length + ", is: " + is.length + ")"); 31 | return vmInfo; 32 | } 33 | 34 | public VmInfo listOverrides() { 35 | if (filteredArgs.size() != 2) { 36 | throw new RuntimeException("expected two params: " + Help.LIST_OVERRIDES_FORMAT); 37 | } 38 | VmInfo vmInfo = CliUtils.getVmInfo(filteredArgs.get(1), vmManager); 39 | String[] overrides = Lib.obtainOverrides(vmInfo, vmManager); 40 | for (String override : overrides) { 41 | System.out.println(override); 42 | } 43 | System.out.println("Total: " + overrides.length); 44 | return vmInfo; 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/data/cli/workers/Shared.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli.workers; 2 | 3 | import org.jrd.backend.data.cli.utils.Saving; 4 | import org.jrd.frontend.frame.main.decompilerview.HexWithControls; 5 | import org.jrd.frontend.utility.CommonUtils; 6 | 7 | import java.io.IOException; 8 | import java.nio.charset.StandardCharsets; 9 | import java.util.stream.Collectors; 10 | 11 | public class Shared { 12 | 13 | private final Saving saving; 14 | private final boolean isHex; 15 | 16 | public Shared(boolean isHex, Saving saving) { 17 | this.isHex = isHex; 18 | this.saving = saving; 19 | } 20 | 21 | public boolean outOrSave(String name, String extension, String s) throws IOException { 22 | return outOrSave(name, extension, s.getBytes(StandardCharsets.UTF_8), false); 23 | } 24 | 25 | public boolean outOrSave(String name, String extension, byte[] body, boolean forceBytes) throws IOException { 26 | if (saving.shouldSave()) { 27 | return CommonUtils.saveByGui(saving.getAs(), saving.toInt(extension), extension, saving, name, body); 28 | } else { 29 | if (forceBytes) { 30 | if (isHex) { 31 | System.out.println(HexWithControls.bytesToStrings(body).stream().collect(Collectors.joining("\n"))); 32 | } else { 33 | System.out.write(body); 34 | } 35 | } else { 36 | System.out.println(new String(body, StandardCharsets.UTF_8)); 37 | } 38 | return true; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/decompiling/DecompilerWrapperDeserializer.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.decompiling; 2 | 3 | import com.google.gson.JsonDeserializationContext; 4 | import com.google.gson.JsonDeserializer; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonObject; 7 | import com.google.gson.JsonParseException; 8 | 9 | import java.lang.reflect.Type; 10 | import java.util.LinkedList; 11 | import java.util.List; 12 | 13 | public class DecompilerWrapperDeserializer implements JsonDeserializer { 14 | 15 | @Override 16 | public DecompilerWrapper deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) 17 | throws JsonParseException { 18 | final JsonObject json = jsonElement.getAsJsonObject(); 19 | 20 | final String name = json.get("Name").getAsString(); 21 | final String wrapperURL = json.get("WrapperURL").getAsString(); 22 | 23 | final List dependencyURLs = new LinkedList<>(); 24 | json.get("DependencyURL").getAsJsonArray().forEach(dependency -> dependencyURLs.add(dependency.getAsString())); 25 | 26 | final String decompilerURL = json.get("DecompilerDownloadURL").getAsString(); 27 | 28 | return new DecompilerWrapper(name, wrapperURL, dependencyURLs, decompilerURL); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/decompiling/DecompilerWrapperSerializer.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.decompiling; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import com.google.gson.JsonSerializationContext; 7 | import com.google.gson.JsonSerializer; 8 | 9 | import java.lang.reflect.Type; 10 | 11 | public class DecompilerWrapperSerializer implements JsonSerializer { 12 | 13 | @Override 14 | public JsonElement serialize(DecompilerWrapper decompilerWrapper, Type type, JsonSerializationContext jsonSerializationContext) { 15 | final JsonObject jsonObject = new JsonObject(); 16 | 17 | jsonObject.addProperty("Name", decompilerWrapper.getName()); 18 | jsonObject.addProperty("WrapperURL", decompilerWrapper.getWrapperUrl().getRawUrl()); 19 | 20 | final JsonArray jsonArray = new JsonArray(); 21 | decompilerWrapper.getDependencyUrls().forEach(url -> { 22 | jsonArray.add(url.getRawUrl()); 23 | }); 24 | 25 | jsonObject.add("DependencyURL", jsonArray); 26 | if (decompilerWrapper.getDecompilerDownloadUrl() == null) { 27 | jsonObject.addProperty("DecompilerDownloadURL", ""); 28 | } else { 29 | jsonObject.addProperty("DecompilerDownloadURL", decompilerWrapper.getDecompilerDownloadUrl().toString()); 30 | } 31 | 32 | return jsonObject; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/backend/decompiling/JavapDisassemblerWrapper.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.decompiling; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.io.PrintWriter; 7 | import java.io.StringWriter; 8 | import java.nio.charset.StandardCharsets; 9 | import java.nio.file.Files; 10 | import java.nio.file.Paths; 11 | 12 | public class JavapDisassemblerWrapper { 13 | 14 | private final String otherArgs; 15 | 16 | public JavapDisassemblerWrapper(String otherArgs) { 17 | this.otherArgs = otherArgs; 18 | } 19 | 20 | public String decompile(byte[] bytecode, String[] options) { 21 | try { 22 | File tempByteFile = bytesToFile(bytecode); 23 | File tempOutputFile = File.createTempFile("decompile-output", ".java"); 24 | PrintWriter printWriter = new PrintWriter(tempOutputFile, StandardCharsets.UTF_8); 25 | StringBuilder optionsString = new StringBuilder(); 26 | if (options != null) { 27 | for (String option : options) { 28 | optionsString.append(option); 29 | } 30 | } 31 | 32 | com.sun.tools.javap.Main.run(new String[]{otherArgs + optionsString, tempByteFile.getAbsolutePath()}, printWriter); 33 | 34 | return readStringFromFile(tempOutputFile.getAbsolutePath()); 35 | } catch (Exception e) { 36 | StringWriter errors = new StringWriter(); 37 | e.printStackTrace(new PrintWriter(errors)); 38 | return "Exception while decompiling" + errors; 39 | } 40 | } 41 | 42 | private File bytesToFile(byte[] bytes) throws IOException { 43 | File tempFile = File.createTempFile("temporary-byte-file", ".class"); 44 | tempFile.deleteOnExit(); 45 | 46 | try (FileOutputStream fos = new FileOutputStream(tempFile.getCanonicalPath())) { 47 | fos.write(bytes); 48 | } 49 | 50 | return tempFile; 51 | } 52 | 53 | private String readStringFromFile(String filePath) throws IOException { 54 | return Files.readString(Paths.get(filePath)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/about/AboutView.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.about; 2 | 3 | import org.jrd.backend.core.Logger; 4 | import org.jrd.backend.data.MetadataProperties; 5 | 6 | import javax.swing.JDialog; 7 | import javax.swing.JEditorPane; 8 | import javax.swing.JFrame; 9 | import javax.swing.JLabel; 10 | import javax.swing.JOptionPane; 11 | import javax.swing.event.HyperlinkEvent; 12 | import java.awt.Color; 13 | import java.awt.Desktop; 14 | import java.awt.Font; 15 | import java.io.IOException; 16 | import java.net.URISyntaxException; 17 | 18 | public class AboutView extends JDialog { 19 | 20 | public AboutView(JFrame mainFrameView, boolean showVersion) { 21 | JLabel label = new JLabel(); 22 | Font font = label.getFont(); 23 | 24 | String style = "font-family:" + font.getFamily() + ";" + "font-weight:" + (font.isBold() ? "bold" : "normal") + ";" + "font-size:" + 25 | font.getSize() + "pt;"; 26 | 27 | JEditorPane editorPane = new JEditorPane( 28 | "text/html", 29 | "" + "

Java-Runtime-Decompiler

" + "Version " + 30 | (showVersion ? MetadataProperties.getInstance().getVersion() : "No version") + "
" + 31 | "Licenced under the GNU General Public License v3.0
" + 32 | "Visit the GitHub repository" + 33 | " for more information.
" + "" 34 | ); 35 | 36 | editorPane.addHyperlinkListener(event -> { 37 | if (event.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { 38 | try { 39 | Desktop.getDesktop().browse(event.getURL().toURI()); 40 | } catch (IOException | URISyntaxException e1) { 41 | Logger.getLogger().log(Logger.Level.ALL, e1); 42 | } catch (UnsupportedOperationException e2) { 43 | Logger.getLogger().log(Logger.Level.ALL, new RuntimeException("Unable to open link.")); 44 | } 45 | 46 | } 47 | }); 48 | editorPane.setEditable(false); 49 | editorPane.setBackground(new Color(label.getBackground().getRGB())); 50 | 51 | JOptionPane.showMessageDialog(mainFrameView, editorPane, "About Java-Runtime-Decompiler", JOptionPane.INFORMATION_MESSAGE); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/filesystem/NewFsVmController.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.filesystem; 2 | 3 | import org.jrd.backend.data.VmManager; 4 | 5 | import javax.swing.JOptionPane; 6 | 7 | import java.io.File; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class NewFsVmController { 12 | 13 | NewFsVmView newConnectionView; 14 | VmManager vmManager; 15 | 16 | public NewFsVmController(NewFsVmView newConnectionView, VmManager vmManager) { 17 | this.newConnectionView = newConnectionView; 18 | this.vmManager = vmManager; 19 | newConnectionView.setAddButtonListener(e -> addFsVm()); 20 | } 21 | 22 | private void addFsVm() { 23 | String cp = newConnectionView.getCp(); 24 | String name = newConnectionView.getNameHelper(); 25 | boolean shouldBeSaved = newConnectionView.shouldBeSaved(); 26 | List r; 27 | 28 | try { 29 | r = cpToFiles(cp); 30 | } catch (InvalidClasspathException e) { 31 | JOptionPane.showMessageDialog(newConnectionView, e.getMessage(), "Unable to create FS VM", JOptionPane.WARNING_MESSAGE); 32 | return; 33 | } 34 | 35 | vmManager.createFsVM(r, name, shouldBeSaved); 36 | newConnectionView.dispose(); 37 | } 38 | 39 | public static List cpToFilesCaught(String input) { 40 | try { 41 | return cpToFiles(input); 42 | } catch (InvalidClasspathException ex) { 43 | throw new RuntimeException(ex); 44 | } 45 | } 46 | 47 | public static List cpToFiles(String input) throws InvalidClasspathException { 48 | String[] classpathElements = input.split(File.pathSeparator); 49 | 50 | if (classpathElements.length == 0) { 51 | throw new InvalidClasspathException("Classpath is empty."); 52 | } 53 | 54 | List classpathList = new ArrayList<>(classpathElements.length); 55 | for (String element : classpathElements) { 56 | File file = new File(element); 57 | 58 | if (!file.exists()) { 59 | throw new InvalidClasspathException("File '" + file.getAbsolutePath() + "' does not exist."); 60 | } else { 61 | classpathList.add(file); 62 | } 63 | } 64 | 65 | return classpathList; 66 | } 67 | 68 | public static class InvalidClasspathException extends Exception { 69 | public InvalidClasspathException(String s, Throwable cause) { 70 | super(s, cause); 71 | } 72 | 73 | public InvalidClasspathException(String s) { 74 | super(s); 75 | } 76 | 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/license/LicenseView.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.license; 2 | 3 | import org.jrd.backend.core.Logger; 4 | import org.jrd.frontend.frame.main.MainFrameView; 5 | 6 | import javax.swing.BorderFactory; 7 | import javax.swing.JDialog; 8 | import javax.swing.JScrollPane; 9 | import javax.swing.JTextArea; 10 | import javax.swing.border.EmptyBorder; 11 | import javax.swing.border.EtchedBorder; 12 | import java.awt.BorderLayout; 13 | import java.awt.Dimension; 14 | import java.io.BufferedReader; 15 | import java.io.IOException; 16 | import java.io.InputStream; 17 | import java.io.InputStreamReader; 18 | import java.nio.charset.StandardCharsets; 19 | 20 | public class LicenseView extends JDialog { 21 | 22 | JTextArea licenseTextArea; 23 | JScrollPane scrollPane; 24 | 25 | public LicenseView(MainFrameView mainFrameView) { 26 | licenseTextArea = new JTextArea(); 27 | licenseTextArea.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 28 | scrollPane = new JScrollPane(licenseTextArea); 29 | scrollPane.setBorder(BorderFactory.createCompoundBorder(new EmptyBorder(10, 10, 10, 10), new EtchedBorder())); 30 | 31 | InputStream in = getClass().getResourceAsStream("/LICENSE"); 32 | StringBuilder sb = new StringBuilder(); 33 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { 34 | reader.lines().forEach(s -> sb.append(s).append('\n')); 35 | } catch (IOException e) { 36 | sb.append("Unable to read LICENSE file."); 37 | Logger.getLogger().log(Logger.Level.ALL, sb.toString()); 38 | } 39 | 40 | licenseTextArea.setText(sb.toString()); 41 | licenseTextArea.setEditable(false); 42 | licenseTextArea.setCaretPosition(0); 43 | 44 | this.setTitle("License"); 45 | this.setSize(new Dimension(600, 650)); 46 | this.setMinimumSize(new Dimension(250, 330)); 47 | this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); 48 | this.setModalityType(ModalityType.APPLICATION_MODAL); 49 | this.setLayout(new BorderLayout()); 50 | this.add(scrollPane, BorderLayout.CENTER); 51 | this.setLocationRelativeTo(mainFrameView.getMainFrame()); 52 | this.setVisible(true); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/LoadingDialog.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main; 2 | 3 | import javax.swing.JButton; 4 | import javax.swing.JDialog; 5 | import javax.swing.JLabel; 6 | import javax.swing.JProgressBar; 7 | import javax.swing.SwingConstants; 8 | import java.awt.BorderLayout; 9 | import java.awt.Dimension; 10 | import java.awt.event.ActionEvent; 11 | import java.awt.event.ActionListener; 12 | import java.awt.event.WindowAdapter; 13 | import java.awt.event.WindowEvent; 14 | 15 | public class LoadingDialog extends JDialog { 16 | 17 | private JProgressBar progressBar = new JProgressBar(); 18 | private JButton abortButton = new JButton("Abort"); 19 | 20 | private ActionListener abortActionListener; 21 | 22 | public LoadingDialog(String title) { 23 | this.setTitle("Connecting"); 24 | this.setSize(new Dimension(256, 144)); 25 | this.setResizable(false); 26 | this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); 27 | this.setModalityType(ModalityType.APPLICATION_MODAL); 28 | this.setLayout(new BorderLayout()); 29 | this.setLocationRelativeTo(null); 30 | 31 | JLabel infoLabel = new JLabel(title); 32 | infoLabel.setHorizontalAlignment(SwingConstants.CENTER); 33 | this.add(infoLabel, BorderLayout.NORTH); 34 | this.add(progressBar, BorderLayout.CENTER); 35 | progressBar.setIndeterminate(true); 36 | this.add(abortButton, BorderLayout.SOUTH); 37 | 38 | this.addWindowListener(new WindowAdapter() { 39 | @Override 40 | public void windowClosing(WindowEvent e) { 41 | abort(); 42 | } 43 | }); 44 | 45 | abortButton.addActionListener(event -> abort()); 46 | } 47 | 48 | private void abort() { 49 | ActionEvent abortEvent = new ActionEvent(this, 0, null); 50 | abortActionListener.actionPerformed(abortEvent); 51 | } 52 | 53 | public void setAbortActionListener(ActionListener listener) { 54 | abortActionListener = listener; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/LoadingDialogProvider.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main; 2 | 3 | import java.awt.event.ActionListener; 4 | 5 | public interface LoadingDialogProvider { 6 | default void showLoadingDialog(ActionListener action, String title) { 7 | } 8 | 9 | default void hideLoadingDialog() { 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/ModelProvider.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main; 2 | 3 | import org.jrd.backend.data.VmInfo; 4 | import org.jrd.backend.data.VmManager; 5 | 6 | import io.github.mkoncek.classpathless.api.ClassesProvider; 7 | 8 | public interface ModelProvider { 9 | 10 | VmInfo getVmInfo(); 11 | 12 | VmManager getVmManager(); 13 | 14 | ClassesProvider getClassesProvider(); 15 | } 16 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/ClassOverwriter.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview; 2 | 3 | import org.jrd.backend.core.ClassInfo; 4 | import org.jrd.backend.decompiling.DecompilerWrapper; 5 | import org.jrd.frontend.frame.overwrite.LatestPaths; 6 | import org.jrd.frontend.frame.overwrite.OverwriteClassDialog; 7 | import org.jrd.frontend.utility.ScreenFinder; 8 | 9 | class ClassOverwriter { 10 | private final DecompilationController decompilationController; 11 | private LatestPaths lastLoaded = new LatestPaths(); 12 | 13 | ClassOverwriter(DecompilationController decompilationController) { 14 | this.decompilationController = decompilationController; 15 | } 16 | 17 | void overwriteClass(DecompilerWrapper selectedDecompiler, ClassInfo name, String buffer, byte[] binBuffer, int tab) { 18 | if (name == null) { 19 | throw new RuntimeException("null name, in overwriteClass"); 20 | } else if (name.getName() == null || name.getName().trim().isEmpty()) { 21 | name = new ClassInfo("???", name.getLocation(), name.getClassLoader(), "???", "???"); 22 | } 23 | 24 | final OverwriteClassDialog overwriteClassDialog = new OverwriteClassDialog( 25 | name, lastLoaded, buffer, binBuffer, decompilationController.getVmInfo(), decompilationController.getVmManager(), 26 | decompilationController.getPluginManager(), selectedDecompiler, tab, decompilationController.isVerbose(), 27 | decompilationController 28 | ); 29 | 30 | ScreenFinder.centerWindowToCurrentScreen(overwriteClassDialog); 31 | overwriteClassDialog.setVisible(true); 32 | 33 | lastLoaded.setLastManualUpload(overwriteClassDialog.getManualUploadPath()); 34 | lastLoaded.setLastSaveSrc(overwriteClassDialog.getSaveSrcPath()); 35 | lastLoaded.setLastSaveBin(overwriteClassDialog.getSaveBinPath()); 36 | lastLoaded.setFilesToCompile(overwriteClassDialog.getFilesToCompile()); 37 | lastLoaded.setOutputExternalFilesDir(overwriteClassDialog.getOutputExternalFilesDir()); 38 | lastLoaded.setOutputBinaries(overwriteClassDialog.getOutputBinaries()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/InitAddClassJar.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview; 2 | 3 | import org.jrd.backend.core.Logger; 4 | 5 | import javax.swing.JOptionPane; 6 | import java.awt.event.ActionEvent; 7 | import java.awt.event.ActionListener; 8 | import java.util.Arrays; 9 | 10 | class InitAddClassJar implements ActionListener { 11 | private final BytecodeDecompilerView bytecodeDecompilerView; 12 | 13 | InitAddClassJar(BytecodeDecompilerView bytecodeDecompilerView) { 14 | this.bytecodeDecompilerView = bytecodeDecompilerView; 15 | } 16 | 17 | @Override 18 | public void actionPerformed(ActionEvent actionEvent) { 19 | try { 20 | String[] fqn = new InitAddClassDialog( 21 | bytecodeDecompilerView.getLastFqn(), bytecodeDecompilerView.getLastAddedFqn(), bytecodeDecompilerView.getLastAddedFile() 22 | ).showAndGet(); 23 | 24 | if (fqn != null && fqn.length > 0) { 25 | if (fqn.length == 1) { 26 | bytecodeDecompilerView.initGui(fqn[0]); 27 | } else if (fqn.length == 2) { 28 | bytecodeDecompilerView.addClassGui(fqn[0], fqn[1]); 29 | } else if (fqn.length == 3) { 30 | bytecodeDecompilerView.addJar(Boolean.parseBoolean(fqn[0]), fqn[2], fqn[1]); 31 | } else { 32 | bytecodeDecompilerView.addClassesGui(Boolean.parseBoolean(fqn[0]), Arrays.copyOfRange(fqn, 4, fqn.length)); 33 | } 34 | } 35 | } catch (Exception ex) { 36 | Logger.getLogger().log(Logger.Level.ALL, ex); 37 | JOptionPane.showMessageDialog(bytecodeDecompilerView.getBuffers(), ex.getMessage()); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/LinesProvider.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview; 2 | 3 | import javax.swing.JComponent; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.util.List; 7 | 8 | public interface LinesProvider { 9 | 10 | enum LinesFormat { 11 | CHARS, 12 | HEX 13 | } 14 | 15 | List getLines(LinesFormat type); 16 | 17 | String getName(); 18 | 19 | void setLines(LinesFormat type, List nwContent) throws Exception; 20 | 21 | boolean isBin(); 22 | 23 | default boolean isText() { 24 | return !isBin(); 25 | } 26 | 27 | File getFile(); 28 | 29 | void setFile(File f); 30 | 31 | void open(File f) throws IOException; 32 | 33 | void save(File f) throws IOException; 34 | 35 | JComponent asComponent(); 36 | 37 | void undo(); 38 | 39 | void redo(); 40 | 41 | void resetUndoRedo(); 42 | 43 | void close(); 44 | 45 | } 46 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/SupportedKeySets.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview; 2 | 3 | import org.jrd.backend.core.Logger; 4 | import org.kcc.CompletionItem; 5 | import org.kcc.CompletionSettings; 6 | import org.kcc.wordsets.BytecodeKeywordsWithHelp; 7 | import org.kcc.wordsets.BytemanKeywords; 8 | import org.kcc.wordsets.JavaKeywordsWithHelp; 9 | import org.kcc.wordsets.JrdApiKeywords; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | public class SupportedKeySets { 16 | public static final SupportedKeySets JRD_KEY_SETS = 17 | new SupportedKeySets(new JrdApiKeywords(), new BytemanKeywords(), new BytecodeKeywordsWithHelp(), new JavaKeywordsWithHelp()); 18 | 19 | public static final CompletionSettings JRD_DEFAULT = new CompletionSettings(getDefaultSet(), CompletionSettings.OP.SPARSE, false, true); 20 | 21 | public static CompletionItem.CompletionItemSet getDefaultSet() { 22 | return JRD_KEY_SETS.sets[0]; 23 | } 24 | 25 | private final CompletionItem.CompletionItemSet[] sets; 26 | 27 | public SupportedKeySets(CompletionItem.CompletionItemSet... sets) { 28 | this.sets = sets; 29 | } 30 | 31 | public CompletionItem.CompletionItemSet[] getSets() { 32 | return Arrays.copyOf(sets, sets.length); 33 | } 34 | 35 | public List recognize(String textWithKeywords) { 36 | CompletionSettings.RecognitionResult[] rr = CompletionSettings.recognize(textWithKeywords, sets); 37 | List result = new ArrayList<>(); 38 | for (CompletionSettings.RecognitionResult r : rr) { 39 | Logger.getLogger().log(Logger.Level.DEBUG, sets[r.getIndex()].toString()); 40 | Logger.getLogger().log(Logger.Level.DEBUG, r.toString()); 41 | if (r.getPercent() > 5 && result.size() < 2) { 42 | result.add(r.getSet()); 43 | Logger.getLogger().log(Logger.Level.DEBUG, "accepted"); 44 | } 45 | } 46 | return result; 47 | } 48 | 49 | public boolean isByteman(CompletionItem.CompletionItemSet set) { 50 | return set.toString().equals(sets[1].toString()); 51 | } 52 | 53 | public boolean isJasm(CompletionItem.CompletionItemSet set) { 54 | return set.toString().equals(sets[2].toString()); 55 | } 56 | 57 | public boolean isJava(CompletionItem.CompletionItemSet set) { 58 | return set.toString().equals(sets[3].toString()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/UndoRedoKeyAdapter.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview; 2 | 3 | import java.awt.event.KeyAdapter; 4 | import java.awt.event.KeyEvent; 5 | 6 | import javax.swing.undo.UndoManager; 7 | 8 | class UndoRedoKeyAdapter extends KeyAdapter { 9 | private final SearchControlsPanel searchControlsPanel; 10 | private UndoManager undoManager = new UndoManager(); 11 | 12 | UndoRedoKeyAdapter(SearchControlsPanel searchControlsPanel) { 13 | this.searchControlsPanel = searchControlsPanel; 14 | } 15 | 16 | @Override 17 | public void keyReleased(KeyEvent e) { 18 | if (e.getKeyCode() == KeyEvent.VK_F3) { 19 | searchControlsPanel.clickNextButton(); 20 | } 21 | if (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK) { 22 | if (e.getKeyCode() == KeyEvent.VK_Z && undoManager.canUndo()) { 23 | undoManager.undo(); 24 | } else if (e.getKeyCode() == KeyEvent.VK_Y && undoManager.canRedo()) { 25 | undoManager.redo(); 26 | } 27 | } 28 | } 29 | 30 | public UndoManager getUndoManager() { 31 | return undoManager; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/dummycompiler/AbstractCompileAction.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.dummycompiler; 2 | 3 | import javax.swing.JMenuItem; 4 | 5 | public abstract class AbstractCompileAction extends JMenuItem { 6 | 7 | private final String stub; 8 | 9 | public AbstractCompileAction(String stub) { 10 | this.stub = stub; 11 | } 12 | 13 | @Override 14 | public String getText() { 15 | return "" + stub + ""; 16 | } 17 | 18 | public Object getStub() { 19 | return stub; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/dummycompiler/AbstractCompileAndRunAction.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.dummycompiler; 2 | 3 | import org.jrd.frontend.frame.main.decompilerview.dummycompiler.providers.ClasspathProvider; 4 | import org.jrd.frontend.frame.main.decompilerview.dummycompiler.providers.ExecuteMethodProvider; 5 | import org.jrd.frontend.frame.main.decompilerview.dummycompiler.providers.SaveProvider; 6 | import org.jrd.frontend.frame.main.decompilerview.dummycompiler.providers.UploadProvider; 7 | 8 | public abstract class AbstractCompileAndRunAction extends AbstractCompileAction { 9 | 10 | protected final ClasspathProvider classesAndMethodsProvider; 11 | protected final SaveProvider save; 12 | protected final UploadProvider upload; 13 | protected final ExecuteMethodProvider execute; 14 | 15 | public AbstractCompileAndRunAction( 16 | String stub, ClasspathProvider classesAndMethodsProvider, SaveProvider save, UploadProvider upload, 17 | ExecuteMethodProvider execute 18 | ) { 19 | super(stub); 20 | this.classesAndMethodsProvider = classesAndMethodsProvider; 21 | this.save = save; 22 | this.upload = upload; 23 | this.execute = execute; 24 | } 25 | 26 | @Override 27 | public String getText() { 28 | String s = super.getText(); 29 | if (classesAndMethodsProvider != null) { 30 | s = s + "
" + classesAndMethodsProvider.getClasspath().cpTextInfo() + "
"; 31 | } else { 32 | s = s + "
"; 33 | } 34 | if (save != null) { 35 | if (save.getSaveDirectory() == null) { 36 | s = s + "; no saving"; 37 | } else { 38 | s = s + "; save to: " + save.getSaveDirectory().getAbsolutePath(); 39 | } 40 | } 41 | if (execute != null) { 42 | if (execute.getMethodToExecute() == null) { 43 | s = s + "; nothing to execute"; 44 | } else { 45 | s = s + "; will be executed: `" + execute.getMethodToExecute() + "`"; 46 | } 47 | } 48 | if (upload != null) { 49 | if (!upload.isUploadEnabled()) { 50 | s = s + "; no adding to running vm"; 51 | } else { 52 | s = s + "; will be added to: " + upload.getTarget().getClasspath().cpTextInfo(); 53 | if (upload.isBoot()) { 54 | s = s + " to Boot cp!!!"; 55 | } 56 | } 57 | } 58 | return s; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/dummycompiler/ClassesAndMethodsProviderBasedClassesProvider.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.dummycompiler; 2 | 3 | import io.github.mkoncek.classpathless.api.ClassIdentifier; 4 | import io.github.mkoncek.classpathless.api.ClassesProvider; 5 | import io.github.mkoncek.classpathless.api.IdentifiedBytecode; 6 | import org.jrd.backend.completion.ClassesAndMethodsProvider; 7 | import org.jrd.backend.core.Logger; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collection; 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | 15 | public class ClassesAndMethodsProviderBasedClassesProvider implements ClassesProvider { 16 | private final ClassesAndMethodsProvider classesAndMethodsProvider; 17 | 18 | public ClassesAndMethodsProviderBasedClassesProvider(ClassesAndMethodsProvider classesAndMethodsProvider) { 19 | this.classesAndMethodsProvider = classesAndMethodsProvider; 20 | } 21 | 22 | @Override 23 | public Collection getClass(ClassIdentifier... classIdentifiers) { 24 | List result = new ArrayList<>(classIdentifiers.length); 25 | for (ClassIdentifier ci : classIdentifiers) { 26 | try { 27 | byte[] b = classesAndMethodsProvider.getClassItself(null, classIdentifiers[0].getFullName()); 28 | result.add(new IdentifiedBytecode(ci, b)); 29 | } catch (Exception ex) { 30 | Logger.getLogger().log(ex); 31 | } 32 | } 33 | return result; 34 | } 35 | 36 | @Override 37 | public List getClassPathListing() { 38 | return Arrays.stream(classesAndMethodsProvider.getClasses(null)).collect(Collectors.toList()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/dummycompiler/JustBearerAction.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.dummycompiler; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | 5 | public class JustBearerAction extends AbstractCompileAction { 6 | 7 | private final String append; 8 | private AbstractCompileAction original; 9 | 10 | public JustBearerAction(String placeholder, String append) { 11 | super(placeholder + " - " + append); 12 | this.append = append; 13 | } 14 | 15 | @Override 16 | public String getText() { 17 | if (original == null) { 18 | return super.getText() + "
"; 19 | } else { 20 | return super.getText() + "
" + original.getText(); 21 | } 22 | } 23 | 24 | @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "looks good") 25 | public void setOriginal(AbstractCompileAction original) { 26 | this.original = original; 27 | this.setEnabled(true); 28 | this.setText("last used - " + original.getText() + " " + append); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/dummycompiler/NullClassesProvider.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.dummycompiler; 2 | 3 | import io.github.mkoncek.classpathless.api.ClassIdentifier; 4 | import io.github.mkoncek.classpathless.api.ClassesProvider; 5 | import io.github.mkoncek.classpathless.api.IdentifiedBytecode; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Collection; 9 | import java.util.List; 10 | 11 | class NullClassesProvider implements ClassesProvider { 12 | @Override 13 | public Collection getClass(ClassIdentifier... classIdentifiers) { 14 | return new ArrayList<>(0); 15 | } 16 | 17 | @Override 18 | public List getClassPathListing() { 19 | return new ArrayList(0); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/dummycompiler/providers/ClasspathProvider.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.dummycompiler.providers; 2 | 3 | import org.jrd.backend.completion.ClassesAndMethodsProvider; 4 | import org.jrd.backend.data.VmInfo; 5 | import org.jrd.backend.data.VmManager; 6 | 7 | public interface ClasspathProvider { 8 | 9 | ClassesAndMethodsProvider getClasspath(); 10 | 11 | VmInfo getVmInfo(); 12 | 13 | VmManager getVmManager(); 14 | 15 | class SettingsClasspathProvider implements ClasspathProvider { 16 | 17 | @Override 18 | public ClassesAndMethodsProvider getClasspath() { 19 | return new ClassesAndMethodsProvider.SettingsClassesAndMethodsProvider(); 20 | } 21 | 22 | @Override 23 | public VmInfo getVmInfo() { 24 | return null; 25 | } 26 | 27 | @Override 28 | public VmManager getVmManager() { 29 | return null; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/dummycompiler/providers/ExecuteMethodProvider.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.dummycompiler.providers; 2 | 3 | public interface ExecuteMethodProvider { 4 | 5 | String getMethodToExecute(); 6 | } 7 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/dummycompiler/providers/MainProviders.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.dummycompiler.providers; 2 | 3 | public class MainProviders { 4 | 5 | private final ClasspathProvider classpathProvider; 6 | private final ExecuteMethodProvider execute; 7 | private final SaveProvider save; 8 | private final UploadProvider uploadProvider; 9 | 10 | public MainProviders( 11 | ClasspathProvider classpathProvider, ExecuteMethodProvider execute, SaveProvider save, UploadProvider uploadProvider 12 | ) { 13 | this.classpathProvider = classpathProvider; 14 | this.execute = execute; 15 | this.save = save; 16 | this.uploadProvider = uploadProvider; 17 | } 18 | 19 | public ClasspathProvider getClasspathProvider() { 20 | return classpathProvider; 21 | } 22 | 23 | public ExecuteMethodProvider getExecute() { 24 | return execute; 25 | } 26 | 27 | public SaveProvider getSave() { 28 | return save; 29 | } 30 | 31 | public UploadProvider getUploadProvider() { 32 | return uploadProvider; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/dummycompiler/providers/SaveProvider.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.dummycompiler.providers; 2 | 3 | import java.io.File; 4 | 5 | public interface SaveProvider { 6 | 7 | File getSaveDirectory(); 8 | } 9 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/dummycompiler/providers/UploadProvider.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.dummycompiler.providers; 2 | 3 | public interface UploadProvider { 4 | 5 | ClasspathProvider getTarget(); 6 | 7 | boolean isUploadEnabled(); 8 | 9 | void resetUpload(); 10 | 11 | boolean isBoot(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/dummycompiler/templates/BytemanSkeletonTemplateMenuItem.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.dummycompiler.templates; 2 | 3 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; 4 | 5 | import java.awt.event.ActionEvent; 6 | import java.awt.event.ActionListener; 7 | 8 | import javax.swing.JMenuItem; 9 | 10 | public class BytemanSkeletonTemplateMenuItem extends JMenuItem { 11 | 12 | private static final String DYNAMIC_SKELETON = 13 | "\n" + "# Warning! If you change name of the rule, the automatic unloads/updates/deletes may stop to work.\n" + 14 | "# use final name before first submit to remote vm\n" + "\n" + "# rule skeleton\n" + "RULE \n" + 15 | " CLASS \n" + " METHOD \n" + " BIND \n" + " IF \n" + 16 | " DO \n" + " ENDRULE"; 17 | 18 | public BytemanSkeletonTemplateMenuItem(final RSyntaxTextArea source) { 19 | super("byteman skeleton"); 20 | this.addActionListener(new ActionListener() { 21 | @Override 22 | public void actionPerformed(ActionEvent actionEvent) { 23 | source.append(getDynamicSkeleton(null)); 24 | } 25 | }); 26 | } 27 | 28 | public static String getDynamicSkeleton(String clazz) { 29 | if (clazz == null) { 30 | return DYNAMIC_SKELETON; 31 | } else { 32 | return DYNAMIC_SKELETON.replace("", clazz).replace("", clazz + ".unique.rule.name"); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/dummycompiler/templates/BytemanTemplateMenuItem.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.dummycompiler.templates; 2 | 3 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; 4 | 5 | import javax.swing.JMenuItem; 6 | import java.awt.event.ActionEvent; 7 | import java.awt.event.ActionListener; 8 | 9 | public class BytemanTemplateMenuItem extends JMenuItem { 10 | 11 | private static final String STATIC_OBJECTFINALIZE = 12 | "\n" + "# Warning! If you change name of the rule, the automatic unloads/updates/deletes may stop to work.\n" + 13 | "# use final name before first submit to remote vm\n" + "RULE trace Object.finalize\n" + " CLASS ^java.lang.Object\n" + 14 | " METHOD " + "finalize\n" + " IF TRUE\n" + " DO System.out.println(\"Finalizing \" + $0)\n" + " ENDRULE"; 15 | 16 | public BytemanTemplateMenuItem(final RSyntaxTextArea source) { 17 | super("byteman all finalizers"); 18 | this.addActionListener(new ActionListener() { 19 | @Override 20 | public void actionPerformed(ActionEvent actionEvent) { 21 | source.append(STATIC_OBJECTFINALIZE); 22 | } 23 | }); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/dummycompiler/templates/Jasm2TemplateMenuItem.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.dummycompiler.templates; 2 | 3 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; 4 | 5 | import java.awt.event.ActionEvent; 6 | import java.awt.event.ActionListener; 7 | 8 | import javax.swing.JMenuItem; 9 | 10 | public class Jasm2TemplateMenuItem extends JMenuItem { 11 | public Jasm2TemplateMenuItem(final RSyntaxTextArea source) { 12 | super("jasm swing hello with dependence example"); 13 | this.addActionListener(new ActionListener() { 14 | @Override 15 | public void actionPerformed(ActionEvent actionEvent) { 16 | source.append( 17 | "package jrd/template;\n" + "\n" + "super public class HelloJasm\n" + "\tversion 52:0\n" + "{\n" + 18 | " public Method \"\":\"()V\"\n" + "\tstack 1 locals 1\n" + " {\n" + "\t\taload_0;\n" + 19 | "\t\tinvokespecial\tMethod java/lang/Object.\"\":\"()V\";\n" + "\t\treturn;\n" + " }\n" + 20 | " public static Method start:\"()V\"\n" + "\tstack 2 locals 0\n" + " {\n" + 21 | "\t\tgetstatic\tField java/lang/System.out:\"Ljava/io/PrintStream;\";\n" + 22 | "\t\tldc\tString \"Hello from jasm! See terminal!\";\n" + 23 | "\t\tinvokevirtual\tMethod java/io/PrintStream.println:\"(Ljava/lang/String;)V\";\n" + 24 | "\t\t//invokestatic\tMethod showJrdAbout:\"()V\";\n" + 25 | "\t\t//invokestatic\tMethod dunmpAgentVars:\"()V\";\n" + 26 | "\t\tgetstatic\tField java/lang/System.out:\"Ljava/io/PrintStream;\";\n" + "\t\tldc\tString \"done\";\n" + 27 | "\t\tinvokevirtual\tMethod java/io/PrintStream.println:\"(Ljava/lang/String;)V\";\n" + "\t\treturn;\n" + 28 | " }\n" + " public static varargs Method main:\"([Ljava/lang/String;)V\"\n" + "\tstack 0 locals 1\n" + 29 | " {\n" + "\t\tinvokestatic\tMethod start:\"()V\";\n" + "\t\treturn;\n" + " }\n" + 30 | " public static Method showJrdAbout:\"()V\"\n" + "\tstack 4 locals 0\n" + " {\n" + 31 | "\t\tnew\tclass org/jrd/frontend/frame/about/AboutView;\n" + "\t\tdup;\n" + "\t\taconst_null;\n" + 32 | "\t\ticonst_0;\n" + "\t\tinvokespecial\t" + 33 | "Method org/jrd/frontend/frame/about/AboutView.\"\":\"(Ljavax/swing/JFrame;Z)V\";\n" + 34 | "\t\ticonst_1;\n" + 35 | "\t\tinvokevirtual\tMethod org/jrd/frontend/frame/about/AboutView.setVisible:\"(Z)V\";\n" + 36 | "\t\treturn;\n" + " }\n" + " public static Method dunmpAgentVars:\"()V\"\n" + "\tstack 1 locals 0\n" + 37 | " {\n" + "\t\tinvokestatic\tMethod org/jrd/agent/api/Variables.dumpAll:\"()Ljava/lang/String;\";\n" + 38 | "\t\tpop;\n" + "\t\treturn;\n" + " }\n" + "\n" + "} // end Class HelloJasm\n" 39 | ); 40 | } 41 | }); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/dummycompiler/templates/JasmTemplateMenuItem.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.dummycompiler.templates; 2 | 3 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; 4 | 5 | import java.awt.event.ActionEvent; 6 | import java.awt.event.ActionListener; 7 | 8 | import javax.swing.JMenuItem; 9 | 10 | public class JasmTemplateMenuItem extends JMenuItem { 11 | public JasmTemplateMenuItem(final RSyntaxTextArea source) { 12 | super("jasm hello world"); 13 | this.addActionListener(new ActionListener() { 14 | @Override 15 | public void actionPerformed(ActionEvent actionEvent) { 16 | source.append( 17 | "\npackage jrd/template;\n" + "\n" + "super public class HelloJasm\n" + "\tversion 52:0\n" + "{\n" + 18 | " public Method \"\":\"()V\"\n" + "\tstack 1 locals 1\n" + " {\n" + "\t\taload_0;\n" + 19 | "\t\tinvokespecial\tMethod java/lang/Object.\"\":\"()V\";\n" + "\t\treturn;\n" + " }\n" + 20 | " public static Method start:\"()V\"\n" + "\tstack 2 locals 0\n" + " {\n" + 21 | "\t\tgetstatic\tField java/lang/System.out:\"Ljava/io/PrintStream;\";\n" + 22 | "\t\tldc\tString \"hello on stdout. See terminal!\";\n" + 23 | "\t\tinvokevirtual\tMethod java/io/PrintStream.println:\"(Ljava/lang/String;)V\";\n" + "\t\treturn;\n" + 24 | " }\n" + " public static Method main:\"([Ljava/lang/String;)V\"\n" + "\tstack 0 locals 1\n" + " {\n" + 25 | "\t\tinvokestatic\tMethod start:\"()V\";\n" + "\t\treturn;\n" + " }\n" + "\n" + 26 | "} // end Class HelloJasm\n" 27 | ); 28 | } 29 | }); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/verifiers/ClassVerifier.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.verifiers; 2 | 3 | import org.jrd.backend.core.Logger; 4 | import org.jrd.backend.data.cli.Lib; 5 | 6 | import javax.swing.event.DocumentEvent; 7 | import java.io.File; 8 | import java.nio.file.Files; 9 | 10 | public class ClassVerifier extends FileVerifier { 11 | 12 | public ClassVerifier(GetSetText source, GetSetText target) { 13 | super(source, target); 14 | } 15 | 16 | public boolean verifySource(DocumentEvent documentEvent) { 17 | boolean intermezo = super.verifySource(documentEvent); 18 | if (intermezo) { 19 | File f = new File(source.getText()); 20 | byte[] b = null; 21 | try { 22 | b = Files.readAllBytes(f.toPath()); 23 | } catch (Exception ex) { 24 | target.setText(ex.getMessage()); 25 | Logger.getLogger().log(ex); 26 | } 27 | if (b == null || b.length == 0) { 28 | target.setText("is empty"); 29 | } else { 30 | try { 31 | target.setText(Lib.readClassNameFromClass(b)); 32 | return true; 33 | } catch (Exception ex) { 34 | target.setText(ex.getMessage()); 35 | Logger.getLogger().log(ex); 36 | } 37 | } 38 | } 39 | return false; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/verifiers/FileVerifier.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.verifiers; 2 | 3 | import javax.swing.event.DocumentEvent; 4 | import javax.swing.event.DocumentListener; 5 | import java.io.File; 6 | 7 | public class FileVerifier implements DocumentListener { 8 | 9 | protected final GetSetText source; 10 | protected final GetSetText target; 11 | 12 | public FileVerifier(GetSetText source, GetSetText target) { 13 | this.source = source; 14 | this.target = target; 15 | } 16 | 17 | @Override 18 | public void insertUpdate(DocumentEvent documentEvent) { 19 | verifySource(documentEvent); 20 | } 21 | 22 | @Override 23 | public void removeUpdate(DocumentEvent documentEvent) { 24 | verifySource(documentEvent); 25 | } 26 | 27 | @Override 28 | public void changedUpdate(DocumentEvent documentEvent) { 29 | verifySource(documentEvent); 30 | } 31 | 32 | public boolean verifySource(DocumentEvent documentEvent) { 33 | File f = new File(source.getText()); 34 | if (!f.exists()) { 35 | target.setText("file do not exists"); 36 | return false; 37 | } else { 38 | if (f.isDirectory()) { 39 | target.setText("is directory"); 40 | return false; 41 | } 42 | target.setText("ok"); 43 | return true; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/verifiers/GetSetText.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.verifiers; 2 | 3 | public interface GetSetText { 4 | 5 | String getText(); 6 | 7 | void setText(String s); 8 | 9 | class DummyGetSet implements GetSetText { 10 | private String text; 11 | 12 | public DummyGetSet(String text) { 13 | this.text = text; 14 | } 15 | 16 | @Override 17 | public String getText() { 18 | return text; 19 | } 20 | 21 | @Override 22 | public void setText(String s) { 23 | text = s; 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/decompilerview/verifiers/JarVerifier.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.decompilerview.verifiers; 2 | 3 | import org.jrd.backend.core.Logger; 4 | 5 | import javax.swing.event.DocumentEvent; 6 | import java.io.File; 7 | import java.util.Enumeration; 8 | import java.util.jar.JarFile; 9 | import java.util.zip.ZipEntry; 10 | 11 | public class JarVerifier extends FileVerifier { 12 | 13 | public JarVerifier(GetSetText source, GetSetText target) { 14 | super(source, target); 15 | } 16 | 17 | public boolean verifySource(DocumentEvent documentEvent) { 18 | boolean intermezo = super.verifySource(documentEvent); 19 | if (intermezo) { 20 | File f = new File(source.getText()); 21 | try { 22 | JarFile jf = new JarFile(f); 23 | int manfest = 0; 24 | int others = 0; 25 | for (Enumeration list = jf.entries(); list.hasMoreElements();) { 26 | ZipEntry entry = (ZipEntry) list.nextElement(); 27 | if (entry.getName().contains("META-INF")) { 28 | manfest++; 29 | } else { 30 | others++; 31 | } 32 | } 33 | target.setText("contains " + others + " classes and " + manfest + " manifest items"); 34 | jf.close(); 35 | return true; 36 | } catch (Exception ex) { 37 | target.setText(ex.getMessage()); 38 | Logger.getLogger().log(ex); 39 | return false; 40 | } 41 | } else { 42 | return false; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/popup/ClassListPopupMenu.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.popup; 2 | 3 | import org.jrd.backend.data.DependenciesReader; 4 | 5 | import javax.swing.JList; 6 | import javax.swing.ListSelectionModel; 7 | import javax.swing.event.PopupMenuEvent; 8 | import javax.swing.event.PopupMenuListener; 9 | import java.util.Optional; 10 | 11 | public class ClassListPopupMenu extends JListPopupMenu { 12 | 13 | public ClassListPopupMenu( 14 | JList parentJList, int originallySelected, boolean showCheckboxes, DependenciesReader dr, Optional classloader 15 | ) { 16 | super(parentJList, showCheckboxes, dr, classloader); 17 | 18 | addPopupMenuListener(new PopupMenuListener() { 19 | @Override 20 | public void popupMenuWillBecomeVisible(PopupMenuEvent popupMenuEvent) { 21 | } 22 | 23 | @Override 24 | public void popupMenuWillBecomeInvisible(PopupMenuEvent popupMenuEvent) { 25 | parentJList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 26 | parentJList.setSelectedIndex(originallySelected); 27 | parentJList.requestFocusInWindow(); 28 | } 29 | 30 | @Override 31 | public void popupMenuCanceled(PopupMenuEvent popupMenuEvent) { 32 | // ...Canceled is called only when it gets dismissed, whereas ...WillBecomeInvisible is called everytime the menu disappears 33 | } 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/main/popup/SingleFilePatch.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.main.popup; 2 | 3 | public class SingleFilePatch { 4 | private final int start; 5 | private final int end; 6 | 7 | public SingleFilePatch(int start, int end) { 8 | this.start = start; 9 | this.end = end; 10 | } 11 | 12 | public int getStart() { 13 | return start; 14 | } 15 | 16 | public int getEnd() { 17 | return end; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/overwrite/FileToClassValidator.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.overwrite; 2 | 3 | import javax.swing.JLabel; 4 | import javax.swing.JTextField; 5 | import javax.swing.event.DocumentEvent; 6 | import javax.swing.event.DocumentListener; 7 | import java.awt.Color; 8 | import java.io.File; 9 | 10 | /** 11 | * 12 | * @author jvanek 13 | */ 14 | public class FileToClassValidator implements DocumentListener { 15 | 16 | private final JLabel output; 17 | private final JTextField file; 18 | private final JTextField clazz; 19 | 20 | public FileToClassValidator(JLabel validation, JTextField filePath, JTextField className) { 21 | this.output = validation; 22 | this.file = filePath; 23 | this.clazz = className; 24 | } 25 | 26 | @Override 27 | public void insertUpdate(DocumentEvent e) { 28 | work(); 29 | } 30 | 31 | @Override 32 | public void removeUpdate(DocumentEvent e) { 33 | work(); 34 | } 35 | 36 | @Override 37 | public void changedUpdate(DocumentEvent e) { 38 | work(); 39 | } 40 | 41 | private void work() { 42 | StringAndScore r = validate(clazz.getText(), file.getText()); 43 | output.setForeground(Color.green); 44 | if (r.score > 0) { 45 | output.setForeground(Color.orange); 46 | } 47 | if (r.score > 9) { 48 | output.setForeground(Color.red); 49 | } 50 | output.setText(r.message); 51 | } 52 | 53 | public static StringAndScore validate(String clazz, String file) { 54 | int score = 0; 55 | StringBuilder message = new StringBuilder(); 56 | File f = new File(file); 57 | if (!f.isFile()) { 58 | score += 10; 59 | message.append("Not a file! "); 60 | } 61 | if (!f.getName().endsWith(".class")) { 62 | score++; 63 | message.append("Not a ending with class! "); 64 | } 65 | String[] pathHunks = f.getAbsolutePath().replace(".class", "").split("[\\.\\\\/]"); 66 | String[] classHunks = clazz.split("[\\.\\\\/]"); 67 | if (!pathHunks[pathHunks.length - 1].equals(classHunks[classHunks.length - 1])) { 68 | score += 10; 69 | message.append("File name and class name do not match! "); 70 | } 71 | //skipping name 72 | int i1 = pathHunks.length - 1; 73 | int i2 = classHunks.length - 1; 74 | while (true) { 75 | i1--; 76 | i2--; 77 | if (i1 < 0 || i2 < 0) { 78 | break; 79 | } 80 | if (!pathHunks[i1].equals(classHunks[i2])) { 81 | score++; 82 | message.append("Class package do not match directories. "); 83 | break; 84 | } 85 | } 86 | if (message.length() == 0) { 87 | message.append("file x class looks OK!"); 88 | } 89 | return new StringAndScore(score, message.toString()); 90 | } 91 | 92 | public static class StringAndScore { 93 | 94 | private final int score; 95 | private final String message; 96 | 97 | public StringAndScore(int score, String message) { 98 | this.score = score; 99 | this.message = message; 100 | } 101 | 102 | public int getScore() { 103 | return score; 104 | } 105 | 106 | public String getMessage() { 107 | return message; 108 | } 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/overwrite/LatestPaths.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.overwrite; 2 | 3 | public final class LatestPaths { 4 | private String lastManualUpload; 5 | private String lastSaveSrc; 6 | private String lastSaveBin; 7 | private String filesToCompile; 8 | private String outputExternalFilesDir; 9 | private String outputBinaries; 10 | 11 | public LatestPaths() { 12 | lastManualUpload = System.getProperty("user.home"); 13 | lastSaveSrc = System.getProperty("user.home"); 14 | lastSaveBin = System.getProperty("user.home"); 15 | filesToCompile = System.getProperty("user.home"); 16 | outputExternalFilesDir = System.getProperty("user.home"); 17 | outputBinaries = System.getProperty("user.home"); 18 | } 19 | 20 | public String getLastManualUpload() { 21 | return lastManualUpload; 22 | } 23 | 24 | public void setLastManualUpload(String lastManualUpload) { 25 | this.lastManualUpload = lastManualUpload; 26 | } 27 | 28 | public String getLastSaveSrc() { 29 | return lastSaveSrc; 30 | } 31 | 32 | public void setLastSaveSrc(String lastSaveSrc) { 33 | this.lastSaveSrc = lastSaveSrc; 34 | } 35 | 36 | public String getLastSaveBin() { 37 | return lastSaveBin; 38 | } 39 | 40 | public void setLastSaveBin(String lastSaveBin) { 41 | this.lastSaveBin = lastSaveBin; 42 | } 43 | 44 | public String getFilesToCompile() { 45 | return filesToCompile; 46 | } 47 | 48 | public void setFilesToCompile(String filesToCompile) { 49 | this.filesToCompile = filesToCompile; 50 | } 51 | 52 | public String getOutputExternalFilesDir() { 53 | return outputExternalFilesDir; 54 | } 55 | 56 | public void setOutputExternalFilesDir(String outputExternalFilesDir) { 57 | this.outputExternalFilesDir = outputExternalFilesDir; 58 | } 59 | 60 | public String getOutputBinaries() { 61 | return outputBinaries; 62 | } 63 | 64 | public void setOutputBinaries(String outputBinaries) { 65 | this.outputBinaries = outputBinaries; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/plugins/ConfigPanel.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.plugins; 2 | 3 | import javax.swing.Box; 4 | import javax.swing.JLabel; 5 | import javax.swing.JPanel; 6 | import java.awt.GridBagConstraints; 7 | import java.awt.GridBagLayout; 8 | import java.awt.Insets; 9 | 10 | public class ConfigPanel extends JPanel { 11 | 12 | private final JLabel jsonFileUrl; 13 | private final MessagePanel messagePanel; 14 | private final TextInputPanel namePanel; 15 | private final FileSelectorPanel wrapperUrlPanel; 16 | private final FileSelectorArrayPanel dependencyUrlPanel; 17 | 18 | public ConfigPanel() { 19 | this.setLayout(new GridBagLayout()); 20 | 21 | jsonFileUrl = new JLabel(); 22 | messagePanel = new MessagePanel( 23 | "Info: You don't have permissions to save this configuration! " + "You can clone it and save the copy." 24 | ); 25 | messagePanel.setVisible(false); 26 | namePanel = new TextInputPanel("Name"); 27 | wrapperUrlPanel = new FileSelectorPanel("Decompiler wrapper"); 28 | dependencyUrlPanel = new FileSelectorArrayPanel("Decompiler dependency jars"); 29 | 30 | GridBagConstraints gbc = new GridBagConstraints(); 31 | gbc.anchor = GridBagConstraints.NORTHWEST; 32 | gbc.fill = GridBagConstraints.BOTH; 33 | gbc.insets = new Insets(3, 3, 3, 3); 34 | gbc.weightx = 1; 35 | this.add(jsonFileUrl, gbc); 36 | gbc.gridy = 1; 37 | this.add(messagePanel, gbc); 38 | gbc.gridy = 2; 39 | this.add(namePanel, gbc); 40 | gbc.gridy = 3; 41 | this.add(wrapperUrlPanel, gbc); 42 | gbc.gridy = 4; 43 | this.add(dependencyUrlPanel, gbc); 44 | gbc.gridy = 99999; 45 | gbc.weighty = 1; 46 | this.add(Box.createVerticalGlue(), gbc); 47 | //sets gbc for addComponent method 48 | gbc.gridy = 0; 49 | gbc.weighty = 0; 50 | } 51 | 52 | public JLabel getJsonFileUrl() { 53 | return jsonFileUrl; 54 | } 55 | 56 | public MessagePanel getMessagePanel() { 57 | return messagePanel; 58 | } 59 | 60 | public TextInputPanel getNamePanel() { 61 | return namePanel; 62 | } 63 | 64 | public FileSelectorPanel getWrapperUrlPanel() { 65 | return wrapperUrlPanel; 66 | } 67 | 68 | public FileSelectorArrayPanel getDependencyUrlPanel() { 69 | return dependencyUrlPanel; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/plugins/FileSelectorArrayAddRow.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.plugins; 2 | 3 | import org.jrd.frontend.utility.ImageButtonFactory; 4 | 5 | import javax.swing.Box; 6 | import javax.swing.JButton; 7 | import javax.swing.JPanel; 8 | import java.awt.GridBagConstraints; 9 | import java.awt.GridBagLayout; 10 | 11 | public class FileSelectorArrayAddRow extends JPanel { 12 | 13 | private JButton addButton; 14 | 15 | FileSelectorArrayAddRow(int rightMargin) { 16 | this.setLayout(new GridBagLayout()); 17 | GridBagConstraints gbc = new GridBagConstraints(); 18 | gbc.anchor = GridBagConstraints.NORTHWEST; 19 | gbc.fill = GridBagConstraints.BOTH; 20 | 21 | addButton = ImageButtonFactory.createAddButton(); 22 | 23 | gbc.weightx = 1; 24 | this.add(Box.createHorizontalGlue(), gbc); 25 | 26 | gbc.weightx = 0; 27 | gbc.gridx = 1; 28 | this.add(addButton, gbc); 29 | 30 | gbc.gridx = 2; 31 | this.add(Box.createHorizontalStrut(rightMargin), gbc); 32 | } 33 | 34 | public JButton getAddButton() { 35 | return addButton; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/plugins/FileSelectorArrayPanel.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.plugins; 2 | 3 | import javax.swing.JLabel; 4 | import javax.swing.JPanel; 5 | import javax.swing.JTextField; 6 | import java.awt.GridBagConstraints; 7 | import java.awt.GridBagLayout; 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | 11 | public class FileSelectorArrayPanel extends JPanel { 12 | 13 | private final JLabel jLabel; 14 | private final FileSelectorArrayAddRow fileSelectorArrayAddRow; 15 | 16 | private final GridBagConstraints gbc; 17 | private List pathTextFields; 18 | 19 | private Boolean first = false; 20 | 21 | FileSelectorArrayPanel(String label) { 22 | gbc = new GridBagConstraints(); 23 | this.setLayout(new GridBagLayout()); 24 | pathTextFields = new LinkedList<>(); 25 | gbc.anchor = GridBagConstraints.NORTHWEST; 26 | gbc.fill = GridBagConstraints.BOTH; 27 | gbc.weightx = 1; 28 | gbc.gridx = 0; 29 | gbc.gridy = 0; 30 | 31 | // Always have at least one row. 32 | FileSelectorArrayRow row = addRow("", false); 33 | first = true; 34 | 35 | jLabel = new JLabel(label); 36 | fileSelectorArrayAddRow = new FileSelectorArrayAddRow(row.getRightBoundMargin()); 37 | fileSelectorArrayAddRow.getAddButton().addActionListener(actionEvent -> { 38 | addRow("", true); 39 | }); 40 | 41 | this.add(jLabel, gbc); 42 | gbc.gridy = 10000; 43 | this.add(fileSelectorArrayAddRow, gbc); 44 | gbc.gridy = 1; 45 | } 46 | 47 | FileSelectorArrayRow addRow(String url, boolean button) { 48 | // Fill first row created by constructor. 49 | if (first) { 50 | if (!button) { 51 | first = false; 52 | pathTextFields.get(0).setText(url); 53 | return null; 54 | } 55 | } 56 | gbc.gridy++; 57 | FileSelectorArrayRow fileSelectorArrayRow = new FileSelectorArrayRow(this, url); 58 | pathTextFields.add(fileSelectorArrayRow.getTextField()); 59 | this.add(fileSelectorArrayRow, gbc); 60 | this.revalidate(); 61 | 62 | return fileSelectorArrayRow; 63 | } 64 | 65 | public void removeRow(FileSelectorArrayRow fileSelectorArrayRow) { 66 | if (pathTextFields.size() > 1) { 67 | pathTextFields.remove(fileSelectorArrayRow.getTextField()); 68 | this.remove(fileSelectorArrayRow); 69 | this.revalidate(); 70 | } 71 | } 72 | 73 | public List getStringList() { 74 | List list = new LinkedList<>(); 75 | for (JTextField jTextField : pathTextFields) { 76 | list.add(jTextField.getText()); 77 | } 78 | return list; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/plugins/FileSelectorPanel.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.plugins; 2 | 3 | import org.jrd.backend.data.Directories; 4 | import org.jrd.frontend.frame.main.decompilerview.BytecodeDecompilerView; 5 | 6 | import javax.swing.Box; 7 | import javax.swing.JButton; 8 | import javax.swing.JFileChooser; 9 | import javax.swing.JLabel; 10 | import javax.swing.JPanel; 11 | import javax.swing.JTextField; 12 | import java.awt.Dimension; 13 | import java.awt.GridBagConstraints; 14 | import java.awt.GridBagLayout; 15 | import java.io.File; 16 | 17 | public class FileSelectorPanel extends JPanel { 18 | 19 | private JTextField textField; 20 | private JLabel jLabel; 21 | private JButton browseButton; 22 | private JFileChooser chooser; 23 | 24 | FileSelectorPanel(String label) { 25 | this(label, "Browse"); 26 | } 27 | 28 | FileSelectorPanel(String label, String buttonLabel) { 29 | 30 | this.textField = new JTextField(); 31 | textField.setPreferredSize(new Dimension(0, 32)); 32 | textField.setToolTipText( 33 | BytecodeDecompilerView.styleTooltip() + "Select a path to the decompiler wrapper .java file.
" + 34 | FileSelectorArrayRow.getTextFieldToolTip() 35 | ); 36 | 37 | this.jLabel = new JLabel(label); 38 | this.browseButton = new JButton(buttonLabel); 39 | 40 | this.chooser = new JFileChooser(); 41 | File dir = new File(Directories.getPluginDirectory()); 42 | chooser.setCurrentDirectory(FileSelectorArrayRow.fallback(dir)); 43 | 44 | browseButton.addActionListener(actionEvent -> { 45 | int returnVar = chooser.showOpenDialog(this); 46 | if (returnVar == JFileChooser.APPROVE_OPTION) { 47 | textField.setText(chooser.getSelectedFile().getPath()); 48 | } 49 | }); 50 | 51 | this.setLayout(new GridBagLayout()); 52 | GridBagConstraints gbc = new GridBagConstraints(); 53 | gbc.anchor = GridBagConstraints.WEST; 54 | gbc.fill = GridBagConstraints.BOTH; 55 | 56 | gbc.gridy = 0; 57 | gbc.gridx = 0; 58 | this.add(this.jLabel, gbc); 59 | 60 | gbc.gridy = 1; 61 | gbc.gridx = 0; 62 | gbc.weightx = 1; 63 | this.add(textField, gbc); 64 | gbc.weightx = 0; 65 | gbc.gridx = 1; 66 | this.add(Box.createHorizontalStrut(20), gbc); 67 | gbc.gridx = 2; 68 | this.add(browseButton, gbc); 69 | this.setPreferredSize(new Dimension(0, 80)); 70 | } 71 | 72 | public String getText() { 73 | return textField.getText(); 74 | } 75 | 76 | public void setText(String text) { 77 | textField.setText(text); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/plugins/MessagePanel.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.plugins; 2 | 3 | import javax.swing.JLabel; 4 | import javax.swing.JPanel; 5 | import java.awt.BorderLayout; 6 | import java.awt.Color; 7 | import java.awt.Dimension; 8 | 9 | public class MessagePanel extends JPanel { 10 | 11 | private final JLabel jLabel; 12 | 13 | MessagePanel(String message) { 14 | this.setLayout(new BorderLayout()); 15 | this.setBackground(Color.decode("#14b7e0")); 16 | this.setPreferredSize(new Dimension(0, 40)); 17 | 18 | jLabel = new JLabel(); 19 | jLabel.setText(message); 20 | this.add(jLabel); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/plugins/PluginListPanel.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.plugins; 2 | 3 | import javax.swing.JButton; 4 | import javax.swing.JList; 5 | import javax.swing.JPanel; 6 | import javax.swing.ListSelectionModel; 7 | import java.awt.BorderLayout; 8 | import java.awt.Dimension; 9 | import org.jrd.backend.decompiling.DecompilerWrapper; 10 | 11 | public class PluginListPanel extends JPanel { 12 | 13 | private final JList wrapperJList; 14 | private final JButton addWrapperButton; 15 | 16 | PluginListPanel() { 17 | this.setLayout(new BorderLayout()); 18 | this.setPreferredSize(new Dimension(180, 0)); 19 | 20 | wrapperJList = new JList<>(); 21 | wrapperJList.setFixedCellHeight(32); 22 | wrapperJList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 23 | addWrapperButton = new JButton("New"); 24 | addWrapperButton.setPreferredSize(new Dimension(0, 28)); 25 | 26 | this.add(wrapperJList, BorderLayout.CENTER); 27 | this.add(addWrapperButton, BorderLayout.NORTH); 28 | } 29 | 30 | public JList getWrapperJList() { 31 | return wrapperJList; 32 | } 33 | 34 | public JButton getAddWrapperButton() { 35 | return addWrapperButton; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/plugins/PluginTopOptionPanel.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.plugins; 2 | 3 | import javax.swing.Box; 4 | import javax.swing.JButton; 5 | import javax.swing.JPanel; 6 | import java.awt.Dimension; 7 | import java.awt.GridBagConstraints; 8 | import java.awt.GridBagLayout; 9 | 10 | public class PluginTopOptionPanel extends JPanel { 11 | 12 | private final JButton cloneButton; 13 | private final JButton deleteButton; 14 | private final JButton openWebsiteButton; 15 | private final JButton importButton; 16 | 17 | public PluginTopOptionPanel() { 18 | final int height = 28; 19 | final int buttonWidth = 84; 20 | Dimension buttonSize = new Dimension(buttonWidth, height); 21 | 22 | this.setLayout(new GridBagLayout()); 23 | this.setPreferredSize(new Dimension(0, height)); 24 | 25 | cloneButton = new JButton("Clone"); 26 | cloneButton.setPreferredSize(buttonSize); 27 | deleteButton = new JButton("Delete"); 28 | deleteButton.setPreferredSize(buttonSize); 29 | openWebsiteButton = new JButton("Website"); 30 | openWebsiteButton.setPreferredSize(buttonSize); 31 | importButton = new JButton("Import"); 32 | importButton.setPreferredSize(buttonSize); 33 | 34 | GridBagConstraints gbc = new GridBagConstraints(); 35 | gbc.weightx = 0; 36 | gbc.gridx = 0; 37 | this.add(cloneButton, gbc); 38 | gbc.gridx = 1; 39 | this.add(deleteButton, gbc); 40 | gbc.gridx = 2; 41 | this.add(openWebsiteButton, gbc); 42 | gbc.gridx = 3; 43 | this.add(importButton, gbc); 44 | gbc.gridx = 4; 45 | gbc.weightx = 1; 46 | this.add(Box.createHorizontalGlue(), gbc); 47 | } 48 | 49 | public JButton getCloneButton() { 50 | return cloneButton; 51 | } 52 | 53 | public JButton getDeleteButton() { 54 | return deleteButton; 55 | } 56 | 57 | public JButton getOpenWebsiteButton() { 58 | return openWebsiteButton; 59 | } 60 | 61 | public JButton getImportButton() { 62 | return importButton; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/plugins/TextInputPanel.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.plugins; 2 | 3 | import javax.swing.JLabel; 4 | import javax.swing.JPanel; 5 | import javax.swing.JTextField; 6 | import java.awt.Dimension; 7 | import java.awt.GridBagConstraints; 8 | import java.awt.GridBagLayout; 9 | 10 | public class TextInputPanel extends JPanel { 11 | 12 | private final JTextField textField; 13 | private final JLabel jLabel; 14 | 15 | TextInputPanel(String label) { 16 | this(); 17 | jLabel.setText(label); 18 | } 19 | 20 | TextInputPanel() { 21 | this.setPreferredSize(new Dimension(0, 80)); 22 | this.setLayout(new GridBagLayout()); 23 | 24 | textField = new JTextField(); 25 | textField.setPreferredSize(new Dimension(0, 32)); 26 | jLabel = new JLabel(); 27 | 28 | GridBagConstraints gbc = new GridBagConstraints(); 29 | gbc.anchor = GridBagConstraints.WEST; 30 | gbc.fill = GridBagConstraints.BOTH; 31 | this.add(jLabel, gbc); 32 | gbc.gridy = 1; 33 | gbc.weightx = 1; 34 | this.add(textField, gbc); 35 | } 36 | 37 | public JTextField getTextField() { 38 | return textField; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/remote/NewConnectionController.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.remote; 2 | 3 | import org.jrd.backend.core.Logger; 4 | import org.jrd.backend.data.VmManager; 5 | 6 | import javax.swing.JOptionPane; 7 | 8 | public class NewConnectionController { 9 | 10 | NewConnectionView newConnectionView; 11 | VmManager vmManager; 12 | 13 | public NewConnectionController(NewConnectionView newConnectionView, VmManager vmManager) { 14 | this.newConnectionView = newConnectionView; 15 | this.vmManager = vmManager; 16 | 17 | newConnectionView.setAddButtonListener(e -> addRemoteVmInfo()); 18 | } 19 | 20 | private void addRemoteVmInfo() { 21 | String hostname = newConnectionView.getHostname(); 22 | String portString = newConnectionView.getPortString(); 23 | boolean shouldBeSaved = newConnectionView.shouldBeSaved(); 24 | 25 | if (hostname.isEmpty()) { 26 | JOptionPane.showMessageDialog(newConnectionView, "Hostname is Empty.", " ", JOptionPane.WARNING_MESSAGE); 27 | return; 28 | } 29 | if (isValidPort(portString)) { 30 | int port = Integer.parseInt(portString); 31 | newConnectionView.reSetLastHostname(); 32 | newConnectionView.reSetLastPort(); 33 | vmManager.createRemoteVM(hostname, port, shouldBeSaved, null); 34 | newConnectionView.dispose(); 35 | } else { 36 | JOptionPane.showMessageDialog(newConnectionView, "VM port is invalid.", " ", JOptionPane.WARNING_MESSAGE); 37 | } 38 | } 39 | 40 | /** 41 | * Validates a port. 42 | * @param portString string representation of the port number 43 | * @return true if portString is an integer between 0 and 65535 44 | */ 45 | boolean isValidPort(String portString) { 46 | try { 47 | int port = Integer.parseInt(portString); 48 | return port > 0 && port <= 65535; 49 | } catch (NumberFormatException e) { 50 | Logger.getLogger().log(Logger.Level.ALL, e); 51 | return false; 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/settings/AgentSettingsPanel.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.settings; 2 | 3 | import javax.swing.JButton; 4 | import javax.swing.JFileChooser; 5 | import javax.swing.JLabel; 6 | import javax.swing.JPanel; 7 | import javax.swing.JTextField; 8 | import java.awt.GridBagConstraints; 9 | import java.awt.GridBagLayout; 10 | import java.awt.Insets; 11 | import java.awt.event.ActionListener; 12 | import java.io.File; 13 | import org.jrd.backend.data.Directories; 14 | import org.jrd.frontend.frame.main.decompilerview.BytecodeDecompilerView; 15 | import org.jrd.frontend.frame.plugins.FileSelectorArrayRow; 16 | 17 | public class AgentSettingsPanel extends JPanel implements ChangeReporter { 18 | 19 | private JTextField agentPathTextField; 20 | private JLabel agentPathLabel; 21 | private JButton browseButton; 22 | private JFileChooser chooser; 23 | 24 | AgentSettingsPanel(String initialAgentPath) { 25 | this.setName("Agent settings"); 26 | agentPathTextField = new JTextField(); 27 | agentPathTextField.setToolTipText( 28 | BytecodeDecompilerView.styleTooltip() + "Select a path to the Decompiler Agent.
" + 29 | FileSelectorArrayRow.getTextFieldToolTip() 30 | ); 31 | agentPathTextField.setText(initialAgentPath); 32 | 33 | agentPathLabel = new JLabel("Decompiler Agent path"); 34 | browseButton = new JButton("Browse"); 35 | 36 | chooser = new JFileChooser(); 37 | File dir; 38 | if (Directories.isPortable()) { 39 | dir = new File(Directories.getJrdLocation() + File.separator + "libs"); 40 | } else { 41 | dir = new File(Directories.getJrdLocation() + File.separator + "decompiler_agent" + File.separator + "target"); 42 | } 43 | chooser.setCurrentDirectory(FileSelectorArrayRow.fallback(dir)); 44 | 45 | browseButton.addActionListener(actionEvent -> { 46 | int dialogResult = chooser.showOpenDialog(null); 47 | if (dialogResult == JFileChooser.APPROVE_OPTION) { 48 | agentPathTextField.setText(chooser.getSelectedFile().getPath()); 49 | } 50 | }); 51 | 52 | this.setLayout(new GridBagLayout()); 53 | GridBagConstraints gbc = new GridBagConstraints(); 54 | gbc.anchor = GridBagConstraints.WEST; 55 | gbc.fill = GridBagConstraints.BOTH; 56 | gbc.insets = new Insets(5, 5, 5, 5); 57 | 58 | this.add(this.agentPathLabel, gbc); 59 | 60 | gbc.weightx = 1; 61 | gbc.gridy = 1; 62 | gbc.gridwidth = 2; 63 | this.add(agentPathTextField, gbc); 64 | 65 | gbc.weightx = 0; 66 | gbc.gridwidth = 1; 67 | gbc.gridx = 2; 68 | browseButton.setPreferredSize(BytecodeDecompilerView.buttonSizeBasedOnTextField(browseButton, agentPathTextField)); 69 | this.add(browseButton, gbc); 70 | } 71 | 72 | public String getAgentPath() { 73 | return agentPathTextField.getText(); 74 | } 75 | 76 | @Override 77 | public void setChangeReporter(ActionListener listener) { 78 | ChangeReporter.addTextChangeListener(listener, agentPathTextField); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/frame/settings/ChangeReporter.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.settings; 2 | 3 | import javax.swing.JList; 4 | import javax.swing.JToggleButton; 5 | import javax.swing.event.DocumentEvent; 6 | import javax.swing.event.DocumentListener; 7 | import javax.swing.event.ListDataEvent; 8 | import javax.swing.event.ListDataListener; 9 | import javax.swing.text.JTextComponent; 10 | import java.awt.event.ActionEvent; 11 | import java.awt.event.ActionListener; 12 | 13 | public interface ChangeReporter { 14 | 15 | /** 16 | * Passes a listener to the implementing class to allow for future reporting. 17 | * @param listener the change listener 18 | */ 19 | void setChangeReporter(ActionListener listener); 20 | 21 | /** 22 | * Convenience method to make a document change listener perform an action on the listener parameter. 23 | * @param listener the listener which will be triggered 24 | * @param textComponent the component whose document will trigger an event 25 | */ 26 | static void addTextChangeListener(ActionListener listener, JTextComponent textComponent) { 27 | textComponent.getDocument().addDocumentListener(new DocumentListener() { 28 | @Override 29 | public void insertUpdate(DocumentEvent documentEvent) { 30 | changedUpdate(documentEvent); 31 | } 32 | 33 | @Override 34 | public void removeUpdate(DocumentEvent documentEvent) { 35 | changedUpdate(documentEvent); 36 | } 37 | 38 | @Override 39 | public void changedUpdate(DocumentEvent documentEvent) { 40 | listener.actionPerformed(createChangeActionEvent(textComponent)); 41 | } 42 | }); 43 | } 44 | 45 | static void addCheckboxListener(ActionListener listener, JToggleButton checkBox) { 46 | checkBox.addActionListener(checked -> listener.actionPerformed(createChangeActionEvent(checkBox))); 47 | } 48 | 49 | static void addJListListener(ActionListener listener, JList list) { 50 | list.getModel().addListDataListener(new ListDataListener() { 51 | @Override 52 | public void intervalAdded(ListDataEvent listDataEvent) { 53 | contentsChanged(listDataEvent); 54 | } 55 | 56 | @Override 57 | public void intervalRemoved(ListDataEvent listDataEvent) { 58 | contentsChanged(listDataEvent); 59 | } 60 | 61 | @Override 62 | public void contentsChanged(ListDataEvent listDataEvent) { 63 | listener.actionPerformed(createChangeActionEvent(list)); 64 | } 65 | }); 66 | } 67 | 68 | static ActionEvent createChangeActionEvent(Object source) { 69 | return new ActionEvent(source, 0, "changed"); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/jrd/frontend/utility/TeeOutputStream.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.utility; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.PrintStream; 5 | import java.nio.charset.StandardCharsets; 6 | 7 | /** 8 | * Behaves like the 'tee' command, sends output to both actual std stream and a 9 | * log 10 | */ 11 | public final class TeeOutputStream extends PrintStream { 12 | 13 | // Everything written to TeeOutputStream is written to this buffer too 14 | private final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 15 | 16 | public TeeOutputStream(PrintStream stdStream) { 17 | super(stdStream, false, StandardCharsets.UTF_8); 18 | } 19 | 20 | /* 21 | * The big ones: these do the actual writing 22 | */ 23 | 24 | @Override 25 | public synchronized void write(byte[] b, int off, int len) { 26 | if (len == 0) { 27 | return; 28 | } 29 | byteArrayOutputStream.write(b, off, len); 30 | super.write(b, off, len); 31 | } 32 | 33 | @Override 34 | public synchronized void write(int b) { 35 | byteArrayOutputStream.write(b); 36 | super.write(b); 37 | } 38 | 39 | public byte[] getByteArray() { 40 | return byteArrayOutputStream.toByteArray(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/kcc/CompletionItem.java: -------------------------------------------------------------------------------- 1 | package org.kcc; 2 | 3 | import java.util.List; 4 | import java.util.Objects; 5 | import java.util.regex.Pattern; 6 | 7 | public class CompletionItem implements Comparable { 8 | 9 | public interface CompletionItemSet { 10 | 11 | CompletionItem[] getItemsArray(); 12 | 13 | List getItemsList(); 14 | 15 | Pattern getRecommendedDelimiterSet(); 16 | 17 | static Pattern delimiterSet() { 18 | return Pattern.compile("[a-zA-Z0-9_\\-\\.\\(\\)]"); 19 | } 20 | 21 | static Pattern delimiterStrictSet2() { 22 | return Pattern.compile("[a-zA-Z0-9\\.]"); 23 | } 24 | 25 | static Pattern delimiterStrictSet3() { 26 | return Pattern.compile("[a-zA-Z0-9_\\.]"); 27 | } 28 | 29 | static Pattern delimiterWordSet() { 30 | return Pattern.compile("[a-zA-Z]"); 31 | } 32 | 33 | static Pattern delimiterWordAndNumberSet() { 34 | return Pattern.compile("[a-zA-Z0-9]"); 35 | } 36 | } 37 | 38 | private final String key; 39 | private final String description; 40 | private final String realReplacement; 41 | private final String searchable; 42 | 43 | public CompletionItem(String key) { 44 | this(key, "", ""); 45 | } 46 | 47 | public CompletionItem(String key, String description) { 48 | this(key, description, ""); 49 | } 50 | 51 | public CompletionItem(String key, String description, String realReplacement) { 52 | this(key, description, realReplacement, key); 53 | } 54 | 55 | public CompletionItem(String key, String description, String realReplacement, String searchable) { 56 | this.key = key; 57 | this.description = description; 58 | this.realReplacement = realReplacement; 59 | this.searchable = searchable; 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return key; 65 | } 66 | 67 | public String getKey() { 68 | return key; 69 | } 70 | 71 | public String getSearchable() { 72 | if ("".equals(searchable)) { 73 | return key; 74 | } else { 75 | return searchable; 76 | } 77 | } 78 | 79 | public String getDescription() { 80 | return description; 81 | } 82 | 83 | public String getRealReplacement() { 84 | if (realReplacement.isEmpty()) { 85 | return key; 86 | } else { 87 | return realReplacement; 88 | } 89 | } 90 | 91 | @Override 92 | public int compareTo(CompletionItem o) { 93 | if (o == null) { 94 | return 1; 95 | } 96 | return this.getKey().compareTo(o.getKey()); 97 | } 98 | 99 | @Override 100 | public boolean equals(Object o) { 101 | if (this == o) { 102 | return true; 103 | } 104 | if (o == null || getClass() != o.getClass()) { 105 | return false; 106 | } 107 | CompletionItem that = (CompletionItem) o; 108 | return key.equals(that.key); 109 | } 110 | 111 | @Override 112 | public int hashCode() { 113 | return Objects.hash(key); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/kcc/KeywordBasedCodeCompletionMain.java: -------------------------------------------------------------------------------- 1 | package org.kcc; 2 | 3 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; 4 | import org.kcc.wordsets.BytecodeKeywordsWithHelp; 5 | import org.kcc.wordsets.ConnectedKeywords; 6 | import org.kcc.wordsets.JavaKeywordsWithHelp; 7 | import org.kcc.wordsets.JrdApiKeywords; 8 | 9 | import javax.swing.JButton; 10 | import javax.swing.JFrame; 11 | import javax.swing.JLabel; 12 | import javax.swing.SwingUtilities; 13 | import javax.swing.WindowConstants; 14 | import java.awt.BorderLayout; 15 | import java.awt.event.ActionEvent; 16 | import java.awt.event.ActionListener; 17 | import java.awt.event.WindowAdapter; 18 | import java.awt.event.WindowEvent; 19 | 20 | public final class KeywordBasedCodeCompletionMain { 21 | 22 | private KeywordBasedCodeCompletionMain() { 23 | } 24 | 25 | /** 26 | * Experiemntal main method showing as demo for KeywordBasedCodeCompletion; 27 | * move from mainX to main once moved to separeate project 28 | * @param args 29 | * @throws Exception 30 | */ 31 | public static void mainX(String[] args) throws Exception { 32 | SwingUtilities.invokeLater(new Runnable() { 33 | @Override 34 | public void run() { 35 | JFrame frame = new JFrame("keyword based code completion example"); 36 | JLabel l1; 37 | JButton b1; 38 | //JTextArea t1; 39 | RSyntaxTextArea t1; 40 | frame.setLayout(new BorderLayout()); 41 | l1 = new JLabel("hi"); 42 | b1 = new JButton("by"); 43 | t1 = new RSyntaxTextArea(); 44 | frame.add(l1, BorderLayout.NORTH); 45 | frame.add(b1, BorderLayout.SOUTH); 46 | frame.add(t1); 47 | final KeywordBasedCodeCompletion comp = new KeywordBasedCodeCompletion( 48 | t1, new CompletionSettings(new BytecodeKeywordsWithHelp(), CompletionSettings.OP.STARTS, true, true) 49 | ); 50 | b1.addActionListener(new ActionListener() { 51 | @Override 52 | public void actionPerformed(ActionEvent actionEvent) { 53 | comp.setCompletionsSet( 54 | new ConnectedKeywords(new BytecodeKeywordsWithHelp(), new JavaKeywordsWithHelp(), new JrdApiKeywords()) 55 | ); 56 | } 57 | }); 58 | frame.addWindowListener(new WindowAdapter() { 59 | @Override 60 | public void windowClosed(WindowEvent e) { 61 | comp.dispose(); 62 | } 63 | }); 64 | frame.setSize(550, 400); 65 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 66 | frame.setVisible(true); 67 | 68 | } 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/java/org/kcc/wordsets/ConnectedKeywords.java: -------------------------------------------------------------------------------- 1 | package org.kcc.wordsets; 2 | 3 | import org.kcc.CompletionItem; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.Collections; 8 | import java.util.HashSet; 9 | import java.util.List; 10 | import java.util.regex.Pattern; 11 | import java.util.stream.Collectors; 12 | 13 | public class ConnectedKeywords implements CompletionItem.CompletionItemSet { 14 | private final CompletionItem.CompletionItemSet[] originalSets; 15 | 16 | public ConnectedKeywords(CompletionItem.CompletionItemSet... originalSets) { 17 | this.originalSets = originalSets; 18 | } 19 | 20 | @Override 21 | public CompletionItem[] getItemsArray() { 22 | return getItemsList().toArray(new CompletionItem[0]); 23 | } 24 | 25 | @Override 26 | public List getItemsList() { 27 | List l = new ArrayList<>(); 28 | for (CompletionItem.CompletionItemSet item : originalSets) { 29 | l.addAll( 30 | item.getItemsList().stream().map( 31 | a -> new CompletionItem(a.getKey(), a.getDescription() + "\n from: " + item.toString(), a.getRealReplacement()) 32 | ).collect(Collectors.toList()) 33 | ); 34 | } 35 | List r = new ArrayList<>(new HashSet<>(l)); 36 | Collections.sort(r); 37 | return r; 38 | } 39 | 40 | @Override 41 | public Pattern getRecommendedDelimiterSet() { 42 | StringBuilder sb = new StringBuilder(); 43 | for (int i = 0; i < originalSets.length; i++) { 44 | CompletionItem.CompletionItemSet item = originalSets[i]; 45 | sb.append("(").append(item.getRecommendedDelimiterSet().toString()).append(")"); 46 | if (i < originalSets.length - 1) { 47 | sb.append("|"); 48 | } 49 | } 50 | return Pattern.compile(sb.toString()); 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return Arrays.stream(originalSets).map(a -> a.toString()).collect(Collectors.joining("; ")); 56 | } 57 | 58 | public CompletionItem.CompletionItemSet[] getOriginalSets() { 59 | return Arrays.copyOf(originalSets, originalSets.length); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /runtime-decompiler/src/main/resources/icons/add_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/java-runtime-decompiler/b9b5b70f7600873fde6d96d491b7fa53ced265a2/runtime-decompiler/src/main/resources/icons/add_24dp.png -------------------------------------------------------------------------------- /runtime-decompiler/src/main/resources/icons/attach_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/java-runtime-decompiler/b9b5b70f7600873fde6d96d491b7fa53ced265a2/runtime-decompiler/src/main/resources/icons/attach_24dp.png -------------------------------------------------------------------------------- /runtime-decompiler/src/main/resources/icons/compile_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/java-runtime-decompiler/b9b5b70f7600873fde6d96d491b7fa53ced265a2/runtime-decompiler/src/main/resources/icons/compile_24dp.png -------------------------------------------------------------------------------- /runtime-decompiler/src/main/resources/icons/detach_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/java-runtime-decompiler/b9b5b70f7600873fde6d96d491b7fa53ced265a2/runtime-decompiler/src/main/resources/icons/detach_24dp.png -------------------------------------------------------------------------------- /runtime-decompiler/src/main/resources/icons/init_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/java-runtime-decompiler/b9b5b70f7600873fde6d96d491b7fa53ced265a2/runtime-decompiler/src/main/resources/icons/init_24dp.png -------------------------------------------------------------------------------- /runtime-decompiler/src/main/resources/icons/insert_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/java-runtime-decompiler/b9b5b70f7600873fde6d96d491b7fa53ced265a2/runtime-decompiler/src/main/resources/icons/insert_24dp.png -------------------------------------------------------------------------------- /runtime-decompiler/src/main/resources/icons/main-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/java-runtime-decompiler/b9b5b70f7600873fde6d96d491b7fa53ced265a2/runtime-decompiler/src/main/resources/icons/main-icon.png -------------------------------------------------------------------------------- /runtime-decompiler/src/main/resources/icons/overwrite_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/java-runtime-decompiler/b9b5b70f7600873fde6d96d491b7fa53ced265a2/runtime-decompiler/src/main/resources/icons/overwrite_24dp.png -------------------------------------------------------------------------------- /runtime-decompiler/src/main/resources/icons/redo_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/java-runtime-decompiler/b9b5b70f7600873fde6d96d491b7fa53ced265a2/runtime-decompiler/src/main/resources/icons/redo_24dp.png -------------------------------------------------------------------------------- /runtime-decompiler/src/main/resources/icons/refresh_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/java-runtime-decompiler/b9b5b70f7600873fde6d96d491b7fa53ced265a2/runtime-decompiler/src/main/resources/icons/refresh_24dp.png -------------------------------------------------------------------------------- /runtime-decompiler/src/main/resources/icons/remove_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/java-runtime-decompiler/b9b5b70f7600873fde6d96d491b7fa53ced265a2/runtime-decompiler/src/main/resources/icons/remove_24dp.png -------------------------------------------------------------------------------- /runtime-decompiler/src/main/resources/icons/trash_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/java-runtime-decompiler/b9b5b70f7600873fde6d96d491b7fa53ced265a2/runtime-decompiler/src/main/resources/icons/trash_24dp.png -------------------------------------------------------------------------------- /runtime-decompiler/src/main/resources/icons/undo_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/java-runtime-decompiler/b9b5b70f7600873fde6d96d491b7fa53ced265a2/runtime-decompiler/src/main/resources/icons/undo_24dp.png -------------------------------------------------------------------------------- /runtime-decompiler/src/main/resources/icons/upload_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judovana/java-runtime-decompiler/b9b5b70f7600873fde6d96d491b7fa53ced265a2/runtime-decompiler/src/main/resources/icons/upload_24dp.png -------------------------------------------------------------------------------- /runtime-decompiler/src/main/resources/org/jrd/backend/data/metadata.prop: -------------------------------------------------------------------------------- 1 | groupId=${project.groupId} 2 | name=${project.parent.name} 3 | timestamp=${timestamp} 4 | version=${project.version} 5 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/CfrDecompilerWrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Cfr", 3 | "WrapperURL": "file://${XDG_CONFIG_HOME}/plugins/CfrDecompilerWrapper.java", 4 | "DependencyURL": [ 5 | "file://${HOME}/.m2/repository/org/benf/cfr/0.151/cfr-0.151.jar" 6 | ], 7 | "DecompilerDownloadURL": "https://www.benf.org/other/cfr/faq.html" 8 | } 9 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/FernflowerDecompilerWrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Fernflower", 3 | "WrapperURL": "file://${XDG_CONFIG_HOME}/plugins/FernflowerDecompilerWrapper.java", 4 | "DependencyURL": [ 5 | "file://${HOME}/.m2/repository/org/jboss/windup/decompiler/decompiler-fernflower/5.1.4.Final/decompiler-fernflower-5.1.4.Final.jar", 6 | "file://${HOME}/.m2/repository/org/jboss/windup/decompiler/fernflower/windup-fernflower/1.0.0.20171018/windup-fernflower-1.0.0.20171018.jar" 7 | ], 8 | "DecompilerDownloadURL": "https://github.com/JetBrains/intellij-community/tree/master/plugins/java-decompiler/engine" 9 | } 10 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/Jasm7DecompilerWrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "jasm7", 3 | "WrapperURL": "file://${XDG_CONFIG_HOME}/plugins/Jasm7DecompilerWrapper.java", 4 | "DependencyURL": [ 5 | "file://${HOME}/.m2/repository/org/openjdk/asmtools/asmtools-core/7.0.b10-ea/asmtools-core-7.0.b10-ea.jar" 6 | ], 7 | "DecompilerDownloadURL": "https://github.com/openjdk/asmtools" 8 | } 9 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/JasmDecompilerWrapper.java: -------------------------------------------------------------------------------- 1 | import java.io.ByteArrayOutputStream; 2 | import java.io.File; 3 | import java.io.IOException; 4 | import java.io.PrintStream; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.io.PrintWriter; 11 | import java.util.Set; 12 | 13 | import org.openjdk.asmtools.common.inputs.*; 14 | import org.openjdk.asmtools.common.outputs.log.*; 15 | import org.openjdk.asmtools.common.outputs.*; 16 | 17 | public class JasmDecompilerWrapper { 18 | 19 | public String decompile(byte[] bytecode, String[] options) { 20 | try { 21 | log(null, "jasm decompiler caled with input of bytes: " + bytecode.length); 22 | ToolInput[] originalFiles = new ToolInput[]{new ByteInput(bytecode)}; 23 | TextOutput decodedFiles = new TextOutput(); 24 | SingleDualOutputStreamOutput decodeLog = new StderrLog(); 25 | org.openjdk.asmtools.jdis.Main jdis = new org.openjdk.asmtools.jdis.Main(decodedFiles, decodeLog, originalFiles); 26 | jdis.setVerboseFlag(true); 27 | int r = jdis.disasm(); 28 | return decodedFiles.getOutputs().get(0).getBody(); 29 | } catch (Exception e) { 30 | e.printStackTrace(); 31 | return e.toString(); 32 | } 33 | } 34 | 35 | private void log(Object logger, String message) { 36 | System.err.println(message); 37 | } 38 | 39 | public Map compile(Map src, String[] options, Object maybeLogger) throws Exception { 40 | log(maybeLogger, "jasm compiler caled with input of files: " + src.size()); 41 | ToolInput[] originalFiles = new ToolInput[src.size()]; 42 | ArrayList> input = new ArrayList<>(src.entrySet()); 43 | for (int i = 0; i < input.size(); i++) { 44 | originalFiles[i] = new StringInput(input.get(i).getValue()); 45 | } 46 | ByteOutput encodedFiles = new ByteOutput(); 47 | SingleDualOutputStreamOutput encodeLog = new StderrLog(); 48 | org.openjdk.asmtools.jasm.Main jasm = new org.openjdk.asmtools.jasm.Main(encodedFiles, encodeLog, originalFiles); 49 | jasm.setVerboseFlag(true); 50 | int r = jasm.compile(); 51 | Map results = new HashMap<>(src.size()); 52 | for(ByteOutput.NamedBinary nb: encodedFiles.getOutputs()) { 53 | results.put(nb.getFqn().replace("/", "."), nb.getBody()); 54 | } 55 | return results; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/JasmDecompilerWrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "jasm", 3 | "WrapperURL": "file://${XDG_CONFIG_HOME}/plugins/JasmDecompilerWrapper.java", 4 | "DependencyURL": [ 5 | "file://${HOME}/.m2/repository/org/openjdk/asmtools/8.0.b09-ea/asmtools-8.0.b09-ea.jar" 6 | ], 7 | "DecompilerDownloadURL": "https://github.com/openjdk/asmtools" 8 | } 9 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/JasmG7DecompilerWrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "jasmG7", 3 | "WrapperURL": "file://${XDG_CONFIG_HOME}/plugins/JasmG7DecompilerWrapper.java", 4 | "DependencyURL": [ 5 | "file://${HOME}/.m2/repository/org/openjdk/asmtools/asmtools-core/7.0.b10-ea/asmtools-core-7.0.b10-ea.jar" 6 | ], 7 | "DecompilerDownloadURL": "https://github.com/openjdk/asmtools" 8 | } 9 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/JasmGDecompilerWrapper.java: -------------------------------------------------------------------------------- 1 | import java.io.ByteArrayOutputStream; 2 | import java.io.File; 3 | import java.io.IOException; 4 | import java.io.PrintStream; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.io.PrintWriter; 11 | import java.util.Set; 12 | 13 | import org.openjdk.asmtools.common.inputs.*; 14 | import org.openjdk.asmtools.common.outputs.log.*; 15 | import org.openjdk.asmtools.common.outputs.*; 16 | import org.openjdk.asmtools.jdis.Options; 17 | 18 | public class JasmGDecompilerWrapper { 19 | 20 | public String decompile(byte[] bytecode, String[] options) { 21 | try { 22 | log(null, "jasmG decompiler caled with input of bytes: " + bytecode.length); 23 | ToolInput[] originalFiles = new ToolInput[]{new ByteInput(bytecode)}; 24 | TextOutput decodedFiles = new TextOutput(); 25 | SingleDualOutputStreamOutput decodeLog = new StderrLog(); 26 | org.openjdk.asmtools.jdis.Main jdis = new org.openjdk.asmtools.jdis.Main(decodedFiles, decodeLog, originalFiles); 27 | jdis.setVerboseFlag(true); 28 | Options.setDetailedOutputOptions(); 29 | int r = jdis.disasm(); 30 | return decodedFiles.getOutputs().get(0).getBody(); 31 | } catch (Exception e) { 32 | e.printStackTrace(); 33 | return e.toString(); 34 | } 35 | } 36 | 37 | private void log(Object logger, String message) { 38 | System.err.println(message); 39 | } 40 | 41 | public Map compile(Map src, String[] options, Object maybeLogger) throws Exception { 42 | log(maybeLogger, "jasmG compiler caled with input of files: " + src.size()); 43 | ToolInput[] originalFiles = new ToolInput[src.size()]; 44 | ArrayList> input = new ArrayList<>(src.entrySet()); 45 | for (int i = 0; i < input.size(); i++) { 46 | originalFiles[i] = new StringInput(input.get(i).getValue()); 47 | } 48 | ByteOutput encodedFiles = new ByteOutput(); 49 | SingleDualOutputStreamOutput encodeLog = new StderrLog(); 50 | org.openjdk.asmtools.jasm.Main jasm = new org.openjdk.asmtools.jasm.Main(encodedFiles, encodeLog, originalFiles); 51 | jasm.setVerboseFlag(true); 52 | int r = jasm.compile(); 53 | Map results = new HashMap<>(src.size()); 54 | for(ByteOutput.NamedBinary nb: encodedFiles.getOutputs()) { 55 | results.put(nb.getFqn().replace("/", "."), nb.getBody()); 56 | } 57 | return results; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/JasmGDecompilerWrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "jasmG", 3 | "WrapperURL": "file://${XDG_CONFIG_HOME}/plugins/JasmGDecompilerWrapper.java", 4 | "DependencyURL": [ 5 | "file://${HOME}/.m2/repository/org/openjdk/asmtools/8.0.b09-ea/asmtools-8.0.b09-ea.jar" 6 | ], 7 | "DecompilerDownloadURL": "https://github.com/openjdk/asmtools" 8 | } 9 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/Jcoder7DecompilerWrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "jcoder7", 3 | "WrapperURL": "file://${XDG_CONFIG_HOME}/plugins/Jcoder7DecompilerWrapper.java", 4 | "DependencyURL": [ 5 | "file://${HOME}/.m2/repository/org/openjdk/asmtools/asmtools-core/7.0.b10-ea/asmtools-core-7.0.b10-ea.jar" 6 | ], 7 | "DecompilerDownloadURL": "https://github.com/openjdk/asmtools" 8 | } 9 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/JcoderDecompilerWrapper.java: -------------------------------------------------------------------------------- 1 | import java.io.ByteArrayOutputStream; 2 | import java.io.File; 3 | import java.io.IOException; 4 | import java.io.PrintStream; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.io.PrintWriter; 11 | import java.util.Set; 12 | 13 | import org.openjdk.asmtools.common.inputs.*; 14 | import org.openjdk.asmtools.common.outputs.log.*; 15 | import org.openjdk.asmtools.common.outputs.*; 16 | 17 | public class JcoderDecompilerWrapper { 18 | 19 | public String decompile(byte[] bytecode, String[] options) { 20 | try { 21 | log(null, "jcoder decompiler caled with input of bytes: " + bytecode.length); 22 | ToolInput[] originalFiles = new ToolInput[]{new ByteInput(bytecode)}; 23 | TextOutput decodedFiles = new TextOutput(); 24 | SingleDualOutputStreamOutput decodeLog = new StderrLog(); 25 | org.openjdk.asmtools.jdec.Main jdec = new org.openjdk.asmtools.jdec.Main(decodedFiles, decodeLog, originalFiles); 26 | jdec.setVerboseFlag(true); 27 | int r = jdec.decode(); 28 | return decodedFiles.getOutputs().get(0).getBody(); 29 | } catch (Exception e) { 30 | e.printStackTrace(); 31 | return e.toString(); 32 | } 33 | } 34 | 35 | private void log(Object logger, String message) { 36 | System.err.println(message); 37 | } 38 | 39 | public Map compile(Map src, String[] options, Object maybeLogger) throws Exception { 40 | log(maybeLogger, "jcoder compiler caled with input of files: " + src.size()); 41 | ToolInput[] originalFiles = new ToolInput[src.size()]; 42 | ArrayList> input = new ArrayList<>(src.entrySet()); 43 | for (int i = 0; i < input.size(); i++) { 44 | originalFiles[i] = new StringInput(input.get(i).getValue()); 45 | } 46 | ByteOutput encodedFiles = new ByteOutput(); 47 | SingleDualOutputStreamOutput encodeLog = new StderrLog(); 48 | org.openjdk.asmtools.jcoder.Main jcoder = new org.openjdk.asmtools.jcoder.Main(encodedFiles, encodeLog, originalFiles); 49 | jcoder.setVerboseFlag(true); 50 | int r = jcoder.compile(); 51 | Map results = new HashMap<>(src.size()); 52 | for(ByteOutput.NamedBinary nb: encodedFiles.getOutputs()) { 53 | results.put(nb.getFqn().replace("/", ".").replaceAll("\\.class$",""), nb.getBody()); 54 | } 55 | return results; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/JcoderDecompilerWrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "jcoder", 3 | "WrapperURL": "file://${XDG_CONFIG_HOME}/plugins/JcoderDecompilerWrapper.java", 4 | "DependencyURL": [ 5 | "file://${HOME}/.m2/repository/org/openjdk/asmtools/8.0.b09-ea/asmtools-8.0.b09-ea.jar" 6 | ], 7 | "DecompilerDownloadURL": "https://github.com/openjdk/asmtools" 8 | } 9 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/JcoderG7DecompilerWrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "jcoderG7", 3 | "WrapperURL": "file://${XDG_CONFIG_HOME}/plugins/JcoderG7DecompilerWrapper.java", 4 | "DependencyURL": [ 5 | "file://${HOME}/.m2/repository/org/openjdk/asmtools/asmtools-core/7.0.b10-ea/asmtools-core-7.0.b10-ea.jar" 6 | ], 7 | "DecompilerDownloadURL": "https://github.com/openjdk/asmtools" 8 | } 9 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/JcoderGDecompilerWrapper.java: -------------------------------------------------------------------------------- 1 | import java.io.ByteArrayOutputStream; 2 | import java.io.File; 3 | import java.io.IOException; 4 | import java.io.PrintStream; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.io.PrintWriter; 11 | import java.util.Set; 12 | 13 | import org.openjdk.asmtools.common.inputs.*; 14 | import org.openjdk.asmtools.common.outputs.log.*; 15 | import org.openjdk.asmtools.common.outputs.*; 16 | import org.openjdk.asmtools.jdis.Options; 17 | 18 | public class JcoderGDecompilerWrapper { 19 | 20 | public String decompile(byte[] bytecode, String[] options) { 21 | try { 22 | log(null, "jcoder decompiler caled with input of bytes: " + bytecode.length); 23 | ToolInput[] originalFiles = new ToolInput[]{new ByteInput(bytecode)}; 24 | TextOutput decodedFiles = new TextOutput(); 25 | SingleDualOutputStreamOutput decodeLog = new StderrLog(); 26 | org.openjdk.asmtools.jdec.Main jdec = new org.openjdk.asmtools.jdec.Main(decodedFiles, decodeLog, originalFiles); 27 | jdec.setVerboseFlag(true); 28 | jdec.setPrintDetails(); 29 | int r = jdec.decode(); 30 | return decodedFiles.getOutputs().get(0).getBody(); 31 | } catch (Exception e) { 32 | e.printStackTrace(); 33 | return e.toString(); 34 | } 35 | } 36 | 37 | private void log(Object logger, String message) { 38 | System.err.println(message); 39 | } 40 | 41 | public Map compile(Map src, String[] options, Object maybeLogger) throws Exception { 42 | log(maybeLogger, "jcoder compiler caled with input of files: " + src.size()); 43 | ToolInput[] originalFiles = new ToolInput[src.size()]; 44 | ArrayList> input = new ArrayList<>(src.entrySet()); 45 | for (int i = 0; i < input.size(); i++) { 46 | originalFiles[i] = new StringInput(input.get(i).getValue()); 47 | } 48 | ByteOutput encodedFiles = new ByteOutput(); 49 | SingleDualOutputStreamOutput encodeLog = new StderrLog(); 50 | org.openjdk.asmtools.jcoder.Main jcoder = new org.openjdk.asmtools.jcoder.Main(encodedFiles, encodeLog, originalFiles); 51 | jcoder.setVerboseFlag(true); 52 | int r = jcoder.compile(); 53 | Map results = new HashMap<>(src.size()); 54 | for(ByteOutput.NamedBinary nb: encodedFiles.getOutputs()) { 55 | results.put(nb.getFqn().replace("/", ".").replaceAll("\\.class$",""), nb.getBody()); 56 | } 57 | return results; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/JcoderGDecompilerWrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "jcoderG", 3 | "WrapperURL": "file://${XDG_CONFIG_HOME}/plugins/JcoderGDecompilerWrapper.java", 4 | "DependencyURL": [ 5 | "file://${HOME}/.m2/repository/org/openjdk/asmtools/8.0.b09-ea/asmtools-8.0.b09-ea.jar" 6 | ], 7 | "DecompilerDownloadURL": "https://github.com/openjdk/asmtools" 8 | } 9 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/JdDecompilerWrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "JD", 3 | "WrapperURL": "file://${XDG_CONFIG_HOME}/plugins/JdDecompilerWrapper.java", 4 | "DependencyURL": [ 5 | "file://${HOME}/.m2/repository/org/jd/jd-core/1.1.3/jd-core-1.1.3.jar" 6 | ], 7 | "DecompilerDownloadURL": "https://github.com/java-decompiler/jd-core/" 8 | } 9 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/ProcyonAssemblerDecompilerWrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Procyon-Assembler", 3 | "WrapperURL": "file://${XDG_CONFIG_HOME}/plugins/ProcyonAssemblerDecompilerWrapper.java", 4 | "DependencyURL": [ 5 | "file://${HOME}/.m2/repository/com/github/mstrobel/procyon-core/1.0-SNAPSHOT/procyon-core-1.0-SNAPSHOT.jar", 6 | "file://${HOME}/.m2/repository/com/github/mstrobel/procyon-compilertools/1.0-SNAPSHOT/procyon-compilertools-1.0-SNAPSHOT.jar", 7 | "file://${HOME}/.m2/repository/com/github/mstrobel/procyon-decompiler/1.0-SNAPSHOT/procyon-decompiler-1.0-SNAPSHOT.jar", 8 | "file://${HOME}/.m2/repository/com/beust/jcommander/1.78/jcommander-1.78.jar" 9 | ], 10 | "DecompilerDownloadURL": "https://github.com/mstrobel/procyon/git " 11 | } 12 | -------------------------------------------------------------------------------- /runtime-decompiler/src/plugins/ProcyonDecompilerWrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Procyon", 3 | "WrapperURL": "file://${XDG_CONFIG_HOME}/plugins/ProcyonDecompilerWrapper.java", 4 | "DependencyURL": [ 5 | "file://${HOME}/.m2/repository/com/github/mstrobel/procyon-core/1.0-SNAPSHOT/procyon-core-1.0-SNAPSHOT.jar", 6 | "file://${HOME}/.m2/repository/com/github/mstrobel/procyon-compilertools/1.0-SNAPSHOT/procyon-compilertools-1.0-SNAPSHOT.jar", 7 | "file://${HOME}/.m2/repository/com/github/mstrobel/procyon-decompiler/1.0-SNAPSHOT/procyon-decompiler-1.0-SNAPSHOT.jar", 8 | "file://${HOME}/.m2/repository/com/beust/jcommander/1.78/jcommander-1.78.jar" 9 | ], 10 | "DecompilerDownloadURL": "https://github.com/mstrobel/procyon/git " 11 | } 12 | -------------------------------------------------------------------------------- /runtime-decompiler/src/test/java/org/jrd/backend/data/cli/ArchiveManagerOptionsTest.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli; 2 | 3 | import org.jrd.backend.data.ArchiveManagerOptions; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.List; 8 | 9 | class ArchiveManagerOptionsTest { 10 | 11 | @Test 12 | void isInnerDefault() { 13 | ArchiveManagerOptions a1 = new ArchiveManagerOptions(); 14 | Assertions.assertTrue(a1.isInner("some/file/filer.zip")); 15 | Assertions.assertTrue(a1.isInner("filer.WAR")); 16 | } 17 | 18 | @Test 19 | void isNotInnerDefault() { 20 | ArchiveManagerOptions a1 = new ArchiveManagerOptions(); 21 | Assertions.assertFalse(a1.isInner("some/file/filerzip")); 22 | Assertions.assertFalse(a1.isInner("filerWAR")); 23 | } 24 | 25 | @Test 26 | void isInnerCustom() { 27 | ArchiveManagerOptions a1 = new ArchiveManagerOptions(); 28 | a1.setExtensions(List.of("ZIP", "WAR")); 29 | Assertions.assertTrue(a1.isInner("some/file/filer.zip")); 30 | Assertions.assertTrue(a1.isInner("filer.WAR")); 31 | } 32 | 33 | @Test 34 | void isNotInnerCustom() { 35 | ArchiveManagerOptions a1 = new ArchiveManagerOptions(); 36 | a1.setExtensions(List.of("Xzip", "xWAR")); 37 | Assertions.assertFalse(a1.isInner("some/file/filerzip")); 38 | Assertions.assertFalse(a1.isInner("filerWAR")); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /runtime-decompiler/src/test/java/org/jrd/backend/data/cli/InMemoryJarTest.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.io.ByteArrayInputStream; 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.util.Enumeration; 11 | import java.util.jar.JarEntry; 12 | import java.util.jar.JarFile; 13 | import java.util.jar.JarInputStream; 14 | import java.util.zip.ZipEntry; 15 | 16 | public class InMemoryJarTest { 17 | 18 | @Test 19 | public void testJarFile() throws IOException { 20 | InMemoryJar jar = new InMemoryJar(); 21 | jar.addFile(new byte[]{0, 1, 2}, "my.clazz"); 22 | jar.close(); 23 | File f = jar.save(); 24 | JarFile jf = new JarFile(f); 25 | boolean one = false; 26 | boolean second = false; 27 | int c = 0; 28 | for (Enumeration list = jf.entries(); list.hasMoreElements();) { 29 | c++; 30 | ZipEntry entry = (ZipEntry) list.nextElement(); 31 | if (entry.getName().equals("my/clazz.class")) { 32 | one = true; 33 | Assertions.assertEquals(3, entry.getSize()); 34 | } 35 | if (entry.getName().equals("META-INF/MANIFEST.MF")) { 36 | second = true; 37 | Assertions.assertTrue(entry.getSize() > 10); 38 | } 39 | } 40 | jf.close(); 41 | Assertions.assertTrue(one); 42 | Assertions.assertTrue(second); 43 | Assertions.assertEquals(2, c); 44 | } 45 | 46 | @Test 47 | public void testJarStream() throws IOException { 48 | InMemoryJar jar = new InMemoryJar(); 49 | jar.addFile(new byte[]{0, 1, 2}, "my.clazz"); 50 | jar.close(); 51 | JarInputStream jf = new JarInputStream(new ByteArrayInputStream(jar.toBytes())); 52 | boolean one = false; 53 | boolean second = false; 54 | int c = 0; 55 | JarEntry entry; 56 | while ((entry = jf.getNextJarEntry()) != null) { 57 | c++; 58 | if (entry.getName().equals("my/clazz.class")) { 59 | one = true; 60 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 61 | while (true) { 62 | int qwe = jf.read(); 63 | if (qwe == -1) { 64 | break; 65 | } 66 | baos.write(qwe); 67 | } 68 | Assertions.assertEquals(3, entry.getSize()); 69 | } 70 | if (entry.getName().equals("META-INF/MANIFEST.MF")) { 71 | second = true; 72 | Assertions.assertTrue(entry.getSize() > 10); 73 | } 74 | } 75 | jf.close(); 76 | Assertions.assertTrue(one); 77 | Assertions.assertFalse(second); 78 | Assertions.assertEquals(1, c); 79 | //unable to read manifest:( 80 | //unable to verify if it is here... 81 | //obviously is, see file 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /runtime-decompiler/src/test/java/org/jrd/backend/data/cli/ModifiableDummyTestingHelper.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli; 2 | 3 | class ModifiableDummyTestingHelper extends AbstractSourceTestClass { 4 | 5 | @Override 6 | protected String getClassName() { 7 | return "TestingModifiableDummy"; 8 | } 9 | 10 | @Override 11 | protected String getPackageName() { 12 | return "testing.modifiabledummy"; 13 | } 14 | 15 | @Override 16 | String getContentWithoutPackage(String nwHello) { 17 | return "public class " + getClassName() + " {\n" + " public static void main(String[] args) throws InterruptedException {\n" + 18 | " while(true) {\n" + " new " + getClassName() + "().print();\n" + " Thread.sleep(100);\n" + 19 | " }\n" + " }\n" + " private void print(){\n/*API_PLACEHOLDER*/\nSystem.out.println(\"" + nwHello + "\");}\n" + 20 | "}\n"; 21 | } 22 | 23 | @Override 24 | String getGreetings() { 25 | return "Hello"; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /runtime-decompiler/src/test/java/org/jrd/backend/data/cli/TestingDummyHelper.java: -------------------------------------------------------------------------------- 1 | package org.jrd.backend.data.cli; 2 | 3 | class TestingDummyHelper extends AbstractSourceTestClass { 4 | 5 | @Override 6 | protected String getClassName() { 7 | return "TestingDummy"; 8 | } 9 | 10 | @Override 11 | protected String getPackageName() { 12 | return "testing.dummy"; 13 | } 14 | 15 | @Override 16 | String getContentWithoutPackage(String nwHello) { 17 | return "public class " + getClassName() + " {\n" + " public static void main(String[] args) throws InterruptedException {\n" + 18 | " while(true) {\n" + " System.out.println(\"" + nwHello + "\");\n" + " Thread.sleep(100);\n" + 19 | " }\n" + " }\n" + "}\n"; 20 | } 21 | 22 | @Override 23 | String getGreetings() { 24 | return "Hello"; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /runtime-decompiler/src/test/java/org/jrd/frontend/frame/overwrite/OverwriteClassDialogTest.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.frame.overwrite; 2 | 3 | import org.jrd.backend.data.Config; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import java.util.NoSuchElementException; 11 | import java.util.Optional; 12 | 13 | public class OverwriteClassDialogTest { 14 | 15 | @Test 16 | void testPurgeSourceTarget() { 17 | List params = new ArrayList<>(); 18 | Config.getConfig().setBestSourceTarget(Optional.of(8)); 19 | OverwriteClassDialog.purgeSourceTarget(params); 20 | Assertions.assertEquals(Arrays.asList("-source", "8", "-target", "8"), params); 21 | Config.getConfig().setBestSourceTarget(Optional.of(11)); 22 | OverwriteClassDialog.purgeSourceTarget(params); 23 | Assertions.assertEquals(Arrays.asList("-source", "11", "-target", "11"), params); 24 | Config.getConfig().setBestSourceTarget(Optional.of(8)); 25 | params.add(0, "param1"); 26 | params.add(1, "param2"); 27 | OverwriteClassDialog.purgeSourceTarget(params); 28 | Assertions.assertEquals(Arrays.asList("-source", "8", "-target", "8", "param1", "param2"), params); 29 | Config.getConfig().setBestSourceTarget(Optional.of(11)); 30 | OverwriteClassDialog.purgeSourceTarget(params); 31 | Assertions.assertEquals(Arrays.asList("-source", "11", "-target", "11", "param1", "param2"), params); 32 | Config.getConfig().setBestSourceTarget(Optional.of(8)); 33 | params.add(0, "param3"); 34 | params.add(1, "param4"); 35 | OverwriteClassDialog.purgeSourceTarget(params); 36 | Assertions.assertEquals(Arrays.asList("-source", "8", "-target", "8", "param3", "param4", "param1", "param2"), params); 37 | params = new ArrayList<>(); 38 | Config.getConfig().setBestSourceTarget(Optional.of(11)); 39 | params.add(0, "param1"); 40 | params.add(1, "param2"); 41 | OverwriteClassDialog.purgeSourceTarget(params); 42 | Assertions.assertEquals(Arrays.asList("-source", "11", "-target", "11", "param1", "param2"), params); 43 | } 44 | 45 | @Test 46 | void testPurgeSourceTargetEmpty() { 47 | List params = new ArrayList<>(); 48 | Config.getConfig().setBestSourceTarget(Optional.empty()); 49 | params.add(0, "param1"); 50 | params.add(1, "param2"); 51 | Exception exception = Assertions.assertThrows(NoSuchElementException.class, () -> OverwriteClassDialog.purgeSourceTarget(params)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /runtime-decompiler/src/test/java/org/jrd/frontend/utility/AgentApiGeneratorTest.java: -------------------------------------------------------------------------------- 1 | package org.jrd.frontend.utility; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | class AgentApiGeneratorTest { 7 | 8 | @Test 9 | void containsAllInOrderEmptyes() { 10 | Assertions.assertTrue(AgentApiGenerator.containsAllInOrder(null, "blah")); 11 | Assertions.assertTrue(AgentApiGenerator.containsAllInOrder("", "blah")); 12 | Assertions.assertTrue(AgentApiGenerator.containsAllInOrder(" ", "blah")); 13 | Assertions.assertFalse(AgentApiGenerator.containsAllInOrder("blah", null)); 14 | Assertions.assertFalse(AgentApiGenerator.containsAllInOrder("blah", "")); 15 | Assertions.assertFalse(AgentApiGenerator.containsAllInOrder("blah", " ")); 16 | } 17 | 18 | @Test 19 | void containsAllInOrder() { 20 | Assertions.assertTrue(AgentApiGenerator.containsAllInOrder("abc", "xxxayyyyaaadddbbbuuuceee")); 21 | Assertions.assertTrue(AgentApiGenerator.containsAllInOrder("abbc", "xxxayyyyaaadddbbbuuuceee")); 22 | Assertions.assertFalse(AgentApiGenerator.containsAllInOrder("abbc", "xxxayyyyaaadddbuuuceee")); 23 | Assertions.assertTrue(AgentApiGenerator.containsAllInOrder("blah", "blah")); 24 | Assertions.assertTrue(AgentApiGenerator.containsAllInOrder("bh", "blah ")); 25 | Assertions.assertTrue(AgentApiGenerator.containsAllInOrder(" l ", "blah")); 26 | Assertions.assertFalse(AgentApiGenerator.containsAllInOrder("hlab", "blah")); 27 | Assertions.assertFalse(AgentApiGenerator.containsAllInOrder("x", "blah")); 28 | Assertions.assertFalse(AgentApiGenerator.containsAllInOrder("bha", "blah")); 29 | Assertions.assertFalse(AgentApiGenerator.containsAllInOrder("ab", "blah")); 30 | } 31 | 32 | @Test 33 | void containsAllInOrderSplit() { 34 | Assertions.assertTrue(AgentApiGenerator.containsAllInOrder("ab", "xxxayy.yyb")); 35 | Assertions.assertFalse(AgentApiGenerator.containsAllInOrder("ab", "xxxayy..yyb")); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /runtime-decompiler/src/test/resources/org/jrd/frontend/frame/main/popup/testPatch1: -------------------------------------------------------------------------------- 1 | From 508a07e33c53b050cda0e3abd242f629c299ed7d Mon Sep 17 00:00:00 2001 2 | From: Michal Karm Babacek 3 | Date: Sun, 16 Oct 2022 10:53:11 +0200 4 | Subject: [PATCH] Quarkus 2.13.0.Final to Quarkus 2.13.2.Final 5 | 6 | --- 7 | jenkins/job_dsl/tests/Constants.groovy | 6 +++--- 8 | .../tests/mandrel_linux_amd64_perfcheck_tests.groovy | 2 +- 9 | 2 files changed, 4 insertions(+), 4 deletions(-) 10 | 11 | diff --git a/jenkins/job_dsl/tests/Constants.groovy b/jenkins/job_dsl/tests/Constants.groovy 12 | index e7e1dffc..25312af8 100644 13 | --- a/jenkins/job_dsl/tests/Constants.groovy 14 | +++ b/jenkins/job_dsl/tests/Constants.groovy 15 | @@ -2,21 +2,21 @@ class Constants { 16 | 17 | static final ArrayList QUARKUS_VERSION_LATEST = 18 | [ 19 | - '2.13.0.Final', 20 | + '2.13.2.Final', 21 | 'main' 22 | ] 23 | 24 | static final ArrayList QUARKUS_VERSION_RELEASED = 25 | [ 26 | '2.7.6.Final', 27 | - '2.13.0.Final' 28 | + '2.13.2.Final' 29 | ] 30 | 31 | static final ArrayList QUARKUS_VERSION_ALL = 32 | [ 33 | '2.7.6.Final', 34 | '2.12.2.Final', 35 | - '2.13.0.Final' 36 | + '2.13.2.Final' 37 | ] 38 | 39 | static final String QUARKUS_MODULES_AWT_TESTS = '' + 40 | diff --git a/jenkins/job_dsl/tests/mandrel_linux_amd64_perfcheck_tests.groovy b/jenkins/job_dsl/tests/mandrel_linux_amd64_perfcheck_tests.groovy 41 | index 036e583d..f7b21022 100644 42 | --- a/jenkins/job_dsl/tests/mandrel_linux_amd64_perfcheck_tests.groovy 43 | +++ b/jenkins/job_dsl/tests/mandrel_linux_amd64_perfcheck_tests.groovy 44 | @@ -7,7 +7,7 @@ matrixJob('mandrel-linux-amd64-perfcheck-tests') { 45 | '22.0.0.2-Final' 46 | ) 47 | text('QUARKUS_VERSION', 48 | - '2.13.0.Final', 49 | + '2.13.2.Final', 50 | '2.8.3.Final', 51 | '2.8.2.Final', 52 | '2.7.1.Final' 53 | -- 54 | GitLab 55 | 56 | -------------------------------------------------------------------------------- /runtime-decompiler/src/test/resources/org/jrd/frontend/frame/main/popup/testPatch2: -------------------------------------------------------------------------------- 1 | diff --git a/jenkins/job_dsl/tests/Constants.groovy b/jenkins/job_dsl/tests/Constants.groovy 2 | index e7e1dffc70b26720c8dba133c49fa921bedd8e8b..25312af810b0298c855a760e3741b8824af6a1eb 100644 3 | --- a/jenkins/job_dsl/tests/Constants.groovy 4 | +++ b/jenkins/job_dsl/tests/Constants.groovy 5 | @@ -2,21 +2,21 @@ class Constants { 6 | 7 | static final ArrayList QUARKUS_VERSION_LATEST = 8 | [ 9 | - '2.13.0.Final', 10 | + '2.13.2.Final', 11 | 'main' 12 | ] 13 | 14 | static final ArrayList QUARKUS_VERSION_RELEASED = 15 | [ 16 | '2.7.6.Final', 17 | - '2.13.0.Final' 18 | + '2.13.2.Final' 19 | ] 20 | 21 | static final ArrayList QUARKUS_VERSION_ALL = 22 | [ 23 | '2.7.6.Final', 24 | '2.12.2.Final', 25 | - '2.13.0.Final' 26 | + '2.13.2.Final' 27 | ] 28 | 29 | static final String QUARKUS_MODULES_AWT_TESTS = '' + 30 | diff --git a/jenkins/job_dsl/tests/mandrel_linux_amd64_perfcheck_tests.groovy b/jenkins/job_dsl/tests/mandrel_linux_amd64_perfcheck_tests.groovy 31 | index 036e583d02a27d2d30de691b959113cfa5ffd650..f7b21022a74354a14ddfe5ca18c5e2fd5ffefe56 100644 32 | --- a/jenkins/job_dsl/tests/mandrel_linux_amd64_perfcheck_tests.groovy 33 | +++ b/jenkins/job_dsl/tests/mandrel_linux_amd64_perfcheck_tests.groovy 34 | @@ -7,7 +7,7 @@ matrixJob('mandrel-linux-amd64-perfcheck-tests') { 35 | '22.0.0.2-Final' 36 | ) 37 | text('QUARKUS_VERSION', 38 | - '2.13.0.Final', 39 | + '2.13.2.Final', 40 | '2.8.3.Final', 41 | '2.8.2.Final', 42 | '2.7.1.Final' 43 | --------------------------------------------------------------------------------