├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── buildSrc ├── .gitignore ├── build.gradle └── src │ └── main │ ├── java │ └── org │ │ └── rogmann │ │ └── gradle │ │ └── shadowplugin │ │ ├── JsmudShadowModel.java │ │ ├── JsmudShadowPlugin.java │ │ └── package-info.java │ └── resources │ └── META-INF │ └── gradle-plugins │ └── JsmudShadowPlugin.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main ├── java │ └── org │ │ └── rogmann │ │ └── jsmud │ │ ├── datatypes │ │ ├── Tag.java │ │ ├── VMArrayID.java │ │ ├── VMArrayRegion.java │ │ ├── VMArrayTypeID.java │ │ ├── VMBoolean.java │ │ ├── VMByte.java │ │ ├── VMClassID.java │ │ ├── VMClassLoaderID.java │ │ ├── VMClassObjectID.java │ │ ├── VMDataField.java │ │ ├── VMFieldID.java │ │ ├── VMFrameID.java │ │ ├── VMInt.java │ │ ├── VMInterfaceID.java │ │ ├── VMLong.java │ │ ├── VMMethodID.java │ │ ├── VMObjectID.java │ │ ├── VMObjectOrExceptionID.java │ │ ├── VMReferenceTypeID.java │ │ ├── VMShort.java │ │ ├── VMString.java │ │ ├── VMStringID.java │ │ ├── VMTaggedObjectId.java │ │ ├── VMThreadGroupID.java │ │ ├── VMThreadID.java │ │ ├── VMValue.java │ │ ├── VMVoid.java │ │ └── package-info.java │ │ ├── debugger │ │ ├── CommandBuffer.java │ │ ├── DebuggerException.java │ │ ├── DebuggerInterface.java │ │ ├── DebuggerJvmVisitor.java │ │ ├── DebuggerJvmVisitorProvider.java │ │ ├── JdwpCommand.java │ │ ├── JdwpCommandProcessor.java │ │ ├── JdwpCommandSet.java │ │ ├── JdwpErrorCode.java │ │ ├── JdwpSuspendPolicy.java │ │ ├── JvmClinitWhileDebuggingException.java │ │ ├── MethodFrameDebugContext.java │ │ ├── SlotRequest.java │ │ ├── SlotValue.java │ │ ├── SourceFileRequester.java │ │ ├── SourceFilesLocalDirectory.java │ │ ├── VMEventType.java │ │ └── package-info.java │ │ ├── events │ │ ├── JdwpEventModifier.java │ │ ├── JdwpEventRequest.java │ │ ├── JdwpModifierClassMatch.java │ │ ├── JdwpModifierClassOnly.java │ │ ├── JdwpModifierCount.java │ │ ├── JdwpModifierFieldOnly.java │ │ ├── JdwpModifierLocationOnly.java │ │ ├── JdwpModifierStep.java │ │ ├── JdwpModifierThreadOnly.java │ │ ├── ModKind.java │ │ └── package-info.java │ │ ├── gen │ │ ├── JsmudGeneratedClasses.java │ │ └── package-info.java │ │ ├── log │ │ ├── Logger.java │ │ ├── LoggerFactory.java │ │ ├── LoggerFactoryJavaLogging.java │ │ ├── LoggerFactorySystemOut.java │ │ ├── LoggerSpi.java │ │ └── package-info.java │ │ ├── replydata │ │ ├── LineCodeIndex.java │ │ ├── LineTable.java │ │ ├── RefFieldBean.java │ │ ├── RefFrameBean.java │ │ ├── RefMethodBean.java │ │ ├── RefTypeBean.java │ │ ├── TypeTag.java │ │ ├── VariableSlot.java │ │ └── package-info.java │ │ ├── source │ │ ├── ExpressionArrayLoad.java │ │ ├── ExpressionBase.java │ │ ├── ExpressionCastPrimitive.java │ │ ├── ExpressionCompare.java │ │ ├── ExpressionConditionalOperator.java │ │ ├── ExpressionConstructor.java │ │ ├── ExpressionDuplicate.java │ │ ├── ExpressionException.java │ │ ├── ExpressionGetField.java │ │ ├── ExpressionGetStatic.java │ │ ├── ExpressionInfixBinary.java │ │ ├── ExpressionInstanceOf.java │ │ ├── ExpressionInstrConstant.java │ │ ├── ExpressionInstrIntConstant.java │ │ ├── ExpressionInstrIntNewarray.java │ │ ├── ExpressionInstrZeroConstant.java │ │ ├── ExpressionInvoke.java │ │ ├── ExpressionInvokeDynamic.java │ │ ├── ExpressionMultiNewarray.java │ │ ├── ExpressionNull.java │ │ ├── ExpressionPrefix.java │ │ ├── ExpressionPutField.java │ │ ├── ExpressionSuffix.java │ │ ├── ExpressionTypeInstr.java │ │ ├── ExpressionTypeNewarray.java │ │ ├── ExpressionVariableLoad.java │ │ ├── SourceBlock.java │ │ ├── SourceBlockList.java │ │ ├── SourceFileWriter.java │ │ ├── SourceFileWriterDecompile.java │ │ ├── SourceLine.java │ │ ├── SourceLines.java │ │ ├── SourceNameRenderer.java │ │ ├── SourceRuntimeException.java │ │ ├── SourceZipGenerator.java │ │ ├── StatementArrayStore.java │ │ ├── StatementBase.java │ │ ├── StatementComment.java │ │ ├── StatementConstructor.java │ │ ├── StatementExpression.java │ │ ├── StatementExpressionDuplicated.java │ │ ├── StatementGoto.java │ │ ├── StatementIf.java │ │ ├── StatementInstr.java │ │ ├── StatementInstrPlain.java │ │ ├── StatementInstrZeroOp.java │ │ ├── StatementInvoke.java │ │ ├── StatementLabel.java │ │ ├── StatementLookupSwitch.java │ │ ├── StatementMonitor.java │ │ ├── StatementPutField.java │ │ ├── StatementPutStatic.java │ │ ├── StatementReturn.java │ │ ├── StatementTableSwitch.java │ │ ├── StatementThrow.java │ │ ├── StatementVariableIinc.java │ │ ├── StatementVariableStore.java │ │ ├── ValueCategory.java │ │ └── package-info.java │ │ ├── visitors │ │ ├── ExecutionVisitorDelegation.java │ │ ├── InstructionVisitor.java │ │ ├── InstructionVisitorProvider.java │ │ ├── MessagePrinter.java │ │ ├── VisitorFrame.java │ │ └── package-info.java │ │ └── vm │ │ ├── AtypeEnum.java │ │ ├── CallSiteContext.java │ │ ├── CallSiteGenerator.java │ │ ├── CallSiteRegistry.java │ │ ├── CallSiteSimulation.java │ │ ├── ClassExecutionFilter.java │ │ ├── ClassOutlineGenerator.java │ │ ├── ClassProvider.java │ │ ├── ClassRegistry.java │ │ ├── ClassRemapper.java │ │ ├── ClassRemapperSymbolTable.java │ │ ├── ClassWithName.java │ │ ├── ClassWriterCallSite.java │ │ ├── FrameDisplay.java │ │ ├── InvokeFlow.java │ │ ├── JsmudClassLoader.java │ │ ├── JsmudConfiguration.java │ │ ├── JvmCallSite.java │ │ ├── JvmCallSiteMarker.java │ │ ├── JvmException.java │ │ ├── JvmExecutionVisitor.java │ │ ├── JvmExecutionVisitorProvider.java │ │ ├── JvmHelper.java │ │ ├── JvmInvocationHandler.java │ │ ├── JvmInvocationHandlerReflection.java │ │ ├── JvmReturnAddress.java │ │ ├── JvmUncaughtException.java │ │ ├── MethodExecutor.java │ │ ├── MethodFrame.java │ │ ├── MockMethods.java │ │ ├── NativeMethodExecutor.java │ │ ├── NativeMethodExecutorReflection.java │ │ ├── ObjectMonitor.java │ │ ├── OpcodeDisplay.java │ │ ├── OperandStack.java │ │ ├── ReflectionHelper.java │ │ ├── SimpleClassExecutor.java │ │ ├── SimpleWeakIdentityHashMap.java │ │ ├── ThreadClassGenerator.java │ │ ├── ThreadExecutor.java │ │ ├── ThreadMonitor.java │ │ ├── UninitializedInstance.java │ │ ├── Utils.java │ │ ├── VM.java │ │ └── package-info.java ├── res_license │ └── META-INF │ │ ├── LICENSE │ │ └── NOTICE └── res_license_all │ └── META-INF │ ├── LICENSE │ └── NOTICE └── test └── java └── org └── rogmann └── jsmud ├── dumps ├── ByteArrayWriter.java ├── ConnectionStreamListener.java ├── ConnectionStreamProxy.java ├── JdwpParser.java ├── JdwpProxy.java ├── WiresharkStreamDecorator.java └── package-info.java ├── test ├── BytecodeSample.java ├── BytecodeSampleMain.java ├── ClassARenamed.java ├── ClassBRenamed.java ├── ClassExecutionMain.java ├── ClassScanMain.java ├── ConstructorNesting.java ├── DebuggerTestMain.java ├── DebuggerTestMethods.java ├── DefInterface.java ├── DefInterfaceImpl.java ├── DefInterfaceSuper.java ├── DefInterfaceSuper2.java ├── ExampleClassLoader.java ├── GenerateJUnitTests.java ├── JsmudTest.java ├── JvmInitTests.java ├── JvmTests.java ├── JvmTestsExecutionMain.java ├── JvmTestsJUnit.java ├── MemoryTestMain.java ├── NativeExecutor.java ├── SampleClass.java ├── SampleClassMain.java ├── SwingTest.java ├── package-info.java └── source │ └── SourceFileOfClassMain.java ├── test2 ├── BuilderInTest2.java ├── ClassInTest2.java └── package-info.java ├── util ├── BytecodeDumpMain.java ├── GenerateCaseStmt.java └── package-info.java └── vm ├── SimpleWeakIdentityHashMapTest.java └── UtilsTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /build/ 3 | .classpath 4 | .project 5 | /target/ 6 | /.gradle/ 7 | /.settings/ 8 | -------------------------------------------------------------------------------- /buildSrc/.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle/ 2 | /build/ 3 | -------------------------------------------------------------------------------- /buildSrc/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * build.gradle of JsmudShadowPlugin 3 | */ 4 | 5 | plugins { 6 | id 'java-gradle-plugin' 7 | } 8 | 9 | repositories { 10 | mavenLocal() 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | implementation gradleApi() 16 | } 17 | -------------------------------------------------------------------------------- /buildSrc/src/main/java/org/rogmann/gradle/shadowplugin/JsmudShadowModel.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.gradle.shadowplugin; 2 | 3 | /** 4 | * Configuration of JsmudShadowPlugin. 5 | */ 6 | public class JsmudShadowModel { 7 | 8 | /** Java-package of dependency, e.g. "org.objectweb.asm" */ 9 | public String depPackage; 10 | /** Java-package of dependency, e.g. "org.rogmann.jsmud.shadow.asm" */ 11 | public String depPackageShadow; 12 | 13 | @Override 14 | public String toString() { 15 | final StringBuilder sb = new StringBuilder(50); 16 | sb.append(JsmudShadowModel.class.getSimpleName()); 17 | sb.append('{'); 18 | sb.append("depPackage='").append(depPackage).append('\''); 19 | sb.append(", "); 20 | sb.append("depPackageShadow=").append(depPackageShadow).append('\''); 21 | sb.append('}'); 22 | return sb.toString(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /buildSrc/src/main/java/org/rogmann/gradle/shadowplugin/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Implementation of a gradle-plugin to shadow dependencies with source. 3 | */ 4 | package org.rogmann.gradle.shadowplugin; -------------------------------------------------------------------------------- /buildSrc/src/main/resources/META-INF/gradle-plugins/JsmudShadowPlugin.properties: -------------------------------------------------------------------------------- 1 | implementation-class=org.rogmann.gradle.shadowplugin.JsmudShadowPlugin 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srogmann/jsmud-analysis/a0472e0b0f94266b70e44a6cac8cbb57d5b5f932/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * settings.gradle of jsmud-analysis. 3 | */ 4 | 5 | if (hasProperty('applyJsmudShadow') && applyJsmudShadow == 'true') { 6 | println("jsmud-analysis-all-build") 7 | rootProject.name = 'jsmud-analysis-all' 8 | } 9 | else { 10 | println("jsmud-analysis-build without asm included") 11 | rootProject.name = 'jsmud-analysis' 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/Tag.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * Tag of a value. 5 | */ 6 | public enum Tag { 7 | 8 | ARRAY('[', null), 9 | BYTE('B', byte.class), 10 | CHAR('C', char.class), 11 | OBJECT('L', null), 12 | FLOAT('F', float.class), 13 | DOUBLE('D', double.class), 14 | INT('I', int.class), 15 | LONG('J', long.class), 16 | SHORT('S', short.class), 17 | VOID('V', void.class), 18 | BOOLEAN('Z', boolean.class), 19 | STRING('s', null), 20 | THREAD('t', null), 21 | THREAD_GROUP('g', null), 22 | CLASS_LOADER('l', null), 23 | CLASS_OBJECT('c', null); 24 | 25 | /** tag */ 26 | private final byte tag; 27 | 28 | /** class of tag (if primitive) */ 29 | private final Class classTag; 30 | 31 | 32 | /** 33 | * Internal constructor 34 | * @param tagChar tar-char 35 | */ 36 | private Tag(final char tagChar, final Class classTag) { 37 | tag = (byte) tagChar; 38 | this.classTag = classTag; 39 | } 40 | 41 | /** 42 | * Gets the tag-byte. 43 | * @return tag 44 | */ 45 | public byte getTag() { 46 | return tag; 47 | } 48 | 49 | /** 50 | * Gets the class of a tag (if primitive). 51 | * @return class or null 52 | */ 53 | public Class getClassTag() { 54 | return classTag; 55 | } 56 | 57 | /** 58 | * Looks for a tag. 59 | * @param tag tag-byte 60 | * @return tag 61 | */ 62 | public static Tag lookupByTag(final byte tag) { 63 | Tag tTag = null; 64 | for (final Tag loopTag : values()) { 65 | if (loopTag.tag == tag) { 66 | tTag = loopTag; 67 | break; 68 | } 69 | } 70 | return tTag; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMArrayID.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * Array-id. 5 | */ 6 | public class VMArrayID extends VMObjectID { 7 | 8 | /** 9 | * Constructor 10 | * @param arrayId array-id 11 | */ 12 | public VMArrayID(final long arrayId) { 13 | super(arrayId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMArrayRegion.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * Region of an array. 5 | */ 6 | public class VMArrayRegion extends VMDataField { 7 | 8 | /** type of the array */ 9 | private Tag tag; 10 | /** values */ 11 | private VMDataField[] values; 12 | 13 | /** 14 | * Constructor 15 | * @param tag type of the array 16 | * @param values values 17 | */ 18 | public VMArrayRegion(final Tag tag, final VMDataField[] values) { 19 | this.tag = tag; 20 | this.values = values; 21 | } 22 | 23 | /** 24 | * Gets the type of the value. 25 | * @return tag 26 | */ 27 | public Tag getTag() { 28 | return tag; 29 | } 30 | 31 | /** 32 | * Gets the values. 33 | * @return values 34 | */ 35 | public VMDataField[] getValue() { 36 | return values; 37 | } 38 | 39 | /** {@inheritDoc} */ 40 | @Override 41 | public int length() { 42 | int len = 1 + 4; 43 | for (VMDataField vmDataField : values) { 44 | len += vmDataField.length(); 45 | } 46 | return len; 47 | } 48 | 49 | /** {@inheritDoc} */ 50 | @Override 51 | public void write(byte[] buf, int pOffset) { 52 | int offset = pOffset; 53 | buf[offset] = tag.getTag(); 54 | offset++; 55 | 56 | final VMInt vmLen = new VMInt(values.length); 57 | vmLen.write(buf, offset); 58 | offset += 4; 59 | 60 | for (VMDataField value : values) { 61 | value.write(buf, offset); 62 | offset += value.length(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMArrayTypeID.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * ArrayType-id. 5 | */ 6 | public class VMArrayTypeID extends VMReferenceTypeID { 7 | 8 | /** 9 | * Constructor 10 | * @param arrayTypeId array-type-id 11 | */ 12 | public VMArrayTypeID(final long arrayTypeId) { 13 | super(arrayTypeId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMBoolean.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * boolean VM-value. 5 | */ 6 | public class VMBoolean extends VMByte { 7 | 8 | /** 9 | * Constructor 10 | * @param b byte-value of boolean 11 | */ 12 | public VMBoolean(final boolean b) { 13 | super(b ? (byte) 1 : 0); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMByte.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * 8-bit VM-value. 5 | */ 6 | public class VMByte extends VMDataField { 7 | 8 | /** byte-value */ 9 | private final byte value; 10 | 11 | /** 12 | * Constructor 13 | * @param value byte-value 14 | */ 15 | public VMByte(final byte value) { 16 | this.value = value; 17 | } 18 | 19 | /** 20 | * Gets the byte-value. 21 | * @return byte 22 | */ 23 | public byte getValue() { 24 | return value; 25 | } 26 | 27 | /** {@inheritDoc} */ 28 | @Override 29 | public int length() { 30 | return 1; 31 | } 32 | 33 | /** {@inheritDoc} */ 34 | @Override 35 | public String toString() { 36 | return new StringBuilder(20).append(getClass().getSimpleName()).append('(').append(value).append(')').toString(); 37 | } 38 | 39 | /** {@inheritDoc} */ 40 | @Override 41 | public void write(byte[] buf, int offset) { 42 | buf[offset] = value; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMClassID.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * Class-id. 5 | */ 6 | public class VMClassID extends VMReferenceTypeID { 7 | 8 | /** 9 | * Constructor 10 | * @param classId class-id 11 | */ 12 | public VMClassID(final long classId) { 13 | super(classId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMClassLoaderID.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * ClassLoader-id. 5 | */ 6 | public class VMClassLoaderID extends VMObjectID { 7 | 8 | /** 9 | * Constructor 10 | * @param classLoaderId classLoader-id 11 | */ 12 | public VMClassLoaderID(final long classLoaderId) { 13 | super(classLoaderId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMClassObjectID.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * VM-class-object-ID. 5 | */ 6 | public class VMClassObjectID extends VMObjectID { 7 | 8 | /** 9 | * Constructor 10 | * @param classObjectId ID 11 | */ 12 | public VMClassObjectID(final long classObjectId) { 13 | super(classObjectId); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMDataField.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * VM-data-field. 5 | */ 6 | public abstract class VMDataField { 7 | 8 | /** 9 | * Gets the length of the value. 10 | * @return length in bytes 11 | */ 12 | public abstract int length(); 13 | 14 | /** 15 | * Writes the field into a buffer. 16 | * @param buf buffer 17 | * @param offset offset in buffer 18 | */ 19 | public abstract void write(byte[] buf, int offset); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMFieldID.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * Field-id. 5 | */ 6 | public class VMFieldID extends VMReferenceTypeID { 7 | 8 | /** 9 | * Constructor 10 | * @param fieldId field-id 11 | */ 12 | public VMFieldID(final long fieldId) { 13 | super(fieldId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMFrameID.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * Frame-id. 5 | */ 6 | public class VMFrameID extends VMReferenceTypeID { 7 | 8 | /** 9 | * Constructor 10 | * @param frameId frame-id 11 | */ 12 | public VMFrameID(final long frameId) { 13 | super(frameId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMInt.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * 32-bit VM-value. 5 | */ 6 | public class VMInt extends VMDataField { 7 | 8 | /** int-value */ 9 | private final int value; 10 | 11 | /** 12 | * Constructor 13 | * @param value int-value 14 | */ 15 | public VMInt(final int value) { 16 | this.value = value; 17 | } 18 | 19 | /** 20 | * Gets the int-value. 21 | * @return int 22 | */ 23 | public int getValue() { 24 | return value; 25 | } 26 | 27 | /** {@inheritDoc} */ 28 | @Override 29 | public int length() { 30 | return 4; 31 | } 32 | 33 | /** {@inheritDoc} */ 34 | @Override 35 | public void write(byte[] buf, int offset) { 36 | buf[offset] = (byte) (value >> 24); 37 | buf[offset + 1] = (byte) ((value >> 16) & 0xff); 38 | buf[offset + 2] = (byte) ((value >> 8) & 0xff); 39 | buf[offset + 3] = (byte) (value & 0xff); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMInterfaceID.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * Interface-id. 5 | */ 6 | public class VMInterfaceID extends VMReferenceTypeID { 7 | 8 | /** 9 | * Constructor 10 | * @param interfaceId interface-id 11 | */ 12 | public VMInterfaceID(final long interfaceId) { 13 | super(interfaceId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMLong.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * 64-bit VM-value. 5 | */ 6 | public class VMLong extends VMDataField { 7 | 8 | /** long-value */ 9 | private final long value; 10 | 11 | /** 12 | * Constructor 13 | * @param value long-value 14 | */ 15 | public VMLong(final long value) { 16 | this.value = value; 17 | } 18 | 19 | /** 20 | * Gets the long-value. 21 | * @return long 22 | */ 23 | public long getValue() { 24 | return value; 25 | } 26 | 27 | /** {@inheritDoc} */ 28 | @Override 29 | public int length() { 30 | return 8; 31 | } 32 | 33 | /** {@inheritDoc} */ 34 | @Override 35 | public void write(byte[] buf, int offset) { 36 | buf[offset] = (byte) (value >> 56); 37 | buf[offset + 1] = (byte) ((value >> 48) & 0xff); 38 | buf[offset + 2] = (byte) ((value >> 40) & 0xff); 39 | buf[offset + 3] = (byte) ((value >> 32) & 0xff); 40 | buf[offset + 4] = (byte) ((value >> 24) & 0xff); 41 | buf[offset + 5] = (byte) ((value >> 16) & 0xff); 42 | buf[offset + 6] = (byte) ((value >> 8) & 0xff); 43 | buf[offset + 7] = (byte) (value & 0xff); 44 | } 45 | 46 | /** {@inheritDoc} */ 47 | @Override 48 | public String toString() { 49 | return new StringBuilder(20).append(getClass().getSimpleName()).append('(').append(value).append(')').toString(); 50 | } 51 | 52 | /** {@inheritDoc} */ 53 | @Override 54 | public int hashCode() { 55 | final int prime = 31; 56 | int result = 1; 57 | result = prime * result + (int) (value ^ (value >>> 32)); 58 | return result; 59 | } 60 | 61 | /** {@inheritDoc} */ 62 | @Override 63 | public boolean equals(Object obj) { 64 | if (this == obj) { 65 | return true; 66 | } 67 | if (obj == null) { 68 | return false; 69 | } 70 | if (!(obj instanceof VMLong)) { 71 | return false; 72 | } 73 | VMLong other = (VMLong) obj; 74 | if (value != other.value) { 75 | return false; 76 | } 77 | return true; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMMethodID.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * Method-id. 5 | */ 6 | public class VMMethodID extends VMReferenceTypeID { 7 | 8 | /** 9 | * Constructor 10 | * @param methodId method-id 11 | */ 12 | public VMMethodID(final long methodId) { 13 | super(methodId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMObjectID.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * Object-id. 5 | */ 6 | public class VMObjectID extends VMLong { 7 | 8 | /** 9 | * Constructor 10 | * @param objectId object-id 11 | */ 12 | public VMObjectID(final long objectId) { 13 | super(objectId); 14 | } 15 | 16 | /** {@inheritDoc} */ 17 | @Override 18 | public String toString() { 19 | return new StringBuilder(20).append(getClass().getSimpleName()).append('(').append("0x").append(Long.toHexString(getValue())).append(')').toString(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMObjectOrExceptionID.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * Newly created object or exception. 5 | */ 6 | public class VMObjectOrExceptionID { 7 | 8 | /** object-id */ 9 | private final VMTaggedObjectId vmObjectID; 10 | /** exception-id */ 11 | private final VMTaggedObjectId vmExceptionID; 12 | 13 | /** 14 | * Constructor 15 | * @param vmObjectID object-id or null 16 | * @param vmExceptionID exception-id or null 17 | */ 18 | public VMObjectOrExceptionID(final VMTaggedObjectId vmObjectID, final VMTaggedObjectId vmExceptionID) { 19 | this.vmObjectID = vmObjectID; 20 | this.vmExceptionID = vmExceptionID; 21 | } 22 | 23 | /** 24 | * Gets the object-id. 25 | * @return tagged-object-id or null 26 | */ 27 | public VMTaggedObjectId getVmObjectID() { 28 | return vmObjectID; 29 | } 30 | 31 | /** 32 | * Gets the exception-id. 33 | * @return tagged-object-id or null 34 | */ 35 | public VMTaggedObjectId getVmExceptionID() { 36 | return vmExceptionID; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMReferenceTypeID.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * Object-id. 5 | */ 6 | public class VMReferenceTypeID extends VMObjectID { 7 | 8 | /** 9 | * Constructor 10 | * @param objectId object-id 11 | */ 12 | public VMReferenceTypeID(final long objectId) { 13 | super(objectId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMShort.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * 16-bit VM-value. 5 | */ 6 | public class VMShort extends VMDataField { 7 | 8 | /** short-value */ 9 | private final short value; 10 | 11 | /** 12 | * Constructor 13 | * @param value short-value 14 | */ 15 | public VMShort(final short value) { 16 | this.value = value; 17 | } 18 | 19 | /** 20 | * Gets the short-value. 21 | * @return short 22 | */ 23 | public short getValue() { 24 | return value; 25 | } 26 | 27 | /** {@inheritDoc} */ 28 | @Override 29 | public int length() { 30 | return 2; 31 | } 32 | 33 | /** {@inheritDoc} */ 34 | @Override 35 | public void write(byte[] buf, int offset) { 36 | buf[offset] = (byte) (value >> 8); 37 | buf[offset + 1] = (byte) (value & 0xff); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMString.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | import java.nio.charset.StandardCharsets; 4 | 5 | /** 6 | * UTF-8-string. 7 | */ 8 | public class VMString extends VMDataField { 9 | 10 | /** string-value */ 11 | private final String value; 12 | 13 | /** UTF-8-value */ 14 | private final byte[] bufValue; 15 | 16 | /** 17 | * Constructor 18 | * @param value string-value 19 | */ 20 | public VMString(final String value) { 21 | if (value == null) { 22 | throw new IllegalArgumentException("String null is not valid."); 23 | } 24 | this.value = value; 25 | bufValue = value.getBytes(StandardCharsets.UTF_8); 26 | } 27 | 28 | /** 29 | * Gets the string-value. 30 | * @return string 31 | */ 32 | public String getValue() { 33 | return value; 34 | } 35 | 36 | /** {@inheritDoc} */ 37 | @Override 38 | public int length() { 39 | return 4 + bufValue.length; 40 | } 41 | 42 | /** {@inheritDoc} */ 43 | @Override 44 | public void write(byte[] buf, int offset) { 45 | final int len = bufValue.length; 46 | buf[offset] = (byte) (len >> 24); 47 | buf[offset + 1] = (byte) ((len >> 16) & 0xff); 48 | buf[offset + 2] = (byte) ((len >> 8) & 0xff); 49 | buf[offset + 3] = (byte) (len & 0xff); 50 | System.arraycopy(bufValue, 0, buf, offset + 4, len); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMStringID.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * String-id. 5 | */ 6 | public class VMStringID extends VMObjectID { 7 | 8 | /** 9 | * Constructor 10 | * @param stringId string-id 11 | */ 12 | public VMStringID(final long stringId) { 13 | super(stringId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMTaggedObjectId.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * Tagged Object-id. 5 | */ 6 | public class VMTaggedObjectId extends VMValue { 7 | 8 | /** null-object */ 9 | public static final VMTaggedObjectId NULL = new VMTaggedObjectId(new VMObjectID(0l)); 10 | 11 | /** 12 | * Constructor 13 | * @param value value of VMObjectID 14 | */ 15 | public VMTaggedObjectId(final VMObjectID value) { 16 | super(Tag.OBJECT.getTag(), value); 17 | } 18 | 19 | /** 20 | * Constructor 21 | * @param tag JDWP-tag 22 | * @param value value of VMObjectID 23 | */ 24 | public VMTaggedObjectId(final Tag tag, final VMObjectID value) { 25 | super(tag.getTag(), value); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMThreadGroupID.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * ThreadGroup-id. 5 | */ 6 | public class VMThreadGroupID extends VMObjectID { 7 | 8 | /** 9 | * Constructor 10 | * @param threadGroupId threadGroup-id 11 | */ 12 | public VMThreadGroupID(final long threadGroupId) { 13 | super(threadGroupId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMThreadID.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * Thread-id. 5 | */ 6 | public class VMThreadID extends VMObjectID { 7 | 8 | /** 9 | * Constructor 10 | * @param threadId thread-id 11 | */ 12 | public VMThreadID(final long threadId) { 13 | super(threadId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMValue.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * Object-id. 5 | */ 6 | public class VMValue extends VMDataField { 7 | 8 | /** type of the value */ 9 | private byte tag; 10 | /** value */ 11 | private VMDataField value; 12 | 13 | /** 14 | * Constructor 15 | * @param tag type of the value 16 | * @param value value of VMObjectID 17 | */ 18 | public VMValue(final byte tag, final VMDataField value) { 19 | this.tag = tag; 20 | this.value = value; 21 | } 22 | 23 | /** 24 | * Gets the type of the value. 25 | * @return tag 26 | */ 27 | public byte getTag() { 28 | return tag; 29 | } 30 | 31 | /** 32 | * Gets the value 33 | * @return value 34 | */ 35 | public VMDataField getValue() { 36 | return value; 37 | } 38 | 39 | /** {@inheritDoc} */ 40 | @Override 41 | public int length() { 42 | return 1 + value.length(); 43 | } 44 | 45 | /** {@inheritDoc} */ 46 | @Override 47 | public void write(byte[] buf, int offset) { 48 | buf[offset] = tag; 49 | value.write(buf, offset + 1); 50 | } 51 | 52 | /** {@inheritDoc} */ 53 | @Override 54 | public String toString() { 55 | return new StringBuilder(20).append(getClass().getSimpleName()) 56 | .append("(tag:").append(Tag.lookupByTag(tag)) 57 | .append(", value:").append(value).append(')').toString(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/VMVoid.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.datatypes; 2 | 3 | /** 4 | * 0-bit VM-value. 5 | */ 6 | public class VMVoid extends VMDataField { 7 | 8 | /** 9 | * Constructor 10 | */ 11 | public VMVoid() { 12 | // no value 13 | } 14 | 15 | /** {@inheritDoc} */ 16 | @Override 17 | public int length() { 18 | return 0; 19 | } 20 | 21 | /** {@inheritDoc} */ 22 | @Override 23 | public void write(byte[] buf, int offset) { 24 | // no value to write 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/datatypes/package-info.java: -------------------------------------------------------------------------------- 1 | /** VM-datatypes */ 2 | package org.rogmann.jsmud.datatypes; 3 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/debugger/DebuggerException.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.debugger; 2 | 3 | /** 4 | * Exception while executing the debugger. 5 | */ 6 | public class DebuggerException extends RuntimeException { 7 | 8 | /** Serialization-Id */ 9 | private static final long serialVersionUID = 20210331L; 10 | 11 | /** 12 | * Constructor 13 | * @param msg exception-message 14 | */ 15 | public DebuggerException(final String msg) { 16 | super(msg); 17 | } 18 | 19 | /** 20 | * Constructor 21 | * @param msg exception-message 22 | * @param e cause-throwable 23 | */ 24 | public DebuggerException(final String msg, Throwable e) { 25 | super(msg, e); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/debugger/DebuggerInterface.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.debugger; 2 | 3 | import java.io.IOException; 4 | 5 | import org.rogmann.jsmud.datatypes.VMDataField; 6 | 7 | /** 8 | * Interface of the debugger-command-processor. 9 | */ 10 | public interface DebuggerInterface { 11 | 12 | /** 13 | * Processes a set of packets. 14 | * @throws IOException in case of an IO-error 15 | */ 16 | void processPackets() throws IOException; 17 | 18 | /** 19 | * Sends an event in an event-command-set. 20 | * @param policy suspend-policy 21 | * @param eventType event-type 22 | * @param fields fields of the event 23 | * @return id of command 24 | * @throws IOException in case of an io-error 25 | */ 26 | int sendVMEvent(final JdwpSuspendPolicy policy, VMEventType eventType, VMDataField... fields) throws IOException; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/debugger/DebuggerJvmVisitorProvider.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.debugger; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | import java.util.concurrent.ConcurrentMap; 5 | 6 | import org.rogmann.jsmud.events.JdwpEventRequest; 7 | import org.rogmann.jsmud.log.Logger; 8 | import org.rogmann.jsmud.log.LoggerFactory; 9 | import org.rogmann.jsmud.vm.ClassRegistry; 10 | import org.rogmann.jsmud.vm.JvmExecutionVisitor; 11 | import org.rogmann.jsmud.vm.JvmExecutionVisitorProvider; 12 | 13 | /** 14 | * JVM-debugger-visitor provider. 15 | */ 16 | public class DebuggerJvmVisitorProvider implements JvmExecutionVisitorProvider { 17 | /** logger */ 18 | private static final Logger LOGGER = LoggerFactory.getLogger(DebuggerJvmVisitorProvider.class); 19 | 20 | /** number of instructions to be logged in detail */ 21 | private final int maxInstrLogged; 22 | 23 | /** number of method-invocations to be logged in detail */ 24 | private final int maxMethodsLogged; 25 | 26 | /** optional source-file-requester */ 27 | private final SourceFileRequester sourceFileRequester; 28 | 29 | /** map from request-id to event-request */ 30 | private final ConcurrentMap eventRequests = new ConcurrentHashMap<>(); 31 | 32 | /** 33 | * default-constructor, 34 | * the first 100 instructions will be logged. 35 | * @param sourceFileRequester optional source-file-requester 36 | */ 37 | public DebuggerJvmVisitorProvider(final SourceFileRequester sourceFileRequester) { 38 | maxInstrLogged = 100; 39 | maxMethodsLogged = 500; 40 | this.sourceFileRequester = sourceFileRequester; 41 | } 42 | 43 | /** 44 | * Constructor 45 | * @param maxInstrLogged number of instructions to be logged at debug-level 46 | * @param maxMethodsLogged number of method-invocations to be logged at debug-level 47 | * @param sourceFileRequester optional source-file-requester 48 | */ 49 | public DebuggerJvmVisitorProvider(final int maxInstrLogged, final int maxMethodsLogged, 50 | final SourceFileRequester sourceFileRequester) { 51 | this.maxInstrLogged = maxInstrLogged; 52 | this.maxMethodsLogged = maxMethodsLogged; 53 | this.sourceFileRequester = sourceFileRequester; 54 | } 55 | 56 | /** {@inheritDoc} */ 57 | @Override 58 | public JvmExecutionVisitor create(final ClassRegistry vm, final Thread currentThread, final JvmExecutionVisitor visitorParent) { 59 | if (LOGGER.isDebugEnabled()) { 60 | LOGGER.debug(String.format("create: vm=%s, cT=%s, visitorParent=%s", 61 | vm, currentThread, visitorParent)); 62 | } 63 | final DebuggerJvmVisitor visitor = new DebuggerJvmVisitor(eventRequests, 64 | maxInstrLogged, maxMethodsLogged, sourceFileRequester); 65 | visitor.setJvmSimulator(vm); 66 | if (visitorParent instanceof DebuggerJvmVisitor) { 67 | final DebuggerJvmVisitor debuggerVisitorParent = (DebuggerJvmVisitor) visitorParent; 68 | visitor.setDebugger(debuggerVisitorParent.getDebugger()); 69 | } 70 | return visitor; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/debugger/JdwpCommandSet.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.debugger; 2 | 3 | /** 4 | * jwdp-command-set. 5 | */ 6 | public enum JdwpCommandSet { 7 | 8 | /** VirtualMachine Command Set */ 9 | VIRTUAL_MACHINE(1), 10 | /** ReferenceType Command Set */ 11 | REFERENCE_TYPE(2), 12 | /** ClassType Command Set */ 13 | CLASS_TYPE(3), 14 | /** Method Command Set */ 15 | METHOD(6), 16 | /** ObjectReference Command Set */ 17 | OBJECT_REFERENCE(9), 18 | /** StringReference Command Set */ 19 | STRING_REFERENCE(10), 20 | /** ThreadReference Command Set */ 21 | THREAD_REFERENCE(11), 22 | /** ThreadGroupReference Command Set */ 23 | THREAD_GROUP_REFERENCE(12), 24 | /** ArrayReference Command Set */ 25 | ARRAY_REFERENCE(13), 26 | /** EventRequest Command Set */ 27 | EVENT_REQUEST(15), 28 | /** StackFrame Command Set */ 29 | STACK_FRAME(16), 30 | /** ClassObjectReference Command Set */ 31 | CLASS_OBJECT_REFERENCE(17), 32 | /** Event Command Set */ 33 | EVENT(64); 34 | 35 | /** Command-sets by number */ 36 | private static final JdwpCommandSet[] A_CS = new JdwpCommandSet[65]; 37 | 38 | /** command-set */ 39 | private final byte commandSet; 40 | 41 | static { 42 | for (JdwpCommandSet cs : values()) { 43 | A_CS[cs.commandSet] = cs; 44 | 45 | } 46 | } 47 | 48 | /** 49 | * Internal constructor 50 | * @param cs command-set 51 | */ 52 | private JdwpCommandSet(final int cs) { 53 | commandSet = (byte) cs; 54 | } 55 | 56 | /** 57 | * Gets the command-set. 58 | * @return command-set 59 | */ 60 | public byte getCommandSet() { 61 | return commandSet; 62 | } 63 | 64 | /** 65 | * Lookups a command-set by number. 66 | * @param number number 67 | * @return command-set or null 68 | */ 69 | public static JdwpCommandSet lookupByKind(final byte number) { 70 | return ((number & 0xff) < A_CS.length) ? A_CS[number] : null; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/debugger/JdwpErrorCode.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.debugger; 2 | 3 | /** 4 | * jwdp-error-code. 5 | */ 6 | public enum JdwpErrorCode { 7 | 8 | /** No error has occurred. */ 9 | NONE(0), 10 | /** Passed thread is null, is not a valid thread or has exited. */ 11 | INVALID_THREAD(10), 12 | /** Thread group invalid. */ 13 | INVALID_THREAD_GROUP(11), 14 | /** If the specified thread has not been suspended by an event. */ 15 | THREAD_NOT_SUSPENDED(13), 16 | /** Thread already suspended. */ 17 | THREAD_SUSPENDED(14), 18 | /** If this reference type has been unloaded and garbage collected. */ 19 | INVALID_OBJECT(20), 20 | /** Invalid class. */ 21 | INVALID_CLASS(21), 22 | /** Invalid method. */ 23 | INVALID_METHODID(23), 24 | /** Invalid field. */ 25 | INVALID_FIELDID(25), 26 | /** Invalid jFrameID. */ 27 | INVALID_FRAMEID(30), 28 | /** Invalid slot. */ 29 | INVALID_SLOT(35), 30 | /** The functionality is not implemented in this virtual machine. */ 31 | NOT_IMPLEMENTED(99), 32 | /** Invalid pointer. */ 33 | NULL_POINTER(100), 34 | /** Desired information is not available. */ 35 | ABSENT_INFORMATION(101), 36 | /** The specified event type id is not recognized. */ 37 | INVALID_EVENT_TYPE(102), 38 | /** Illegal Argument. */ 39 | ILLEGAL_ARGUMENT(103), 40 | /** The function needed to allocate memory and no more memory was available for allocation. */ 41 | OUT_OF_MEMORY(110), 42 | /** The virtual machine is not running. */ 43 | VM_DEAD(112), 44 | /** Invalid Length. */ 45 | INVALID_LENGTH(504), 46 | /** Invalid String. */ 47 | INVALID_STRING(506), 48 | /** Invalid Array. */ 49 | INVALID_ARRAY(508); 50 | 51 | /** error-code */ 52 | private final short errorCode; 53 | 54 | /** 55 | * Constructor 56 | * @param errorCode error-code 57 | */ 58 | private JdwpErrorCode(final int errorCode) { 59 | this.errorCode = (short) errorCode; 60 | } 61 | 62 | /** 63 | * Gets the error-code. 64 | * @return error-code 65 | */ 66 | public short getErrorCode() { 67 | return errorCode; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/debugger/JdwpSuspendPolicy.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.debugger; 2 | 3 | /** 4 | * jwdp-suspend-policy. 5 | */ 6 | public enum JdwpSuspendPolicy { 7 | 8 | /** Suspend no threads when this event is encountered. */ 9 | NONE(0), 10 | /** Suspend the event thread when this event is encountered. */ 11 | EVENT_THREAD(1), 12 | /** Suspend all threads when this event is encountered. */ 13 | ALL(2); 14 | 15 | /** suspend-policy */ 16 | private final byte policy; 17 | 18 | /** 19 | * Internal constructor 20 | * @param policy suspend-policy 21 | */ 22 | private JdwpSuspendPolicy(final int policy) { 23 | this.policy = (byte) policy; 24 | } 25 | 26 | /** 27 | * Gets the suspend-policy. 28 | * @return suspend-policy 29 | */ 30 | public byte getPolicy() { 31 | return policy; 32 | } 33 | 34 | /** 35 | * Lookups a suspend-policy 36 | * @param bSuspendPolicy suspend-poliy 37 | * @return suspend-policy or null 38 | */ 39 | public static JdwpSuspendPolicy lookupBySuspendPolicy(final byte bSuspendPolicy) { 40 | for (JdwpSuspendPolicy loopPolicy : values()) { 41 | if (loopPolicy.policy == bSuspendPolicy) { 42 | return loopPolicy; 43 | } 44 | } 45 | return null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/debugger/JvmClinitWhileDebuggingException.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.debugger; 2 | 3 | import org.rogmann.jsmud.vm.JvmException; 4 | 5 | /** 6 | * This exception is thrown when a clinit-method (static initializer) 7 | * should be invoke while processing debugger-packages. 8 | * 9 | *

Example: Invoking of $$EnhancerByCGLIB$$3d7dcd21.hashCode triggers a clinit-method.

10 | */ 11 | public class JvmClinitWhileDebuggingException extends JvmException { 12 | 13 | /** serialization-id */ 14 | private static final long serialVersionUID = 20220203L; 15 | 16 | /** 17 | * Constructor 18 | * @param message message-text 19 | */ 20 | public JvmClinitWhileDebuggingException(final String message) { 21 | super(message); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/debugger/MethodFrameDebugContext.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.debugger; 2 | 3 | import org.rogmann.jsmud.events.JdwpEventRequest; 4 | import org.rogmann.jsmud.events.JdwpModifierStep; 5 | import org.rogmann.jsmud.vm.MethodFrame; 6 | 7 | /** 8 | * Debugger-context of a method-frame. 9 | */ 10 | public class MethodFrameDebugContext { 11 | 12 | /** method-frame */ 13 | final MethodFrame frame; 14 | 15 | /** current STEP-event-request */ 16 | JdwpEventRequest eventRequestStep; 17 | 18 | /** current STEP-OUT-event-request for parent frame */ 19 | JdwpEventRequest eventRequestStepUp; 20 | 21 | /** current STEP-modifier */ 22 | JdwpModifierStep modStep; 23 | 24 | /** current STEP-OUT-modifier for parent frame */ 25 | JdwpModifierStep modStepUp; 26 | 27 | /** line of step-start */ 28 | int stepLine; 29 | 30 | /** 31 | * Constructor 32 | * @param frame method-frame 33 | */ 34 | public MethodFrameDebugContext(final MethodFrame frame) { 35 | this.frame = frame; 36 | } 37 | 38 | /** {@inheritDoc} */ 39 | @Override 40 | public String toString() { 41 | final StringBuilder sb = new StringBuilder(12); 42 | sb.append("MFDC#").append(Integer.toHexString(System.identityHashCode(this))); 43 | return sb.toString(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/debugger/SlotRequest.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.debugger; 2 | 3 | /** 4 | * Index and tag of a variable-slot in a method-frame. 5 | */ 6 | public class SlotRequest { 7 | 8 | /** variable's index in the frame */ 9 | private final int slot; 10 | /** type of the variable */ 11 | private final byte tag; 12 | 13 | /** 14 | * Constructor 15 | * @param slot variable's index 16 | * @param tag type of the variable 17 | */ 18 | public SlotRequest(int slot, byte tag) { 19 | this.slot = slot; 20 | this.tag = tag; 21 | } 22 | 23 | /** 24 | * Gets the variable's index in the frame. 25 | * @return index 26 | */ 27 | public int getSlot() { 28 | return slot; 29 | } 30 | 31 | /** 32 | * Gets the type of the variale. 33 | * @return tag 34 | */ 35 | public byte getTag() { 36 | return tag; 37 | } 38 | 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/debugger/SlotValue.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.debugger; 2 | 3 | import org.rogmann.jsmud.datatypes.VMValue; 4 | 5 | /** 6 | * Index and value of a variable-slot in a method-frame. 7 | */ 8 | public class SlotValue { 9 | 10 | /** variable's index in the frame */ 11 | private final int slot; 12 | /** type and value of the variable */ 13 | private final VMValue variable; 14 | 15 | /** 16 | * Constructor 17 | * @param slot variable's index 18 | * @param variable type and value of the variable 19 | */ 20 | public SlotValue(int slot, VMValue variable) { 21 | this.slot = slot; 22 | this.variable= variable; 23 | } 24 | 25 | /** 26 | * Gets the variable's index in the frame. 27 | * @return index 28 | */ 29 | public int getSlot() { 30 | return slot; 31 | } 32 | 33 | /** 34 | * Gets the type and value of the variale. 35 | * @return tag and value 36 | */ 37 | public VMValue getVariable() { 38 | return variable; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/debugger/SourceFileRequester.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.debugger; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.IOException; 5 | 6 | /** 7 | * Interface to request the on-the-fly-generation of pseudo-byte-code of java-classes. 8 | */ 9 | public interface SourceFileRequester { 10 | 11 | /** 12 | * Checks if a source-file should be generated 13 | * @param clazzLoaded loaded class 14 | * @return true if a source-file is requested 15 | */ 16 | boolean isSourceRequested(Class clazzLoaded); 17 | 18 | /** 19 | * Creates a buffered writer for writing pseudo-code 20 | * @param clazz class 21 | * @return writer 22 | * @throws IOException in case of an IO-error 23 | */ 24 | BufferedWriter createBufferedWriter(Class clazz) throws IOException; 25 | 26 | /** 27 | * line-break (CR of CRLF) of generated source-file. 28 | * @return line-break 29 | */ 30 | String lineBreak(); 31 | 32 | /** 33 | * Extension of a generated file. 34 | * @return extension, e.g. "java" or "asm" 35 | */ 36 | String getExtension(); 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/debugger/SourceFilesLocalDirectory.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.debugger; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.File; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.io.OutputStreamWriter; 8 | import java.nio.charset.Charset; 9 | import java.nio.file.Files; 10 | import java.util.function.Predicate; 11 | 12 | import org.rogmann.jsmud.vm.Utils; 13 | 14 | /** 15 | * Generation of source-files into a local directory. 16 | */ 17 | public class SourceFilesLocalDirectory implements SourceFileRequester { 18 | 19 | /** class-filter */ 20 | private final Predicate> classFilter; 21 | /** destination directory */ 22 | private final File dirDest; 23 | /** extension of generated files (e.g. "java" or "asm") */ 24 | private final String extension; 25 | /** encoding */ 26 | private final Charset charset; 27 | /** line-break in generated files */ 28 | private final String lineBreak; 29 | 30 | /** 31 | * Constructor 32 | * @param classFilter classes to generate sources 33 | * @param dirDest destination source-folder 34 | * @param extension extension of generated files 35 | * @param charset encoding of generated files 36 | * @param lineBreak line-break in generated files 37 | */ 38 | public SourceFilesLocalDirectory(final Predicate> classFilter, 39 | final File dirDest, final String extension, final Charset charset, final String lineBreak) { 40 | this.classFilter = classFilter; 41 | this.dirDest = dirDest; 42 | this.extension = extension; 43 | this.charset = charset; 44 | this.lineBreak = lineBreak; 45 | } 46 | 47 | /** {@inheritDoc} */ 48 | @Override 49 | public boolean isSourceRequested(Class clazzLoaded) { 50 | return classFilter.test(clazzLoaded); 51 | } 52 | 53 | /** {@inheritDoc} */ 54 | @Override 55 | public BufferedWriter createBufferedWriter(Class clazz) throws IOException { 56 | final String qualifiedName = clazz.getName(); 57 | final int idxName = qualifiedName.lastIndexOf('.'); 58 | final File dirPackage; 59 | if (idxName >= 0) { 60 | final String namePackage = qualifiedName.substring(0, idxName); 61 | dirPackage = new File(dirDest, namePackage.replace('.', File.separatorChar)); 62 | } 63 | else { 64 | dirPackage = dirDest; 65 | } 66 | if (!dirPackage.isDirectory()) { 67 | Files.createDirectories(dirPackage.toPath()); 68 | } 69 | final File fileSource = new File(dirPackage, Utils.guessSourceFile(clazz, extension)); 70 | return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileSource), charset)); 71 | } 72 | 73 | /** {@inheritDoc} */ 74 | @Override 75 | public String lineBreak() { 76 | return lineBreak; 77 | } 78 | 79 | /** {@inheritDoc} */ 80 | @Override 81 | public String getExtension() { 82 | return extension; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/debugger/VMEventType.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.debugger; 2 | 3 | /** 4 | * Type of a JVM-event. 5 | */ 6 | public enum VMEventType { 7 | 8 | /** Notification of step completion in the target VM. */ 9 | SINGLE_STEP(1), 10 | /** Notification of a breakpoint in the target VM. */ 11 | BREAKPOINT(2), 12 | /** Notification of an exception in the target VM. */ 13 | EXCEPTION(4), 14 | /** Notification of a new running thread in the target VM. */ 15 | THREAD_START(6), 16 | /** Notification of a completed thread in the target VM. */ 17 | THREAD_DEATH(7), 18 | /** Notification of a class prepare in the target VM. */ 19 | CLASS_PREPARE(8), 20 | /** Notification of a class unload in the target VM. */ 21 | CLASS_UNLOAD(9), 22 | /** Notification of a field access in the target VM. */ 23 | FIELD_ACCESS(20), 24 | /** Notification of a field modification in the target VM. */ 25 | FIELD_MODIFICATION(21), 26 | /** Notification of a method invocation in the target VM. */ 27 | METHOD_ENTRY(40), 28 | /** Notification of a method return in the target VM. */ 29 | METHOD_EXIT(41), 30 | /** Notification of a method return in the target VM. */ 31 | METHOD_EXIT_WITH_RETURN_VALUE(42), 32 | /** Notification of initialization of a target VM. */ 33 | VM_START(90), 34 | /** Notification of initialization of a target VM. */ 35 | VM_DEATH(99); 36 | 37 | /** event-kind */ 38 | private final byte eventKind; 39 | 40 | /** 41 | * Constructor 42 | * @param eventKind event-kind 43 | */ 44 | private VMEventType(final int eventKind) { 45 | this.eventKind = (byte) eventKind; 46 | } 47 | 48 | /** 49 | * Gets the event-kind. 50 | * @return event-kind 51 | */ 52 | public byte getEventKind() { 53 | return eventKind; 54 | } 55 | 56 | /** 57 | * Looks up an event-type by kind. 58 | * @param kind event-kind 59 | * @return event-type or null 60 | */ 61 | public static VMEventType lookupByKind(byte kind) { 62 | for (VMEventType type : values()) { 63 | if (type.eventKind == kind) { 64 | return type; 65 | } 66 | } 67 | return null; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/debugger/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * JDWP-based debugger. 3 | * 4 | *

{@link org.rogmann.jsmud.debugger.JdwpCommandProcessor} is the central class in this package.

5 | *

{@link org.rogmann.jsmud.debugger.DebuggerJvmVisitor} is the link to the JVM-simulation.

6 | * 7 | */ 8 | package org.rogmann.jsmud.debugger; -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/events/JdwpEventModifier.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.events; 2 | 3 | /** 4 | * Abstract base-class of a event-modifier. 5 | */ 6 | public abstract class JdwpEventModifier { 7 | 8 | /** modifier kind */ 9 | private final ModKind modKind; 10 | 11 | /** 12 | * Constructor 13 | * @param modKind modifier kind 14 | */ 15 | protected JdwpEventModifier(final ModKind modKind) { 16 | this.modKind = modKind; 17 | } 18 | 19 | /** 20 | * Gets the modifier kind. 21 | * @return modifier kind 22 | */ 23 | public ModKind getModKind() { 24 | return modKind; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/events/JdwpEventRequest.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.events; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.rogmann.jsmud.debugger.JdwpSuspendPolicy; 7 | import org.rogmann.jsmud.debugger.VMEventType; 8 | 9 | /** 10 | * Details of an event-request. 11 | */ 12 | public class JdwpEventRequest { 13 | 14 | /** request-id */ 15 | private final int requestId; 16 | 17 | /** event-type */ 18 | private final VMEventType eventType; 19 | /** suspend-policy */ 20 | private final JdwpSuspendPolicy suspendPolicy; 21 | 22 | /** modifiers */ 23 | private final List modifiers; 24 | 25 | /** 26 | * Constructor 27 | * @param requestId request-id 28 | * @param eventType event-kind 29 | * @param suspendPolicy suspend-policy 30 | * @param modifers number of constraints 31 | */ 32 | public JdwpEventRequest(int requestId, VMEventType eventType, JdwpSuspendPolicy suspendPolicy, int modifers) { 33 | this.requestId = requestId; 34 | this.eventType = eventType; 35 | this.suspendPolicy = suspendPolicy; 36 | modifiers = new ArrayList<>(modifers); 37 | } 38 | 39 | /** 40 | * Gets the request-id. 41 | * @return request-id 42 | */ 43 | public int getRequestId() { 44 | return requestId; 45 | } 46 | 47 | /** 48 | * Gets the event-type. 49 | * @return event-kind 50 | */ 51 | public VMEventType getEventType() { 52 | return eventType; 53 | } 54 | 55 | /** 56 | * Gets the suspend-policy. 57 | * @return suspend-policy 58 | */ 59 | public JdwpSuspendPolicy getSuspendPolicy() { 60 | return suspendPolicy; 61 | } 62 | 63 | /** 64 | * Adds a modifier. 65 | * @param modifier event-modifier 66 | */ 67 | public void addModifier(final JdwpEventModifier modifier) { 68 | this.modifiers.add(modifier); 69 | } 70 | 71 | /** 72 | * Gets the list of modifiers. 73 | * @return modifiers 74 | */ 75 | public List getModifiers() { 76 | return modifiers; 77 | } 78 | 79 | /** {@inheritDoc} */ 80 | @Override 81 | public String toString() { 82 | final StringBuilder sb = new StringBuilder(50); 83 | sb.append(getClass().getSimpleName()); 84 | sb.append('{'); 85 | sb.append("id:").append(requestId); 86 | sb.append(", eventType:").append(eventType); 87 | sb.append('}'); 88 | return sb.toString(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/events/JdwpModifierClassMatch.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.events; 2 | 3 | import java.util.regex.Pattern; 4 | import java.util.regex.PatternSyntaxException; 5 | 6 | import org.rogmann.jsmud.debugger.DebuggerException; 7 | 8 | /** 9 | * Case ClassMatch or ClassExclude. 10 | */ 11 | public class JdwpModifierClassMatch extends JdwpEventModifier { 12 | 13 | /** class-pattern */ 14 | private final Pattern pClassPattern; 15 | 16 | /** 17 | * Constructor 18 | * @param modKind CLASS_MATCH or CLASS_EXCLUDE 19 | * @param classPattern pattern of classes to be matched 20 | */ 21 | public JdwpModifierClassMatch(final ModKind modKind, final String classPattern) { 22 | super(modKind); 23 | if (modKind != ModKind.CLASS_MATCH && modKind != ModKind.CLASS_EXCLUDE) { 24 | throw new DebuggerException("Invalid modkind in classPattern-modifier: " + modKind); 25 | } 26 | final String regExp = classPattern.replace("$", "\\$").replace("*", ".*"); 27 | try { 28 | pClassPattern = Pattern.compile(regExp); 29 | } catch (PatternSyntaxException e) { 30 | throw new DebuggerException(String.format("Unexpected class-pattern (%s) in %s-modifier", 31 | classPattern, modKind), e); 32 | } 33 | } 34 | 35 | /** 36 | * Gets the class-pattern. 37 | * @return pattern 38 | */ 39 | public Pattern getClassPattern() { 40 | return pClassPattern; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/events/JdwpModifierClassOnly.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.events; 2 | 3 | import org.rogmann.jsmud.datatypes.VMReferenceTypeID; 4 | 5 | /** 6 | * Case ClassOnly. 7 | */ 8 | public class JdwpModifierClassOnly extends JdwpEventModifier { 9 | 10 | /** reference-type */ 11 | private final VMReferenceTypeID clazz; 12 | 13 | /** 14 | * Constructor 15 | * @param clazz class-id 16 | */ 17 | public JdwpModifierClassOnly(final VMReferenceTypeID clazz) { 18 | super(ModKind.CLASS_ONLY); 19 | this.clazz = clazz; 20 | } 21 | 22 | /** 23 | * Gets the reference-type-id 24 | * @return reference-type-id 25 | */ 26 | public VMReferenceTypeID getClazz() { 27 | return clazz; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/events/JdwpModifierCount.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.events; 2 | 3 | /** 4 | * Case count. 5 | */ 6 | public class JdwpModifierCount extends JdwpEventModifier { 7 | 8 | /** count */ 9 | private int count; 10 | 11 | /** 12 | * Constructor 13 | * @param count count 14 | */ 15 | public JdwpModifierCount(final int count) { 16 | super(ModKind.COUNT); 17 | this.count = count; 18 | } 19 | 20 | /** 21 | * Gets the count of the modifier. 22 | * @return count 23 | */ 24 | public int getCount() { 25 | return count; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/events/JdwpModifierFieldOnly.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.events; 2 | 3 | import org.rogmann.jsmud.datatypes.VMFieldID; 4 | import org.rogmann.jsmud.datatypes.VMReferenceTypeID; 5 | 6 | /** 7 | * Case FieldOnly (used to watch field access or modification). 8 | */ 9 | public class JdwpModifierFieldOnly extends JdwpEventModifier { 10 | 11 | /** reference-type */ 12 | private final VMReferenceTypeID classId; 13 | 14 | /** class */ 15 | private final Class clazz; 16 | 17 | /** field-type */ 18 | private final VMFieldID fieldId; 19 | 20 | /** field-name */ 21 | private final String fieldName; 22 | 23 | /** 24 | * Constructor 25 | * @param classId class-id 26 | * @param fieldId field-id 27 | * @param clazz class 28 | * @param fieldName field name 29 | */ 30 | public JdwpModifierFieldOnly(final VMReferenceTypeID classId, final VMFieldID fieldId, 31 | final Class clazz, final String fieldName) { 32 | super(ModKind.FIELD_ONLY); 33 | this.classId = classId; 34 | this.fieldId = fieldId; 35 | this.clazz = clazz; 36 | this.fieldName = fieldName; 37 | } 38 | 39 | /** 40 | * Gets the reference-type-id 41 | * @return reference-type-id 42 | */ 43 | public VMReferenceTypeID getClassId() { 44 | return classId; 45 | } 46 | 47 | /** 48 | * Gets the class. 49 | * @return class 50 | */ 51 | public Class getClazz() { 52 | return clazz; 53 | } 54 | 55 | /** 56 | * Gets the field-id 57 | * @return field-id 58 | */ 59 | public VMReferenceTypeID getFieldId() { 60 | return fieldId; 61 | } 62 | 63 | /** 64 | * Gets the field-name. 65 | * @return field-name 66 | */ 67 | public String getFieldName() { 68 | return fieldName; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/events/JdwpModifierLocationOnly.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.events; 2 | 3 | import org.rogmann.jsmud.datatypes.VMClassID; 4 | import org.rogmann.jsmud.datatypes.VMMethodID; 5 | 6 | /** 7 | * Details of a BREAKPOINT-event-request. 8 | * The breakpoint hat a location which consists of type-tag, class-id, method-id and index. 9 | */ 10 | public class JdwpModifierLocationOnly extends JdwpEventModifier { 11 | 12 | /** type-tag */ 13 | private final byte typeTag; 14 | /** class-id */ 15 | private final VMClassID classID; 16 | /** method-id */ 17 | private final VMMethodID methodId; 18 | /** index in method */ 19 | private final long index; 20 | 21 | /** 22 | * Constructor 23 | * @param typeTag type-tag 24 | * @param classID class-id 25 | * @param methodId method-id 26 | * @param index index in method 27 | */ 28 | public JdwpModifierLocationOnly(final byte typeTag, 29 | final VMClassID classID, final VMMethodID methodId, final long index) { 30 | super(ModKind.LOCATION_ONLY); 31 | this.typeTag = typeTag; 32 | this.classID = classID; 33 | this.methodId = methodId; 34 | this.index = index; 35 | } 36 | 37 | /** 38 | * Gets the type-tag. 39 | * @return type-tag 40 | */ 41 | public byte getTypeTag() { 42 | return typeTag; 43 | } 44 | 45 | /** 46 | * Gets the class-id. 47 | * @return class-id 48 | */ 49 | public VMClassID getClassID() { 50 | return classID; 51 | } 52 | 53 | /** 54 | * Gets the method-id. 55 | * @return method-id 56 | */ 57 | public VMMethodID getMethodId() { 58 | return methodId; 59 | } 60 | 61 | /** 62 | * Gets the index of the location in the method (VM-dependend). 63 | * @return index 64 | */ 65 | public long getIndex() { 66 | return index; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/events/JdwpModifierStep.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.events; 2 | 3 | import org.rogmann.jsmud.datatypes.VMThreadID; 4 | 5 | /** 6 | * Details of a STEP-event-request. 7 | */ 8 | public class JdwpModifierStep extends JdwpEventModifier { 9 | /** step into method-calls */ 10 | public static final int STEP_DEPTH_INTO = 0; 11 | /** step over method-calls */ 12 | public static final int STEP_DEPTH_OVER = 1; 13 | /** step out of current method */ 14 | public static final int STEP_DEPTH_OUT = 2; 15 | 16 | /** step by minimum possible amount */ 17 | public static final int STEP_SIZE_MIN = 0; 18 | /** step to next source line */ 19 | public static final int STEP_SIZE_LINE = 1; 20 | 21 | 22 | /** thread-id */ 23 | private VMThreadID threadID; 24 | /** step-size */ 25 | private int stepSize; 26 | /** step-depth */ 27 | private int stepDepth; 28 | 29 | /** 30 | * Constructor 31 | * @param threadID thread-id 32 | * @param stepDepth step-size (0 = min, 1 = line) 33 | * @param stepSize step-depth (0 = into, 1 = over, 2 = out) 34 | */ 35 | public JdwpModifierStep(final VMThreadID threadID, final int stepSize, final int stepDepth) { 36 | super(ModKind.STEP); 37 | this.threadID = threadID; 38 | this.stepSize = stepSize; 39 | this.stepDepth = stepDepth; 40 | } 41 | 42 | /** 43 | * Gets the thread-id. 44 | * @return thread-id 45 | */ 46 | public VMThreadID getThreadID() { 47 | return threadID; 48 | } 49 | 50 | /** 51 | * Gets the step-size (0 = min, 1 = line). 52 | * @return step-size 53 | */ 54 | public int getStepSize() { 55 | return stepSize; 56 | } 57 | 58 | /** 59 | * Gets the step-depth (0 = into, 1 = over, 2 = out). 60 | * @return step-depth 61 | */ 62 | public int getStepDepth() { 63 | return stepDepth; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/events/JdwpModifierThreadOnly.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.events; 2 | 3 | import org.rogmann.jsmud.datatypes.VMThreadID; 4 | 5 | /** 6 | * Case ThreadOnly. 7 | */ 8 | public class JdwpModifierThreadOnly extends JdwpEventModifier { 9 | 10 | /** thread-id */ 11 | private VMThreadID threadId; 12 | 13 | /** 14 | * Constructor 15 | * @param threadId thread-id 16 | */ 17 | public JdwpModifierThreadOnly(final VMThreadID threadId) { 18 | super(ModKind.THREAD_ONLY); 19 | this.threadId = threadId; 20 | } 21 | 22 | /** 23 | * Gets the thread-id 24 | * @return thread-id 25 | */ 26 | public VMThreadID getThreadId() { 27 | return threadId; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/events/ModKind.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.events; 2 | 3 | /** 4 | * Modifier kind. 5 | */ 6 | public enum ModKind { 7 | 8 | /** Limit the requested event */ 9 | COUNT(1), 10 | /** Conditional on expression */ 11 | CONDITIONAL(2), 12 | /** Restricts thread */ 13 | THREAD_ONLY(3), 14 | /** Restricts reference type and subtypes */ 15 | CLASS_ONLY(4), 16 | /** Restricts class-names (match) */ 17 | CLASS_MATCH(5), 18 | /** Restricts class-names (exclude) */ 19 | CLASS_EXCLUDE(6), 20 | /** Restricts location */ 21 | LOCATION_ONLY(7), 22 | /** Restricts exception */ 23 | EXCEPTION_ONLY(8), 24 | /** Restricts for a given field */ 25 | FIELD_ONLY(9), 26 | /** Restricts reported step */ 27 | STEP(10), 28 | /** Restricts to given object-instance */ 29 | INSTANCE_ONLY(11), 30 | /** Restricts source-name */ 31 | SOURCE_NAME_MATCH(12); 32 | 33 | /** modifier-kind */ 34 | private final byte modKind; 35 | 36 | /** 37 | * Internal contstructor 38 | * @param modKind modifier kind 39 | */ 40 | private ModKind(final int modKind) { 41 | this.modKind = (byte) modKind; 42 | } 43 | 44 | /** 45 | * Gets the modifier kind. 46 | * @return modifier kind 47 | */ 48 | public byte getModKindAsByte() { 49 | return modKind; 50 | } 51 | 52 | /** 53 | * Looks up a modifier-kind. 54 | * @param bModKind modKind 55 | * @return modifier-kind or null 56 | */ 57 | public static ModKind lookupByKind(byte bModKind) { 58 | for (ModKind modKind : values()) { 59 | if (modKind.modKind == bModKind) { 60 | return modKind; 61 | } 62 | } 63 | return null; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/events/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing event-requests. 3 | */ 4 | package org.rogmann.jsmud.events; -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/gen/JsmudGeneratedClasses.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.gen; 2 | 3 | /** 4 | * Marker-class of folder containing generated classes. 5 | */ 6 | public final class JsmudGeneratedClasses { 7 | 8 | /** 9 | * Internal Constructor. 10 | */ 11 | private JsmudGeneratedClasses() { 12 | // internal 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/gen/package-info.java: -------------------------------------------------------------------------------- 1 | /** Folder to store classes generated at runtime */ 2 | package org.rogmann.jsmud.gen; -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/log/Logger.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.log; 2 | 3 | /** 4 | * logger. 5 | */ 6 | public interface Logger { 7 | 8 | /** 9 | * Checks if the logger is enabled for debug-level. 10 | * @return debug-flag 11 | */ 12 | boolean isDebugEnabled(); 13 | 14 | /** 15 | * Log a message at debug-level. 16 | * @param msg message 17 | */ 18 | void debug(String msg); 19 | 20 | /** 21 | * Checks if the logger is enabled for info-level. 22 | * @return info-flag 23 | */ 24 | boolean isInfoEnabled(); 25 | 26 | /** 27 | * Log a message at info-level. 28 | * @param msg message 29 | */ 30 | void info(String msg); 31 | 32 | /** 33 | * Log a message at error-level. 34 | * @param msg message 35 | */ 36 | void error(String msg); 37 | 38 | /** 39 | * Log a message at error-level. 40 | * @param msg message 41 | * @param t throwable 42 | */ 43 | void error(String msg, Throwable t); 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/log/LoggerFactory.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.log; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.util.Iterator; 5 | import java.util.ServiceLoader; 6 | import java.util.concurrent.atomic.AtomicReference; 7 | 8 | import org.rogmann.jsmud.vm.JvmException; 9 | 10 | /** logger-factory */ 11 | public class LoggerFactory { 12 | /** property-name */ 13 | public static final String PROPERTY_NAME = LoggerSpi.class.getName(); 14 | 15 | /** logger-SPI */ 16 | private static final AtomicReference LOGGER_SPI = new AtomicReference<>(); 17 | 18 | /** 19 | * Returns a logger. 20 | * @param clazz owner-class of the logger 21 | * @return logger 22 | */ 23 | public static final Logger getLogger(final Class clazz) { 24 | LoggerSpi loggerSpi = LOGGER_SPI.get(); 25 | if (loggerSpi == null) { 26 | // Some service-declaration in META-INF/services? 27 | final ClassLoader classLoader = LoggerSpi.class.getClassLoader(); 28 | final ServiceLoader serviceLoader = ServiceLoader.load(LoggerSpi.class, classLoader); 29 | final Iterator it = serviceLoader.iterator(); 30 | if (it.hasNext()) { 31 | loggerSpi = it.next(); 32 | LOGGER_SPI.set(loggerSpi); 33 | } 34 | } 35 | if (loggerSpi == null) { 36 | // logger-spi via system-property. 37 | final String classNameLoggerSpi = System.getProperty(PROPERTY_NAME); 38 | if (classNameLoggerSpi != null) { 39 | final ClassLoader classLoader = LoggerSpi.class.getClassLoader(); 40 | final Class classLoggerSpi; 41 | try { 42 | classLoggerSpi = classLoader.loadClass(classNameLoggerSpi); 43 | } catch (ClassNotFoundException e) { 44 | throw new JvmException(String.format("Can't find logger-implementation (%s) in class-loader (%s)", 45 | classNameLoggerSpi, classLoader)); 46 | } 47 | final Object oLoggerSpi; 48 | try { 49 | oLoggerSpi = classLoggerSpi.getDeclaredConstructor().newInstance(); 50 | } 51 | catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) { 52 | throw new JvmException(String.format("Can't instanciate logger-implementation (%s)", 53 | classNameLoggerSpi), e); 54 | } 55 | if (oLoggerSpi instanceof LoggerSpi) { 56 | loggerSpi = (LoggerSpi) oLoggerSpi; 57 | LOGGER_SPI.set(loggerSpi); 58 | } 59 | else { 60 | throw new JvmException(String.format("logger-implementation (%s) has type (%s) instead of (%s)", 61 | classNameLoggerSpi, oLoggerSpi.getClass(), LoggerSpi.class)); 62 | } 63 | } 64 | } 65 | if (loggerSpi == null) { 66 | loggerSpi = new LoggerFactorySystemOut(); 67 | } 68 | return loggerSpi.getLogger(clazz); 69 | } 70 | 71 | /** 72 | * Sets a logger-implementation. 73 | * @param loggerSpi logger-spi 74 | */ 75 | public static final void setLoggerSpi(final LoggerSpi loggerSpi) { 76 | LOGGER_SPI.set(loggerSpi); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/log/LoggerFactoryJavaLogging.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.log; 2 | 3 | import java.util.logging.Level; 4 | 5 | /** 6 | * java.util.logging-backend. 7 | */ 8 | public class LoggerFactoryJavaLogging implements LoggerSpi { 9 | 10 | /** {@inheritDoc} */ 11 | @Override 12 | public Logger getLogger(Class clazz) { 13 | final java.util.logging.Logger loggerImpl = java.util.logging.Logger.getLogger(clazz.getName()); 14 | return new LoggerJUL(loggerImpl); 15 | } 16 | 17 | /** JUL-bridge */ 18 | static class LoggerJUL implements Logger { 19 | /** logger-implementation */ 20 | private final java.util.logging.Logger loggerImpl; 21 | 22 | /** 23 | * Constructor 24 | * @param loggerImpl logger-implementation 25 | */ 26 | LoggerJUL(java.util.logging.Logger loggerImpl) { 27 | this.loggerImpl = loggerImpl; 28 | } 29 | 30 | /** {@inheritDoc} */ 31 | @Override 32 | public boolean isDebugEnabled() { 33 | return loggerImpl.isLoggable(Level.FINE); 34 | } 35 | 36 | /** {@inheritDoc} */ 37 | @Override 38 | public void debug(String msg) { 39 | loggerImpl.log(Level.FINE, msg); 40 | } 41 | 42 | /** {@inheritDoc} */ 43 | @Override 44 | public boolean isInfoEnabled() { 45 | return loggerImpl.isLoggable(Level.FINE); 46 | } 47 | 48 | /** {@inheritDoc} */ 49 | @Override 50 | public void info(String msg) { 51 | loggerImpl.info(msg); 52 | } 53 | 54 | /** {@inheritDoc} */ 55 | @Override 56 | public void error(String msg) { 57 | loggerImpl.severe(msg); 58 | } 59 | 60 | /** {@inheritDoc} */ 61 | @Override 62 | public void error(String msg, Throwable t) { 63 | loggerImpl.log(Level.SEVERE, msg, t); 64 | } 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/log/LoggerFactorySystemOut.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.log; 2 | 3 | import java.io.PrintStream; 4 | 5 | /** 6 | * Simple System.out-logger. 7 | */ 8 | public class LoggerFactorySystemOut implements LoggerSpi, Logger { 9 | /** output-stream */ 10 | private final PrintStream psOut; 11 | 12 | /** debug-flag */ 13 | private final boolean hasDebug; 14 | 15 | /** info-flag */ 16 | private final boolean hasInfo; 17 | 18 | /** default-logger: everything into System.out */ 19 | public LoggerFactorySystemOut() { 20 | psOut = System.out; 21 | hasDebug = true; 22 | hasInfo = true; 23 | } 24 | 25 | /** 26 | * Constructor 27 | * @param psOut output-stream 28 | * @param hasDebug debug-enabled-flag 29 | * @param hasInfo info-enabled-flag 30 | */ 31 | public LoggerFactorySystemOut(final PrintStream psOut, final boolean hasDebug, final boolean hasInfo) { 32 | this.psOut = psOut; 33 | this.hasDebug = hasDebug; 34 | this.hasInfo = hasInfo; 35 | } 36 | 37 | /** {@inheritDoc} */ 38 | @Override 39 | public Logger getLogger(Class clazz) { 40 | return this; 41 | } 42 | 43 | /** {@inheritDoc} */ 44 | @Override 45 | public boolean isDebugEnabled() { 46 | return hasDebug; 47 | } 48 | 49 | /** {@inheritDoc} */ 50 | @Override 51 | public void debug(String msg) { 52 | if (hasDebug) { 53 | psOut.println(msg); 54 | } 55 | } 56 | 57 | /** {@inheritDoc} */ 58 | @Override 59 | public boolean isInfoEnabled() { 60 | return hasInfo; 61 | } 62 | 63 | /** {@inheritDoc} */ 64 | @Override 65 | public void info(String msg) { 66 | if (hasInfo) { 67 | psOut.println(msg); 68 | } 69 | } 70 | 71 | /** {@inheritDoc} */ 72 | @Override 73 | public void error(String msg) { 74 | psOut.println(msg); 75 | } 76 | 77 | /** {@inheritDoc} */ 78 | @Override 79 | public void error(String msg, Throwable t) { 80 | psOut.println(msg); 81 | t.printStackTrace(psOut); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/log/LoggerSpi.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.log; 2 | 3 | /** 4 | * SPI for inserting a logger-implementation. 5 | */ 6 | public interface LoggerSpi { 7 | 8 | /** 9 | * Returns a logger. 10 | * @param clazz owner-class of the logger 11 | * @return logger 12 | */ 13 | Logger getLogger(final Class clazz); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/log/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Logging in Java, is there one language having more logging-frameworks? 3 | * 4 | *

You may implement LoggerSpi to support your preferred logger (SLJ4J, Log4j2, ...).

5 | */ 6 | package org.rogmann.jsmud.log; -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/replydata/LineCodeIndex.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.replydata; 2 | 3 | /** 4 | * Initial index of a source-line. 5 | */ 6 | public class LineCodeIndex { 7 | 8 | /** line-code index */ 9 | private final long lineCodeIndex; 10 | /* line-number */ 11 | private final int lineNumber; 12 | 13 | /** 14 | * Constructor 15 | * @param lineCodeIndex line-code index 16 | * @param lineNumber line-number 17 | */ 18 | public LineCodeIndex(long lineCodeIndex, int lineNumber) { 19 | this.lineCodeIndex = lineCodeIndex; 20 | this.lineNumber = lineNumber; 21 | } 22 | 23 | /** 24 | * Gets the line-code index 25 | * @return index 26 | */ 27 | public long getLineCodeIndex() { 28 | return lineCodeIndex; 29 | } 30 | 31 | /** 32 | * Gets the line-number. 33 | * @return line-number 34 | */ 35 | public int getLineNumber() { 36 | return lineNumber; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/replydata/LineTable.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.replydata; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Line-table of a method. 7 | */ 8 | public class LineTable { 9 | 10 | /** lowest valid index of the method */ 11 | private final long start; 12 | /** highest valid index of the method */ 13 | private final long end; 14 | /** list of line-numbers */ 15 | private final List listLci; 16 | 17 | /** 18 | * Constructor 19 | * @param start lowest valid index of the method 20 | * @param end highest valid index of the method 21 | * @param listLci list of line-numbers 22 | */ 23 | public LineTable(long start, long end, List listLci) { 24 | this.start = start; 25 | this.end = end; 26 | this.listLci = listLci; 27 | } 28 | 29 | /** 30 | * Gets the lowest valid index of the method 31 | * @return index 32 | */ 33 | public long getStart() { 34 | return start; 35 | } 36 | 37 | /** 38 | * Gets the highest valid index of the method. 39 | * @return index 40 | */ 41 | public long getEnd() { 42 | return end; 43 | } 44 | 45 | /** 46 | * Gets a list of indexes of the first instruction of each line. 47 | * @return line-code-indexes 48 | */ 49 | public List getListLci() { 50 | return listLci; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/replydata/RefFieldBean.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.replydata; 2 | 3 | import org.rogmann.jsmud.datatypes.VMFieldID; 4 | 5 | /** 6 | * ID, signature and modifiers of a field. 7 | */ 8 | public class RefFieldBean { 9 | 10 | /** field-id */ 11 | private final VMFieldID fieldID; 12 | /** name of the field */ 13 | private final String name; 14 | /** JNI-signature */ 15 | private final String signature; 16 | /** generic signature (may be empty if there is none) */ 17 | private final String genericSignature; 18 | /** modifiers */ 19 | private final int modBits; 20 | 21 | /** 22 | * Constructor 23 | * @param fieldID type-id 24 | * @param name name of the field 25 | * @param signature signature 26 | * @param genericSignature generic signature, empty if there is none 27 | * @param modBits modification-bits of the field 28 | */ 29 | public RefFieldBean(final VMFieldID fieldID, final String name, 30 | final String signature, final String genericSignature, final int modBits) { 31 | this.fieldID = fieldID; 32 | this.name = name; 33 | this.signature = signature; 34 | this.genericSignature = genericSignature; 35 | this.modBits = modBits; 36 | } 37 | 38 | /** 39 | * Gets the field-id 40 | * @return id 41 | */ 42 | public VMFieldID getFieldID() { 43 | return fieldID; 44 | } 45 | 46 | /** 47 | * Gets the name of the field. 48 | * @return field's name 49 | */ 50 | public String getName() { 51 | return name; 52 | } 53 | 54 | /** 55 | * Gets the JNI-signature. 56 | * @return signature 57 | */ 58 | public String getSignature() { 59 | return signature; 60 | } 61 | 62 | /** 63 | * Gets the generic signature. This is empty if there is none. 64 | * @return generic signature 65 | */ 66 | public String getGenericSignature() { 67 | return genericSignature; 68 | } 69 | 70 | /** 71 | * Gets the modifiers. 72 | * @return mod-bits 73 | */ 74 | public int getModBits() { 75 | return modBits; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/replydata/RefFrameBean.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.replydata; 2 | 3 | import org.rogmann.jsmud.datatypes.VMFrameID; 4 | import org.rogmann.jsmud.datatypes.VMMethodID; 5 | import org.rogmann.jsmud.datatypes.VMReferenceTypeID; 6 | 7 | /** 8 | * Details of a frame in a frame-stack of a thread. 9 | */ 10 | public class RefFrameBean { 11 | 12 | /** frame-id */ 13 | private final VMFrameID frameId; 14 | 15 | /** type-tag */ 16 | private final TypeTag typeTag; 17 | 18 | /** type-id */ 19 | private final VMReferenceTypeID typeId; 20 | 21 | /** method-id */ 22 | private final VMMethodID methodId; 23 | 24 | /** index in method (VM-dependent) */ 25 | private long index; 26 | 27 | /** 28 | * Constructor 29 | * @param frameId frame-id 30 | * @param typeTag type-tag 31 | * @param typeId type-id 32 | * @param methodId method-id 33 | * @param index index in method (VM-dependent) 34 | */ 35 | public RefFrameBean(VMFrameID frameId, TypeTag typeTag, VMReferenceTypeID typeId, VMMethodID methodId, long index) { 36 | assert frameId != null; 37 | assert typeTag != null; 38 | assert typeId != null; 39 | assert methodId != null; 40 | 41 | this.frameId = frameId; 42 | this.typeTag = typeTag; 43 | this.typeId = typeId; 44 | this.methodId = methodId; 45 | this.index = index; 46 | } 47 | 48 | /** 49 | * Gets the frame-id 50 | * @return frame-id 51 | */ 52 | public VMFrameID getFrameId() { 53 | return frameId; 54 | } 55 | 56 | /** 57 | * Gets the type-tag. 58 | * @return type-tag 59 | */ 60 | public TypeTag getTypeTag() { 61 | return typeTag; 62 | } 63 | 64 | /** 65 | * Gets the type-id. 66 | * @return type-id 67 | */ 68 | public VMReferenceTypeID getTypeId() { 69 | return typeId; 70 | } 71 | 72 | /** 73 | * Gets the method-id. 74 | * @return method-id 75 | */ 76 | public VMMethodID getMethodId() { 77 | return methodId; 78 | } 79 | 80 | /** 81 | * Gets the VM-dependent index in the method 82 | * @return index 83 | */ 84 | public long getIndex() { 85 | return index; 86 | } 87 | 88 | /** 89 | * Updates the index in the method. 90 | * @param index instruction-index 91 | */ 92 | public void setIndex(long index) { 93 | this.index = index; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/replydata/RefMethodBean.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.replydata; 2 | 3 | import org.rogmann.jsmud.datatypes.VMMethodID; 4 | 5 | /** 6 | * ID, signature and modifiers of a method. 7 | */ 8 | public class RefMethodBean { 9 | 10 | /** method-id */ 11 | private final VMMethodID methodID; 12 | /** name of the method */ 13 | private final String name; 14 | /** JNI-signature */ 15 | private final String signature; 16 | /** generic signature (may be empty if there is none) */ 17 | private final String genericSignature; 18 | /** modifiers */ 19 | private final int modBits; 20 | 21 | /** 22 | * Constructor 23 | * @param methodID type-id 24 | * @param name name of the method 25 | * @param signature signature 26 | * @param genericSignature generic signature, empty if there is none 27 | * @param modBits modification-bits of the method 28 | */ 29 | public RefMethodBean(final VMMethodID methodID, final String name, 30 | final String signature, final String genericSignature, final int modBits) { 31 | this.methodID = methodID; 32 | this.name = name; 33 | this.signature = signature; 34 | this.genericSignature = genericSignature; 35 | this.modBits = modBits; 36 | } 37 | 38 | /** 39 | * Gets the method-id 40 | * @return id 41 | */ 42 | public VMMethodID getMethodID() { 43 | return methodID; 44 | } 45 | 46 | /** 47 | * Gets the name of the method. 48 | * @return method's name 49 | */ 50 | public String getName() { 51 | return name; 52 | } 53 | 54 | /** 55 | * Gets the JNI-signature. 56 | * @return signature 57 | */ 58 | public String getSignature() { 59 | return signature; 60 | } 61 | 62 | /** 63 | * Gets the generic signature. This is empty if there is none. 64 | * @return generic signature 65 | */ 66 | public String getGenericSignature() { 67 | return genericSignature; 68 | } 69 | 70 | /** 71 | * Gets the modifiers. 72 | * @return mod-bits 73 | */ 74 | public int getModBits() { 75 | return modBits; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/replydata/RefTypeBean.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.replydata; 2 | 3 | import org.rogmann.jsmud.datatypes.VMReferenceTypeID; 4 | 5 | /** 6 | * Type, ID and status of a reference-type. 7 | */ 8 | public class RefTypeBean { 9 | 10 | /** class-status "verified" */ 11 | public static final int STATUS_VERIFIED = 1; 12 | /** class-status "prepared" */ 13 | public static final int STATUS_PREPARED = 2; 14 | /** class-status "initialized" */ 15 | public static final int STATUS_INITIALIZED = 4; 16 | /** class-status "error" */ 17 | public static final int STATUS_ERROR = 8; 18 | 19 | /** type-tag */ 20 | private final TypeTag typeTag; 21 | /** type-id */ 22 | private final VMReferenceTypeID typeID; 23 | /** JNI-signature */ 24 | private final String signature; 25 | /** generic signature (may be empty if there is none) */ 26 | private final String genericSignature; 27 | /** current status */ 28 | private final int status; 29 | 30 | /** 31 | * Construtor 32 | * @param typeTag type-tag 33 | * @param typeID type-id 34 | * @param signature signature 35 | * @param genericSignature generic signature, empty if there is none 36 | * @param status status of class 37 | */ 38 | public RefTypeBean(TypeTag typeTag, VMReferenceTypeID typeID, String signature, String genericSignature, int status) { 39 | this.typeTag = typeTag; 40 | this.typeID = typeID; 41 | this.signature = signature; 42 | this.genericSignature = genericSignature; 43 | this.status = status; 44 | } 45 | 46 | /** 47 | * Gets the type-tag. 48 | * @return type-tag 49 | */ 50 | public TypeTag getTypeTag() { 51 | return typeTag; 52 | } 53 | 54 | /** 55 | * Gets the type-id 56 | * @return id 57 | */ 58 | public VMReferenceTypeID getTypeID() { 59 | return typeID; 60 | } 61 | 62 | /** 63 | * Gets the JNI-signature. 64 | * @return signature 65 | */ 66 | public String getSignature() { 67 | return signature; 68 | } 69 | 70 | /** 71 | * Gets the generic signature. This is empty if there is none. 72 | * @return generic signature 73 | */ 74 | public String getGenericSignature() { 75 | return genericSignature; 76 | } 77 | 78 | /** 79 | * Gets the class' status. 80 | * @return status 81 | */ 82 | public int getStatus() { 83 | return status; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/replydata/TypeTag.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.replydata; 2 | 3 | /** 4 | * Tag of a reference-type. 5 | */ 6 | public enum TypeTag { 7 | 8 | /** ReferenceType is a class */ 9 | CLASS(1), 10 | /** ReferenceType is an interface */ 11 | INTERFACE(2), 12 | /** ReferenceType is an array */ 13 | ARRAY(3); 14 | 15 | private final byte tag; 16 | 17 | /** 18 | * Internal constructor 19 | * @param tag tag 20 | */ 21 | private TypeTag(final int tag) { 22 | this.tag = (byte) tag; 23 | } 24 | 25 | /** 26 | * Tag. 27 | * @return tag of the type 28 | */ 29 | public byte getTag() { 30 | return tag; 31 | } 32 | 33 | /** 34 | * Looks up an type-tag by tag. 35 | * @param bTag tag-byte 36 | * @return tag-type or null 37 | */ 38 | public static TypeTag lookupByKind(byte bTag) { 39 | for (TypeTag typeTag : values()) { 40 | if (typeTag.tag == bTag) { 41 | return typeTag; 42 | } 43 | } 44 | return null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/replydata/VariableSlot.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.replydata; 2 | 3 | /** 4 | * Information of a variable in a method. 5 | */ 6 | public class VariableSlot { 7 | 8 | /** first code index at which the variable is visible */ 9 | private final long codeIndex; 10 | 11 | /** variable's name */ 12 | private final String name; 13 | 14 | /** JNI-signature of the type of the variable */ 15 | private final String signature; 16 | 17 | /** generic signature or empty string */ 18 | private final String genericSignature; 19 | 20 | /** length of variable-visibility beginning at codeIndex */ 21 | private final int length; 22 | 23 | /** index of variable in its frame */ 24 | private final int slot; 25 | 26 | /** 27 | * Constructor 28 | * @param codeIndex first code index at which the variable is visible 29 | * @param name variable's name 30 | * @param signature JNI-signature of the type of the variable 31 | * @param genericSignature generic signature or empty string 32 | * @param length length of variable-visibility beginning at codeIndex 33 | * @param slot index of variable in its frame 34 | */ 35 | public VariableSlot(long codeIndex, String name, String signature, String genericSignature, int length, int slot) { 36 | this.codeIndex = codeIndex; 37 | this.name = name; 38 | this.signature = signature; 39 | this.genericSignature = genericSignature; 40 | this.length = length; 41 | this.slot = slot; 42 | } 43 | 44 | /** 45 | * Gets the first code index at which the variable is visible. 46 | * @return codeIndex 47 | */ 48 | public long getCodeIndex() { 49 | return codeIndex; 50 | } 51 | 52 | /** 53 | * Gets the variable's name. 54 | * @return name 55 | */ 56 | public String getName() { 57 | return name; 58 | } 59 | 60 | /** 61 | * @return signature JNI-signature of the type of the variable 62 | */ 63 | public String getSignature() { 64 | return signature; 65 | } 66 | 67 | /** 68 | * Gets the generic signature or empty string. 69 | * @return genericSignature 70 | */ 71 | public String getGenericSignature() { 72 | return genericSignature; 73 | } 74 | 75 | /** 76 | * Gets the length of variable-visibility beginning at codeIndex. 77 | * @return length 78 | */ 79 | public int getLength() { 80 | return length; 81 | } 82 | 83 | /** 84 | * Gets the slot index of variable in its frame. 85 | * @return slot index 86 | */ 87 | public int getSlot() { 88 | return slot; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/replydata/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Helper-classes for transfering reply-data. 3 | */ 4 | package org.rogmann.jsmud.replydata; -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionArrayLoad.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.InsnNode; 4 | 5 | /** 6 | * Expression loading an array-element. 7 | */ 8 | public class ExpressionArrayLoad extends ExpressionBase{ 9 | 10 | /** array */ 11 | private final ExpressionBase expArray; 12 | /** index */ 13 | private final ExpressionBase expIndex; 14 | 15 | /** 16 | * Constructor 17 | * @param insn type-instruction, e.g. AALOAD 18 | * @param expArray array-expression 19 | * @param expIndex index-expression 20 | */ 21 | public ExpressionArrayLoad(final InsnNode insn, final ExpressionBase expArray, final ExpressionBase expIndex) { 22 | super(insn); 23 | this.expArray = expArray; 24 | this.expIndex = expIndex; 25 | } 26 | 27 | /** {@inheritDoc} */ 28 | @Override 29 | public void render(StringBuilder sb) { 30 | expArray.render(sb); 31 | sb.append('['); 32 | expIndex.render(sb); 33 | sb.append(']'); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionBase.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Type; 4 | import org.objectweb.asm.tree.AbstractInsnNode; 5 | 6 | /** 7 | * Expression which evaluates to an object or primitive. 8 | * 9 | * @param type of instruction 10 | */ 11 | public abstract class ExpressionBase extends StatementInstr { 12 | 13 | /** 14 | * Constructor. 15 | * @param insn instruction 16 | */ 17 | protected ExpressionBase(A insn) { 18 | super(insn); 19 | } 20 | 21 | /** 22 | * Returns the type of the expression. 23 | * @return type or null if unknown 24 | */ 25 | @SuppressWarnings("static-method") 26 | public Type getType() { 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionCastPrimitive.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Type; 4 | import org.objectweb.asm.tree.InsnNode; 5 | 6 | /** 7 | * Cast-expression with primitive type. 8 | */ 9 | public class ExpressionCastPrimitive extends ExpressionBase { 10 | 11 | /** primitive type */ 12 | private final Type primitiveType; 13 | /** expression to be casted */ 14 | private final ExpressionBase expr; 15 | 16 | /** 17 | * Constructor 18 | * @param insn instruction, e.g. I2S 19 | * @param primitiveType primitive type 20 | * @param expr expression to be casted 21 | */ 22 | public ExpressionCastPrimitive(final InsnNode insn, final Type primitiveType, 23 | final ExpressionBase expr) { 24 | super(insn); 25 | this.primitiveType = primitiveType; 26 | this.expr = expr; 27 | } 28 | 29 | /** {@inheritDoc} */ 30 | @Override 31 | public Type getType() { 32 | return primitiveType; 33 | } 34 | 35 | /** {@inheritDoc} */ 36 | @Override 37 | public void render(StringBuilder sb) { 38 | boolean isNeedBrackets = !ExpressionInfixBinary.isNeedsNoBrackets(expr); 39 | sb.append('('); 40 | sb.append(primitiveType.getClassName()); 41 | sb.append(')'); 42 | sb.append(' '); 43 | if (isNeedBrackets) { 44 | sb.append('('); 45 | } 46 | expr.render(sb); 47 | if (isNeedBrackets) { 48 | sb.append(')'); 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionConditionalOperator.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | 5 | /** 6 | * Conditional operator, (exprCond) ? expr1 : expr2. 7 | */ 8 | public class ExpressionConditionalOperator extends ExpressionBase { 9 | 10 | /** operator */ 11 | private final ExpressionBase exprCond; 12 | /** argument 1 */ 13 | private final ExpressionBase expr1; 14 | /** argument 2 */ 15 | private final ExpressionBase expr2; 16 | 17 | /** 18 | * Constructor 19 | * @param insn type-instruction, e.g. IADD 20 | * @param exprCond conditional expression 21 | * @param exprArg1 first value 22 | * @param exprArg2 second value 23 | */ 24 | public ExpressionConditionalOperator(final A insn, final ExpressionBase exprCond, 25 | final ExpressionBase exprArg1, final ExpressionBase exprArg2) { 26 | super(insn); 27 | this.exprCond = exprCond; 28 | this.expr1 = exprArg1; 29 | this.expr2 = exprArg2; 30 | } 31 | 32 | /** {@inheritDoc} */ 33 | @Override 34 | public void render(StringBuilder sb) { 35 | sb.append('('); 36 | sb.append('('); 37 | exprCond.render(sb); 38 | sb.append(')'); 39 | sb.append(' ').append('?').append(' '); 40 | expr1.render(sb); 41 | sb.append(' ').append(':').append(' '); 42 | expr2.render(sb); 43 | sb.append(')'); 44 | } 45 | 46 | /** {@inheritDoc} */ 47 | @Override 48 | public String toString() { 49 | return String.format("%s(%s ? %s : %s);", 50 | getClass().getSimpleName(), exprCond, expr1, expr2); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionConstructor.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.MethodInsnNode; 4 | 5 | /** 6 | * Execution of a constructor with new. 7 | */ 8 | public class ExpressionConstructor extends ExpressionBase{ 9 | 10 | /** source-name renderer */ 11 | private final SourceNameRenderer sourceNameRenderer; 12 | 13 | /** object to be initialized */ 14 | private final ExpressionTypeInstr exprNew; 15 | /** arguments of constructor */ 16 | private final ExpressionBase[] exprArgs; 17 | 18 | 19 | /** 20 | * Constructor 21 | * @param insn INVOKESPECIAL-instruction 22 | * @param sourceNameRenderer source-name renderer 23 | * @param exprNew object to be initialized 24 | * @param exprArgs arguments of constructor 25 | */ 26 | public ExpressionConstructor(final MethodInsnNode insn, 27 | final SourceNameRenderer sourceNameRenderer, 28 | final ExpressionTypeInstr exprNew, 29 | final ExpressionBase... exprArgs) { 30 | super(insn); 31 | this.sourceNameRenderer = sourceNameRenderer; 32 | this.exprNew = exprNew; 33 | this.exprArgs = exprArgs; 34 | } 35 | 36 | /** {@inheritDoc} */ 37 | @Override 38 | public void render(StringBuilder sb) { 39 | sb.append("new "); 40 | final String newTypeInternal = exprNew.insn.desc; 41 | final String name = newTypeInternal.replace('/', '.'); 42 | sb.append(sourceNameRenderer.renderClassName(name)); 43 | sb.append('('); 44 | boolean isFirst = true; 45 | for (final ExpressionBase arg : exprArgs) { 46 | if (isFirst) { 47 | isFirst = false; 48 | } 49 | else { 50 | sb.append(", "); 51 | } 52 | arg.render(sb); 53 | } 54 | sb.append(')'); 55 | } 56 | 57 | /** {@inheritDoc} */ 58 | @Override 59 | public String toString() { 60 | return String.format("%s(new %s(...));", 61 | getClass().getSimpleName(), exprNew.insn.desc.replace('/', '.')); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionDuplicate.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Type; 4 | import org.objectweb.asm.tree.AbstractInsnNode; 5 | 6 | /** 7 | * Duplicate of an expression. 8 | */ 9 | public class ExpressionDuplicate extends ExpressionBase { 10 | 11 | /** duplicated expression */ 12 | private final StatementExpressionDuplicated exprDup; 13 | 14 | /** 15 | * Constructor 16 | * @param exprDup statement of expression duplicated 17 | */ 18 | public ExpressionDuplicate(final StatementExpressionDuplicated exprDup) { 19 | super(exprDup.insn); 20 | this.exprDup = exprDup; 21 | } 22 | 23 | /** 24 | * Gets the statement carrying the expression whose value was duplicated. 25 | * @return expression 26 | */ 27 | public StatementExpressionDuplicated getStatementExpressionDuplicated() { 28 | return exprDup; 29 | } 30 | 31 | /** {@inheritDoc} */ 32 | @Override 33 | public Type getType() { 34 | return exprDup.getExpression().getType(); 35 | } 36 | 37 | /** {@inheritDoc} */ 38 | @Override 39 | public void render(StringBuilder sb) { 40 | sb.append(exprDup.getDummyName()); 41 | } 42 | 43 | /** {@inheritDoc} */ 44 | @Override 45 | public String toString() { 46 | return String.format("%s(%s);", 47 | getClass().getSimpleName(), exprDup); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionException.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Type; 4 | 5 | /** 6 | * Exception on stack at the beginning of a catch-block. 7 | */ 8 | public class ExpressionException extends ExpressionInstrZeroConstant { 9 | 10 | /** type of the exception */ 11 | private final Type typeException; 12 | 13 | /** source-name renderer */ 14 | private final SourceNameRenderer sourceNameRenderer; 15 | 16 | /** 17 | * Constructor 18 | * @param typeException type of exception, null in case of a finally-block 19 | * @param sourceNameRenderer source-name renderer 20 | */ 21 | public ExpressionException(final Type typeException, final SourceNameRenderer sourceNameRenderer) { 22 | super(null); 23 | this.typeException = typeException; 24 | this.sourceNameRenderer = sourceNameRenderer; 25 | } 26 | 27 | /** {@inheritDoc} */ 28 | @Override 29 | public void render(StringBuilder sb) { 30 | if (typeException != null) { 31 | final String className = sourceNameRenderer.renderType(typeException); 32 | sb.append("catched ").append(className); 33 | } 34 | else { 35 | final Type typeThrowable = Type.getType(Throwable.class); 36 | sb.append("finally ").append(sourceNameRenderer.renderType(typeThrowable)); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionGetField.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.ClassNode; 4 | import org.objectweb.asm.tree.FieldInsnNode; 5 | 6 | /** 7 | * Field-instruction which gets a field of an object-instance. 8 | */ 9 | public class ExpressionGetField extends ExpressionBase { 10 | 11 | /** current class */ 12 | protected final ClassNode classNode; 13 | 14 | /** expression of object-instance */ 15 | private ExpressionBase exprObj; 16 | 17 | /** 18 | * Constructor 19 | * @param insn variable-instruction, e.g. ASTORE_1 20 | * @param classNode node of current class 21 | * @param exprObj expression of object-instance 22 | */ 23 | public ExpressionGetField(FieldInsnNode insn, ClassNode classNode, final ExpressionBase exprObj) { 24 | super(insn); 25 | this.classNode = classNode; 26 | this.exprObj = exprObj; 27 | } 28 | 29 | /** {@inheritDoc} */ 30 | @Override 31 | public void render(StringBuilder sb) { 32 | exprObj.render(sb); 33 | sb.append('.'); 34 | sb.append(insn.name); 35 | } 36 | 37 | /** {@inheritDoc} */ 38 | @Override 39 | public String toString() { 40 | return String.format("%s(%s)", getClass().getSimpleName(), insn.name); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionGetStatic.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.ClassNode; 4 | import org.objectweb.asm.tree.FieldInsnNode; 5 | 6 | /** 7 | * Field-instruction which gets a static field. 8 | */ 9 | public class ExpressionGetStatic extends ExpressionBase { 10 | 11 | /** current class */ 12 | protected final ClassNode classNode; 13 | 14 | /** source-name renderer */ 15 | private final SourceNameRenderer sourceNameRenderer; 16 | 17 | /** 18 | * Constructor 19 | * @param insn variable-instruction, e.g. ASTORE_1 20 | * @param classNode node of current class 21 | * @param sourceNameRenderer source-name renderer 22 | */ 23 | public ExpressionGetStatic(FieldInsnNode insn, ClassNode classNode, 24 | SourceNameRenderer sourceNameRenderer) { 25 | super(insn); 26 | this.classNode = classNode; 27 | this.sourceNameRenderer = sourceNameRenderer; 28 | } 29 | 30 | /** {@inheritDoc} */ 31 | @Override 32 | public void render(StringBuilder sb) { 33 | if (!insn.owner.equals(classNode.name)) { 34 | String name = insn.owner.replace('/', '.'); 35 | sb.append(sourceNameRenderer.renderClassName(name)); 36 | sb.append('.'); 37 | } 38 | sb.append(insn.name); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionInfixBinary.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Type; 4 | import org.objectweb.asm.tree.AbstractInsnNode; 5 | 6 | /** 7 | * Binary infix-expression, e.g. "a + b". 8 | */ 9 | public class ExpressionInfixBinary extends ExpressionBase { 10 | 11 | /** operator */ 12 | private final String operator; 13 | /** argument 1 */ 14 | private final ExpressionBase expArg1; 15 | /** argument 2 */ 16 | private final ExpressionBase expArg2; 17 | 18 | /** 19 | * Constructor 20 | * @param insn type-instruction, e.g. IADD 21 | * @param operator operator, e.g. "+" 22 | * @param exprArg1 first argument 23 | * @param exprArg2 second argument 24 | */ 25 | public ExpressionInfixBinary(final A insn, final String operator, 26 | final ExpressionBase exprArg1, final ExpressionBase exprArg2) { 27 | super(insn); 28 | this.operator = operator; 29 | this.expArg1 = exprArg1; 30 | this.expArg2 = exprArg2; 31 | } 32 | 33 | /** {@inheritDoc} */ 34 | @Override 35 | public Type getType() { 36 | return expArg1.getType(); 37 | } 38 | 39 | /** {@inheritDoc} */ 40 | @Override 41 | public void render(StringBuilder sb) { 42 | // Is arg1 the same infix-operator? 43 | boolean isSameOperator = false; 44 | if (expArg1 instanceof ExpressionInfixBinary) { 45 | ExpressionInfixBinary exprIb1 = (ExpressionInfixBinary) expArg1; 46 | if (exprIb1.operator.equals(operator)) { 47 | isSameOperator = true; 48 | } 49 | } 50 | 51 | final boolean isNeedBrackets1 = !isNeedsNoBrackets(expArg1) && !isSameOperator; 52 | final boolean isNeedBrackets2 = !isNeedsNoBrackets(expArg2); 53 | if (isNeedBrackets1) { 54 | sb.append('('); 55 | } 56 | expArg1.render(sb); 57 | if (isNeedBrackets1) { 58 | sb.append(')'); 59 | } 60 | sb.append(' ').append(operator).append(' '); 61 | if (isNeedBrackets2) { 62 | sb.append('('); 63 | } 64 | expArg2.render(sb); 65 | if (isNeedBrackets2) { 66 | sb.append(')'); 67 | } 68 | } 69 | 70 | public static boolean isNeedsNoBrackets(final ExpressionBase expr) { 71 | return (expr instanceof ExpressionInstrConstant 72 | || expr instanceof ExpressionInstrZeroConstant 73 | || expr instanceof ExpressionVariableLoad); 74 | } 75 | 76 | /** {@inheritDoc} */ 77 | @Override 78 | public String toString() { 79 | return String.format("%s(%s %s %s);", 80 | getClass().getSimpleName(), expArg1, operator, expArg2); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionInstanceOf.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Type; 4 | import org.objectweb.asm.tree.TypeInsnNode; 5 | 6 | /** 7 | * instanceof-expression. 8 | */ 9 | public class ExpressionInstanceOf extends ExpressionBase { 10 | 11 | /** argument */ 12 | private final ExpressionBase exprRef; 13 | 14 | /** source-name renderer */ 15 | private final SourceNameRenderer sourceNameRenderer; 16 | 17 | /** 18 | * Constructor 19 | * @param insn INSTANCEOF-instruction 20 | * @param exprRef argument 21 | * @param sourceNameRenderer source-name renderer 22 | */ 23 | public ExpressionInstanceOf(final TypeInsnNode insn, final ExpressionBase exprRef, 24 | final SourceNameRenderer sourceNameRenderer) { 25 | super(insn); 26 | this.exprRef = exprRef; 27 | this.sourceNameRenderer = sourceNameRenderer; 28 | } 29 | 30 | /** {@inheritDoc} */ 31 | @Override 32 | public void render(StringBuilder sb) { 33 | exprRef.render(sb); 34 | sb.append(' ').append("instanceof").append(' '); 35 | sb.append(sourceNameRenderer.renderType(Type.getObjectType(insn.desc))); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionInstrConstant.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Type; 4 | import org.objectweb.asm.tree.LdcInsnNode; 5 | import org.rogmann.jsmud.vm.Utils; 6 | 7 | /** 8 | * Expression which loads a constant value. 9 | */ 10 | public class ExpressionInstrConstant extends ExpressionBase { 11 | /** 12 | * Constructor 13 | * @param insn instruction 14 | */ 15 | public ExpressionInstrConstant(final LdcInsnNode insn) { 16 | super(insn); 17 | } 18 | 19 | /** {@inheritDoc} */ 20 | @Override 21 | public void render(StringBuilder sb) { 22 | final Object cst = insn.cst; 23 | Utils.appendConstant(sb, cst); 24 | } 25 | 26 | /** {@inheritDoc} */ 27 | @Override 28 | public Type getType() { 29 | final Type type; 30 | final Object cst = insn.cst; 31 | if (cst instanceof Integer) { 32 | type = Type.INT_TYPE; 33 | } 34 | else if (cst instanceof String) { 35 | type = Type.getType(String.class); 36 | } 37 | else if (cst instanceof Long) { 38 | type = Type.LONG_TYPE; 39 | } 40 | else if (cst instanceof Float) { 41 | type = Type.FLOAT_TYPE; 42 | } 43 | else if (cst instanceof Double) { 44 | type = Type.DOUBLE_TYPE; 45 | } 46 | else if (cst instanceof Type) { 47 | type = (Type) cst; 48 | } 49 | else if (cst instanceof Byte) { 50 | type = Type.BYTE_TYPE; 51 | } 52 | else if (cst instanceof Boolean) { 53 | type = Type.BOOLEAN_TYPE; 54 | } 55 | else if (cst instanceof Character) { 56 | type = Type.CHAR_TYPE; 57 | } 58 | else if (cst instanceof Short) { 59 | type = Type.SHORT_TYPE; 60 | } 61 | else { 62 | throw new SourceRuntimeException(String.format("Unexpected type (%s): %s", 63 | cst.getClass(), cst)); 64 | } 65 | return type; 66 | } 67 | 68 | /** {@inheritDoc} */ 69 | @Override 70 | public String toString() { 71 | return String.format("%s(%s)", getClass().getSimpleName(), insn.cst); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionInstrIntConstant.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Opcodes; 4 | import org.objectweb.asm.Type; 5 | import org.objectweb.asm.tree.IntInsnNode; 6 | 7 | /** 8 | * int-operand constant instruction. 9 | */ 10 | public class ExpressionInstrIntConstant extends ExpressionBase{ 11 | 12 | /** 13 | * Constructor 14 | * @param insn type-instruction, e.g. BIPUSH 15 | */ 16 | public ExpressionInstrIntConstant(final IntInsnNode insn) { 17 | super(insn); 18 | } 19 | 20 | /** {@inheritDoc} */ 21 | @Override 22 | public void render(StringBuilder sb) { 23 | final int opcode = insn.getOpcode(); 24 | switch (opcode) { 25 | case Opcodes.BIPUSH: sb.append(insn.operand); break; 26 | case Opcodes.SIPUSH: sb.append(insn.operand); break; 27 | default: 28 | super.render(sb); 29 | } 30 | } 31 | 32 | /** 33 | * Gets the int-operand. 34 | * @return int-value 35 | */ 36 | public int getIntValue() { 37 | return insn.operand; 38 | } 39 | 40 | /** {@inheritDoc} */ 41 | @Override 42 | public Type getType() { 43 | return Type.INT_TYPE; 44 | } 45 | 46 | /** {@inheritDoc} */ 47 | @Override 48 | public String toString() { 49 | return String.format("%s(%d);", 50 | getClass().getSimpleName(), Integer.valueOf(insn.operand)); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionInvoke.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Opcodes; 4 | import org.objectweb.asm.Type; 5 | import org.objectweb.asm.tree.MethodInsnNode; 6 | 7 | /** 8 | * Method-instruction which executes a method returning a result. 9 | */ 10 | public class ExpressionInvoke extends ExpressionBase { 11 | 12 | /** source-name renderer */ 13 | private final SourceNameRenderer sourceNameRenderer; 14 | 15 | /** expression of object-instance */ 16 | private final ExpressionBase exprObj; 17 | /** arguments of constructor */ 18 | private final ExpressionBase[] exprArgs; 19 | 20 | /** 21 | * Constructor 22 | * @param insn variable-instruction, e.g. ASTORE_1 23 | * @param sourceNameRenderer source-name renderer 24 | * @param exprObj expression of object-instance (null in case of INVOKESTATIC) 25 | * @param exprArgs arguments of method 26 | */ 27 | public ExpressionInvoke(MethodInsnNode insn, SourceNameRenderer sourceNameRenderer, final ExpressionBase exprObj, 28 | final ExpressionBase... exprArgs) { 29 | super(insn); 30 | this.sourceNameRenderer = sourceNameRenderer; 31 | this.exprObj = exprObj; 32 | this.exprArgs = exprArgs; 33 | } 34 | 35 | /** {@inheritDoc} */ 36 | @Override 37 | public Type getType() { 38 | return Type.getReturnType(insn.desc); 39 | } 40 | 41 | /** {@inheritDoc} */ 42 | @Override 43 | public void render(StringBuilder sb) { 44 | if (insn.getOpcode() == Opcodes.INVOKESTATIC) { 45 | final String className = insn.owner.replace('/', '.'); 46 | sb.append(sourceNameRenderer.renderClassName(className)); 47 | } 48 | else { 49 | exprObj.render(sb); 50 | } 51 | sb.append('.'); 52 | sb.append(insn.name); 53 | sb.append('('); 54 | boolean isFirst = true; 55 | for (final ExpressionBase arg : exprArgs) { 56 | if (isFirst) { 57 | isFirst = false; 58 | } 59 | else { 60 | sb.append(", "); 61 | } 62 | arg.render(sb); 63 | } 64 | sb.append(')'); 65 | } 66 | 67 | /** {@inheritDoc} */ 68 | @Override 69 | public String toString() { 70 | return String.format("%s(%s%s);", 71 | getClass().getSimpleName(), 72 | insn.name, insn.desc); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionMultiNewarray.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Type; 4 | import org.objectweb.asm.tree.MultiANewArrayInsnNode; 5 | 6 | /** 7 | * Type-instruction ANEWARRAY. 8 | */ 9 | public class ExpressionMultiNewarray extends ExpressionBase{ 10 | 11 | /** dimensions */ 12 | private final ExpressionBase[] aExprDims; 13 | 14 | /** 15 | * Constructor 16 | * @param manai MULTIANEWARRAY-instruction 17 | * @param aExprDims dimensions of new array 18 | */ 19 | public ExpressionMultiNewarray(final MultiANewArrayInsnNode manai, ExpressionBase[] aExprDims) { 20 | super(manai); 21 | this.aExprDims = aExprDims; 22 | } 23 | 24 | /** {@inheritDoc} */ 25 | @Override 26 | public void render(StringBuilder sb) { 27 | sb.append("new").append(' '); 28 | final Type aType = Type.getObjectType(insn.desc); 29 | final Type elType = aType.getElementType(); 30 | sb.append(SourceFileWriter.simplifyClassName(elType)); 31 | for (ExpressionBase exprDim : aExprDims) { 32 | sb.append('['); 33 | exprDim.render(sb); 34 | sb.append(']'); 35 | } 36 | } 37 | 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionNull.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | /** 4 | * null-value. 5 | */ 6 | public class ExpressionNull extends ExpressionInstrZeroConstant { 7 | /** 8 | * Constructor 9 | */ 10 | public ExpressionNull() { 11 | super(null); 12 | } 13 | 14 | /** {@inheritDoc} */ 15 | @Override 16 | public void render(StringBuilder sb) { 17 | sb.append("null"); 18 | } 19 | 20 | /** {@inheritDoc} */ 21 | @Override 22 | public String toString() { 23 | return String.format("%s(null);", 24 | getClass().getSimpleName()); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionPrefix.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Type; 4 | import org.objectweb.asm.tree.AbstractInsnNode; 5 | 6 | /** 7 | * Prefix-expression, e.g. "++", "-" or casting 8 | */ 9 | public class ExpressionPrefix extends ExpressionBase { 10 | 11 | /** prefix */ 12 | private final String prefix; 13 | 14 | /** argument */ 15 | private final ExpressionBase expr; 16 | 17 | /** 18 | * Constructor 19 | * @param insn instruction, e.g. CHECKCAST 20 | * @param prefix prefix of expression 21 | * @param expr argument 22 | */ 23 | public ExpressionPrefix(final A insn, final String prefix, final ExpressionBase expr) { 24 | super(insn); 25 | this.prefix = prefix; 26 | this.expr = expr; 27 | } 28 | 29 | /** {@inheritDoc} */ 30 | @Override 31 | public Type getType() { 32 | Type type = null; 33 | if ("-".equals(prefix)) { 34 | type = expr.getType(); 35 | } 36 | return type; 37 | } 38 | 39 | /** {@inheritDoc} */ 40 | @Override 41 | public void render(StringBuilder sb) { 42 | sb.append(prefix); 43 | 44 | final boolean needsBrackets = !ExpressionSuffix.isNeedsNoBrackets(expr); 45 | if (needsBrackets) { 46 | sb.append('('); 47 | } 48 | expr.render(sb); 49 | if (needsBrackets) { 50 | sb.append(')'); 51 | } 52 | } 53 | 54 | public static boolean isNeedsNoBrackets(final ExpressionBase expr) { 55 | return (expr instanceof ExpressionInstrConstant 56 | || expr instanceof ExpressionInstrZeroConstant 57 | || expr instanceof ExpressionVariableLoad); 58 | } 59 | 60 | /** {@inheritDoc} */ 61 | @Override 62 | public String toString() { 63 | return String.format("%s(%s%s);", 64 | getClass().getSimpleName(), 65 | prefix, expr); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionPutField.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.FieldInsnNode; 4 | 5 | /** 6 | * Field-instruction which stores into field. 7 | */ 8 | public class ExpressionPutField extends ExpressionBase{ 9 | 10 | /** object-instance */ 11 | private ExpressionBase expObject; 12 | /** value to be stored */ 13 | private ExpressionBase exprValue; 14 | 15 | /** 16 | * Constructor 17 | * @param insn field-instruction, e.g. PUTFIELD 18 | * @param expObject expression of object containing the field 19 | * @param exprValue value to be stored 20 | */ 21 | public ExpressionPutField(FieldInsnNode insn, 22 | final ExpressionBase expObject, 23 | final ExpressionBase exprValue) { 24 | super(insn); 25 | this.expObject = expObject; 26 | this.exprValue = exprValue; 27 | } 28 | 29 | /** {@inheritDoc} */ 30 | @Override 31 | public void render(StringBuilder sb) { 32 | expObject.render(sb); 33 | sb.append('.'); 34 | sb.append(insn.name); 35 | sb.append(" = "); 36 | exprValue.render(sb); 37 | sb.append(';'); 38 | } 39 | 40 | /** {@inheritDoc} */ 41 | @Override 42 | public String toString() { 43 | return String.format("%s(%s = %s)", getClass().getSimpleName(), 44 | insn.name, exprValue); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionSuffix.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Opcodes; 4 | import org.objectweb.asm.Type; 5 | import org.objectweb.asm.tree.AbstractInsnNode; 6 | import org.objectweb.asm.tree.IincInsnNode; 7 | 8 | /** 9 | * Suffix-expression, e.g. ".length". 10 | */ 11 | public class ExpressionSuffix extends ExpressionBase { 12 | 13 | /** argument */ 14 | private final ExpressionBase expr; 15 | 16 | /** 17 | * Constructor 18 | * @param insn type-instruction, e.g. ARRAYLENGTH 19 | * @param expr argument 20 | */ 21 | public ExpressionSuffix(final A insn, final ExpressionBase expr) { 22 | super(insn); 23 | this.expr = expr; 24 | } 25 | 26 | /** {@inheritDoc} */ 27 | @Override 28 | public Type getType() { 29 | final Type type; 30 | final int opcode = insn.getOpcode(); 31 | if (opcode == Opcodes.ARRAYLENGTH) { 32 | type = Type.INT_TYPE; 33 | } 34 | else if (opcode == Opcodes.IINC) { 35 | type = Type.INT_TYPE; 36 | } 37 | else { 38 | throw new SourceRuntimeException("Unexpected opcode " + opcode); 39 | } 40 | return type; 41 | } 42 | 43 | /** {@inheritDoc} */ 44 | @Override 45 | public void render(StringBuilder sb) { 46 | final boolean neeedsBrackets = !isNeedsNoBrackets(expr); 47 | if (neeedsBrackets) { 48 | sb.append('('); 49 | } 50 | expr.render(sb); 51 | if (neeedsBrackets) { 52 | sb.append(')'); 53 | } 54 | final int opcode = insn.getOpcode(); 55 | if (opcode == Opcodes.ARRAYLENGTH) { 56 | sb.append('.').append("length"); 57 | } 58 | else if (opcode == Opcodes.IINC) { 59 | IincInsnNode ii = (IincInsnNode) insn; 60 | if (ii.incr == +1) { 61 | sb.append("++"); 62 | } 63 | else if (ii.incr == -1) { 64 | sb.append("--"); 65 | } 66 | else { 67 | throw new SourceRuntimeException(String.format("Unexpected incrment %d for local %d", 68 | Integer.valueOf(ii.incr), Integer.valueOf(ii.var))); 69 | } 70 | } 71 | else { 72 | throw new SourceRuntimeException("Unexpected opcode " + opcode); 73 | } 74 | } 75 | 76 | public static boolean isNeedsNoBrackets(final ExpressionBase expr) { 77 | return (expr instanceof ExpressionInstrConstant 78 | || expr instanceof ExpressionInstrZeroConstant 79 | || expr instanceof ExpressionVariableLoad); 80 | } 81 | 82 | /** {@inheritDoc} */ 83 | @Override 84 | public String toString() { 85 | final String suffix; 86 | final int opcode = insn.getOpcode(); 87 | if (opcode == Opcodes.ARRAYLENGTH) { 88 | suffix = ".length"; 89 | } 90 | else if (opcode == Opcodes.IINC) { 91 | final IincInsnNode ii = (IincInsnNode) insn; 92 | final int incr = ii.incr; 93 | suffix = (incr == +1) ? "++" : ((incr == -1) ? "--" : "+" + incr); 94 | } 95 | else { 96 | throw new SourceRuntimeException("Unexpected opcode " + opcode); 97 | } 98 | return String.format("%s(%s%s);", 99 | getClass().getSimpleName(), expr, suffix); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionTypeInstr.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Opcodes; 4 | import org.objectweb.asm.Type; 5 | import org.objectweb.asm.tree.ClassNode; 6 | import org.objectweb.asm.tree.TypeInsnNode; 7 | import org.rogmann.jsmud.vm.OpcodeDisplay; 8 | import org.rogmann.jsmud.vm.Utils; 9 | 10 | /** 11 | * Type-instruction of an expression. 12 | */ 13 | public class ExpressionTypeInstr extends ExpressionBase{ 14 | 15 | /** class-node */ 16 | private final ClassNode classNode; 17 | 18 | /** true if a pseudo NEW-instruction may be rendered */ 19 | private final boolean isNewRenderingAllowed; 20 | 21 | /** 22 | * Constructor 23 | * @param insn type-instruction, e.g. NEW 24 | * @param classNode class-node 25 | * @param isNewRenderingAllowed true if a pseudo NEW-instruction may be rendered (necessary at difficult constructor-arguments) 26 | */ 27 | public ExpressionTypeInstr(final TypeInsnNode insn, final ClassNode classNode, 28 | final boolean isNewRenderingAllowed) { 29 | super(insn); 30 | this.classNode = classNode; 31 | this.isNewRenderingAllowed = isNewRenderingAllowed; 32 | } 33 | 34 | /** {@inheritDoc} */ 35 | @Override 36 | public void render(StringBuilder sb) { 37 | final int opcode = insn.getOpcode(); 38 | if (opcode == Opcodes.NEW) { 39 | if (!isNewRenderingAllowed) { 40 | throw new SourceRuntimeException("The NEW-instruction should be rendered at INVOKESPECIAL "); 41 | } 42 | sb.append("NEW").append('('); 43 | final Type type = Type.getObjectType(insn.desc); 44 | String packageThis = Utils.getPackage(classNode.name.replace('/', '.')); 45 | final String className = SourceFileWriter.simplifyClassName(type, packageThis); 46 | sb.append(className).append(')'); 47 | } 48 | else { 49 | throw new SourceRuntimeException("Unexpected opcode " + opcode); 50 | } 51 | } 52 | 53 | /** {@inheritDoc} */ 54 | @Override 55 | public String toString() { 56 | return String.format("%s(%s %s);", 57 | getClass().getSimpleName(), 58 | OpcodeDisplay.lookup(insn.getOpcode()), insn.desc); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionTypeNewarray.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.objectweb.asm.Opcodes; 6 | import org.objectweb.asm.tree.TypeInsnNode; 7 | 8 | /** 9 | * Type-instruction ANEWARRAY. 10 | */ 11 | public class ExpressionTypeNewarray extends ExpressionBase{ 12 | 13 | /** length of new array */ 14 | private final ExpressionBase exprCount; 15 | 16 | /** optional array containing initial values */ 17 | private ExpressionBase[] aExprInitial; 18 | 19 | /** 20 | * Constructor 21 | * @param insn type-instruction, ANEWARRAY 22 | * @param exprCount length of new array 23 | */ 24 | public ExpressionTypeNewarray(final TypeInsnNode insn, ExpressionBase exprCount) { 25 | super(insn); 26 | this.exprCount = exprCount; 27 | } 28 | 29 | /** 30 | * Gets the count-expression. 31 | * @return count-expression 32 | */ 33 | public ExpressionBase getExprCount() { 34 | return exprCount; 35 | } 36 | 37 | /** 38 | * Sets an initial value. 39 | * 40 | * @param index index in array 41 | * @param exprInitial initial value at given index 42 | * @param len length of initial array (exprCount must evaluate to this length) 43 | */ 44 | public void setInitialValue(final int index, final ExpressionBase exprInitial, final int len) { 45 | if (aExprInitial == null) { 46 | aExprInitial = new ExpressionBase[len]; 47 | final ExpressionBase exprDefault = new ExpressionNull(); 48 | Arrays.fill(aExprInitial, exprDefault); 49 | } 50 | aExprInitial[index] = exprInitial; 51 | } 52 | 53 | /** {@inheritDoc} */ 54 | @Override 55 | public void render(StringBuilder sb) { 56 | final int opcode = insn.getOpcode(); 57 | if (opcode != Opcodes.ANEWARRAY) { 58 | throw new SourceRuntimeException("Unexpected opcode " + opcode); 59 | } 60 | sb.append("new").append(' '); 61 | final String className = insn.desc.replace('/', '.'); 62 | sb.append(SourceFileWriter.simplifyClassName(className)); 63 | sb.append('['); 64 | exprCount.render(sb); 65 | sb.append(']'); 66 | 67 | if (aExprInitial != null) { 68 | sb.append('{'); 69 | for (int i = 0; i < aExprInitial.length; i++) { 70 | if (i > 0) { 71 | sb.append(", "); 72 | } 73 | aExprInitial[i].render(sb); 74 | } 75 | sb.append('}'); 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ExpressionVariableLoad.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Type; 4 | import org.objectweb.asm.tree.LocalVariableNode; 5 | import org.objectweb.asm.tree.MethodNode; 6 | import org.objectweb.asm.tree.VarInsnNode; 7 | 8 | /** 9 | * Variable-instruction which loads a variable. 10 | */ 11 | public class ExpressionVariableLoad extends ExpressionBase { 12 | 13 | /** method-node */ 14 | private final MethodNode method; 15 | private Type typeVar; 16 | 17 | /** 18 | * Constructor 19 | * @param insn variable-instruction, e.g. ASTORE_1 20 | * @param typeVar type of variable (if known) 21 | * @param method method-node 22 | */ 23 | public ExpressionVariableLoad(VarInsnNode insn, Type typeVar, MethodNode method) { 24 | super(insn); 25 | this.typeVar = typeVar; 26 | this.method = method; 27 | } 28 | 29 | /** {@inheritDoc} */ 30 | @Override 31 | public Type getType() { 32 | return typeVar; 33 | } 34 | 35 | /** {@inheritDoc} */ 36 | @Override 37 | public void render(StringBuilder sb) { 38 | String varName = computeVarName(); 39 | 40 | sb.append(varName); 41 | } 42 | 43 | /** 44 | * Computes the name of the variable. 45 | * @return name 46 | */ 47 | private String computeVarName() { 48 | String varName = null; 49 | if (method != null && method.localVariables != null) { 50 | LocalVariableNode varNode = null; 51 | for (LocalVariableNode varNodeLoop : method.localVariables) { 52 | if (varNodeLoop.index == insn.var) { 53 | varNode = varNodeLoop; 54 | break; 55 | } 56 | } 57 | if (varNode != null) { 58 | varName = varNode.name; 59 | } 60 | } 61 | if (varName == null) { 62 | varName = "__local" + insn.var; 63 | } 64 | return varName; 65 | } 66 | 67 | /** {@inheritDoc} */ 68 | @Override 69 | public String toString() { 70 | return String.format("%s(%s);", 71 | getClass().getSimpleName(), computeVarName()); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/SourceBlock.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import java.io.IOException; 4 | import java.util.List; 5 | 6 | /** 7 | * Block of source-code consisting of header, inner blocks and a trailing block. 8 | */ 9 | public abstract class SourceBlock { 10 | 11 | /** level of indentation */ 12 | protected int level; 13 | 14 | // Some source-block statistics 15 | // 16 | 17 | protected int expectedLineMin = Integer.MAX_VALUE; 18 | protected int expectedLineMax = 0; 19 | protected int numLines = 0; 20 | 21 | /** 22 | * Constructor. 23 | * @param level indentation-level 24 | */ 25 | public SourceBlock(final int level) { 26 | this.level = level; 27 | } 28 | 29 | 30 | /** 31 | * Gets the first computed line-number of this block. 32 | * @return line-number, 0 in case of zero lines 33 | */ 34 | public abstract int getFirstLineComputed(); 35 | 36 | /** 37 | * Gets the indentation-level. 38 | * @return level 39 | */ 40 | public int getLevel() { 41 | return level; 42 | } 43 | 44 | /** 45 | * Collect the lines into a list of source-lines. 46 | * @param sourceLines list to be filled 47 | * @param lastLine number of last written line 48 | * @return number of last written line after execution 49 | * @throws IOException in case of an IO-error 50 | */ 51 | public abstract int collectLines(List sourceLines, int lastLine) throws IOException; 52 | 53 | /** 54 | * Sink each line until the expected line is reached if possible. 55 | * 56 | * @param lineNoNextBlock top line-number of next block (0 if there are no known line-numbers) 57 | * @return line number of topmost line of this block, 0 if unknown 58 | */ 59 | public abstract int lowerExpectedLines(int lineNoNextBlock); 60 | 61 | /** 62 | * Sink each line until the expected line is reached if possible. 63 | */ 64 | public abstract void lowerHeaderLines(); 65 | 66 | /** 67 | * Computes the expected minimum and maximum line-numbers of the blocks. 68 | * @param doReordering try to reorder the blocks 69 | */ 70 | protected abstract void refreshSourceBlockStatistics(boolean doReordering); 71 | 72 | /** 73 | * Dumps the structure. 74 | * @param sb string-builder 75 | * @param level indentation level 76 | */ 77 | public abstract void dumpStructure(final StringBuilder sb, final int level); 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/SourceLine.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | /** 4 | * Line of code in the source-file. 5 | */ 6 | public class SourceLine { 7 | /** current line in source-file */ 8 | private int lineCurrent; 9 | 10 | /** line-number in class-file (0 if unknown) */ 11 | private int lineExpected; 12 | 13 | /** indentation-level */ 14 | private int level; 15 | 16 | /** line in the source-file (without indentation) */ 17 | private final String sourceLine; 18 | 19 | /** 20 | * Constructor 21 | * @param lineCurrent current line-number 22 | * @param lineExpected expected line-number (0 if unknown) 23 | * @param sourceLine line in the source-file (without indentation) 24 | */ 25 | public SourceLine(final int lineCurrent, final int lineExpected, 26 | final String sourceLine) { 27 | this.lineCurrent = lineCurrent; 28 | this.lineExpected = lineExpected; 29 | this.sourceLine = sourceLine; 30 | } 31 | 32 | /** 33 | * Gets the current line-number in the source-file. 34 | * @return line-number 35 | */ 36 | public int getLineCurrent() { 37 | return lineCurrent; 38 | } 39 | 40 | /** 41 | * Gets the line-number as defined in the class-file. 42 | * @return expected line-number, 0 if unknown 43 | */ 44 | public int getLineExpected() { 45 | return lineExpected; 46 | } 47 | 48 | /** 49 | * Gets the indetation-level. 50 | * @return level 51 | */ 52 | public int getLevel() { 53 | return level; 54 | } 55 | 56 | /** 57 | * Gets the source-line (without indentation). 58 | * @return source-line 59 | */ 60 | public String getSourceLine() { 61 | return sourceLine; 62 | } 63 | 64 | /** 65 | * Sets the current line-number. 66 | * @param lineCurrent line-number 67 | */ 68 | public void setLineCurrent(int lineCurrent) { 69 | this.lineCurrent = lineCurrent; 70 | } 71 | 72 | /** 73 | * Sets the current indentation-level. 74 | * @param level indentation-level 75 | */ 76 | public void setIndentationLevel(int level) { 77 | this.level = level; 78 | } 79 | 80 | /** {@inheritDoc} */ 81 | @Override 82 | public String toString() { 83 | final StringBuilder sb = new StringBuilder(20); 84 | sb.append("SL{"); 85 | sb.append("lC:").append(lineCurrent); 86 | if (lineExpected > 0) { 87 | sb.append(", lE:").append(lineExpected); 88 | } 89 | sb.append('}'); 90 | return sb.toString(); 91 | } 92 | 93 | /** 94 | * Sets the line-number. 95 | * This method is used to set the line-number of a field-declaration which is determined in a constructor-body. 96 | * @param lineNo line-number 97 | */ 98 | public void setLineExpected(int lineNo) { 99 | lineExpected = lineNo; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/SourceNameRenderer.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import java.util.Collection; 4 | import java.util.SortedMap; 5 | import java.util.SortedSet; 6 | import java.util.TreeMap; 7 | import java.util.TreeSet; 8 | import java.util.function.Predicate; 9 | 10 | import org.objectweb.asm.Type; 11 | import org.rogmann.jsmud.vm.Utils; 12 | 13 | /** 14 | * This class supports in rendering class-names and collecting import-statements. 15 | */ 16 | public class SourceNameRenderer { 17 | 18 | /** map from imported class-names to simple class-name */ 19 | private final SortedMap mapClassNames = new TreeMap<>(); 20 | 21 | /** map from simple class-name to imported class-names */ 22 | private final SortedMap mapSimpleClassNames = new TreeMap<>(); 23 | 24 | /** Package of the class having import-statements */ 25 | private final String currentPackage; 26 | 27 | /** filter which gives true if a class-name may be imported */ 28 | private final Predicate filter; 29 | 30 | /** 31 | * Constructor 32 | * @param classPackage package of the class 33 | * @param filter give true if a class-name may be imported 34 | */ 35 | public SourceNameRenderer(final String classPackage, final Predicate filter) { 36 | this.currentPackage = classPackage; 37 | this.filter = filter; 38 | } 39 | 40 | /** 41 | * Creates a list of imported class-names. 42 | * @return list of fully qualified names 43 | */ 44 | public Collection getImportedClassNames() { 45 | final SortedSet collImport = new TreeSet(); 46 | for (String className : mapClassNames.keySet()) { 47 | if (!currentPackage.equals(Utils.getPackage(className))) { 48 | collImport.add(className); 49 | } 50 | } 51 | return collImport; 52 | } 53 | 54 | /** 55 | * Renders a type-name. 56 | * @param type class-type 57 | * @return fully qualified name or simple-name (with import-statement) 58 | */ 59 | public String renderType(final Type type) { 60 | final String className = SourceFileWriter.simplifyClassName(type); 61 | return renderClassName(className); 62 | } 63 | 64 | /** 65 | * Renders a type-name. 66 | * @param className class-name, e.g. "java.util.List" 67 | * @return fully qualified name or simple-name (with import-statement) 68 | */ 69 | public String renderClassName(final String className) { 70 | String nameRendered; 71 | if (className.indexOf('.') < 0 || !filter.test(className)) { 72 | nameRendered = className; 73 | } 74 | else { 75 | nameRendered = mapClassNames.get(className); 76 | if (nameRendered == null) { 77 | final int idx = className.lastIndexOf('.'); 78 | final String simpleName = className.substring(idx + 1); 79 | if (mapSimpleClassNames.containsKey(simpleName)) { 80 | // The simple name is used in another package. 81 | nameRendered = className; 82 | } 83 | else { 84 | nameRendered = simpleName; 85 | mapClassNames.put(className, simpleName); 86 | mapSimpleClassNames.put(simpleName, className); 87 | } 88 | } 89 | } 90 | return nameRendered; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/SourceRuntimeException.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | /** 4 | * Exception while analyzing bytecode to display a source-file. 5 | */ 6 | public class SourceRuntimeException extends RuntimeException { 7 | 8 | /** Serialization-Id */ 9 | private static final long serialVersionUID = 20220210L; 10 | 11 | /** 12 | * Constructor 13 | * @param msg exception-message 14 | */ 15 | public SourceRuntimeException(final String msg) { 16 | super(msg); 17 | } 18 | 19 | /** 20 | * Constructor 21 | * @param msg exception-message 22 | * @param e cause-throwable 23 | */ 24 | public SourceRuntimeException(final String msg, Throwable e) { 25 | super(msg, e); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementArrayStore.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.InsnNode; 4 | 5 | /** 6 | * Statement storing an array-element. 7 | */ 8 | public class StatementArrayStore extends StatementInstr{ 9 | 10 | /** array */ 11 | private final ExpressionBase exprArray; 12 | /** index */ 13 | private final ExpressionBase exprIndex; 14 | /** value */ 15 | private final ExpressionBase exprValue; 16 | 17 | /** 18 | * Constructor 19 | * @param insn type-instruction, e.g. BASTORE 20 | * @param exprArray array 21 | * @param exprIndex index 22 | * @param exprValue value 23 | */ 24 | public StatementArrayStore(final InsnNode insn, final ExpressionBase exprArray, 25 | final ExpressionBase exprIndex, final ExpressionBase exprValue) { 26 | super(insn); 27 | this.exprArray = exprArray; 28 | this.exprIndex = exprIndex; 29 | this.exprValue = exprValue; 30 | } 31 | 32 | /** {@inheritDoc} */ 33 | @Override 34 | public void render(StringBuilder sb) { 35 | exprArray.render(sb); 36 | sb.append('['); 37 | exprIndex.render(sb); 38 | sb.append(']'); 39 | sb.append(' '); 40 | sb.append('='); 41 | sb.append(' '); 42 | exprValue.render(sb); 43 | sb.append(';'); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementBase.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | /** 4 | * Expression or instruction. 5 | */ 6 | public abstract class StatementBase { 7 | 8 | /** 9 | * Gets true if the statement is visible in the generated source-file. 10 | * @return visible-flag 11 | */ 12 | @SuppressWarnings("static-method") 13 | public boolean isVisible() { 14 | return true; 15 | } 16 | 17 | /** 18 | * Renders an expression or instruction. 19 | * @param sb string-builder 20 | */ 21 | public abstract void render(final StringBuilder sb); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementComment.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | /** 4 | * Block comment. 5 | */ 6 | public class StatementComment extends StatementBase { 7 | 8 | private final String comment; 9 | 10 | /** 11 | * Constructor 12 | * @param comment comment to be rendered 13 | */ 14 | public StatementComment(final String comment) { 15 | this.comment = comment; 16 | } 17 | 18 | /** {@inheritDoc} */ 19 | @Override 20 | public void render(StringBuilder sb) { 21 | sb.append("/** "); 22 | if (comment != null) { 23 | sb.append(comment.replace("*/", "*°/")); 24 | } 25 | sb.append(" */"); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementConstructor.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.ClassNode; 4 | import org.objectweb.asm.tree.MethodInsnNode; 5 | 6 | /** 7 | * Execution of a constructor (e.g. super or this) without new. 8 | */ 9 | public class StatementConstructor extends StatementInstr{ 10 | 11 | /** class-node */ 12 | private final ClassNode classNode; 13 | /** object to be initialized */ 14 | protected final ExpressionBase exprObj; 15 | /** arguments of constructor */ 16 | private final ExpressionBase[] exprArgs; 17 | 18 | /** 19 | * Constructor 20 | * @param insn INVOKESPECIAL-instruction 21 | * @param classNode class containing the method to be called 22 | * @param exprObj object to be initialized 23 | * @param exprArgs arguments of constructor 24 | */ 25 | public StatementConstructor(MethodInsnNode insn, final ClassNode classNode, 26 | final ExpressionBase exprObj, 27 | final ExpressionBase... exprArgs) { 28 | super(insn); 29 | this.classNode = classNode; 30 | this.exprObj = exprObj; 31 | this.exprArgs = exprArgs; 32 | } 33 | 34 | /** {@inheritDoc} */ 35 | @Override 36 | public void render(StringBuilder sb) { 37 | final String classRef; 38 | if (classNode.name.equals(insn.owner)) { 39 | classRef = "this"; 40 | } 41 | else { 42 | classRef = "super"; 43 | } 44 | sb.append(classRef); 45 | sb.append('('); 46 | boolean isFirst = true; 47 | for (final ExpressionBase arg : exprArgs) { 48 | if (isFirst) { 49 | isFirst = false; 50 | } 51 | else { 52 | sb.append(", "); 53 | } 54 | arg.render(sb); 55 | } 56 | sb.append(')'); 57 | sb.append(';'); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementExpression.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | 5 | /** 6 | * An expression converted into a statement, e.g. because of an POP-instruction. 7 | */ 8 | public class StatementExpression extends StatementInstr { 9 | 10 | /** expression */ 11 | private final ExpressionBase expr; 12 | 13 | /** 14 | * Constructor 15 | * @param expr expression 16 | */ 17 | public StatementExpression(final ExpressionBase expr) { 18 | super(expr.insn); 19 | this.expr = expr; 20 | } 21 | 22 | /** 23 | * Gets the expression. 24 | * @return expression 25 | */ 26 | public ExpressionBase getExpression() { 27 | return expr; 28 | } 29 | 30 | /** {@inheritDoc} */ 31 | @Override 32 | public void render(StringBuilder sb) { 33 | expr.render(sb); 34 | sb.append(';'); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementExpressionDuplicated.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | 5 | /** 6 | * Duplication of an expression, original saved as statement storing this expression 7 | * into a temporary variable. 8 | */ 9 | public class StatementExpressionDuplicated extends StatementInstr { 10 | 11 | /** duplicated expression */ 12 | private final ExpressionBase exprDuplicated; 13 | 14 | /** dummy-name */ 15 | private final String dummyName; 16 | 17 | /** 18 | * Constructor 19 | * @param expr expression to be duplicated 20 | * @param dummyName name of duplicated expression 21 | */ 22 | public StatementExpressionDuplicated(final ExpressionBase expr, final String dummyName) { 23 | super(expr.insn); 24 | this.exprDuplicated = expr; 25 | this.dummyName = dummyName; 26 | } 27 | 28 | /** 29 | * Gets the expression whose value was duplicated. 30 | * @return expression 31 | */ 32 | public ExpressionBase getExpression() { 33 | return exprDuplicated; 34 | } 35 | 36 | /** 37 | * Gets the dummy-name of the duplicated expression. 38 | * @return dummy-name 39 | */ 40 | public String getDummyName() { 41 | return dummyName; 42 | } 43 | 44 | /** {@inheritDoc} */ 45 | @Override 46 | public void render(StringBuilder sb) { 47 | sb.append(dummyName); 48 | sb.append(' ').append('=').append(' '); 49 | exprDuplicated.render(sb); 50 | sb.append(';'); 51 | } 52 | 53 | /** {@inheritDoc} */ 54 | @Override 55 | public String toString() { 56 | return String.format("%s(%s->%s);", 57 | getClass().getSimpleName(), exprDuplicated, dummyName); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementGoto.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Label; 4 | import org.objectweb.asm.tree.JumpInsnNode; 5 | 6 | /** 7 | * goto-Statement. 8 | */ 9 | public class StatementGoto extends StatementInstr { 10 | 11 | /** destination label */ 12 | protected final Label labelDest; 13 | 14 | /** display-name of label */ 15 | private final String labelName; 16 | 17 | /** 18 | * Constructor 19 | * @param insn jump-instruction, e.g. GOTO 20 | * @param labelDest destination label 21 | * @param labelName display-name of label 22 | */ 23 | public StatementGoto(final JumpInsnNode insn, final Label labelDest, final String labelName) { 24 | super(insn); 25 | this.labelDest = labelDest; 26 | this.labelName = labelName; 27 | } 28 | 29 | /** 30 | * Gets the name of the destination-label. 31 | * @return label-name 32 | */ 33 | public String getLabelName() { 34 | return labelName; 35 | } 36 | 37 | /** {@inheritDoc} */ 38 | @Override 39 | public void render(StringBuilder sb) { 40 | sb.append("goto").append(' '); 41 | sb.append(labelName); 42 | sb.append(';'); 43 | } 44 | 45 | /** {@inheritDoc} */ 46 | @Override 47 | public String toString() { 48 | return String.format("%s(goto %s);", 49 | getClass().getSimpleName(), labelName); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementIf.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Label; 4 | import org.objectweb.asm.tree.JumpInsnNode; 5 | 6 | /** 7 | * if-Statement. 8 | */ 9 | public class StatementIf extends StatementInstr { 10 | 11 | /** destination label */ 12 | protected final Label labelDest; 13 | 14 | /** display-name of label */ 15 | private final String labelName; 16 | 17 | /** condition-expression */ 18 | private final ExpressionBase exprCond; 19 | 20 | /** 21 | * Constructor 22 | * @param insn jump-instruction, e.g. IF_ICMPLE 23 | * @param exprCond conditional-expression 24 | * @param labelDest destination label 25 | * @param labelName display-name of label 26 | */ 27 | public StatementIf(final JumpInsnNode insn, final ExpressionBase exprCond, final Label labelDest, final String labelName) { 28 | super(insn); 29 | this.exprCond = exprCond; 30 | this.labelDest = labelDest; 31 | this.labelName = labelName; 32 | } 33 | 34 | /** 35 | * Gets the conditional expression. 36 | * @return condition 37 | */ 38 | public ExpressionBase getExprCond() { 39 | return exprCond; 40 | } 41 | 42 | /** 43 | * Gets the name of the destination-label. 44 | * @return label-name 45 | */ 46 | public String getLabelName() { 47 | return labelName; 48 | } 49 | 50 | /** {@inheritDoc} */ 51 | @Override 52 | public void render(StringBuilder sb) { 53 | sb.append("if").append(' ').append('('); 54 | exprCond.render(sb); 55 | sb.append(')').append(' '); 56 | sb.append("goto").append(' ').append(labelName); 57 | sb.append(';'); 58 | } 59 | 60 | /** {@inheritDoc} */ 61 | @Override 62 | public String toString() { 63 | return String.format("%s(if (%s) goto %s);", 64 | getClass().getSimpleName(), exprCond, labelName); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementInstr.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import org.objectweb.asm.tree.MethodNode; 5 | 6 | /** 7 | * One instruction. 8 | * 9 | * @param type of instruction 10 | */ 11 | public abstract class StatementInstr extends StatementBase { 12 | 13 | /** instruction-node */ 14 | protected final A insn; 15 | 16 | /** 17 | * Constructor 18 | * @param insn instruction 19 | */ 20 | protected StatementInstr(final A insn) { 21 | this.insn = insn; 22 | } 23 | 24 | /** {@inheritDoc} */ 25 | @Override 26 | public void render(StringBuilder sb) { 27 | MethodNode methodNode = null; 28 | render(sb, methodNode); 29 | } 30 | 31 | /** 32 | * Renders an instruction. 33 | * @param sb string-builder 34 | * @param methodNode optional method-node (used to display local-variables in variable instructions) 35 | */ 36 | protected void render(StringBuilder sb, MethodNode methodNode) { 37 | // You may use StatementInstrPlain to display raw bytecodes. 38 | throw new SourceRuntimeException(String.format("Missing render-implementation for opcode %d", 39 | Integer.valueOf(insn.getOpcode()))); 40 | } 41 | 42 | /** 43 | * Gets the corresponding bytecode-instruction. 44 | * @return instruction 45 | */ 46 | public A getInsn() { 47 | return insn; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementInstrPlain.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import org.objectweb.asm.tree.MethodNode; 5 | import org.rogmann.jsmud.visitors.InstructionVisitor; 6 | 7 | /** 8 | * One instruction, displayed as bytecode only. 9 | * 10 | * @param type of instruction 11 | */ 12 | public class StatementInstrPlain extends StatementInstr { 13 | 14 | /** method-node */ 15 | private final MethodNode methodNode; 16 | 17 | /** 18 | * Constructor 19 | * @param insn instruction 20 | * @param methodNode method 21 | */ 22 | protected StatementInstrPlain(final A insn, final MethodNode methodNode) { 23 | super(insn); 24 | this.methodNode = methodNode; 25 | } 26 | 27 | /** {@inheritDoc} */ 28 | @Override 29 | public void render(StringBuilder sb) { 30 | sb.append(InstructionVisitor.displayInstruction(insn, methodNode)); 31 | sb.append(';'); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementInstrZeroOp.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.InsnNode; 4 | import org.objectweb.asm.tree.MethodNode; 5 | import org.rogmann.jsmud.visitors.InstructionVisitor; 6 | 7 | /** 8 | * Zero operand instruction on stack. 9 | */ 10 | public class StatementInstrZeroOp extends StatementInstr{ 11 | 12 | /** 13 | * Constructor 14 | * @param insn type-instruction, e.g. DUP or ICONST_1 15 | */ 16 | public StatementInstrZeroOp(InsnNode insn) { 17 | super(insn); 18 | } 19 | 20 | /** {@inheritDoc} */ 21 | @Override 22 | protected void render(StringBuilder sb, MethodNode methodNode) { 23 | switch (insn.getOpcode()) { 24 | default: 25 | sb.append(InstructionVisitor.displayInstruction(insn, methodNode)); 26 | } 27 | sb.append(';'); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementInvoke.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Opcodes; 4 | import org.objectweb.asm.tree.MethodInsnNode; 5 | 6 | /** 7 | * Method-instruction which executes a void method as a statement. 8 | */ 9 | public class StatementInvoke extends StatementInstr { 10 | 11 | /** source-name renderer */ 12 | private final SourceNameRenderer sourceNameRenderer; 13 | 14 | /** expression of object-instance */ 15 | private final ExpressionBase exprObj; 16 | /** arguments of constructor */ 17 | private final ExpressionBase[] exprArgs; 18 | 19 | /** 20 | * Constructor 21 | * @param insn variable-instruction, e.g. ASTORE_1 22 | * @param sourceNameRenderer source-name renderer 23 | * @param exprObj expression of object-instance (null in case of INVOKESTATIC) 24 | * @param exprArgs arguments of method 25 | */ 26 | public StatementInvoke(final MethodInsnNode insn, final SourceNameRenderer sourceNameRenderer, 27 | final ExpressionBase exprObj, 28 | final ExpressionBase... exprArgs) { 29 | super(insn); 30 | this.sourceNameRenderer = sourceNameRenderer; 31 | this.exprObj = exprObj; 32 | this.exprArgs = exprArgs; 33 | } 34 | 35 | /** {@inheritDoc} */ 36 | @Override 37 | public void render(StringBuilder sb) { 38 | if (insn.getOpcode() == Opcodes.INVOKESTATIC) { 39 | final String className = insn.owner.replace('/', '.'); 40 | sb.append(sourceNameRenderer.renderClassName(className)); 41 | } 42 | else { 43 | exprObj.render(sb); 44 | } 45 | sb.append('.'); 46 | sb.append(insn.name); 47 | sb.append('('); 48 | boolean isFirst = true; 49 | for (final ExpressionBase arg : exprArgs) { 50 | if (isFirst) { 51 | isFirst = false; 52 | } 53 | else { 54 | sb.append(", "); 55 | } 56 | arg.render(sb); 57 | } 58 | sb.append(')'); 59 | sb.append(';'); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementLabel.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import java.util.Map; 4 | 5 | import org.objectweb.asm.Label; 6 | import org.objectweb.asm.tree.LabelNode; 7 | 8 | /** 9 | * Label (possible destination of goto-instruction). 10 | */ 11 | public class StatementLabel extends StatementInstr { 12 | 13 | /** map with used labels as keys and display-names as values */ 14 | private Map mapUsedLabels; 15 | 16 | /** 17 | * Constructor 18 | * @param insn label-instruction 19 | * @param mapUsedLabels map from label to display-name 20 | */ 21 | protected StatementLabel(LabelNode insn, Map mapUsedLabels) { 22 | super(insn); 23 | this.mapUsedLabels = mapUsedLabels; 24 | } 25 | 26 | /** 27 | * Gets the name of this label. 28 | * @return label-name, may be null if this label is unused 29 | */ 30 | public String getLabelName() { 31 | final Label label = insn.getLabel(); 32 | final String labelName = mapUsedLabels.get(label); 33 | return labelName; 34 | } 35 | 36 | /** {@inheritDoc} */ 37 | @Override 38 | public boolean isVisible() { 39 | return mapUsedLabels.containsKey(insn.getLabel()); 40 | } 41 | 42 | /** {@inheritDoc} */ 43 | @Override 44 | public void render(StringBuilder sb) { 45 | final String labelName = getLabelName(); 46 | if (labelName != null) { 47 | sb.append(labelName).append(':'); 48 | } 49 | } 50 | 51 | /** {@inheritDoc} */ 52 | @Override 53 | public String toString() { 54 | String labelName = mapUsedLabels.get(insn.getLabel()); 55 | if (labelName == null) { 56 | labelName = insn.getLabel().toString(); 57 | } 58 | return String.format("%s(%s:);", 59 | getClass().getSimpleName(), labelName); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementLookupSwitch.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.LookupSwitchInsnNode; 4 | 5 | /** 6 | * switch-Statement (LOOKUPSWITCH-instruction). 7 | */ 8 | public class StatementLookupSwitch extends StatementInstr { 9 | 10 | /** key */ 11 | private final ExpressionBase exprKey; 12 | 13 | /** display-name of default-label */ 14 | private final String nameDefault; 15 | 16 | /** display-names of case-labels */ 17 | private final String[] aLabelName; 18 | 19 | /** 20 | * Constructor 21 | * @param insn TABLESWITCH-instruction 22 | * @param exprKey index-expression 23 | * @param nameDefault name of default label 24 | * @param aLabelName display-name of labels 25 | */ 26 | public StatementLookupSwitch(final LookupSwitchInsnNode insn, final ExpressionBase exprKey, 27 | final String nameDefault, final String[] aLabelName) { 28 | super(insn); 29 | this.exprKey = exprKey; 30 | this.nameDefault = nameDefault; 31 | this.aLabelName = aLabelName; 32 | } 33 | 34 | /** {@inheritDoc} */ 35 | @Override 36 | public void render(StringBuilder sb) { 37 | sb.append("switch").append(' ').append('('); 38 | exprKey.render(sb); 39 | sb.append(')').append(' ').append('{'); 40 | final int numCases = insn.keys.size(); 41 | for (int i = 0; i < numCases; i++) { 42 | sb.append(' ').append("case").append(' '); 43 | sb.append(insn.keys.get(i)).append(':').append(' '); 44 | sb.append("goto").append(' '); 45 | sb.append(aLabelName[i]); 46 | sb.append(';'); 47 | } 48 | sb.append(' ').append("default").append(':').append(' '); 49 | sb.append("goto").append(' '); 50 | sb.append(nameDefault); 51 | sb.append(';'); 52 | sb.append('}'); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementMonitor.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Opcodes; 4 | import org.objectweb.asm.tree.InsnNode; 5 | import org.rogmann.jsmud.vm.OpcodeDisplay; 6 | 7 | /** 8 | * monitor-statement (synchronized). 9 | */ 10 | public class StatementMonitor extends StatementInstr { 11 | 12 | /** monitor-object */ 13 | private final ExpressionBase exprObj; 14 | 15 | /** 16 | * Constructor 17 | * @param iz MONITORENTER- or MONITOREXIT-instruction 18 | * @param exprObj expression 19 | */ 20 | public StatementMonitor(final InsnNode iz, final ExpressionBase exprObj) { 21 | super(iz); 22 | this.exprObj = exprObj; 23 | } 24 | 25 | /** {@inheritDoc} */ 26 | @Override 27 | public void render(StringBuilder sb) { 28 | sb.append("synchronized").append('('); 29 | if (insn.getOpcode() == Opcodes.MONITORENTER) { 30 | sb.append("\"enter\"").append(", "); 31 | } 32 | else { 33 | sb.append("\"exit\"").append(", "); 34 | } 35 | exprObj.render(sb); 36 | sb.append(')'); 37 | sb.append(';'); 38 | } 39 | 40 | /** {@inheritDoc} */ 41 | @Override 42 | public String toString() { 43 | return String.format("%s(%s, %s);", 44 | getClass().getSimpleName(), 45 | OpcodeDisplay.lookup(insn.getOpcode()), exprObj); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementPutField.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.FieldInsnNode; 4 | 5 | /** 6 | * Field-instruction which stores into field. 7 | */ 8 | public class StatementPutField extends StatementInstr{ 9 | 10 | /** object-instance */ 11 | private ExpressionBase expObject; 12 | /** value to be stored */ 13 | private ExpressionBase exprValue; 14 | 15 | /** 16 | * Constructor 17 | * @param insn field-instruction, e.g. PUTFIELD 18 | * @param expObject object of field to be written 19 | * @param exprValue value to be stored 20 | */ 21 | public StatementPutField(FieldInsnNode insn, 22 | final ExpressionBase expObject, 23 | final ExpressionBase exprValue) { 24 | super(insn); 25 | this.expObject = expObject; 26 | this.exprValue = exprValue; 27 | } 28 | 29 | /** {@inheritDoc} */ 30 | @Override 31 | public void render(StringBuilder sb) { 32 | expObject.render(sb); 33 | sb.append('.'); 34 | sb.append(insn.name); 35 | sb.append(" = "); 36 | exprValue.render(sb); 37 | sb.append(';'); 38 | } 39 | 40 | /** {@inheritDoc} */ 41 | @Override 42 | public String toString() { 43 | return String.format("%s(%s = %s)", getClass().getSimpleName(), 44 | insn.name, exprValue); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementPutStatic.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.ClassNode; 4 | import org.objectweb.asm.tree.FieldInsnNode; 5 | 6 | /** 7 | * Field-instruction which stores into a static field. 8 | */ 9 | public class StatementPutStatic extends StatementInstr{ 10 | 11 | /** current class */ 12 | protected final ClassNode classNode; 13 | 14 | /** value to be stored */ 15 | private ExpressionBase exprValue; 16 | 17 | /** 18 | * Constructor 19 | * @param insn field-instruction, PUTSTATIC 20 | * @param classNode class-node of current class 21 | * @param exprValue value to be stored 22 | */ 23 | public StatementPutStatic(final FieldInsnNode insn, final ClassNode classNode, 24 | final ExpressionBase exprValue) { 25 | super(insn); 26 | this.classNode = classNode; 27 | this.exprValue = exprValue; 28 | } 29 | 30 | /** {@inheritDoc} */ 31 | @Override 32 | public void render(StringBuilder sb) { 33 | if (!insn.owner.equals(classNode.name)) { 34 | String name = insn.owner.replace('/', '.'); 35 | sb.append(SourceFileWriter.simplifyClassName(name)); 36 | sb.append('.'); 37 | } 38 | sb.append(insn.name); 39 | sb.append(" = "); 40 | exprValue.render(sb); 41 | sb.append(';'); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementReturn.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.InsnNode; 4 | 5 | /** 6 | * return-Statement. 7 | */ 8 | public class StatementReturn extends StatementInstrZeroOp { 9 | 10 | /** returned object */ 11 | private ExpressionBase expr; 12 | 13 | /** 14 | * Constructor 15 | * @param insn zero-op-instruction, e.g. RETURN 16 | */ 17 | public StatementReturn(final InsnNode insn) { 18 | this(insn, null); 19 | } 20 | 21 | /** 22 | * Constructor 23 | * @param insn zero-op-instruction, e.g. ARETURN 24 | * @param expr optional expression if an object is returned 25 | */ 26 | public StatementReturn(final InsnNode insn, final ExpressionBase expr) { 27 | super(insn); 28 | this.expr = expr; 29 | } 30 | 31 | /** 32 | * Checks if there is a return-object. 33 | * @return object-flag 34 | */ 35 | public boolean hasReturnObject() { 36 | return expr != null; 37 | } 38 | 39 | /** {@inheritDoc} */ 40 | @Override 41 | public void render(StringBuilder sb) { 42 | sb.append("return"); 43 | if (expr != null) { 44 | sb.append(' '); 45 | expr.render(sb); 46 | } 47 | sb.append(';'); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementTableSwitch.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.TableSwitchInsnNode; 4 | 5 | /** 6 | * switch-Statement (TABLESWITCH-instruction). 7 | */ 8 | public class StatementTableSwitch extends StatementInstr { 9 | 10 | /** index */ 11 | private final ExpressionBase exprIndex; 12 | 13 | /** display-name of default-label */ 14 | private final String nameDefault; 15 | 16 | /** display-names of case-labels */ 17 | private final String[] aLabelName; 18 | 19 | /** 20 | * Constructor 21 | * @param insn TABLESWITCH-instruction 22 | * @param exprIndex index-expression 23 | * @param nameDefault default label 24 | * @param aLabelName display-names of labels 25 | */ 26 | public StatementTableSwitch(final TableSwitchInsnNode insn, final ExpressionBase exprIndex, 27 | final String nameDefault, final String[] aLabelName) { 28 | super(insn); 29 | this.exprIndex = exprIndex; 30 | this.nameDefault = nameDefault; 31 | this.aLabelName = aLabelName; 32 | } 33 | 34 | /** {@inheritDoc} */ 35 | @Override 36 | public void render(StringBuilder sb) { 37 | sb.append("switch").append(' ').append('('); 38 | exprIndex.render(sb); 39 | sb.append(')').append(' ').append('{'); 40 | final int lMin = insn.min; 41 | final int lMax = insn.max; 42 | final int num = lMax - lMin + 1; 43 | for (int i = 0; i < num; i++) { 44 | sb.append(' ').append("case").append(' '); 45 | sb.append(i).append(':').append(' '); 46 | sb.append("goto").append(' '); 47 | sb.append(aLabelName[i]); 48 | sb.append(';'); 49 | } 50 | sb.append(' ').append("default").append(':').append(' '); 51 | sb.append("goto").append(' '); 52 | sb.append(nameDefault); 53 | sb.append(';'); 54 | sb.append('}'); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementThrow.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.InsnNode; 4 | 5 | /** 6 | * throw-statement. 7 | */ 8 | public class StatementThrow extends StatementInstr { 9 | 10 | /** expression */ 11 | private final ExpressionBase expr; 12 | 13 | /** 14 | * Constructor 15 | * @param insn THROW-instruction 16 | * @param expr expression 17 | */ 18 | public StatementThrow(final InsnNode insn, final ExpressionBase expr) { 19 | super(insn); 20 | this.expr = expr; 21 | } 22 | 23 | /** {@inheritDoc} */ 24 | @Override 25 | public void render(StringBuilder sb) { 26 | sb.append("throw").append(' '); 27 | expr.render(sb); 28 | sb.append(';'); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementVariableIinc.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.tree.IincInsnNode; 4 | import org.objectweb.asm.tree.LocalVariableNode; 5 | import org.objectweb.asm.tree.MethodNode; 6 | 7 | /** 8 | * Instruction which changes a local variable (e.g. "++"). 9 | */ 10 | public class StatementVariableIinc extends StatementInstr{ 11 | 12 | /** method */ 13 | private final MethodNode method; 14 | 15 | /** 16 | * Constructor 17 | * @param insn variable-instruction, e.g. IINC 1,1 18 | * @param method method-node 19 | */ 20 | public StatementVariableIinc(IincInsnNode insn, final MethodNode method) { 21 | super(insn); 22 | this.method = method; 23 | } 24 | 25 | /** {@inheritDoc} */ 26 | @Override 27 | public void render(StringBuilder sb) { 28 | String varName = null; 29 | if (method != null && method.localVariables != null) { 30 | LocalVariableNode varNode = null; 31 | for (LocalVariableNode varNodeLoop : method.localVariables) { 32 | if (varNodeLoop.index == insn.var) { 33 | varNode = varNodeLoop; 34 | break; 35 | } 36 | } 37 | if (varNode != null) { 38 | varName = varNode.name; 39 | } 40 | } 41 | if (varName == null) { 42 | varName = "__local" + insn.var; 43 | } 44 | 45 | sb.append(varName); 46 | final int incr = insn.incr; 47 | if (incr == -1) { 48 | sb.append("--"); 49 | } 50 | else if (incr == +1) { 51 | sb.append("++"); 52 | } 53 | else { 54 | sb.append(" = "); 55 | sb.append(varName); 56 | sb.append(' '); 57 | if (incr >= 0) { 58 | sb.append('+'); 59 | } 60 | sb.append(incr); 61 | } 62 | sb.append(';'); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/StatementVariableStore.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Type; 4 | import org.objectweb.asm.tree.LocalVariableNode; 5 | import org.objectweb.asm.tree.MethodNode; 6 | import org.objectweb.asm.tree.VarInsnNode; 7 | 8 | /** 9 | * Variable-instruction which stores into variable. 10 | */ 11 | public class StatementVariableStore extends StatementInstr{ 12 | 13 | /** source-name renderer */ 14 | private final SourceNameRenderer sourceNameRenderer; 15 | 16 | /** method-node */ 17 | private final MethodNode method; 18 | /** type of expression */ 19 | private final Type typeExpr; 20 | /** value to be stored */ 21 | private final ExpressionBase exprValue; 22 | 23 | /** 24 | * Constructor 25 | * @param insn variable-instruction, e.g. ALOAD_1 26 | * @param method method-node 27 | * @param typeExpr type of expression or null 28 | * @param exprValue value to be stored 29 | * @param sourceNameRenderer source-name renderer 30 | */ 31 | public StatementVariableStore(VarInsnNode insn, MethodNode method, 32 | Type typeExpr, final ExpressionBase exprValue, SourceNameRenderer sourceNameRenderer) { 33 | super(insn); 34 | this.method = method; 35 | this.typeExpr = typeExpr; 36 | this.exprValue = exprValue; 37 | this.sourceNameRenderer = sourceNameRenderer; 38 | } 39 | 40 | /** {@inheritDoc} */ 41 | @Override 42 | public void render(StringBuilder sb) { 43 | if (typeExpr != null) { 44 | sb.append(sourceNameRenderer.renderType(typeExpr)); 45 | sb.append(' '); 46 | } 47 | else { 48 | sb.append("__unknown_type").append(' '); 49 | } 50 | String varName = null; 51 | if (method != null && method.localVariables != null) { 52 | LocalVariableNode varNode = null; 53 | for (LocalVariableNode varNodeLoop : method.localVariables) { 54 | if (varNodeLoop.index == insn.var) { 55 | varNode = varNodeLoop; 56 | break; 57 | } 58 | } 59 | if (varNode != null) { 60 | varName = varNode.name; 61 | } 62 | } 63 | if (varName == null) { 64 | varName = "__local" + insn.var; 65 | } 66 | 67 | sb.append(varName); 68 | sb.append(" = "); 69 | exprValue.render(sb); 70 | sb.append(';'); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/ValueCategory.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.source; 2 | 3 | import org.objectweb.asm.Type; 4 | 5 | /** 6 | * Value-category of an expression on stack. 7 | */ 8 | public enum ValueCategory { 9 | 10 | /** category 1 (needs one element on stack) */ 11 | CAT1("cat1"), 12 | /** category 2 (needs two elements on stack: long or double */ 13 | CAT2("cat2"); 14 | 15 | /** display-text */ 16 | private final String shortName; 17 | 18 | /** 19 | * Constructor 20 | * @param shortName display-text 21 | */ 22 | private ValueCategory(final String shortName) { 23 | this.shortName = shortName; 24 | } 25 | 26 | /** 27 | * Gets a short display-text. 28 | * @return display-text 29 | */ 30 | public String getShortName() { 31 | return shortName; 32 | } 33 | 34 | /** 35 | * Checks if the type of an expression belongs to category 2 (long or double). 36 | * @param expr expression 37 | * @return true if long or double 38 | */ 39 | public static boolean isCat2(final ExpressionBase expr) { 40 | return lookup(expr) == ValueCategory.CAT2; 41 | } 42 | 43 | /** 44 | * Computes the category of the expression. 45 | * @param expr expression 46 | * @return value-category 47 | */ 48 | public static ValueCategory lookup(final ExpressionBase expr) { 49 | final ValueCategory category; 50 | final Type type = expr.getType(); 51 | if (type == Type.LONG_TYPE || type == Type.DOUBLE_TYPE) { 52 | category = ValueCategory.CAT2; 53 | } 54 | else { 55 | category = ValueCategory.CAT1; 56 | } 57 | return category; 58 | } 59 | 60 | /** 61 | * Gets the short-name. 62 | * @return short-name 63 | */ 64 | @Override 65 | public String toString() { 66 | return getShortName(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/source/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This package contains a (pseudocode)-decompiler respecting line-numbers in class-files. 3 | */ 4 | package org.rogmann.jsmud.source; -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/visitors/MessagePrinter.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.visitors; 2 | 3 | /** 4 | * Interface used for printing lines or stacktraces. 5 | */ 6 | public interface MessagePrinter { 7 | 8 | /** 9 | * Prints a message. 10 | * @param msg message to be printed 11 | */ 12 | void println(final String msg); 13 | 14 | /** 15 | * Dumps a stacktrace. 16 | * @param e throwable 17 | */ 18 | void dump(final Throwable e); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/visitors/VisitorFrame.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.visitors; 2 | 3 | import org.rogmann.jsmud.vm.MethodFrame; 4 | 5 | /** 6 | * Method-frame in a visitor. 7 | */ 8 | class VisitorFrame { 9 | 10 | Class clazz; 11 | MethodFrame frame; 12 | boolean isJreClass; 13 | int currLine = -1; 14 | int printedLine = -2; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/visitors/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This package contains examples of JVM-visitors. 3 | */ 4 | package org.rogmann.jsmud.visitors; -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/AtypeEnum.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | public enum AtypeEnum { 4 | T_BOOLEAN(4, boolean.class), 5 | T_CHAR(5, char.class), 6 | T_FLOAT(6, float.class), 7 | T_DOUBLE(7, double.class), 8 | T_BYTE(8, byte.class), 9 | T_SHORT(9, short.class), 10 | T_INT(10, int.class), 11 | T_LONG(11, long.class); 12 | 13 | /** atype-code */ 14 | private final int fAtype; 15 | /** primitive-type */ 16 | private final Class fAtypeClass; 17 | 18 | /** array of atypes */ 19 | private static final Class[] ATYPES = new Class[12]; 20 | 21 | static { 22 | for (AtypeEnum atypeEnum : values()) { 23 | ATYPES[atypeEnum.fAtype] = atypeEnum.fAtypeClass; 24 | } 25 | } 26 | 27 | /** 28 | * Constructor 29 | * @param atype atype-code 30 | * @param atypeClass primitive class of atype 31 | */ 32 | private AtypeEnum(final int atype, final Class atypeClass) { 33 | fAtype = atype; 34 | fAtypeClass = atypeClass; 35 | } 36 | 37 | /** 38 | * Gets the atype-code. 39 | * @return code 40 | */ 41 | public int getAtype() { 42 | return fAtype; 43 | } 44 | 45 | /** 46 | * Gets the primitive type. 47 | * @return type 48 | */ 49 | public Class getAtypeClass() { 50 | return fAtypeClass; 51 | } 52 | 53 | /** 54 | * Looks up the primitive type of a atype-code. 55 | * @param atypeCode atype-code 56 | * @return type 57 | */ 58 | public static Class lookupAtypeClass(final int atypeCode) { 59 | assert atypeCode >= 0 && atypeCode < ATYPES.length : "atype-code " + atypeCode + "out of range"; 60 | final Class classAtype = ATYPES[atypeCode]; 61 | assert classAtype != null : "unknown atype-code " + atypeCode; 62 | return classAtype; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/ClassExecutionFilter.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | /** 4 | * Interface for filtering classes to be simulated by the interpreter. 5 | */ 6 | public interface ClassExecutionFilter { 7 | 8 | /** 9 | * Checks if a given class should be interpreted by the simulator. 10 | * @param clazz class to be executed 11 | * @return true if the class should be interpreted by the simulator, false if the class should be executed by the underlying JVM 12 | */ 13 | boolean isClassToBeSimulated(final Class clazz); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/ClassProvider.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | /** 4 | * Optional interface to provide classes to a class-loader. 5 | * One usage might be to provide classes in a dex-file. 6 | */ 7 | public interface ClassProvider { 8 | 9 | /** 10 | * Checks for a class. 11 | * @param classLoader class-loader of the class 12 | * @param name class-name, e.g. "example.android.Activity" 13 | * @return provided class or null 14 | */ 15 | Class checkForClass(ClassLoader classLoader, String name); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/ClassRemapper.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | import org.objectweb.asm.ClassWriter; 4 | 5 | /** 6 | * Interface of a class-remapper. 7 | */ 8 | public interface ClassRemapper { 9 | 10 | /** 11 | * Puts an entry. 12 | * @param internalName internal class name of class to be mapped 13 | * @param mappedInternalName internal name of mapped class in mapped package 14 | */ 15 | void put(final String internalName, final String mappedInternalName); 16 | 17 | /** 18 | * Gets the mapped name of a class, if it is a mapped class. 19 | * @param className class-name 20 | * @return mapped name in case of a mapped class, otherwise null 21 | */ 22 | String remapName(final String className); 23 | 24 | /** 25 | * Maps a class to another package. 26 | * @param classWriter class-writer of unmapped class 27 | * @param className name of unmapped class 28 | * @return class-writer of mapped class (or same class-writer) 29 | */ 30 | ClassWriter remapClassWriter(ClassWriter classWriter, String className); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/ClassWithName.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | /** 4 | * Bytecode of a class and its -- possibly remapped -- name. 5 | */ 6 | public class ClassWithName { 7 | 8 | /** name of the class */ 9 | private final String name; 10 | /** bytecode */ 11 | private final byte[] bytecode; 12 | 13 | /** 14 | * Constructor 15 | * @param name name of the class 16 | * @param bytecode bytecode of the class 17 | */ 18 | public ClassWithName(String name, byte[] bytecode) { 19 | this.name = name; 20 | this.bytecode = bytecode; 21 | } 22 | 23 | /** 24 | * Gets the -- possibly remapped -- name of the class. 25 | * @return fully qualified name 26 | */ 27 | public String getName() { 28 | return name; 29 | } 30 | 31 | /** 32 | * Gets the bytecode. 33 | * @return bytecode 34 | */ 35 | public byte[] getBytecode() { 36 | return bytecode; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/ClassWriterCallSite.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | import org.objectweb.asm.ClassWriter; 4 | import org.objectweb.asm.Type; 5 | 6 | /** 7 | * ClassWriter for generating classes whose super-class is {@link Object}. 8 | * 9 | *

The class is helpful if there are errors in the bytecode-generation of CallSiteGenerator 10 | * when converting return-types in call-site-method.

11 | */ 12 | class ClassWriterCallSite extends ClassWriter { 13 | 14 | /** class-loader of the call-site's owner-class */ 15 | private ClassLoader classLoader; 16 | /** internal name of class to be generated */ 17 | private String classNameInt; 18 | 19 | /** 20 | * Constructs a new {@link ClassWriter} object. 21 | * @param flags option flags that can be used to modify the default behavior of this class. Must 22 | * be zero or more of {@link #COMPUTE_MAXS} and {@link #COMPUTE_FRAMES}. 23 | * @param classLoader class-loader of the call-site's owner-class 24 | * @param classNameInt internal name of the class to be generated 25 | */ 26 | public ClassWriterCallSite(final int flags, final ClassLoader classLoader, final String classNameInt) { 27 | super(flags); 28 | this.classLoader = classLoader; 29 | this.classNameInt = classNameInt; 30 | } 31 | 32 | /** 33 | * This method can check the super-class of classNameInt which 34 | * isn't inside the classloader yet because of generation-in-progress. 35 | * 36 | * @param type1 internal name of first class 37 | * @param type2 internal name of second class 38 | * @return internal name of common super-class 39 | */ 40 | @Override 41 | protected String getCommonSuperClass(final String type1, final String type2) { 42 | if (classNameInt.equals(type1) && classNameInt.equals(type2)) { 43 | return classNameInt; 44 | } 45 | if (classNameInt.equals(type1) || classNameInt.equals(type2)) { 46 | return Type.getInternalName(Object.class); 47 | } 48 | return super.getCommonSuperClass(type1, type2); 49 | } 50 | 51 | @Override 52 | protected ClassLoader getClassLoader() { 53 | return classLoader; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/FrameDisplay.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | /** 4 | * Helper-class for displaying types of frames. 5 | */ 6 | public enum FrameDisplay { 7 | /** An expanded frame. See ClassReader#EXPAND_FRAMES in asm. */ 8 | F_NEW(-1), 9 | /** A compressed frame with complete frame data. */ 10 | F_FULL(0), 11 | /** 12 | * A compressed frame where locals are the same as the locals in the previous frame, except that 13 | * additional 1-3 locals are defined, and with an empty stack. 14 | */ 15 | F_APPEND(1), 16 | /** 17 | * A compressed frame where locals are the same as the locals in the previous frame, except that 18 | * the last 1-3 locals are absent and with an empty stack. 19 | */ 20 | F_CHOP(2), 21 | /** 22 | * A compressed frame with exactly the same locals as the previous frame and with an empty stack. 23 | */ 24 | F_SAME(3), 25 | /** 26 | * A compressed frame with exactly the same locals as the previous frame and with a single value 27 | * on the stack. 28 | */ 29 | F_SAME1(4); 30 | 31 | /** type */ 32 | private final int type; 33 | 34 | /** 35 | * Internal constructor 36 | * @param type type-num 37 | */ 38 | private FrameDisplay(int type) { 39 | this.type = type; 40 | } 41 | 42 | /** 43 | * Gets the type-number. 44 | * @return type 45 | */ 46 | public int getType() { 47 | return type; 48 | } 49 | 50 | /** 51 | * Lookups a frame-type by the type-number. 52 | * @param type type-number 53 | * @return frame or null 54 | */ 55 | public static final FrameDisplay lookup(final int type) { 56 | for (FrameDisplay frame : values()) { 57 | if (frame.type == type) { 58 | return frame; 59 | } 60 | } 61 | return null; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/InvokeFlow.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | /** 4 | * Type of flow after invoking an invocation-handler before executing a 5 | * constructor or method. 6 | */ 7 | public enum InvokeFlow { 8 | 9 | /** normal continuation (same as null) */ 10 | CONTINUE(false), 11 | 12 | /** the method has been executed (or in some way simulated), execute next instruction */ 13 | EXEC_OK(false), 14 | 15 | /** the method has been executed but throw a caught exception, execute catch block */ 16 | EXEC_CATCH(true); 17 | 18 | /** continue-while-flag (false = normal continuation, true = execute catch block) */ 19 | private final boolean continueWhileFlag; 20 | 21 | /** 22 | * Internal constructor 23 | * @param continueWhileFlag continue-while-flag 24 | */ 25 | private InvokeFlow(final boolean continueWhileFlag) { 26 | this.continueWhileFlag = continueWhileFlag; 27 | } 28 | 29 | /** 30 | * Gets true if the while-loop executing instructions should be continue. 31 | * true to skip the instruction increment, execute catch block. 32 | * @return handle-exception flag 33 | */ 34 | public boolean isHandleException() { 35 | return continueWhileFlag; 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/JvmCallSite.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | /** 4 | * Contains the bootstrap-method of a INVOKEDYNAMIC-class. 5 | * An instance of this class is placed on the stack to simulate an INVOKEDYNAMIC-call. 6 | */ 7 | public class JvmCallSite { 8 | 9 | /** owner-class (calling-class) */ 10 | private String lambdaOwner; 11 | /** name of lambda-method */ 12 | private String lambdaName; 13 | /** signature of lambda-method */ 14 | private String lambdaDesc; 15 | 16 | /** 17 | * Constructor 18 | * @param lambdaOwner owner-class (calling-class) 19 | * @param lambdaName name of lambda-method 20 | * @param lambdaDesc signature of lambda-method 21 | */ 22 | public JvmCallSite(String lambdaOwner, String lambdaName, String lambdaDesc) { 23 | this.lambdaOwner = lambdaOwner.replace('/', '.'); 24 | this.lambdaName = lambdaName; 25 | this.lambdaDesc = lambdaDesc; 26 | } 27 | 28 | /** 29 | * Gets the owner-class (calling-class) 30 | * @return full qualified class-name 31 | */ 32 | public String getLambdaOwner() { 33 | return lambdaOwner; 34 | } 35 | 36 | /** 37 | * Gets the name of the lambda-method. 38 | * @return method-name 39 | */ 40 | public String getLambdaName() { 41 | return lambdaName; 42 | } 43 | 44 | /** 45 | * Gets the signature of the lambda-method. 46 | * @return method-signature 47 | */ 48 | public String getLambdaDesc() { 49 | return lambdaDesc; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/JvmCallSiteMarker.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | /** 4 | * Marker-interface for call-site-proxies. 5 | */ 6 | public interface JvmCallSiteMarker { 7 | 8 | // no methods yet 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/JvmException.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | /** 4 | * Unexpected exception while executing bytecode. 5 | */ 6 | public class JvmException extends RuntimeException { 7 | 8 | /** serialization-id */ 9 | private static final long serialVersionUID = 20210501L; 10 | 11 | /** 12 | * Constructor 13 | * @param message message-text 14 | */ 15 | public JvmException(final String message) { 16 | super(message); 17 | } 18 | 19 | /** 20 | * Constructor 21 | * @param message message-text 22 | * @param cause cause of the exception 23 | */ 24 | public JvmException(final String message, final Throwable cause) { 25 | super(message, cause); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/JvmExecutionVisitorProvider.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | /** 4 | * Interface of a {@link JvmExecutionVisitor}-provider. 5 | */ 6 | public interface JvmExecutionVisitorProvider { 7 | 8 | /** 9 | * Creates a JVM-execution-visitor 10 | * @param vm interface of VM-simulation 11 | * @param currentThread current thread 12 | * @param visitorParent visitor of parent-thread or null 13 | * @return execution-visitor 14 | */ 15 | JvmExecutionVisitor create(ClassRegistry vm, Thread currentThread, JvmExecutionVisitor visitorParent); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/JvmInvocationHandler.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | import org.objectweb.asm.tree.MethodInsnNode; 4 | 5 | /** 6 | * This interfaces can be used to modify method-executions. 7 | */ 8 | public interface JvmInvocationHandler { 9 | 10 | /** 11 | * Called before an invocation of a static method. 12 | * @param methodFrame current method-frame 13 | * @param mi method-invocation-instruction 14 | * @param stack current operand-stack 15 | * @return how to continue execution 16 | * @throws Exception in case of an unhandled exception 17 | */ 18 | InvokeFlow preprocessStaticCall(MethodFrame methodFrame, MethodInsnNode mi, OperandStack stack) throws Throwable; 19 | 20 | /** 21 | * Called before an invocation of an instance-method (or constructor). 22 | * This offers the possibility to replace the invocation with an own implementation, 23 | * return InvokeFlow.EXEC_OK in that case. 24 | * 25 | * @param methodFrame current method-frame 26 | * @param mi method-invocation-instruction 27 | * @param objRefStack instance-object 28 | * @param stack current operand-stack 29 | * @return how to continue execution 30 | * @throws Throwable in case of an unhandled exception 31 | */ 32 | InvokeFlow preprocessInstanceCall(MethodFrame methodFrame, MethodInsnNode mi, 33 | Object objRefStack, OperandStack stack) throws Throwable; 34 | 35 | /** 36 | * Called before an invocation of an INVOKESPECIAL-execution (e.g. constructor-call). 37 | * @param mi method-invocation-instruction 38 | * @param frame current method-frame 39 | * @param stack current operand-stack 40 | */ 41 | void preprocessInvokeSpecialCall(MethodInsnNode mi, MethodFrame frame, OperandStack stack); 42 | 43 | /** 44 | * Called after a invocation of a method (or constructor). 45 | * @param frame current method-frame 46 | * @param mi method-invocation-instruction 47 | * @param stack current operand-stack 48 | * @return continue-while-flag 49 | * @throws Throwable in case of an unhandled throwable 50 | */ 51 | boolean postprocessCall(MethodFrame frame, MethodInsnNode mi, OperandStack stack) throws Throwable; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/JvmReturnAddress.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | /** 4 | * value-object returnAddress used in instructions JSR and RET. 5 | * 6 | *

JSR and RET where used in pre Java 1.6-finally-implementations.

7 | *

Example: org.apache.xerces.parsers.XML11Configuration

8 | */ 9 | public class JvmReturnAddress { 10 | 11 | /** index of an instruction */ 12 | private final int address; 13 | 14 | public JvmReturnAddress(final int address) { 15 | this.address = address; 16 | } 17 | 18 | /** 19 | * Gets the address of an instruction. 20 | * @return address 21 | */ 22 | public int getAddress() { 23 | return address; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/JvmUncaughtException.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Uncaught exception thrown in a method. 8 | */ 9 | public class JvmUncaughtException extends JvmException { 10 | 11 | /** serialization-id */ 12 | private static final long serialVersionUID = 20210811L; 13 | 14 | /** stacktrace */ 15 | private final List simStacktrace = new ArrayList<>(); 16 | 17 | /** 18 | * Constructor 19 | * @param message message-text 20 | * @param cause uncaught exception 21 | */ 22 | public JvmUncaughtException(final String message, final Throwable cause) { 23 | super(message, cause); 24 | } 25 | 26 | /** 27 | * Gets the simulated stacktrace. 28 | * @return stacktrace 29 | */ 30 | public List getSimStacktrace() { 31 | return simStacktrace; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/MethodExecutor.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | /** 4 | * Interface of a generic method executor. 5 | */ 6 | public interface MethodExecutor { 7 | 8 | /** 9 | * Executes or simulates a method execution. 10 | * @param executable implementation dependent object describing the method to be simulated 11 | * @param obj object-instance 12 | * @param args JVM-arguments 13 | * @return result 14 | */ 15 | Object execute(final Object executable, final Object obj, final Object[] args); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/MockMethods.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | import java.security.PrivilegedAction; 4 | import java.security.PrivilegedActionException; 5 | import java.security.PrivilegedExceptionAction; 6 | 7 | /** 8 | * Dummy-implementation of some JVM-methods. 9 | */ 10 | public class MockMethods { 11 | 12 | /** 13 | * Don't allow anyone to instantiate a MockMethods 14 | */ 15 | private MockMethods() { } 16 | 17 | /** 18 | * Execute an action as in java.security.AccessControlContext but without checks. 19 | * @param return-type 20 | * @param action action to be executed 21 | * @return return-value 22 | */ 23 | public static T doPrivileged(PrivilegedAction action) { 24 | return action.run(); 25 | } 26 | 27 | /** 28 | * Execute an action as in java.security.AccessControlContext but without checks. 29 | * @param return-type 30 | * @param action action to be executed 31 | * @return return-value 32 | * @throws PrivilegedActionException in case of an exception 33 | */ 34 | public static T doPrivileged(PrivilegedExceptionAction action) throws PrivilegedActionException { 35 | try { 36 | return action.run(); 37 | } 38 | catch (RuntimeException e) { 39 | throw e; 40 | } 41 | catch (Exception e) { 42 | throw new PrivilegedActionException(e); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/NativeMethodExecutor.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.InvocationTargetException; 5 | import java.lang.reflect.Method; 6 | 7 | /** 8 | * Interface to execute methods without jsmud-analysis. 9 | * A typical implementation is to use reflection. 10 | * This interface can be used to do the execution in a different simulation-engine. 11 | */ 12 | public interface NativeMethodExecutor { 13 | 14 | /** 15 | * Executes a constructor. 16 | * @param constructor constructor to be executed 17 | * @param aJvmArgs parameters of the constructor (JVM arguments) 18 | * @return new instance 19 | * @throws IllegalArgumentException in case of an illegal argument 20 | * @throws IllegalAccessException in case of an access-violation 21 | * @throws InstantiationException in case of an instantiation-error 22 | * @throws InvocationTargetException in case of an error while execution 23 | */ 24 | Object executeConstructorNative(Constructor constructor, final Object[] aJvmArgs) 25 | throws IllegalAccessException, IllegalArgumentException, InstantiationException, InvocationTargetException; 26 | 27 | /** 28 | * Executes a method. 29 | * @param method method to be executed 30 | * @param objRef object-instance 31 | * @param aJvmArgs parameters of the method (JVM arguments) 32 | * @return return-value 33 | * @throws IllegalAccessException in case of an access-violation 34 | * @throws IllegalArgumentException in case of an illegal argument 35 | * @throws InvocationTargetException in case of an error while execution 36 | */ 37 | Object executeMethodNative(Method method, Object objRef, final Object[] aJvmArgs) 38 | throws IllegalAccessException, IllegalArgumentException, InvocationTargetException; 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/NativeMethodExecutorReflection.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.InvocationTargetException; 5 | import java.lang.reflect.Method; 6 | 7 | /** 8 | * Execution of methods via reflection. 9 | */ 10 | public class NativeMethodExecutorReflection implements NativeMethodExecutor { 11 | 12 | /** {@inheritDoc} */ 13 | @Override 14 | public Object executeConstructorNative(Constructor constructor, Object[] aJvmArgs) 15 | throws IllegalAccessException, IllegalArgumentException, InstantiationException, InvocationTargetException { 16 | constructor.setAccessible(true); 17 | final Object newObj = constructor.newInstance(aJvmArgs); 18 | return newObj; 19 | } 20 | 21 | /** {@inheritDoc} */ 22 | @Override 23 | public Object executeMethodNative(Method methodExec, Object objRef, Object[] initargs) 24 | throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { 25 | final Object returnObj; 26 | methodExec.setAccessible(true); 27 | returnObj = methodExec.invoke(objRef, initargs); 28 | return returnObj; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/ObjectMonitor.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | public interface ObjectMonitor { 4 | 5 | /** 6 | * Enters a monitor. 7 | * @param objMonitor monitor-object 8 | * @return current monitor-counter 9 | */ 10 | int enterMonitor(final Object objMonitor); 11 | 12 | /** 13 | * Exits a monitor. 14 | * @param objMonitor monitor-object 15 | * @return current monitor-counter 16 | */ 17 | int exitMonitor(final Object objMonitor); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/UninitializedInstance.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | import java.util.concurrent.atomic.AtomicLong; 4 | 5 | /** 6 | * Uninitialized instance of a class, placed on the stack in case of a new class-instance. 7 | */ 8 | public class UninitializedInstance { 9 | 10 | /** Id-Counter */ 11 | private static final AtomicLong COUNTER = new AtomicLong(); 12 | 13 | /** type */ 14 | private final Class fType; 15 | 16 | /** Id */ 17 | private final long fId; 18 | 19 | /** 20 | * Constructor 21 | * @param type type of the uninitialized class 22 | */ 23 | public UninitializedInstance(final Class type) { 24 | fType = type; 25 | fId = COUNTER.incrementAndGet(); 26 | } 27 | 28 | /** 29 | * Gets the id of the instance. 30 | * @return id 31 | */ 32 | public long getId() { 33 | return fId; 34 | } 35 | 36 | /** 37 | * Gets the type of the uninitialized class. 38 | * @return class 39 | */ 40 | public Class getType() { 41 | return fType; 42 | } 43 | 44 | /** {@inheritDoc} */ 45 | @Override 46 | public String toString() { 47 | final StringBuilder sb = new StringBuilder(20); 48 | sb.append(getClass().getSimpleName()); 49 | sb.append('{'); 50 | sb.append(fId); 51 | sb.append(':'); 52 | sb.append(fType); 53 | sb.append('}'); 54 | return sb.toString(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/rogmann/jsmud/vm/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This package contains classes to execute bytecode. 3 | * 4 | *

The "JVM" is located in the {@link org.rogmann.jsmud.vm.VM} implementing class {@link org.rogmann.jsmud.vm.ClassRegistry}.

5 | *

The "bytecode execution" is located in the class {@link org.rogmann.jsmud.vm.MethodFrame}.

6 | */ 7 | package org.rogmann.jsmud.vm; 8 | -------------------------------------------------------------------------------- /src/main/res_license/META-INF/NOTICE: -------------------------------------------------------------------------------- 1 | jsmud-analysis 2 | Copyright 2021-2022 Sascha Rogmann 3 | 4 | This product includes software developed by 5 | * Sascha Rogmann (https://github.com/srogmann/jsmud-analysis/, http://www.rogmann.org/) 6 | 7 | License: 8 | * Apache License 2.0 9 | -------------------------------------------------------------------------------- /src/main/res_license_all/META-INF/NOTICE: -------------------------------------------------------------------------------- 1 | jsmud-analysis-all 2 | Copyright 2021-2022 Sascha Rogmann (except org.rogmann.jsmud.shadow.asm.*) 3 | 4 | This product includes software developed by 5 | * Sascha Rogmann (https://github.com/srogmann/jsmud-analysis/, http://www.rogmann.org/) 6 | * org.rogmann.jsmud.shadow.asm.*: OW2 (Eric Bruneton et al., https://asm.ow2.io/) 7 | 8 | Licenses: 9 | * Apache License 2.0 10 | * org.rogmann.jsmud.shadow.asm.*: 3-Clause BSD License 11 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/dumps/ByteArrayWriter.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.dumps; 2 | 3 | import java.io.BufferedOutputStream; 4 | import java.io.File; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.io.OutputStream; 8 | import java.nio.charset.StandardCharsets; 9 | 10 | /** 11 | * Reads a byte array dump (e.g. "buf = [72, 105, 33]") and writes the raw data into a file. 12 | */ 13 | public class ByteArrayWriter { 14 | 15 | /** 16 | * main-method. 17 | * @param args output-file 18 | */ 19 | public static void main(String[] args) { 20 | if (args.length != 1) { 21 | throw new IllegalArgumentException("Usage: output-file"); 22 | } 23 | final File fileOut = new File(args[0]); 24 | 25 | final StringBuilder sb = new StringBuilder(200); 26 | final byte[] buf = new byte[500]; 27 | while (true) { 28 | int len; 29 | try { 30 | len = System.in.read(buf); 31 | } catch (IOException e) { 32 | throw new RuntimeException("IO-error while reading STDIN", e); 33 | } 34 | if (len <= 0) { 35 | break; 36 | } 37 | // ISO-8859-1: We are interested in ASCII only. 38 | sb.append(new String(buf, 0, len, StandardCharsets.ISO_8859_1)); 39 | } 40 | final String sBytes = sb.toString(); 41 | final int idxStart = sBytes.indexOf('['); 42 | if (idxStart == -1) { 43 | throw new IllegalArgumentException("'[' is missing: " + sBytes); 44 | } 45 | final int idxEnd = sBytes.indexOf(']', idxStart + 1); 46 | if (idxEnd == -1) { 47 | throw new IllegalArgumentException("']' is missing: " + sBytes); 48 | } 49 | final String[] aBytes = sBytes.substring(idxStart + 1, idxEnd).split(","); 50 | try (final OutputStream os = new BufferedOutputStream(new FileOutputStream(fileOut))) { 51 | for (String sByte : aBytes) { 52 | final int b = Integer.parseInt(sByte.trim()); 53 | if (b < -128 || b > 127) { 54 | throw new IllegalArgumentException("Unexpected byte: " + sByte); 55 | } 56 | os.write(b); 57 | } 58 | } 59 | catch (IOException e) { 60 | throw new RuntimeException(String.format("IO-exception while writing (%s)", fileOut), e); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/dumps/ConnectionStreamListener.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.dumps; 2 | 3 | /** 4 | * Listener on a stream. 5 | */ 6 | public interface ConnectionStreamListener { 7 | 8 | /** 9 | * Adds a block of bytes. 10 | * @param buf buffer 11 | * @param offset offset of block in buffer 12 | * @param len length of block in buffer 13 | */ 14 | void addPacket(final byte[] buf, final int offset, final int len); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/dumps/ConnectionStreamProxy.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.dumps; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.util.concurrent.atomic.AtomicBoolean; 7 | import java.util.function.BiConsumer; 8 | import java.util.function.Consumer; 9 | 10 | /** 11 | * Watches a stream and informs a listener about each packet transferred. 12 | */ 13 | public class ConnectionStreamProxy implements Runnable { 14 | 15 | /** input-stream */ 16 | private final InputStream is; 17 | /** output-stream */ 18 | private final OutputStream os; 19 | 20 | /** listener */ 21 | private ConnectionStreamListener listener; 22 | 23 | /** stop-flag */ 24 | private AtomicBoolean shouldStop; 25 | 26 | /** exception-listener */ 27 | private final Consumer consumerIoExc; 28 | 29 | /** statistics-consumer */ 30 | private final BiConsumer consumerStats; 31 | 32 | /** buffer */ 33 | private final byte[] buf = new byte[65536]; 34 | 35 | /** 36 | * Constructor 37 | * @param is input-stream 38 | * @param os output-stream 39 | * @param listener listener on stream 40 | * @param shouldStop stop-flag 41 | * @param consumerIoExc exception-consumer 42 | * @param consumerStats statistics-consumer (number of packets, sum of sizes) 43 | */ 44 | ConnectionStreamProxy(final InputStream is, final OutputStream os, 45 | final ConnectionStreamListener listener, 46 | final AtomicBoolean shouldStop, 47 | final Consumer consumerIoExc, 48 | final BiConsumer consumerStats) { 49 | this.is = is; 50 | this.os = os; 51 | this.listener = listener; 52 | this.shouldStop = shouldStop; 53 | this.consumerIoExc = consumerIoExc; 54 | this.consumerStats = consumerStats; 55 | } 56 | 57 | /** {@inheritDoc} */ 58 | @Override 59 | public void run() { 60 | long numPackets = 0; 61 | long sumSize = 0; 62 | while (!shouldStop.get()) { 63 | int len; 64 | try { 65 | len = is.read(buf); 66 | } catch (IOException e) { 67 | shouldStop.set(true); 68 | if (shouldStop.get() || e.getMessage().contains("Connection reset")) { 69 | break; 70 | } 71 | consumerIoExc.accept(new IOException(String.format("IO-error while reading at offset %d", 72 | Long.valueOf(sumSize)), e)); 73 | break; 74 | } 75 | if (len <= 0) { 76 | break; 77 | } 78 | numPackets++; 79 | listener.addPacket(buf, 0, len); 80 | try { 81 | os.write(buf, 0, len); 82 | } catch (IOException e) { 83 | shouldStop.set(true); 84 | if (shouldStop.get()) { 85 | break; 86 | } 87 | consumerIoExc.accept(new IOException(String.format("IO-error while writing at offset %d", 88 | Long.valueOf(sumSize)), e)); 89 | break; 90 | } 91 | sumSize += len; 92 | } 93 | consumerStats.accept(Long.valueOf(numPackets), Long.valueOf(sumSize)); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/dumps/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing dump-tools. 3 | * 4 | *

JdwpProxy can be used to analyze JDWP-connections.

5 | */ 6 | package org.rogmann.jsmud.dumps; -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/BytecodeSampleMain.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.File; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.io.OutputStreamWriter; 8 | import java.nio.charset.StandardCharsets; 9 | 10 | import org.rogmann.jsmud.source.SourceFileWriterDecompile; 11 | 12 | public class BytecodeSampleMain { 13 | 14 | /** 15 | * Prints the class BytecodeSample. 16 | * @param args output-file 17 | */ 18 | public static void main(String[] args) { 19 | if (args.length == 0) { 20 | throw new IllegalArgumentException("Usage: .java-output-file"); 21 | } 22 | final File fileOutput = new File(args[0]); 23 | try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileOutput), StandardCharsets.UTF_8))) { 24 | final Class clazz = BytecodeSample.class; 25 | SourceFileWriterDecompile.writeSource(clazz, clazz.getClassLoader(), bw); 26 | } 27 | catch (IOException e) { 28 | throw new RuntimeException("IO-exception while writing " + fileOutput, e); 29 | } 30 | System.out.println("File written: " + fileOutput); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/ClassARenamed.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test; 2 | 3 | import java.util.function.Supplier; 4 | 5 | /** 6 | * Test-class to be used in a different class-loader. 7 | * This functions creates a supplier using ClassB. 8 | */ 9 | public class ClassARenamed implements Supplier { 10 | 11 | @Override 12 | public String get() { 13 | final Supplier supplier = () -> ClassBRenamed.getName(); 14 | return supplier.get(); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/ClassBRenamed.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test; 2 | 3 | public class ClassBRenamed { 4 | 5 | public static String getName() { 6 | return ClassBRenamed.class.getSimpleName(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/ConstructorNesting.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test; 2 | 3 | /** 4 | * Example of constructor-nesting. 5 | */ 6 | public class ConstructorNesting { 7 | 8 | private final int a; 9 | private final char b; 10 | private final boolean c; 11 | private int counter = 0; 12 | 13 | ConstructorNesting(int a, char b, boolean c) { 14 | this.a = a; 15 | this.b = b; 16 | this.c = c; 17 | counter = (counter * 10) + 1; 18 | } 19 | 20 | ConstructorNesting(int a, char b) { 21 | this(a, b, true); 22 | counter = (counter * 10) + 2; 23 | } 24 | 25 | public ConstructorNesting(int a) { 26 | this(a, 'E'); 27 | counter = (counter * 10) + 1; 28 | } 29 | 30 | public static String test(int n) { 31 | ConstructorNesting cn = new ConstructorNesting(n); 32 | return cn.toString(); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | final StringBuilder sb = new StringBuilder(); 38 | sb.append(a); 39 | sb.append(b); 40 | sb.append(c); 41 | sb.append('#'); 42 | sb.append(counter); 43 | return sb.toString(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/DebuggerTestMethods.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.lang.reflect.Method; 5 | 6 | public class DebuggerTestMethods { 7 | 8 | public static void main(String[] args) { 9 | //localVariables(); 10 | stepIntoSSLContext(); 11 | } 12 | 13 | public static int localVariables() { 14 | int a = 1; 15 | { 16 | char b = ' '; 17 | a += b; 18 | } 19 | { 20 | short b = (short) 32; 21 | a += b; 22 | } 23 | { 24 | int b = 32; 25 | a += b; 26 | } 27 | return a; 28 | } 29 | 30 | public static void stepIntoSSLContext() { 31 | final Class classDepr; 32 | try { 33 | classDepr = Class.forName("com.sun.net.ssl.SSLContext"); 34 | } catch (ClassNotFoundException e) { 35 | throw new RuntimeException("Can't load deprecated class", e); 36 | } 37 | try { 38 | final Method method = classDepr.getDeclaredMethod("getInstance", String.class); 39 | method.invoke(classDepr, "INVALID_TLS"); 40 | } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException 41 | | InvocationTargetException e) { 42 | throw new RuntimeException("Reflection-error while executing getInstance", e); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/DefInterface.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test; 2 | 3 | public interface DefInterface extends DefInterfaceSuper { 4 | @Override 5 | default String addSuffix(String s) { 6 | return s + "Child"; 7 | } 8 | 9 | } -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/DefInterfaceImpl.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test; 2 | 3 | public class DefInterfaceImpl implements DefInterface, DefInterfaceSuper2 4 | { 5 | @Override 6 | public String addSuffix(String s) { 7 | return "Impl" + DefInterface.super.getName(); 8 | } 9 | 10 | @Override 11 | public String getName() { 12 | return "Impl" + DefInterfaceSuper2.super.getName() + DefInterface.super.addSuffix("B-"); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/DefInterfaceSuper.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test; 2 | 3 | public interface DefInterfaceSuper { 4 | default String addSuffix(String s) { 5 | return s + "Super"; 6 | } 7 | 8 | default String getName() { 9 | return "DefS"; 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/DefInterfaceSuper2.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test; 2 | 3 | public interface DefInterfaceSuper2 { 4 | default String addSuffix(String s) { 5 | return s + "Super2"; 6 | } 7 | 8 | default String getName() { 9 | return "DefS2"; 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/GenerateJUnitTests.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.PrintStream; 6 | import java.util.List; 7 | 8 | import org.objectweb.asm.ClassReader; 9 | import org.objectweb.asm.Type; 10 | import org.objectweb.asm.tree.AnnotationNode; 11 | import org.objectweb.asm.tree.ClassNode; 12 | import org.objectweb.asm.tree.MethodNode; 13 | 14 | /** 15 | * Generated JUnit-test-methods. 16 | */ 17 | public class GenerateJUnitTests { 18 | 19 | /** 20 | * Main entry 21 | * @param args no arguments 22 | */ 23 | public static void main(String[] args) { 24 | final PrintStream psOut = System.out; 25 | final Class classJvmTests = JvmTests.class; 26 | final String resName = classJvmTests.getSimpleName() + ".class"; 27 | try (InputStream is = JvmTests.class.getResourceAsStream(resName)) { 28 | final ClassReader cr = new ClassReader(is); 29 | final ClassNode node = new ClassNode(); 30 | cr.accept(node, 0); 31 | final String descJsmudTest = Type.getDescriptor(JsmudTest.class); 32 | int orderNo = 0; 33 | for (final MethodNode methodNode : node.methods) { 34 | boolean isJsmudTest = false; 35 | List visibleAnnotations = methodNode.visibleAnnotations; 36 | if (visibleAnnotations == null) { 37 | continue; 38 | } 39 | for (AnnotationNode ann : visibleAnnotations) { 40 | if (descJsmudTest.equals(ann.desc)) { 41 | isJsmudTest = true; 42 | } 43 | } 44 | if (!isJsmudTest) { 45 | continue; 46 | } 47 | psOut.println(String.format("\t/** JUnit-Test of method {@link %s#%s()} */", 48 | classJvmTests.getSimpleName(), methodNode.name)); 49 | psOut.println("\t@Test"); 50 | psOut.println(String.format("\t@Order(%d)", Integer.valueOf(++orderNo))); 51 | psOut.println(String.format("\tpublic void test%s() {", toUpperFirst(methodNode.name))); 52 | psOut.println("\t\tfinal JvmTests jvmTests = new JvmTests();"); 53 | psOut.println("\t\tfinal Runnable runnable = new Runnable() {"); 54 | psOut.println("\t\t\t@Override"); 55 | psOut.println("\t\t\tpublic void run() {"); 56 | psOut.println(String.format("\t\t\t\tjvmTests.%s();", methodNode.name)); 57 | psOut.println("\t\t\t}"); 58 | psOut.println("\t\t};"); 59 | psOut.println("\t\texecute(runnable);"); 60 | psOut.println("\t}"); 61 | psOut.println(); 62 | } 63 | } 64 | catch (IOException e) { 65 | throw new RuntimeException("IO-error while reading " + resName, e); 66 | } 67 | } 68 | 69 | /** 70 | * Changes the first letter of a name into upper-case. 71 | * @param s name 72 | * @return name, first letter upper-case 73 | */ 74 | static String toUpperFirst(final String s) { 75 | return new StringBuilder(s.length()) 76 | .append(Character.toUpperCase(s.charAt(0))) 77 | .append(s.substring(1, s.length())) 78 | .toString(); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/JsmudTest.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test; 2 | 3 | import static java.lang.annotation.ElementType.METHOD; 4 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 5 | 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Marker-annotation of a method which tests JSMUD. 11 | */ 12 | @Retention(RUNTIME) 13 | @Target({METHOD}) 14 | public @interface JsmudTest { 15 | 16 | String description() default "junit-test of jsmud-analysis"; 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/MemoryTestMain.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test; 2 | 3 | import java.io.PrintStream; 4 | import java.util.function.Supplier; 5 | 6 | import org.rogmann.jsmud.vm.ClassExecutionFilter; 7 | import org.rogmann.jsmud.vm.JvmHelper; 8 | 9 | public class MemoryTestMain { 10 | 11 | public static void main(String[] args) { 12 | final PrintStream psOut = System.out; 13 | final Supplier supplier = new Supplier() { 14 | @Override 15 | public Long get() { 16 | long sum = 0; 17 | for (int i = 0; i < 10000; i++) { 18 | final byte[] buf = new byte[1048576]; 19 | sum += buf.length; 20 | } 21 | return Long.valueOf(sum); 22 | } 23 | }; 24 | final ClassExecutionFilter filter = JvmHelper.createNonJavaExecutionFilter(); 25 | final Long result = JvmHelper.executeSupplier(supplier, filter, psOut); 26 | psOut.println("Result: " + result); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/NativeExecutor.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test; 2 | 3 | import java.util.concurrent.Callable; 4 | 5 | /** 6 | * Helper-class to execute a method without JSMUD. 7 | * This has to be configured in a filter. 8 | */ 9 | public class NativeExecutor { 10 | 11 | /** 12 | * Executes a callable. 13 | * @param callable callable 14 | * @param return-type of the callable 15 | * @return result 16 | */ 17 | public static T executeCallable(Callable callable) { 18 | try { 19 | return callable.call(); 20 | } catch (Exception e) { 21 | throw new RuntimeException("Exception while executing callable", e); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/SampleClassMain.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test; 2 | 3 | /** 4 | * Calls SampleClass. 5 | */ 6 | public class SampleClassMain { 7 | 8 | /** 9 | * Entry-point, calls SampleClass. 10 | * @param args no arguments 11 | */ 12 | public static void main(String[] args) { 13 | SampleClass.example(3, 5); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/SwingTest.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.event.ActionEvent; 5 | import java.awt.event.ActionListener; 6 | 7 | import javax.swing.AbstractButton; 8 | import javax.swing.JButton; 9 | import javax.swing.JFrame; 10 | import javax.swing.JPanel; 11 | import javax.swing.JTextField; 12 | import javax.swing.SwingUtilities; 13 | 14 | public class SwingTest extends JPanel implements ActionListener { 15 | /** Serialization-id */ 16 | private static final long serialVersionUID = 1L; 17 | 18 | protected final JButton button1; 19 | protected final JButton button2; 20 | protected final JTextField textfield; 21 | 22 | public SwingTest() { 23 | button1 = new JButton("Test-Button 1"); 24 | button1.setVerticalTextPosition(AbstractButton.CENTER); 25 | button1.setHorizontalTextPosition(AbstractButton.LEADING); 26 | button1.setActionCommand("Button 1"); 27 | button1.addActionListener(this); 28 | add(button1); 29 | 30 | button2 = new JButton("Test-Button 2"); 31 | button2.setVerticalTextPosition(AbstractButton.CENTER); 32 | button2.setHorizontalTextPosition(AbstractButton.LEADING); 33 | button2.setActionCommand("Button 2"); 34 | button2.addActionListener(this); 35 | add(button2); 36 | 37 | textfield = new JTextField("[...]", 20); 38 | add(textfield); 39 | } 40 | 41 | public static void main(final String[] args) { 42 | SwingUtilities.invokeLater(new Runnable() { 43 | @Override 44 | public void run() { 45 | final JFrame frame = new JFrame("SwingDemo"); 46 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 47 | 48 | SwingTest newContentPane = new SwingTest(); 49 | newContentPane.setOpaque(true); //content panes must be opaque 50 | frame.setContentPane(newContentPane); 51 | 52 | frame.setMinimumSize(new Dimension(400, 200)); 53 | frame.pack(); 54 | frame.setVisible(true); 55 | } 56 | }); 57 | } 58 | 59 | @Override 60 | public void actionPerformed(ActionEvent e) { 61 | textfield.setText("Action: " + e.getActionCommand()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Tests and samples. 3 | * 4 | *

The package is not equal to the jsmud-package because the tests don't belong to jsmud itself.

5 | */ 6 | package org.rogmann.jsmud.test; -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test2/BuilderInTest2.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test2; 2 | 3 | public class BuilderInTest2 { 4 | 5 | /** 6 | * Build an instance of ClassInTest2 via {@link Class#newInstance()}. 7 | * @return instance 8 | */ 9 | public static ClassInTest2 build() { 10 | final Class clazz = ClassInTest2.class; 11 | try { 12 | final ClassInTest2 obj = clazz.newInstance(); 13 | return obj; 14 | } catch (InstantiationException | IllegalAccessException e) { 15 | throw new RuntimeException("Can't build class " + clazz, e); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test2/ClassInTest2.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.test2; 2 | 3 | /** 4 | * Test-class. 5 | */ 6 | public class ClassInTest2 { 7 | 8 | private int a; 9 | 10 | /** Constructor with package-visibility */ 11 | ClassInTest2() { 12 | a = 2; 13 | } 14 | 15 | public int getA() { 16 | return a; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/test2/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Another test-package to test constructors with package-visibility. 3 | */ 4 | package org.rogmann.jsmud.test2; -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/util/GenerateCaseStmt.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.util; 2 | 3 | import java.io.PrintStream; 4 | 5 | import org.rogmann.jsmud.vm.OpcodeDisplay; 6 | 7 | /** 8 | * Generates a switch-statement. 9 | */ 10 | public class GenerateCaseStmt { 11 | 12 | /** 13 | * Entry-method. 14 | * @param args none 15 | */ 16 | public static void main(String[] args) { 17 | PrintStream psOut = System.out; 18 | psOut.println("switch (opcode) {"); 19 | final int lastOpcode = 199; 20 | for (int op = 0; op < lastOpcode; op++) { 21 | psOut.println(String.format("case Opcodes.%s: // 0x%02x", 22 | OpcodeDisplay.lookup(op), Integer.valueOf(op))); 23 | psOut.println(String.format("\tthrow new UnsupportedOperationException(\"Opcode 0x%02x (%s) not yet supported.", 24 | Integer.valueOf(op), OpcodeDisplay.lookup(op))); 25 | psOut.println("\tbreak;"); 26 | } 27 | psOut.println("default:"); 28 | psOut.println(" break;"); 29 | psOut.println("}"); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/util/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Util-classes. 3 | */ 4 | package org.rogmann.jsmud.util; -------------------------------------------------------------------------------- /src/test/java/org/rogmann/jsmud/vm/SimpleWeakIdentityHashMapTest.java: -------------------------------------------------------------------------------- 1 | package org.rogmann.jsmud.vm; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | /** 7 | * JUnit-tests of {@link SimpleWeakIdentityHashMap}. 8 | */ 9 | @SuppressWarnings("static-method") 10 | class SimpleWeakIdentityHashMapTest { 11 | 12 | @Test 13 | void testSimple() { 14 | final SimpleWeakIdentityHashMap map = new SimpleWeakIdentityHashMap<>(); 15 | map.put("A", Integer.valueOf(1)); 16 | map.put("B", Integer.valueOf(2)); 17 | map.put("C", Integer.valueOf(3)); 18 | map.put("D", Integer.valueOf(4)); 19 | Assertions.assertEquals(Integer.valueOf(1), map.get("A")); 20 | Assertions.assertEquals(Integer.valueOf(2), map.get("B")); 21 | Assertions.assertEquals(Integer.valueOf(3), map.get("C")); 22 | Assertions.assertEquals(Integer.valueOf(4), map.get("D")); 23 | Assertions.assertEquals(null, map.get("E")); 24 | } 25 | 26 | @Test 27 | void testLargeMap() { 28 | final SimpleWeakIdentityHashMap map = new SimpleWeakIdentityHashMap<>(); 29 | int numObj = 65536; 30 | int numHashCols = 3; 31 | final String[][] keys = new String[numHashCols][numObj]; 32 | for (int h = 0; h < numHashCols; h++) { 33 | final int range = numHashCols * 1000000; 34 | for (int i = 0; i < numObj; i++) { 35 | // We use new String to get numHashCols different instances with the same hash-code. 36 | final String key = new String(Integer.toString(i)); 37 | keys[h][i] = key; 38 | map.put(key, Integer.valueOf(range + i)); 39 | } 40 | } 41 | for (int h = 0; h < numHashCols; h++) { 42 | final int range = numHashCols * 1000000; 43 | for (int i = 0; i < numObj; i++) { 44 | final String key = keys[h][i]; 45 | Assertions.assertEquals(Integer.valueOf(range + i), map.get(key), 46 | "h=" + h + ", i=" + i); 47 | } 48 | } 49 | } 50 | 51 | } 52 | --------------------------------------------------------------------------------