├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── classfile ├── access_flags.go ├── attr_bootstrap_methods.go ├── attr_code.go ├── attr_constant_value.go ├── attr_enclosing_method.go ├── attr_exceptions.go ├── attr_inner_classes.go ├── attr_line_number_table.go ├── attr_local_variable_table.go ├── attr_local_variable_type_table.go ├── attr_markers.go ├── attr_module.go ├── attr_signature.go ├── attr_source_file.go ├── attribute_info.go ├── attribute_names.go ├── attribute_table.go ├── attribute_test.go ├── class_file.go ├── class_parser.go ├── class_reader.go ├── class_reader_test.go ├── class_test.go ├── constant_info.go ├── constant_pool.go ├── cp_class_and_module.go ├── cp_invoke_dynamic.go ├── cp_member_ref.go ├── cp_name_and_type.go ├── cp_numeric_and_utf8.go ├── cp_string.go └── member_info.go ├── classpath ├── classpath.go ├── entry.go ├── entry_dir.go └── entry_zip.go ├── cmd ├── java │ └── main.go ├── javap │ └── main.go ├── jimage │ └── main.go ├── jmod │ └── main.go └── listnative │ └── main.go ├── cpu ├── keep_alive.go └── loop.go ├── docs ├── classfile.txt ├── classpath.txt └── jimage.txt ├── go.mod ├── go.sum ├── instructions ├── base │ ├── branch_logic.go │ ├── code_reader.go │ └── instruction.go ├── comparisons │ ├── cmp.go │ └── if.go ├── constants │ ├── const.go │ ├── ldc.go │ ├── nop.go │ └── push.go ├── control │ ├── goto.go │ ├── jsr.go │ ├── lookupswitch.go │ ├── return.go │ └── tableswitch.go ├── conversions │ └── x2y.go ├── decoder.go ├── decoder_test.go ├── extended │ └── wide.go ├── factory.go ├── instr_test.go ├── loads │ ├── aload.go │ ├── load.go │ └── load_n.go ├── math │ ├── dop.go │ ├── fop.go │ ├── iop.go │ └── lop.go ├── opcodes.go ├── references │ ├── arraylength.go │ ├── athrow.go │ ├── checkcast.go │ ├── getfield.go │ ├── getstatic.go │ ├── instanceof.go │ ├── invokedynamic.go │ ├── invokeinterface.go │ ├── invokespecial.go │ ├── invokestatic.go │ ├── invokevirtual.go │ ├── monitor.go │ ├── new.go │ ├── newarray.go │ ├── putfield.go │ └── putstatic.go ├── reserved │ ├── bootstrap.go │ └── invokenative.go ├── stack │ ├── dup.go │ ├── pop.go │ └── swap.go └── stores │ ├── astore.go │ ├── store.go │ └── store_n.go ├── jimage ├── jimage_file.go ├── jimage_header.go ├── jimage_reader.go ├── location.go ├── string.go ├── string_test.go └── utils.go ├── logo.png ├── logo.psd ├── module ├── info.go ├── info_test.go ├── module.go ├── module_automatic.go ├── module_exploded.go ├── module_jar.go ├── module_jmod.go ├── module_unnamed.go ├── path.go ├── path_checker.go └── path_parser.go ├── native ├── all │ └── init.go ├── box │ └── boxing.go ├── java │ ├── awt │ │ ├── Component.go │ │ ├── Container.go │ │ ├── Cursor.go │ │ ├── Font.go │ │ ├── Frame.go │ │ ├── Toolkit.go │ │ └── Window.go │ ├── io │ │ ├── FileDescriptor.go │ │ ├── FileInputStream.go │ │ ├── FileOutputStream.go │ │ ├── FileSystem.go │ │ ├── ObjectStreamClass.go │ │ ├── RandomAccessFile.go │ │ └── UnixFileSystem.go │ ├── lang │ │ ├── Class.go │ │ ├── ClassLoader.go │ │ ├── Class_annotations.go │ │ ├── Class_getDeclaredConstructors0.go │ │ ├── Class_getDeclaredFields0.go │ │ ├── Class_getDeclaredMethods0.go │ │ ├── Class_helper.go │ │ ├── Class_static.go │ │ ├── Double.go │ │ ├── Float.go │ │ ├── Object.go │ │ ├── Package.go │ │ ├── Runtime.go │ │ ├── String.go │ │ ├── System.go │ │ ├── Thread.go │ │ ├── Thread_static.go │ │ ├── Throwable.go │ │ ├── invoke │ │ │ └── MethodHandleNatives.go │ │ └── reflect │ │ │ ├── Array.go │ │ │ └── Proxy.go │ ├── net │ │ ├── Inet4Address.go │ │ ├── Inet6AddressImpl.go │ │ ├── InetAddress.go │ │ ├── InetAddressImplFactory.go │ │ ├── PlainSocketImpl.go │ │ ├── SocketInputStream.go │ │ └── SocketOutputStream.go │ ├── security │ │ └── AccessController.go │ └── util │ │ ├── TimeZone.go │ │ ├── concurrent │ │ └── atomic │ │ │ └── AtomicLong.go │ │ ├── jar │ │ └── JarFile.go │ │ └── zip │ │ ├── CRC32.go │ │ ├── Inflater.go │ │ ├── ZipFile.go │ │ └── zip_file_hack.go ├── registry.go └── sun │ ├── awt │ └── CGraphicsEnvironment.go │ ├── io │ └── Win32ErrorMode.go │ ├── java2d │ └── opengl │ │ └── CGLGraphicsConfig.go │ ├── management │ └── VMManagementImpl.go │ ├── misc │ ├── Perf.go │ ├── Signal.go │ ├── URLClassPath.go │ ├── Unsafe.go │ ├── Unsafe_cas.go │ ├── Unsafe_class.go │ ├── Unsafe_memory.go │ ├── Unsafe_object.go │ ├── Unsafe_test.go │ ├── VM.go │ └── malloc.go │ ├── nio │ └── ch │ │ ├── IOUtil.go │ │ ├── Net.go │ │ └── ServerSocketChannelImpl.go │ └── reflect │ ├── ConstantPool.go │ ├── NativeConstructorAccessorImpl.go │ ├── NativeMethodAccessorImpl.go │ ├── Reflection.go │ └── helper.go ├── rtda ├── class_initialization.go ├── frame.go ├── frame_cache.go ├── frame_native.go ├── frame_test.go ├── heap │ ├── class.go │ ├── class_array.go │ ├── class_factory.go │ ├── class_helper.go │ ├── class_hierarchy.go │ ├── class_loader.go │ ├── class_reflection.go │ ├── constant_pool.go │ ├── cp_class.go │ ├── cp_invoke_dynamic.go │ ├── cp_method_handle.go │ ├── cp_method_type.go │ ├── cp_ref_field.go │ ├── cp_ref_interface_method.go │ ├── cp_ref_member.go │ ├── cp_ref_method.go │ ├── cp_string.go │ ├── descriptor.go │ ├── descriptor_parser.go │ ├── descriptor_test.go │ ├── field.go │ ├── member.go │ ├── method.go │ ├── method_reflection.go │ ├── method_vtable.go │ ├── monitor.go │ ├── object.go │ ├── object_array.go │ ├── object_instanceof.go │ ├── object_str.go │ ├── object_test.go │ ├── runtime.go │ └── slot.go ├── jvm_stack.go ├── local_vars.go ├── operand_stack.go ├── shim_frames.go ├── shim_methods.go ├── thread.go ├── thread_sleep.go └── thread_throw.go ├── test ├── hw_module │ ├── src │ │ └── hello.modules │ │ │ ├── hello │ │ │ └── HelloWorld.java │ │ │ └── module-info.java │ └── test.sh ├── testclasses │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ └── main │ │ └── java │ │ ├── HelloWorld.java │ │ ├── PrimeTest.java │ │ ├── PrintArgs.java │ │ ├── UnitTests.java │ │ ├── helper │ │ ├── MyAssert.java │ │ ├── ReflectionHelper.java │ │ ├── TestHelper.java │ │ └── UnitTestRunner.java │ │ ├── jls8 │ │ ├── StringOut.java │ │ └── ch12 │ │ │ ├── Eg12_4_1_1.java │ │ │ ├── Eg12_4_1_2.java │ │ │ ├── Eg12_4_1_3.java │ │ │ └── Eg12_5_2.java │ │ ├── jvm │ │ ├── InheritanceTest.java │ │ ├── MethodCall.java │ │ ├── ObjectInit.java │ │ ├── ex │ │ │ ├── CatchTest.java │ │ │ ├── ClassLibExTest.java │ │ │ ├── FinallyTest.java │ │ │ ├── InstructionExTest.java │ │ │ ├── InstructionNpeTest.java │ │ │ ├── StackTraceTest.java │ │ │ └── UncaughtTest.java │ │ ├── field │ │ │ ├── ConstantStaticFieldsTest.java │ │ │ ├── FieldAccessTest.java │ │ │ └── FieldsTest.java │ │ ├── instructions │ │ │ ├── ANewArray.java │ │ │ ├── AThrow.java │ │ │ ├── CheckCast.java │ │ │ ├── InvokeTest.java │ │ │ ├── LookupSwitch.java │ │ │ ├── MultiANewArrayTest.java │ │ │ └── TableSwitch.java │ │ ├── jsr292 │ │ │ ├── LookupTest.java │ │ │ ├── MemberNameTest.java │ │ │ ├── MethodHandleNativesTest.java │ │ │ ├── MethodHandleTest.java │ │ │ └── MethodTypeTest.java │ │ └── lambda │ │ │ ├── InterfaceDefaultMethodTest.java │ │ │ ├── InterfaceMethodTest.java │ │ │ └── LambdaTest.java │ │ ├── stdlib │ │ ├── basic │ │ │ ├── RuntimeTest.java │ │ │ ├── SysProps.java │ │ │ ├── SysPropsTest.java │ │ │ ├── TimeZoneTest.java │ │ │ ├── cl │ │ │ │ ├── ClassLoaderTest.java │ │ │ │ ├── GetClassLoaderTest.java │ │ │ │ └── UrlClassLoaderTest.java │ │ │ ├── reflection │ │ │ │ ├── ArrayClassTest.java │ │ │ │ ├── ArrayGetTest.java │ │ │ │ ├── ArraySetTest.java │ │ │ │ ├── CallerClassTest.java │ │ │ │ ├── ClassInitTest.java │ │ │ │ ├── ClassTest.java │ │ │ │ ├── DeclaringClassTest.java │ │ │ │ ├── FieldTest.java │ │ │ │ ├── GenericTest.java │ │ │ │ ├── InnerClassTest.java │ │ │ │ ├── MethodTest.java │ │ │ │ └── PrimitiveClassTest.java │ │ │ ├── string │ │ │ │ ├── Mutf8Test.java │ │ │ │ ├── StringTest.java │ │ │ │ └── StringTest2.java │ │ │ ├── sunmisc │ │ │ │ ├── UnsafeGetter.java │ │ │ │ ├── UnsafeMemoryTest.java │ │ │ │ ├── UnsafeObjectTest.java │ │ │ │ ├── UnsafeParkTest.java │ │ │ │ └── UnsafeTest.java │ │ │ ├── thread │ │ │ │ ├── AliveTest.java │ │ │ │ ├── DaemonTest.java │ │ │ │ ├── InterruptFlagTest.java │ │ │ │ ├── InterruptionTest.java │ │ │ │ ├── MainThreadTest.java │ │ │ │ ├── RunnableTest.java │ │ │ │ ├── SleepTest.java │ │ │ │ ├── SynchronizedTest.java │ │ │ │ ├── ThreadSubClassTest.java │ │ │ │ └── WaitTest.java │ │ │ └── wrapper │ │ │ │ ├── DoubleTest.java │ │ │ │ └── FloatTest.java │ │ ├── gui │ │ │ ├── awt │ │ │ │ └── AwtTest.java │ │ │ └── swing │ │ │ │ └── SwingTest.java │ │ ├── juc │ │ │ └── atomic │ │ │ │ └── AtomicIntegerTest.java │ │ ├── net │ │ │ ├── InetAddressTest.java │ │ │ ├── SocketConnectTest.java │ │ │ ├── SocketListenTest.java │ │ │ └── UrlTest.java │ │ ├── nio │ │ │ ├── ByteBufferTest.java │ │ │ └── io │ │ │ │ └── file │ │ │ │ ├── FileDescriptorTest.java │ │ │ │ ├── FileIoTest.java │ │ │ │ ├── FileTest.java │ │ │ │ └── RandomAccessFileTest.java │ │ └── util │ │ │ ├── TimeZoneTest.java │ │ │ └── ZipFileTest.java │ │ └── thirdparty │ │ ├── gson │ │ └── GsonTest.java │ │ └── jetty │ │ └── FileServer.java └── testdata │ ├── java13 │ ├── HelloWorld.class │ └── module-info.class │ └── java8 │ └── HelloWorld.class ├── vm ├── err_class_not_found.go └── options.go └── vmutils ├── bytes_reader.go ├── bytes_reader_test.go ├── dir.go ├── file_jmod.go ├── file_zip.go ├── files.go ├── native_endian.go ├── string_builder.go ├── strings.go ├── strings_test.go ├── unsafe.go └── unsafe_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .idea 4 | *.iml 5 | .gradle 6 | 7 | bin 8 | build 9 | out 10 | myjre 11 | /java 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.4.x 5 | - 1.5.x 6 | - 1.6.x 7 | - 1.7.x 8 | - 1.8.x 9 | - 1.9.x 10 | - 1.10.x 11 | - 1.11.x 12 | - tip 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Xiuhong Zhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jvm.go 2 | A JVM written in Go. 3 | ![jvm.go Logo](logo.png) 4 | 5 | # Introduction 6 | jvm.go is a toy JVM (which is far from complete) programmed in Go. The main purpose of this project is learning Go and the JVM. So the number one goal of the project is readability of code. The basic idea is to just implement the core JVM, and use `rt.jar` (from OpenJDK) as its class library. The garbage collector is implemented by directly using Go’s GC. 7 | 8 | # My dev environment 9 | * Mac OS X 10.13.6 10 | * Java 1.8.0_201 11 | * Go 1.12 12 | 13 | # Build jvm.go 14 | ```sh 15 | git clone https://github.com/zxh0/jvm.go.git 16 | cd jvm.go 17 | go build github.com/zxh0/jvm.go/cmd/java 18 | ``` 19 | 20 | # Run jvm.go using your Java installation 21 | Check your Java version and ensure JAVA_HOME env was set 22 | ```sh 23 | ./java -XuseJavaHome -cp path/to/jars:path/to/classes HelloWorld 24 | ``` 25 | 26 | # Run jvm.go using Zulu 27 | Download [zulu jdk 8](http://www.azulsystems.com/products/zulu/downloads#mac) ([Zulu](http://www.azulsystems.com/products/zulu) is a certified build of OpenJDK that is fully compliant with the Java SE standard.) and unzip it to somewhere, Copy `jvm.go/java` into unzipped folder 28 | ```sh 29 | cd path/to/zulu8-macosx 30 | cp path/to/jvm.go/java jvmgo 31 | jvmgo -cp path/to/jars:path/to/classes HelloWorld 32 | ``` 33 | 34 | -------------------------------------------------------------------------------- /classfile/attr_bootstrap_methods.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | BootstrapMethods_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 num_bootstrap_methods; 8 | { u2 bootstrap_method_ref; 9 | u2 num_bootstrap_arguments; 10 | u2 bootstrap_arguments[num_bootstrap_arguments]; 11 | } bootstrap_methods[num_bootstrap_methods]; 12 | } 13 | */ 14 | type BootstrapMethodsAttribute struct { 15 | BootstrapMethods []BootstrapMethod 16 | } 17 | 18 | type BootstrapMethod struct { 19 | BootstrapMethodRef uint16 20 | BootstrapArguments []uint16 21 | } 22 | 23 | func readBootstrapMethodsAttribute(reader *ClassReader) BootstrapMethodsAttribute { 24 | return BootstrapMethodsAttribute{ 25 | BootstrapMethods: reader.readTable(func(reader *ClassReader) BootstrapMethod { 26 | return BootstrapMethod{ 27 | BootstrapMethodRef: reader.ReadUint16(), 28 | BootstrapArguments: reader.readUint16s(), 29 | } 30 | }).([]BootstrapMethod), 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /classfile/attr_code.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | Code_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 max_stack; 8 | u2 max_locals; 9 | u4 code_length; 10 | u1 code[code_length]; 11 | u2 exception_table_length; 12 | { u2 start_pc; 13 | u2 end_pc; 14 | u2 handler_pc; 15 | u2 catch_type; 16 | } exception_table[exception_table_length]; 17 | u2 attributes_count; 18 | attribute_info attributes[attributes_count]; 19 | } 20 | */ 21 | type CodeAttribute struct { 22 | MaxStack uint16 23 | MaxLocals uint16 24 | Code []byte 25 | ExceptionTable []ExceptionTableEntry 26 | AttributeTable 27 | } 28 | 29 | type ExceptionTableEntry struct { 30 | StartPc uint16 31 | EndPc uint16 32 | HandlerPc uint16 33 | CatchType uint16 34 | } 35 | 36 | func readCodeAttribute(reader *ClassReader) CodeAttribute { 37 | return CodeAttribute{ 38 | MaxStack: reader.ReadUint16(), 39 | MaxLocals: reader.ReadUint16(), 40 | Code: reader.ReadBytes(int(reader.ReadUint32())), 41 | ExceptionTable: readExceptionTable(reader), 42 | AttributeTable: readAttributes(reader), 43 | } 44 | } 45 | 46 | func readExceptionTable(reader *ClassReader) []ExceptionTableEntry { 47 | return reader.readTable(func(reader *ClassReader) ExceptionTableEntry { 48 | return ExceptionTableEntry{ 49 | StartPc: reader.ReadUint16(), 50 | EndPc: reader.ReadUint16(), 51 | HandlerPc: reader.ReadUint16(), 52 | CatchType: reader.ReadUint16(), 53 | } 54 | }).([]ExceptionTableEntry) 55 | } 56 | -------------------------------------------------------------------------------- /classfile/attr_constant_value.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | ConstantValue_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 constantvalue_index; 8 | } 9 | */ 10 | type ConstantValueAttribute struct { 11 | ConstantValueIndex uint16 12 | } 13 | 14 | func readConstantValueAttribute(reader *ClassReader) ConstantValueAttribute { 15 | return ConstantValueAttribute{ 16 | ConstantValueIndex: reader.ReadUint16(), 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /classfile/attr_enclosing_method.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | EnclosingMethod_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 class_index; 8 | u2 method_index; 9 | } 10 | */ 11 | type EnclosingMethodAttribute struct { 12 | ClassIndex uint16 13 | MethodIndex uint16 14 | } 15 | 16 | func readEnclosingMethodAttribute(reader *ClassReader) EnclosingMethodAttribute { 17 | return EnclosingMethodAttribute{ 18 | ClassIndex: reader.ReadUint16(), 19 | MethodIndex: reader.ReadUint16(), 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classfile/attr_exceptions.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | Exceptions_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 number_of_exceptions; 8 | u2 exception_index_table[number_of_exceptions]; 9 | } 10 | */ 11 | type ExceptionsAttribute struct { 12 | ExceptionIndexTable []uint16 13 | } 14 | 15 | func readExceptionsAttribute(reader *ClassReader) ExceptionsAttribute { 16 | return ExceptionsAttribute{ 17 | ExceptionIndexTable: reader.readUint16s(), 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /classfile/attr_inner_classes.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | InnerClasses_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 number_of_classes; 8 | { u2 inner_class_info_index; 9 | u2 outer_class_info_index; 10 | u2 inner_name_index; 11 | u2 inner_class_access_flags; 12 | } classes[number_of_classes]; 13 | } 14 | */ 15 | type InnerClassesAttribute struct { 16 | Classes []InnerClassInfo 17 | } 18 | 19 | type InnerClassInfo struct { 20 | InnerClassInfoIndex uint16 21 | OuterClassInfoIndex uint16 22 | InnerNameIndex uint16 23 | InnerClassAccessFlags uint16 24 | } 25 | 26 | func readInnerClassesAttribute(reader *ClassReader) InnerClassesAttribute { 27 | return InnerClassesAttribute{ 28 | Classes: reader.readTable(func(reader *ClassReader) InnerClassInfo { 29 | return InnerClassInfo{ 30 | InnerClassInfoIndex: reader.ReadUint16(), 31 | OuterClassInfoIndex: reader.ReadUint16(), 32 | InnerNameIndex: reader.ReadUint16(), 33 | InnerClassAccessFlags: reader.ReadUint16(), 34 | } 35 | }).([]InnerClassInfo), 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /classfile/attr_line_number_table.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | LineNumberTable_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 line_number_table_length; 8 | { u2 start_pc; 9 | u2 line_number; 10 | } line_number_table[line_number_table_length]; 11 | } 12 | */ 13 | type LineNumberTableAttribute struct { 14 | LineNumberTable []LineNumberTableEntry 15 | } 16 | 17 | type LineNumberTableEntry struct { 18 | StartPC uint16 19 | LineNumber uint16 20 | } 21 | 22 | func readLineNumberTableAttribute(reader *ClassReader) LineNumberTableAttribute { 23 | return LineNumberTableAttribute{ 24 | LineNumberTable: reader.readTable(func(reader *ClassReader) LineNumberTableEntry { 25 | return LineNumberTableEntry{ 26 | StartPC: reader.ReadUint16(), 27 | LineNumber: reader.ReadUint16(), 28 | } 29 | }).([]LineNumberTableEntry), 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /classfile/attr_local_variable_table.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | LocalVariableTable_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 local_variable_table_length; 8 | { u2 start_pc; 9 | u2 length; 10 | u2 name_index; 11 | u2 descriptor_index; 12 | u2 index; 13 | } local_variable_table[local_variable_table_length]; 14 | } 15 | */ 16 | type LocalVariableTableAttribute struct { 17 | LocalVariableTable []LocalVariableTableEntry 18 | } 19 | 20 | type LocalVariableTableEntry struct { 21 | StartPc uint16 22 | Length uint16 23 | NameIndex uint16 24 | DescriptorIndex uint16 25 | Index uint16 26 | } 27 | 28 | func readLocalVariableTableAttribute(reader *ClassReader) LocalVariableTableAttribute { 29 | return LocalVariableTableAttribute{ 30 | LocalVariableTable: reader.readTable(func(reader *ClassReader) LocalVariableTableEntry { 31 | return LocalVariableTableEntry{ 32 | StartPc: reader.ReadUint16(), 33 | Length: reader.ReadUint16(), 34 | NameIndex: reader.ReadUint16(), 35 | DescriptorIndex: reader.ReadUint16(), 36 | Index: reader.ReadUint16(), 37 | } 38 | }).([]LocalVariableTableEntry), 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /classfile/attr_local_variable_type_table.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | LocalVariableTypeTable_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 local_variable_type_table_length; 8 | { u2 start_pc; 9 | u2 length; 10 | u2 name_index; 11 | u2 signature_index; 12 | u2 index; 13 | } local_variable_type_table[local_variable_type_table_length]; 14 | } 15 | */ 16 | type LocalVariableTypeTableAttribute struct { 17 | LocalVariableTypeTable []LocalVariableTypeTableEntry 18 | } 19 | 20 | type LocalVariableTypeTableEntry struct { 21 | StartPc uint16 22 | Length uint16 23 | NameIndex uint16 24 | SignatureIndex uint16 25 | Index uint16 26 | } 27 | 28 | func readLocalVariableTypeTableAttribute(reader *ClassReader) LocalVariableTypeTableAttribute { 29 | return LocalVariableTypeTableAttribute{ 30 | LocalVariableTypeTable: reader.readTable(func(reader *ClassReader) LocalVariableTypeTableEntry { 31 | return LocalVariableTypeTableEntry{ 32 | StartPc: reader.ReadUint16(), 33 | Length: reader.ReadUint16(), 34 | NameIndex: reader.ReadUint16(), 35 | SignatureIndex: reader.ReadUint16(), 36 | Index: reader.ReadUint16(), 37 | } 38 | }).([]LocalVariableTypeTableEntry), 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /classfile/attr_markers.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | type MarkerAttribute struct{} 4 | 5 | /* 6 | Deprecated_attribute { 7 | u2 attribute_name_index; 8 | u4 attribute_length; 9 | } 10 | */ 11 | type DeprecatedAttribute struct { 12 | MarkerAttribute 13 | } 14 | 15 | /* 16 | Synthetic_attribute { 17 | u2 attribute_name_index; 18 | u4 attribute_length; 19 | } 20 | */ 21 | type SyntheticAttribute struct { 22 | MarkerAttribute 23 | } 24 | -------------------------------------------------------------------------------- /classfile/attr_signature.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | Signature_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 signature_index; 8 | } 9 | */ 10 | type SignatureAttribute struct { 11 | SignatureIndex uint16 12 | } 13 | 14 | func readSignatureAttribute(reader *ClassReader) SignatureAttribute { 15 | return SignatureAttribute{ 16 | SignatureIndex: reader.ReadUint16(), 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /classfile/attr_source_file.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | SourceFile_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 sourcefile_index; 8 | } 9 | */ 10 | type SourceFileAttribute struct { 11 | SourceFileIndex uint16 12 | } 13 | 14 | func readSourceFileAttribute(reader *ClassReader) SourceFileAttribute { 15 | return SourceFileAttribute{SourceFileIndex: reader.ReadUint16()} 16 | } 17 | -------------------------------------------------------------------------------- /classfile/attribute_test.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestAttributeTable(t *testing.T) { 10 | at := AttributeTable{ 11 | CodeAttribute{MaxStack: 1}, 12 | ConstantValueAttribute{ConstantValueIndex: 2}, 13 | ExceptionsAttribute{ExceptionIndexTable: []uint16{3}}, 14 | BootstrapMethodsAttribute{[]BootstrapMethod{{BootstrapMethodRef: 4}}}, 15 | SignatureAttribute{SignatureIndex: 5}, 16 | SourceFileAttribute{SourceFileIndex: 6}, 17 | LineNumberTableAttribute{[]LineNumberTableEntry{{StartPC: 7}}}, 18 | } 19 | code, _ := at.GetCodeAttribute() 20 | require.Equal(t, uint16(1), code.MaxStack) 21 | require.Equal(t, uint16(2), at.GetConstantValueIndex()) 22 | require.Equal(t, uint16(3), at.GetExceptionIndexTable()[0]) 23 | require.Equal(t, uint16(4), at.GetBootstrapMethods()[0].BootstrapMethodRef) 24 | require.Equal(t, uint16(5), at.GetSignatureIndex()) 25 | require.Equal(t, uint16(6), at.GetSourceFileIndex()) 26 | require.Equal(t, uint16(7), at.GetLineNumberTable()[0].StartPC) 27 | } 28 | -------------------------------------------------------------------------------- /classfile/class_parser.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func Parse(classData []byte) (cf *ClassFile, err error) { 8 | defer func() { 9 | if r := recover(); r != nil { 10 | var ok bool 11 | err, ok = r.(error) 12 | if !ok { 13 | err = fmt.Errorf("%v", r) 14 | } 15 | } 16 | }() 17 | 18 | cr := newClassReader(classData) 19 | cf = &ClassFile{} 20 | cf.read(&cr) 21 | return 22 | } 23 | -------------------------------------------------------------------------------- /classfile/class_reader.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | import ( 4 | "encoding/binary" 5 | "reflect" 6 | 7 | "github.com/zxh0/jvm.go/vmutils" 8 | ) 9 | 10 | type ClassReader struct { 11 | vmutils.BytesReader 12 | cf *ClassFile 13 | } 14 | 15 | func newClassReader(data []byte) ClassReader { 16 | br := vmutils.NewBytesReader(data, binary.BigEndian) 17 | return ClassReader{BytesReader: br} 18 | } 19 | 20 | func (reader *ClassReader) readUint16s() []uint16 { 21 | n := reader.ReadUint16() 22 | s := make([]uint16, n) 23 | for i := range s { 24 | s[i] = reader.ReadUint16() 25 | } 26 | return s 27 | } 28 | 29 | // readFn: func(reader *ClassReader) XXX 30 | func (reader *ClassReader) readTable(readFn interface{}) interface{} { 31 | n := int(reader.ReadUint16()) 32 | 33 | itemType := reflect.TypeOf(readFn).Out(0) 34 | sliceType := reflect.SliceOf(itemType) 35 | s := reflect.MakeSlice(sliceType, n, n) // make([]x, n, n) 36 | 37 | readFnVal := reflect.ValueOf(readFn) 38 | args := []reflect.Value{reflect.ValueOf(reader)} 39 | 40 | for i := 0; i < n; i++ { 41 | x := readFnVal.Call(args)[0] 42 | s.Index(i).Set(x) // s[i] = x 43 | } 44 | 45 | return s.Interface() 46 | } 47 | -------------------------------------------------------------------------------- /classfile/class_reader_test.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestReadTable(t *testing.T) { 10 | readFn := func(reader *ClassReader) uint16 { 11 | return reader.ReadUint16() 12 | } 13 | 14 | reader := newClassReader([]byte{0x00, 0x00}) 15 | s := reader.readTable(readFn) 16 | require.Equal(t, []uint16{}, s) 17 | 18 | reader = newClassReader([]byte{0x00, 0x01, 0x02, 0x03}) 19 | s = reader.readTable(readFn) 20 | require.Equal(t, []uint16{0x0203}, s) 21 | } 22 | -------------------------------------------------------------------------------- /classfile/constant_pool.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | func readConstantPool(reader *ClassReader) []ConstantInfo { 4 | cpCount := int(reader.ReadUint16()) 5 | cp := make([]ConstantInfo, cpCount) 6 | 7 | // The constant_pool table is indexed from 1 to constant_pool_count - 1. 8 | for i := 1; i < cpCount; i++ { 9 | cp[i] = readConstantInfo(reader) 10 | // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.5 11 | // All 8-byte constants take up two entries in the constant_pool table of the class file. 12 | // If a CONSTANT_Long_info or CONSTANT_Double_info structure is the item in the constant_pool 13 | // table at index n, then the next usable item in the pool is located at index n+2. 14 | // The constant_pool index n+1 must be valid but is considered unusable. 15 | switch cp[i].(type) { 16 | case int64, float64: 17 | i++ 18 | } 19 | } 20 | 21 | return cp 22 | } 23 | -------------------------------------------------------------------------------- /classfile/cp_class_and_module.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | CONSTANT_Class_info { 5 | u1 tag; 6 | u2 name_index; 7 | } 8 | 9 | CONSTANT_Module_info { 10 | u1 tag; 11 | u2 name_index; 12 | } 13 | 14 | CONSTANT_Package_info { 15 | u1 tag; 16 | u2 name_index; 17 | } 18 | */ 19 | 20 | type ConstantClassInfo constantWithNameIdx 21 | type ConstantModuleInfo constantWithNameIdx 22 | type ConstantPackageInfo constantWithNameIdx 23 | 24 | type constantWithNameIdx struct { 25 | NameIndex uint16 26 | } 27 | 28 | func readConstantClassInfo(reader *ClassReader) ConstantClassInfo { 29 | return ConstantClassInfo(readConstantWithNameIdx(reader)) 30 | } 31 | func readConstantModuleInfo(reader *ClassReader) ConstantModuleInfo { 32 | return ConstantModuleInfo(readConstantWithNameIdx(reader)) 33 | } 34 | func readConstantPackageInfo(reader *ClassReader) ConstantPackageInfo { 35 | return ConstantPackageInfo(readConstantWithNameIdx(reader)) 36 | } 37 | 38 | func readConstantWithNameIdx(reader *ClassReader) constantWithNameIdx { 39 | return constantWithNameIdx{ 40 | NameIndex: reader.ReadUint16(), 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /classfile/cp_invoke_dynamic.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | CONSTANT_InvokeDynamic_info { 5 | u1 tag; 6 | u2 bootstrap_method_attr_index; 7 | u2 name_and_type_index; 8 | } 9 | */ 10 | type ConstantInvokeDynamicInfo struct { 11 | BootstrapMethodAttrIndex uint16 12 | NameAndTypeIndex uint16 13 | } 14 | 15 | func readConstantInvokeDynamicInfo(reader *ClassReader) ConstantInvokeDynamicInfo { 16 | return ConstantInvokeDynamicInfo{ 17 | BootstrapMethodAttrIndex: reader.ReadUint16(), 18 | NameAndTypeIndex: reader.ReadUint16(), 19 | } 20 | } 21 | 22 | /* 23 | CONSTANT_MethodHandle_info { 24 | u1 tag; 25 | u1 reference_kind; 26 | u2 reference_index; 27 | } 28 | */ 29 | type ConstantMethodHandleInfo struct { 30 | ReferenceKind uint8 31 | ReferenceIndex uint16 32 | } 33 | 34 | func readConstantMethodHandleInfo(reader *ClassReader) ConstantMethodHandleInfo { 35 | return ConstantMethodHandleInfo{ 36 | ReferenceKind: reader.ReadUint8(), 37 | ReferenceIndex: reader.ReadUint16(), 38 | } 39 | } 40 | 41 | /* 42 | CONSTANT_MethodType_info { 43 | u1 tag; 44 | u2 descriptor_index; 45 | } 46 | */ 47 | type ConstantMethodTypeInfo struct { 48 | DescriptorIndex uint16 49 | } 50 | 51 | func readConstantMethodTypeInfo(reader *ClassReader) ConstantMethodTypeInfo { 52 | return ConstantMethodTypeInfo{ 53 | DescriptorIndex: reader.ReadUint16(), 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /classfile/cp_member_ref.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | CONSTANT_Fieldref_info { 5 | u1 tag; 6 | u2 class_index; 7 | u2 name_and_type_index; 8 | } 9 | CONSTANT_Methodref_info { 10 | u1 tag; 11 | u2 class_index; 12 | u2 name_and_type_index; 13 | } 14 | CONSTANT_InterfaceMethodref_info { 15 | u1 tag; 16 | u2 class_index; 17 | u2 name_and_type_index; 18 | } 19 | */ 20 | 21 | type ConstantFieldRefInfo constantMemberRefInfo 22 | type ConstantMethodRefInfo constantMemberRefInfo 23 | type ConstantInterfaceMethodRefInfo constantMemberRefInfo 24 | 25 | type constantMemberRefInfo struct { 26 | ClassIndex uint16 27 | NameAndTypeIndex uint16 28 | } 29 | 30 | func readConstantFieldRefInfo(reader *ClassReader) ConstantFieldRefInfo { 31 | return ConstantFieldRefInfo(readConstantMemberRefInfo(reader)) 32 | } 33 | func readConstantMethodRefInfo(reader *ClassReader) ConstantMethodRefInfo { 34 | return ConstantMethodRefInfo(readConstantMemberRefInfo(reader)) 35 | } 36 | func readConstantInterfaceMethodRefInfo(reader *ClassReader) ConstantInterfaceMethodRefInfo { 37 | return ConstantInterfaceMethodRefInfo(readConstantMemberRefInfo(reader)) 38 | } 39 | 40 | func readConstantMemberRefInfo(reader *ClassReader) constantMemberRefInfo { 41 | return constantMemberRefInfo{ 42 | ClassIndex: reader.ReadUint16(), 43 | NameAndTypeIndex: reader.ReadUint16(), 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /classfile/cp_name_and_type.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | CONSTANT_NameAndType_info { 5 | u1 tag; 6 | u2 name_index; 7 | u2 descriptor_index; 8 | } 9 | */ 10 | type ConstantNameAndTypeInfo struct { 11 | NameIndex uint16 12 | DescriptorIndex uint16 13 | } 14 | 15 | func readConstantNameAndTypeInfo(reader *ClassReader) ConstantNameAndTypeInfo { 16 | return ConstantNameAndTypeInfo{ 17 | NameIndex: reader.ReadUint16(), 18 | DescriptorIndex: reader.ReadUint16(), 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /classfile/cp_numeric_and_utf8.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | import ( 4 | "math" 5 | ) 6 | 7 | /* 8 | CONSTANT_Integer_info { 9 | u1 tag; 10 | u4 bytes; 11 | } 12 | */ 13 | func readConstantIntegerInfo(reader *ClassReader) int32 { 14 | return int32(reader.ReadUint32()) 15 | } 16 | 17 | /* 18 | CONSTANT_Float_info { 19 | u1 tag; 20 | u4 bytes; 21 | } 22 | */ 23 | func readConstantFloatInfo(reader *ClassReader) float32 { 24 | return math.Float32frombits(reader.ReadUint32()) 25 | } 26 | 27 | /* 28 | CONSTANT_Long_info { 29 | u1 tag; 30 | u4 high_bytes; 31 | u4 low_bytes; 32 | } 33 | */ 34 | func readConstantLongInfo(reader *ClassReader) int64 { 35 | return int64(reader.ReadUint64()) 36 | } 37 | 38 | /* 39 | CONSTANT_Double_info { 40 | u1 tag; 41 | u4 high_bytes; 42 | u4 low_bytes; 43 | } 44 | */ 45 | func readConstantDoubleInfo(reader *ClassReader) float64 { 46 | return math.Float64frombits(reader.ReadUint64()) 47 | } 48 | 49 | /* 50 | CONSTANT_Utf8_info { 51 | u1 tag; 52 | u2 length; 53 | u1 bytes[length]; 54 | } 55 | */ 56 | func readConstantUtf8Info(reader *ClassReader) []byte { 57 | length := int(reader.ReadUint16()) 58 | return reader.ReadBytes(length) 59 | } 60 | -------------------------------------------------------------------------------- /classfile/cp_string.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | CONSTANT_String_info { 5 | u1 tag; 6 | u2 string_index; 7 | } 8 | */ 9 | type ConstantStringInfo struct { 10 | StringIndex uint16 11 | } 12 | 13 | func readConstantStringInfo(reader *ClassReader) ConstantStringInfo { 14 | return ConstantStringInfo{ 15 | StringIndex: reader.ReadUint16(), 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /classfile/member_info.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | field_info { 5 | u2 access_flags; 6 | u2 name_index; 7 | u2 descriptor_index; 8 | u2 attributes_count; 9 | attribute_info attributes[attributes_count]; 10 | } 11 | method_info { 12 | u2 access_flags; 13 | u2 name_index; 14 | u2 descriptor_index; 15 | u2 attributes_count; 16 | attribute_info attributes[attributes_count]; 17 | } 18 | */ 19 | type MemberInfo struct { 20 | AccessFlags uint16 21 | NameIndex uint16 22 | DescriptorIndex uint16 23 | AttributeTable 24 | } 25 | 26 | // read field or method table 27 | func readMembers(reader *ClassReader) []MemberInfo { 28 | return reader.readTable(readMember).([]MemberInfo) 29 | } 30 | 31 | func readMember(reader *ClassReader) MemberInfo { 32 | return MemberInfo{ 33 | AccessFlags: reader.ReadUint16(), 34 | NameIndex: reader.ReadUint16(), 35 | DescriptorIndex: reader.ReadUint16(), 36 | AttributeTable: readAttributes(reader), 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /classpath/classpath.go: -------------------------------------------------------------------------------- 1 | package classpath 2 | 3 | import ( 4 | "path/filepath" 5 | "strings" 6 | 7 | "github.com/zxh0/jvm.go/vm" 8 | ) 9 | 10 | type ClassPath struct { 11 | entries []Entry 12 | } 13 | 14 | func Parse(opts *vm.Options) *ClassPath { 15 | cp := &ClassPath{} 16 | cp.parseBootAndExtClassPath(opts.AbsJavaHome) 17 | cp.parseUserClassPath(opts.ClassPath) 18 | return cp 19 | } 20 | 21 | func (cp *ClassPath) parseBootAndExtClassPath(absJavaHome string) { 22 | // jre/lib/* 23 | jreLibPath := filepath.Join(absJavaHome, "lib", "*") 24 | cp.entries = append(cp.entries, spreadWildcardEntry(jreLibPath)...) 25 | 26 | // jre/lib/ext/* 27 | jreExtPath := filepath.Join(absJavaHome, "lib", "ext", "*") 28 | cp.entries = append(cp.entries, spreadWildcardEntry(jreExtPath)...) 29 | } 30 | 31 | func (cp *ClassPath) parseUserClassPath(cpOption string) { 32 | if cpOption == "" { 33 | cpOption = "." 34 | } 35 | cp.entries = append(cp.entries, parsePath(cpOption)...) 36 | } 37 | 38 | // className: fully/qualified/ClassName 39 | func (cp *ClassPath) ReadClass(className string) (Entry, []byte) { 40 | className = className + ".class" 41 | for _, entry := range cp.entries { 42 | if data, err := entry.readClass(className); err == nil { 43 | return entry, data 44 | } 45 | } 46 | return nil, nil 47 | } 48 | 49 | func IsBootClassPath(entry Entry, absJreLib string) bool { 50 | if entry == nil { 51 | // todo 52 | return true 53 | } 54 | 55 | return strings.HasPrefix(entry.String(), absJreLib) 56 | } 57 | -------------------------------------------------------------------------------- /classpath/entry.go: -------------------------------------------------------------------------------- 1 | package classpath 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/zxh0/jvm.go/vmutils" 10 | ) 11 | 12 | type Entry interface { 13 | // className: fully/qualified/ClassName.class 14 | readClass(className string) ([]byte, error) 15 | String() string 16 | } 17 | 18 | func parsePath(path string) []Entry { 19 | switch { 20 | case strings.IndexByte(path, os.PathListSeparator) >= 0: 21 | return splitPath(path) 22 | case strings.HasSuffix(path, "*"): 23 | return spreadWildcardEntry(path) 24 | case vmutils.IsJarFile(path) || vmutils.IsZipFile(path): 25 | return []Entry{newZipEntry(path)} 26 | default: 27 | return []Entry{newDirEntry(path)} 28 | } 29 | } 30 | 31 | func splitPath(pathList string) []Entry { 32 | list := make([]Entry, 0, 4) 33 | 34 | for _, path := range strings.Split(pathList, string(os.PathListSeparator)) { 35 | list = append(list, parsePath(path)...) 36 | } 37 | 38 | return list 39 | } 40 | 41 | func spreadWildcardEntry(path string) []Entry { 42 | baseDir := path[:len(path)-1] // remove * 43 | files, err := ioutil.ReadDir(baseDir) 44 | if err != nil { 45 | panic(err) // TODO 46 | } 47 | 48 | list := make([]Entry, 0, 4) 49 | for _, file := range files { 50 | if vmutils.IsJarFile(file.Name()) { 51 | filename := filepath.Join(baseDir, file.Name()) 52 | list = append(list, newZipEntry(filename)) 53 | } 54 | } 55 | 56 | return list 57 | } 58 | -------------------------------------------------------------------------------- /classpath/entry_dir.go: -------------------------------------------------------------------------------- 1 | package classpath 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/vmutils" 5 | ) 6 | 7 | type DirEntry struct { 8 | dir *vmutils.Dir 9 | } 10 | 11 | func newDirEntry(path string) *DirEntry { 12 | if dir, err := vmutils.NewDir(path); err != nil { 13 | panic(err) // TODO 14 | } else { 15 | return &DirEntry{dir: dir} 16 | } 17 | } 18 | 19 | func (entry *DirEntry) readClass(className string) ([]byte, error) { 20 | return entry.dir.ReadFile(className) 21 | } 22 | 23 | func (entry *DirEntry) String() string { 24 | return entry.dir.AbsPath() 25 | } 26 | -------------------------------------------------------------------------------- /classpath/entry_zip.go: -------------------------------------------------------------------------------- 1 | package classpath 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/vmutils" 5 | ) 6 | 7 | type ZipEntry struct { 8 | zipFile *vmutils.ZipFile 9 | } 10 | 11 | func newZipEntry(path string) *ZipEntry { 12 | if zipFile, err := vmutils.NewZipFile(path); err != nil { 13 | panic(err) // TODO 14 | } else { 15 | return &ZipEntry{zipFile: zipFile} 16 | } 17 | } 18 | 19 | func (entry *ZipEntry) readClass(className string) ([]byte, error) { 20 | // TODO: close ZipFile 21 | if !entry.zipFile.IsOpen() { 22 | if err := entry.zipFile.Open(); err != nil { 23 | return nil, err 24 | } 25 | } 26 | 27 | return entry.zipFile.ReadFile(className) 28 | } 29 | 30 | func (entry *ZipEntry) String() string { 31 | return entry.zipFile.AbsPath() 32 | } 33 | -------------------------------------------------------------------------------- /cpu/keep_alive.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // TODO: use WaitGroup? 8 | var ( 9 | aliveCount = 0 10 | lock = &sync.Mutex{} 11 | cond = sync.NewCond(lock) 12 | ) 13 | 14 | func nonDaemonThreadStart() { 15 | lock.Lock() 16 | defer lock.Unlock() 17 | 18 | aliveCount++ 19 | } 20 | 21 | func nonDaemonThreadStop() { 22 | lock.Lock() 23 | defer lock.Unlock() 24 | 25 | aliveCount-- 26 | if aliveCount == 0 { 27 | cond.Broadcast() 28 | } 29 | } 30 | 31 | func KeepAlive() { 32 | lock.Lock() 33 | defer lock.Unlock() 34 | 35 | if aliveCount > 0 { 36 | cond.Wait() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /docs/classfile.txt: -------------------------------------------------------------------------------- 1 | +----------------+ * +---------------+ 2 | | AttributeTable |<>------| AttributeInfo | 3 | +----------------+ +---------------+ 4 | △ 5 | | 6 | +----------+-----------+ 7 | | | 8 | +-----------+ * +--------------+ 9 | +->| ClassFile |<>--+----| MemberInfo | 10 | | +-----------+ | +--------------+ 11 | | | | 12 | | | V 13 | | | +--------------+ * +--------------+ 14 | | +----| ConstantPool |<>-----| ConstantInfo | 15 | | +--------------+ +--------------+ 16 | | | 17 | +-------------------------------+ 18 | -------------------------------------------------------------------------------- /docs/classpath.txt: -------------------------------------------------------------------------------- 1 | +-------+ * 2 | | Entry |<------------------------+ 3 | +-------+ | 4 | △ | 5 | | | 6 | +-------------+----------------+ | 7 | | | | 8 | +----------+ +----------+ +----------------+ | 9 | | DirEntry | | ZipEntry | | CompositeEntry |<>--+ 10 | +----------+ +----------+ +----------------+ 11 | △ 12 | | 13 | +----------------+ 14 | | WildcardEntry | 15 | +----------------+ -------------------------------------------------------------------------------- /docs/jimage.txt: -------------------------------------------------------------------------------- 1 | header --+ 2 | redirect | 3 | offsets |> index 4 | locations | 5 | strings --+ 6 | 7 | 8 | 9 | header 10 | redirect 11 | int32 12 | +--- int32 <----------------------------------------------------------------------+ 13 | | int32 | 14 | | ... | 15 | | offsets | 16 | | uint32 ---+ | 17 | +--> uint32 | | 18 | uint32 | | 19 | ... | | 20 | locations | | 21 | byte | | 22 | byte <----+ ~~+ decompress() fullName() hash()% | 23 | byte |>--------------> [uint64, ...] ------------> name --------> idx 24 | byte ~~~~~~~~~~+ | 25 | ... | 26 | strings | 27 | [bytes, 0] | 28 | [bytes, 0] | 29 | [bytes, 0] <-----------------------+ 30 | ... 31 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/zxh0/jvm.go 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 7 | github.com/stretchr/testify v1.4.0 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= 4 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 8 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 9 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 10 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 11 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 12 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 13 | -------------------------------------------------------------------------------- /instructions/base/branch_logic.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/rtda" 5 | ) 6 | 7 | func Branch(frame *rtda.Frame, offset int) { 8 | pc := frame.Thread.PC 9 | frame.NextPC = pc + offset 10 | } 11 | -------------------------------------------------------------------------------- /instructions/base/code_reader.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "github.com/zxh0/jvm.go/vmutils" 7 | ) 8 | 9 | type CodeReader struct { 10 | vmutils.BytesReader 11 | } 12 | 13 | func NewCodeReader(code []byte) *CodeReader { 14 | br := vmutils.NewBytesReader(code, binary.BigEndian) 15 | return &CodeReader{BytesReader: br} 16 | } 17 | 18 | func (reader *CodeReader) ReadInt8() int8 { 19 | return int8(reader.ReadUint8()) 20 | } 21 | 22 | func (reader *CodeReader) ReadInt16() int16 { 23 | return int16(reader.ReadUint16()) 24 | } 25 | 26 | func (reader *CodeReader) ReadInt32() int32 { 27 | return int32(reader.ReadUint32()) 28 | } 29 | 30 | func (reader *CodeReader) ReadInt32s(count int32) []int32 { 31 | s := make([]int32, count) 32 | for i := range s { 33 | s[i] = reader.ReadInt32() 34 | } 35 | return s 36 | } 37 | 38 | // used by lookupswitch and tableswitch 39 | func (reader *CodeReader) SkipPadding() { 40 | for reader.Position()%4 != 0 { 41 | reader.ReadUint8() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /instructions/base/instruction.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/rtda" 5 | ) 6 | 7 | type Instruction interface { 8 | FetchOperands(reader *CodeReader) 9 | Execute(frame *rtda.Frame) 10 | } 11 | 12 | type NoOperandsInstruction struct { 13 | // empty 14 | } 15 | 16 | func (instr *NoOperandsInstruction) FetchOperands(reader *CodeReader) { 17 | // nothing to do 18 | } 19 | 20 | type BranchInstruction struct { 21 | Offset int 22 | } 23 | 24 | func (instr *BranchInstruction) FetchOperands(reader *CodeReader) { 25 | instr.Offset = int(reader.ReadInt16()) 26 | } 27 | 28 | type Index8Instruction struct { 29 | Index uint 30 | } 31 | 32 | func (instr *Index8Instruction) FetchOperands(reader *CodeReader) { 33 | instr.Index = uint(reader.ReadUint8()) 34 | } 35 | 36 | type Index16Instruction struct { 37 | Index uint 38 | } 39 | 40 | func (instr *Index16Instruction) FetchOperands(reader *CodeReader) { 41 | instr.Index = uint(reader.ReadUint16()) 42 | } 43 | -------------------------------------------------------------------------------- /instructions/constants/const.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | "github.com/zxh0/jvm.go/rtda/heap" 7 | ) 8 | 9 | func NewConstNull() *Const { return &Const{k: heap.EmptySlot, d: false} } 10 | func NewConstInt(n int32) *Const { return &Const{k: heap.NewIntSlot(n), d: false} } 11 | func NewConstLong(n int64) *Const { return &Const{k: heap.NewLongSlot(n), d: true} } 12 | func NewConstFloat(n float32) *Const { return &Const{k: heap.NewFloatSlot(n), d: false} } 13 | func NewConstDouble(n float64) *Const { return &Const{k: heap.NewDoubleSlot(n), d: true} } 14 | 15 | // xconst: Push XXX 16 | type Const struct { 17 | base.NoOperandsInstruction 18 | k heap.Slot 19 | d bool // long or double 20 | } 21 | 22 | func (instr *Const) Execute(frame *rtda.Frame) { 23 | frame.PushL(instr.k, instr.d) 24 | } 25 | -------------------------------------------------------------------------------- /instructions/constants/ldc.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/zxh0/jvm.go/instructions/base" 7 | "github.com/zxh0/jvm.go/rtda" 8 | "github.com/zxh0/jvm.go/rtda/heap" 9 | ) 10 | 11 | // Push item from run-time constant pool 12 | type LDC struct{ base.Index8Instruction } 13 | 14 | func (instr *LDC) Execute(frame *rtda.Frame) { 15 | _ldc(frame, instr.Index) 16 | } 17 | 18 | // Push item from run-time constant pool (wide index) 19 | type LDC_W struct{ base.Index16Instruction } 20 | 21 | func (instr *LDC_W) Execute(frame *rtda.Frame) { 22 | _ldc(frame, instr.Index) 23 | } 24 | 25 | func _ldc(frame *rtda.Frame, index uint) { 26 | c := frame.GetConstantPool().GetConstant(index) 27 | 28 | switch x := c.(type) { 29 | case int32: 30 | frame.PushInt(x) 31 | case float32: 32 | frame.PushFloat(x) 33 | case *heap.ConstantString: // string 34 | frame.PushRef(x.GetJString()) 35 | case *heap.ConstantClass: 36 | frame.PushRef(x.GetClass().JClass) 37 | default: 38 | // todo 39 | // ref to MethodType or MethodHandle 40 | panic(fmt.Errorf("todo: ldc! %v", c)) 41 | } 42 | } 43 | 44 | // Push long or double from run-time constant pool (wide index) 45 | type LDC2_W struct{ base.Index16Instruction } 46 | 47 | func (instr *LDC2_W) Execute(frame *rtda.Frame) { 48 | c := frame.GetConstantPool().GetConstant(instr.Index) 49 | 50 | switch c.(type) { 51 | case int64: 52 | frame.PushLong(c.(int64)) 53 | case float64: 54 | frame.PushDouble(c.(float64)) 55 | default: 56 | panic(fmt.Errorf("ldc2_w! %v", c)) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /instructions/constants/nop.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | // Do nothing 9 | type NOP struct{ base.NoOperandsInstruction } 10 | 11 | func (instr *NOP) Execute(frame *rtda.Frame) { 12 | // really do nothing 13 | } 14 | -------------------------------------------------------------------------------- /instructions/constants/push.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | // Push byte 9 | type BIPush struct { 10 | Val int32 // TODO 11 | } 12 | 13 | func (instr *BIPush) FetchOperands(reader *base.CodeReader) { 14 | instr.Val = int32(reader.ReadInt8()) 15 | } 16 | func (instr *BIPush) Execute(frame *rtda.Frame) { 17 | frame.PushInt(instr.Val) 18 | } 19 | 20 | // Push short 21 | type SIPush struct { 22 | Val int32 23 | } 24 | 25 | func (instr *SIPush) FetchOperands(reader *base.CodeReader) { 26 | instr.Val = int32(reader.ReadInt16()) 27 | } 28 | func (instr *SIPush) Execute(frame *rtda.Frame) { 29 | frame.PushInt(instr.Val) 30 | } 31 | -------------------------------------------------------------------------------- /instructions/control/goto.go: -------------------------------------------------------------------------------- 1 | package control 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | // Branch always 9 | type Goto struct{ base.BranchInstruction } 10 | 11 | func (instr *Goto) Execute(frame *rtda.Frame) { 12 | base.Branch(frame, instr.Offset) 13 | } 14 | 15 | // Branch always (wide index) 16 | type GotoW struct { 17 | offset int 18 | } 19 | 20 | func (instr *GotoW) FetchOperands(reader *base.CodeReader) { 21 | instr.offset = int(reader.ReadInt32()) 22 | } 23 | func (instr *GotoW) Execute(frame *rtda.Frame) { 24 | base.Branch(frame, instr.offset) 25 | } 26 | -------------------------------------------------------------------------------- /instructions/control/jsr.go: -------------------------------------------------------------------------------- 1 | package control 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | // Jump subroutine 9 | type JSR struct{ base.BranchInstruction } 10 | 11 | func (instr *JSR) Execute(frame *rtda.Frame) { 12 | panic("todo") 13 | } 14 | 15 | // Jump subroutine (wide index) 16 | type JSR_W struct { 17 | offset int 18 | } 19 | 20 | func (instr *JSR_W) FetchOperands(reader *base.CodeReader) { 21 | instr.offset = int(reader.ReadInt32()) 22 | } 23 | func (instr *JSR_W) Execute(frame *rtda.Frame) { 24 | panic("todo") 25 | } 26 | 27 | // Return from subroutine 28 | type RET struct{ base.Index8Instruction } 29 | 30 | func (instr *RET) Execute(frame *rtda.Frame) { 31 | panic("todo") 32 | } 33 | -------------------------------------------------------------------------------- /instructions/control/lookupswitch.go: -------------------------------------------------------------------------------- 1 | package control 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | /* 9 | lookupswitch 10 | <0-3 byte pad> 11 | defaultbyte1 12 | defaultbyte2 13 | defaultbyte3 14 | defaultbyte4 15 | npairs1 16 | npairs2 17 | npairs3 18 | npairs4 19 | match-offset pairs... 20 | */ 21 | // Access jump table by key match and jump 22 | type LookupSwitch struct { // TODO 23 | defaultOffset int32 24 | npairs int32 25 | matchOffsets []int32 26 | } 27 | 28 | func (instr *LookupSwitch) FetchOperands(reader *base.CodeReader) { 29 | reader.SkipPadding() 30 | instr.defaultOffset = reader.ReadInt32() 31 | instr.npairs = reader.ReadInt32() 32 | instr.matchOffsets = reader.ReadInt32s(instr.npairs * 2) 33 | } 34 | 35 | func (instr *LookupSwitch) Execute(frame *rtda.Frame) { 36 | key := frame.PopInt() 37 | for i := int32(0); i < instr.npairs*2; i += 2 { 38 | if instr.matchOffsets[i] == key { 39 | offset := instr.matchOffsets[i+1] 40 | base.Branch(frame, int(offset)) 41 | return 42 | } 43 | } 44 | base.Branch(frame, int(instr.defaultOffset)) 45 | } 46 | -------------------------------------------------------------------------------- /instructions/control/return.go: -------------------------------------------------------------------------------- 1 | package control 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func NewXReturn(d bool) *XReturn { return &XReturn{d: d} } 9 | 10 | // Return void from method 11 | type Return struct{ base.NoOperandsInstruction } 12 | 13 | func (instr *Return) Execute(frame *rtda.Frame) { 14 | thread := frame.Thread 15 | thread.PopFrame() 16 | } 17 | 18 | // xreturn: Return XXX from method 19 | type XReturn struct { 20 | base.NoOperandsInstruction 21 | d bool 22 | } 23 | 24 | func (instr *XReturn) Execute(frame *rtda.Frame) { 25 | thread := frame.Thread 26 | currentFrame := thread.PopFrame() 27 | invokerFrame := thread.TopFrame() 28 | ref := currentFrame.PopL(instr.d) 29 | invokerFrame.PushL(ref, instr.d) 30 | } 31 | -------------------------------------------------------------------------------- /instructions/control/tableswitch.go: -------------------------------------------------------------------------------- 1 | package control 2 | 3 | import ( 4 | //"fmt" 5 | "github.com/zxh0/jvm.go/instructions/base" 6 | "github.com/zxh0/jvm.go/rtda" 7 | ) 8 | 9 | /* 10 | tableswitch 11 | <0-3 byte pad> 12 | defaultbyte1 13 | defaultbyte2 14 | defaultbyte3 15 | defaultbyte4 16 | lowbyte1 17 | lowbyte2 18 | lowbyte3 19 | lowbyte4 20 | highbyte1 21 | highbyte2 22 | highbyte3 23 | highbyte4 24 | jump offsets... 25 | */ 26 | // Access jump table by index and jump 27 | type TableSwitch struct { // TODO 28 | defaultOffset int32 29 | low int32 30 | high int32 31 | jumpOffsets []int32 32 | } 33 | 34 | func (instr *TableSwitch) FetchOperands(reader *base.CodeReader) { 35 | reader.SkipPadding() 36 | instr.defaultOffset = reader.ReadInt32() 37 | instr.low = reader.ReadInt32() 38 | instr.high = reader.ReadInt32() 39 | jumpOffsetsCount := instr.high - instr.low + 1 40 | instr.jumpOffsets = reader.ReadInt32s(jumpOffsetsCount) 41 | } 42 | 43 | func (instr *TableSwitch) Execute(frame *rtda.Frame) { 44 | index := frame.PopInt() 45 | 46 | var offset int 47 | if index >= instr.low && index <= instr.high { 48 | offset = int(instr.jumpOffsets[index-instr.low]) 49 | } else { 50 | offset = int(instr.defaultOffset) 51 | } 52 | 53 | base.Branch(frame, offset) 54 | } 55 | -------------------------------------------------------------------------------- /instructions/decoder.go: -------------------------------------------------------------------------------- 1 | package instructions 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | ) 6 | 7 | func Decode(code []byte) []base.Instruction { 8 | reader := base.NewCodeReader(code) 9 | decoded := make([]base.Instruction, len(code)) 10 | 11 | for reader.Position() < len(code) { 12 | decoded[reader.Position()] = decodeInstruction(reader) 13 | } 14 | 15 | return decoded 16 | } 17 | 18 | func decodeInstruction(reader *base.CodeReader) base.Instruction { 19 | opcode := reader.ReadUint8() 20 | instr := newInstruction(opcode) 21 | instr.FetchOperands(reader) 22 | return instr 23 | } 24 | -------------------------------------------------------------------------------- /instructions/loads/load.go: -------------------------------------------------------------------------------- 1 | package loads 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func NewLoad(d bool) *Load { 9 | return &Load{d: d} 10 | } 11 | 12 | // xload: Load XXX from local variable 13 | type Load struct { 14 | base.Index8Instruction 15 | d bool // long or double 16 | } 17 | 18 | func (instr *Load) Execute(frame *rtda.Frame) { 19 | frame.Load(instr.Index, instr.d) 20 | } 21 | -------------------------------------------------------------------------------- /instructions/loads/load_n.go: -------------------------------------------------------------------------------- 1 | package loads 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func NewLoadN(n uint, d bool) *LoadN { 9 | return &LoadN{n: n, d: d} 10 | } 11 | 12 | // xload_n: Load XXX from local variable 13 | type LoadN struct { 14 | base.NoOperandsInstruction 15 | n uint 16 | d bool // long or double 17 | } 18 | 19 | func (instr *LoadN) Execute(frame *rtda.Frame) { 20 | frame.Load(instr.n, instr.d) 21 | } 22 | -------------------------------------------------------------------------------- /instructions/math/dop.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/zxh0/jvm.go/instructions/base" 7 | "github.com/zxh0/jvm.go/rtda" 8 | ) 9 | 10 | func NewDNeg() *DNeg { return &DNeg{} } 11 | func NewDAdd() *DOp { return &DOp{op: dadd} } 12 | func NewDSub() *DOp { return &DOp{op: dsub} } 13 | func NewDMul() *DOp { return &DOp{op: dmul} } 14 | func NewDDiv() *DOp { return &DOp{op: ddiv} } 15 | func NewDRem() *DOp { return &DOp{op: drem} } 16 | 17 | func dadd(a, b float64) float64 { return a + b } 18 | func dsub(a, b float64) float64 { return a - b } 19 | func dmul(a, b float64) float64 { return a * b } 20 | func ddiv(a, b float64) float64 { return a / b } 21 | func drem(a, b float64) float64 { return math.Mod(a, b) } // todo 22 | 23 | type DOp struct { 24 | base.NoOperandsInstruction 25 | op func(a, b float64) float64 26 | } 27 | 28 | func (instr *DOp) Execute(frame *rtda.Frame) { 29 | v2 := frame.PopDouble() 30 | v1 := frame.PopDouble() 31 | frame.PushDouble(instr.op(v1, v2)) 32 | } 33 | 34 | // Negate double 35 | type DNeg struct{ base.NoOperandsInstruction } 36 | 37 | func (instr *DNeg) Execute(frame *rtda.Frame) { 38 | val := frame.PopDouble() 39 | frame.PushDouble(-val) 40 | } 41 | -------------------------------------------------------------------------------- /instructions/math/fop.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/zxh0/jvm.go/instructions/base" 7 | "github.com/zxh0/jvm.go/rtda" 8 | ) 9 | 10 | func NewFNeg() *FNeg { return &FNeg{} } 11 | func NewFAdd() *FOp { return &FOp{op: fadd} } 12 | func NewFSub() *FOp { return &FOp{op: fsub} } 13 | func NewFMul() *FOp { return &FOp{op: fmul} } 14 | func NewFDiv() *FOp { return &FOp{op: fdiv} } 15 | func NewFRem() *FOp { return &FOp{op: frem} } 16 | 17 | func fadd(a, b float32) float32 { return a + b } 18 | func fsub(a, b float32) float32 { return a - b } 19 | func fmul(a, b float32) float32 { return a * b } 20 | func fdiv(a, b float32) float32 { return a / b } 21 | func frem(a, b float32) float32 { return float32(math.Mod(float64(a), float64(b))) } // todo 22 | 23 | type FOp struct { 24 | base.NoOperandsInstruction 25 | op func(a, b float32) float32 26 | } 27 | 28 | func (instr *FOp) Execute(frame *rtda.Frame) { 29 | v2 := frame.PopFloat() 30 | v1 := frame.PopFloat() 31 | frame.PushFloat(instr.op(v1, v2)) 32 | } 33 | 34 | // Negate float 35 | type FNeg struct{ base.NoOperandsInstruction } 36 | 37 | func (instr *FNeg) Execute(frame *rtda.Frame) { 38 | val := frame.PopFloat() 39 | frame.PushFloat(-val) 40 | } 41 | -------------------------------------------------------------------------------- /instructions/references/arraylength.go: -------------------------------------------------------------------------------- 1 | package references 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | // Get length of array 9 | type ArrayLength struct{ base.NoOperandsInstruction } 10 | 11 | func (instr *ArrayLength) Execute(frame *rtda.Frame) { 12 | arrRef := frame.PopRef() 13 | 14 | if arrRef == nil { 15 | frame.Thread.ThrowNPE() 16 | return 17 | } 18 | 19 | arrLen := arrRef.ArrayLength() 20 | frame.PushInt(arrLen) 21 | } 22 | -------------------------------------------------------------------------------- /instructions/references/athrow.go: -------------------------------------------------------------------------------- 1 | package references 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | // Throw exception or error 9 | type AThrow struct{ base.NoOperandsInstruction } 10 | 11 | func (instr *AThrow) Execute(frame *rtda.Frame) { 12 | thread := frame.Thread 13 | 14 | ex := frame.PopRef() 15 | if ex == nil { 16 | thread.ThrowNPE() 17 | return 18 | } 19 | 20 | for { 21 | frame := thread.CurrentFrame() 22 | pc := frame.NextPC - 1 23 | 24 | handlerPC := frame.Method.FindExceptionHandler(ex.Class, pc) // TODO 25 | if handlerPC >= 0 { 26 | frame.ClearStack() 27 | frame.PushRef(ex) 28 | frame.NextPC = handlerPC 29 | return 30 | } 31 | 32 | thread.PopFrame() 33 | if thread.IsStackEmpty() { 34 | break 35 | } 36 | } 37 | 38 | thread.HandleUncaughtException(ex) 39 | } 40 | -------------------------------------------------------------------------------- /instructions/references/checkcast.go: -------------------------------------------------------------------------------- 1 | package references 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | "github.com/zxh0/jvm.go/rtda/heap" 7 | ) 8 | 9 | // Check whether object is of given type 10 | type CheckCast struct { 11 | base.Index16Instruction 12 | class *heap.Class 13 | } 14 | 15 | func (instr *CheckCast) Execute(frame *rtda.Frame) { 16 | if instr.class == nil { 17 | cp := frame.GetConstantPool() 18 | kClass := cp.GetConstantClass(instr.Index) 19 | instr.class = kClass.GetClass() 20 | } 21 | 22 | ref := frame.PopRef() 23 | frame.PushRef(ref) 24 | 25 | if ref == nil { 26 | return 27 | } 28 | 29 | if !ref.IsInstanceOf(instr.class) { 30 | frame.Thread.ThrowClassCastException(ref.Class, instr.class) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /instructions/references/getfield.go: -------------------------------------------------------------------------------- 1 | package references 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | "github.com/zxh0/jvm.go/rtda/heap" 7 | ) 8 | 9 | // Fetch field from object 10 | type GetField struct { 11 | base.Index16Instruction 12 | field *heap.Field 13 | } 14 | 15 | func (instr *GetField) Execute(frame *rtda.Frame) { 16 | if instr.field == nil { 17 | cp := frame.GetConstantPool() 18 | kFieldRef := cp.GetConstantFieldRef(instr.Index) 19 | instr.field = kFieldRef.GetField(false) 20 | } 21 | 22 | ref := frame.PopRef() 23 | if ref == nil { 24 | frame.Thread.ThrowNPE() 25 | return 26 | } 27 | 28 | val := instr.field.GetValue(ref) 29 | frame.PushL(val, instr.field.IsLongOrDouble) 30 | } 31 | -------------------------------------------------------------------------------- /instructions/references/getstatic.go: -------------------------------------------------------------------------------- 1 | package references 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | "github.com/zxh0/jvm.go/rtda/heap" 7 | ) 8 | 9 | // Get static field from class 10 | type GetStatic struct { 11 | base.Index16Instruction 12 | field *heap.Field 13 | } 14 | 15 | func (instr *GetStatic) Execute(frame *rtda.Frame) { 16 | if instr.field == nil { 17 | cp := frame.GetConstantPool() 18 | kFieldRef := cp.GetConstantFieldRef(instr.Index) 19 | instr.field = kFieldRef.GetField(true) 20 | } 21 | 22 | class := instr.field.Class 23 | if class.InitializationNotStarted() { 24 | frame.RevertNextPC() // undo getstatic 25 | frame.Thread.InitClass(class) 26 | return 27 | } 28 | 29 | val := instr.field.GetStaticValue() 30 | frame.PushL(val, instr.field.IsLongOrDouble) 31 | } 32 | -------------------------------------------------------------------------------- /instructions/references/instanceof.go: -------------------------------------------------------------------------------- 1 | package references 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | // Determine if object is of given type 9 | type InstanceOf struct{ base.Index16Instruction } 10 | 11 | func (instr *InstanceOf) Execute(frame *rtda.Frame) { 12 | ref := frame.PopRef() 13 | 14 | cp := frame.GetConstantPool() 15 | kClass := cp.GetConstantClass(instr.Index) 16 | class := kClass.GetClass() 17 | 18 | if ref == nil { 19 | frame.PushInt(0) 20 | } else if ref.IsInstanceOf(class) { 21 | frame.PushInt(1) 22 | } else { 23 | frame.PushInt(0) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /instructions/references/invokeinterface.go: -------------------------------------------------------------------------------- 1 | package references 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | "github.com/zxh0/jvm.go/rtda/heap" 7 | ) 8 | 9 | // Invoke interface method 10 | type InvokeInterface struct { 11 | index uint 12 | // count uint8 13 | // zero uint8 14 | 15 | // optimization 16 | kMethodRef *heap.ConstantInterfaceMethodRef 17 | argSlotCount uint 18 | } 19 | 20 | func (instr *InvokeInterface) FetchOperands(reader *base.CodeReader) { 21 | instr.index = uint(reader.ReadUint16()) 22 | reader.ReadUint8() // count 23 | reader.ReadUint8() // must be 0 24 | } 25 | 26 | func (instr *InvokeInterface) Execute(frame *rtda.Frame) { 27 | if instr.kMethodRef == nil { 28 | cp := frame.GetConstantPool() 29 | instr.kMethodRef = cp.GetConstant(instr.index).(*heap.ConstantInterfaceMethodRef) 30 | instr.argSlotCount = instr.kMethodRef.ParamSlotCount 31 | } 32 | 33 | ref := frame.TopRef(instr.argSlotCount) 34 | if ref == nil { 35 | panic("NPE") // todo 36 | } 37 | 38 | method := instr.kMethodRef.FindInterfaceMethod(ref) 39 | frame.Thread.InvokeMethod(method) 40 | } 41 | -------------------------------------------------------------------------------- /instructions/references/invokespecial.go: -------------------------------------------------------------------------------- 1 | package references 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | "github.com/zxh0/jvm.go/rtda/heap" 7 | ) 8 | 9 | // Invoke instance method; 10 | // special handling for superclass, private, and instance initialization method invocations 11 | type InvokeSpecial struct{ base.Index16Instruction } 12 | 13 | func (instr *InvokeSpecial) Execute(frame *rtda.Frame) { 14 | cp := frame.GetConstantPool() 15 | k := cp.GetConstant(instr.Index) 16 | if kMethodRef, ok := k.(*heap.ConstantMethodRef); ok { 17 | method := kMethodRef.GetMethod(false) 18 | frame.Thread.InvokeMethod(method) 19 | } else { 20 | method := k.(*heap.ConstantInterfaceMethodRef).GetMethod(false) 21 | frame.Thread.InvokeMethod(method) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /instructions/references/invokestatic.go: -------------------------------------------------------------------------------- 1 | package references 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | "github.com/zxh0/jvm.go/rtda/heap" 7 | ) 8 | 9 | // Invoke a class (static) method 10 | type InvokeStatic struct { 11 | base.Index16Instruction 12 | method *heap.Method 13 | } 14 | 15 | func (instr *InvokeStatic) Execute(frame *rtda.Frame) { 16 | if instr.method == nil { 17 | cp := frame.GetConstantPool() 18 | k := cp.GetConstant(instr.Index) 19 | if kMethodRef, ok := k.(*heap.ConstantMethodRef); ok { 20 | instr.method = kMethodRef.GetMethod(true) 21 | } else { 22 | instr.method = k.(*heap.ConstantInterfaceMethodRef).GetMethod(true) 23 | } 24 | } 25 | 26 | // init class 27 | class := instr.method.Class 28 | if class.InitializationNotStarted() { 29 | frame.RevertNextPC() 30 | frame.Thread.InitClass(class) 31 | return 32 | } 33 | 34 | frame.Thread.InvokeMethod(instr.method) 35 | } 36 | -------------------------------------------------------------------------------- /instructions/references/invokevirtual.go: -------------------------------------------------------------------------------- 1 | package references 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | "github.com/zxh0/jvm.go/rtda/heap" 7 | ) 8 | 9 | // Invoke instance method; dispatch based on class 10 | type InvokeVirtual struct { 11 | base.Index16Instruction 12 | kMethodRef *heap.ConstantMethodRef 13 | argSlotCount uint 14 | } 15 | 16 | func (instr *InvokeVirtual) Execute(frame *rtda.Frame) { 17 | if instr.kMethodRef == nil { 18 | cp := frame.GetConstantPool() 19 | instr.kMethodRef = cp.GetConstant(instr.Index).(*heap.ConstantMethodRef) 20 | instr.argSlotCount = instr.kMethodRef.ParamSlotCount 21 | } 22 | 23 | ref := frame.TopRef(instr.argSlotCount) 24 | if ref == nil { 25 | frame.Thread.ThrowNPE() 26 | return 27 | } 28 | 29 | method := instr.kMethodRef.GetVirtualMethod(ref) 30 | frame.Thread.InvokeMethod(method) 31 | } 32 | -------------------------------------------------------------------------------- /instructions/references/monitor.go: -------------------------------------------------------------------------------- 1 | package references 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | // Enter monitor for object 9 | type MonitorEnter struct{ base.NoOperandsInstruction } 10 | 11 | func (instr *MonitorEnter) Execute(frame *rtda.Frame) { 12 | thread := frame.Thread 13 | ref := frame.PopRef() 14 | if ref == nil { 15 | frame.RevertNextPC() 16 | thread.ThrowNPE() 17 | } else { 18 | ref.Monitor.Enter(thread) 19 | } 20 | } 21 | 22 | // Exit monitor for object 23 | type MonitorExit struct{ base.NoOperandsInstruction } 24 | 25 | func (instr *MonitorExit) Execute(frame *rtda.Frame) { 26 | thread := frame.Thread 27 | ref := frame.PopRef() 28 | if ref == nil { 29 | frame.RevertNextPC() 30 | thread.ThrowNPE() 31 | } else { 32 | ref.Monitor.Exit(thread) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /instructions/references/new.go: -------------------------------------------------------------------------------- 1 | package references 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | "github.com/zxh0/jvm.go/rtda/heap" 7 | ) 8 | 9 | // Create new object 10 | type New struct { 11 | base.Index16Instruction 12 | class *heap.Class 13 | } 14 | 15 | func (instr *New) Execute(frame *rtda.Frame) { 16 | if instr.class == nil { 17 | cp := frame.GetConstantPool() 18 | kClass := cp.GetConstantClass(instr.Index) 19 | instr.class = kClass.GetClass() 20 | } 21 | 22 | // init class 23 | if instr.class.InitializationNotStarted() { 24 | frame.RevertNextPC() // undo new 25 | frame.Thread.InitClass(instr.class) 26 | return 27 | } 28 | 29 | ref := instr.class.NewObj() 30 | frame.PushRef(ref) 31 | } 32 | -------------------------------------------------------------------------------- /instructions/references/putfield.go: -------------------------------------------------------------------------------- 1 | package references 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | "github.com/zxh0/jvm.go/rtda/heap" 7 | ) 8 | 9 | // Set field in object 10 | type PutField struct { 11 | base.Index16Instruction 12 | field *heap.Field 13 | } 14 | 15 | func (instr *PutField) Execute(frame *rtda.Frame) { 16 | if instr.field == nil { 17 | cp := frame.GetConstantPool() 18 | kFieldRef := cp.GetConstantFieldRef(instr.Index) 19 | instr.field = kFieldRef.GetField(false) 20 | } 21 | 22 | val := frame.PopL(instr.field.IsLongOrDouble) 23 | ref := frame.PopRef() 24 | if ref == nil { 25 | frame.Thread.ThrowNPE() 26 | return 27 | } 28 | 29 | instr.field.PutValue(ref, val) 30 | } 31 | -------------------------------------------------------------------------------- /instructions/references/putstatic.go: -------------------------------------------------------------------------------- 1 | package references 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | "github.com/zxh0/jvm.go/rtda/heap" 7 | ) 8 | 9 | // Set static field in class 10 | type PupStatic struct { 11 | base.Index16Instruction 12 | field *heap.Field 13 | } 14 | 15 | func (instr *PupStatic) Execute(frame *rtda.Frame) { 16 | if instr.field == nil { 17 | cp := frame.GetConstantPool() 18 | kFieldRef := cp.GetConstantFieldRef(instr.Index) 19 | instr.field = kFieldRef.GetField(true) 20 | } 21 | 22 | class := instr.field.Class 23 | if class.InitializationNotStarted() { 24 | frame.RevertNextPC() 25 | frame.Thread.InitClass(class) 26 | return 27 | } 28 | 29 | val := frame.PopL(instr.field.IsLongOrDouble) 30 | instr.field.PutStaticValue(val) 31 | } 32 | -------------------------------------------------------------------------------- /instructions/reserved/invokenative.go: -------------------------------------------------------------------------------- 1 | package reserved 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/zxh0/jvm.go/instructions/base" 7 | "github.com/zxh0/jvm.go/native" 8 | "github.com/zxh0/jvm.go/rtda" 9 | ) 10 | 11 | // Invoke native method 12 | type InvokeNative struct{ base.NoOperandsInstruction } 13 | 14 | func (instr *InvokeNative) Execute(frame *rtda.Frame) { 15 | method := frame.Method 16 | if frame.Thread.VMOptions.VerboseJNI { 17 | fmt.Printf("invokenative: %s.%s%s\n", 18 | method.Class.Name, method.Name, method.Descriptor) 19 | } 20 | 21 | // TODO: cache native method 22 | nativeMethod := native.FindNativeMethod(method) 23 | nativeMethod(frame) 24 | } 25 | -------------------------------------------------------------------------------- /instructions/stack/pop.go: -------------------------------------------------------------------------------- 1 | package stack 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | // Pop the top operand stack value 9 | type Pop struct{ base.NoOperandsInstruction } 10 | 11 | func (instr *Pop) Execute(frame *rtda.Frame) { 12 | frame.Pop() 13 | } 14 | 15 | // Pop the top one or two operand stack values 16 | type Pop2 struct{ base.NoOperandsInstruction } 17 | 18 | func (instr *Pop2) Execute(frame *rtda.Frame) { 19 | frame.Pop() 20 | frame.Pop() 21 | } 22 | -------------------------------------------------------------------------------- /instructions/stack/swap.go: -------------------------------------------------------------------------------- 1 | package stack 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | // Swap the top two operand stack values 9 | type Swap struct{ base.NoOperandsInstruction } 10 | 11 | func (instr *Swap) Execute(frame *rtda.Frame) { 12 | val1 := frame.Pop() 13 | val2 := frame.Pop() 14 | frame.Push(val1) 15 | frame.Push(val2) 16 | } 17 | -------------------------------------------------------------------------------- /instructions/stores/store.go: -------------------------------------------------------------------------------- 1 | package stores 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func NewStore(d bool) *Store { 9 | return &Store{d: d} 10 | } 11 | 12 | // xstore: Store XXX into local variable 13 | type Store struct { 14 | base.Index8Instruction 15 | d bool // long or double 16 | } 17 | 18 | func (instr *Store) Execute(frame *rtda.Frame) { 19 | frame.Store(instr.Index, instr.d) 20 | } 21 | -------------------------------------------------------------------------------- /instructions/stores/store_n.go: -------------------------------------------------------------------------------- 1 | package stores 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/instructions/base" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func NewStoreN(n uint, d bool) *StoreN { 9 | return &StoreN{n: n, d: d} 10 | } 11 | 12 | // xstore_n: Store XXX into local variable 13 | type StoreN struct { 14 | base.NoOperandsInstruction 15 | n uint 16 | d bool // long or double 17 | } 18 | 19 | func (instr *StoreN) Execute(frame *rtda.Frame) { 20 | frame.Store(instr.n, instr.d) 21 | } 22 | -------------------------------------------------------------------------------- /jimage/string_test.go: -------------------------------------------------------------------------------- 1 | package jimage 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestUnmaskedHashCode(t *testing.T) { 10 | require.Equal(t, int32(16777619), unmaskedHashCode("", HashMultiplier)) 11 | require.Equal(t, int32(1213053849), unmaskedHashCode("foo", HashMultiplier)) 12 | require.Equal(t, int32(977475810), unmaskedHashCode("bar", HashMultiplier)) 13 | require.Equal(t, int32(-1678740824), unmaskedHashCode("Hello, World!", HashMultiplier)) 14 | require.Equal(t, int32(1641313752), unmaskedHashCode("你好,世界!", HashMultiplier)) 15 | require.Equal(t, int32(-348596783), unmaskedHashCode("123456789:一二三四五六七八九", HashMultiplier)) 16 | } 17 | -------------------------------------------------------------------------------- /jimage/utils.go: -------------------------------------------------------------------------------- 1 | package jimage 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | func IsTreeInfoResource(path string) bool { 8 | return strings.HasPrefix(path, "/packages") || 9 | strings.HasPrefix(path, "/modules") 10 | } 11 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/jvm.go/129b147ebcc820279748f6095c916da1986ebef1/logo.png -------------------------------------------------------------------------------- /logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/jvm.go/129b147ebcc820279748f6095c916da1986ebef1/logo.psd -------------------------------------------------------------------------------- /module/info_test.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "io/ioutil" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestModuleInfo(t *testing.T) { 11 | bytes, err := ioutil.ReadFile("../test/testdata/java13/module-info.class") 12 | require.NoError(t, err) 13 | 14 | info := ParseModuleInfo(bytes) 15 | require.Equal(t, info, &Info{ 16 | Name: "hello.modules", 17 | Flags: 0, 18 | Version: "0.1", 19 | Requires: []Require{ 20 | { 21 | Name: "java.base", 22 | Flags: 0x8000, 23 | Version: "13.0.1", 24 | }, 25 | }, 26 | Exports: []Export{ 27 | { 28 | Package: "hello", 29 | Flags: 0, 30 | To: []string{}, 31 | }, 32 | }, 33 | Opens: []Open{}, 34 | Uses: []string{}, 35 | Provides: []Provide{}, 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /module/module.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | // TODO 4 | type Module interface { 5 | GetInfo() *Info 6 | GetName() string 7 | GetVersion() string 8 | ReadClass(className string) ([]byte, error) 9 | } 10 | 11 | type BaseModule struct { 12 | info *Info 13 | } 14 | 15 | func (bm *BaseModule) GetInfo() *Info { 16 | return bm.info 17 | } 18 | 19 | func (bm *BaseModule) GetName() string { 20 | return bm.info.Name 21 | } 22 | func (bm *BaseModule) GetVersion() string { 23 | return bm.info.Version 24 | } 25 | -------------------------------------------------------------------------------- /module/module_automatic.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | // JAR without module-info.class 4 | type AutomaticModule struct { 5 | BaseModule 6 | // TODO 7 | } 8 | 9 | func NewAutomaticModule(path string) *AutomaticModule { 10 | return &AutomaticModule{ 11 | // TODO 12 | } 13 | } 14 | 15 | func (m *AutomaticModule) ReadClass(name string) ([]byte, error) { 16 | panic("TODO") 17 | } 18 | -------------------------------------------------------------------------------- /module/module_exploded.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/vmutils" 5 | ) 6 | 7 | type ExplodedModule struct { 8 | BaseModule 9 | dir *vmutils.Dir 10 | } 11 | 12 | func NewExplodedModule(path string) *ExplodedModule { 13 | dir, err := vmutils.NewDir(path) 14 | if err != nil { 15 | panic(err) // TODO 16 | } 17 | 18 | classData, err := dir.ReadFile("module-info.class") 19 | if err != nil { 20 | panic(err) // TODO 21 | } 22 | 23 | return &ExplodedModule{ 24 | dir: dir, 25 | BaseModule: BaseModule{ 26 | info: ParseModuleInfo(classData), 27 | }, 28 | } 29 | } 30 | 31 | func (m *ExplodedModule) ReadClass(name string) ([]byte, error) { 32 | return m.dir.ReadFile(name + ".class") 33 | } 34 | -------------------------------------------------------------------------------- /module/module_jar.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/vmutils" 5 | ) 6 | 7 | // JAR with module-info.class 8 | type ModularJAR struct { 9 | BaseModule 10 | jar *vmutils.ZipFile 11 | } 12 | 13 | func NewModularJAR(path string) *ModularJAR { 14 | jar, err := vmutils.OpenZipFile(path) 15 | if err != nil { 16 | panic(err) // TODO 17 | } 18 | defer jar.Close() 19 | 20 | classData, err := jar.ReadFile("module-info.class") 21 | if err != nil { 22 | panic(err) // TODO 23 | } 24 | 25 | return &ModularJAR{ 26 | jar: jar, 27 | BaseModule: BaseModule{ 28 | info: ParseModuleInfo(classData), 29 | }, 30 | } 31 | } 32 | 33 | func (m *ModularJAR) ReadClass(name string) ([]byte, error) { 34 | if !m.jar.IsOpen() { 35 | if err := m.jar.Open(); err != nil { 36 | return nil, err 37 | } 38 | } 39 | return m.jar.ReadFile(name + ".class") 40 | } 41 | -------------------------------------------------------------------------------- /module/module_jmod.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/vmutils" 5 | ) 6 | 7 | type JModModule struct { 8 | BaseModule 9 | jmod *vmutils.JModFile 10 | } 11 | 12 | func NewJModModule(path string) *JModModule { 13 | jmod, err := vmutils.OpenJModFile(path) 14 | if err != nil { 15 | panic(err) // TODO 16 | } 17 | 18 | classData, err := jmod.ReadFile("classes/module-info.class") 19 | if err != nil { 20 | panic(err) // TODO 21 | } 22 | 23 | jmod.Close() 24 | return &JModModule{ 25 | jmod: jmod, 26 | BaseModule: BaseModule{ 27 | info: ParseModuleInfo(classData), 28 | }, 29 | } 30 | } 31 | 32 | func (m *JModModule) ReadClass(name string) ([]byte, error) { 33 | if !m.jmod.IsOpen() { 34 | if err := m.jmod.Open(); err != nil { 35 | return nil, err 36 | } 37 | } 38 | return m.jmod.ReadFile("classes/" + name + ".class") 39 | } 40 | -------------------------------------------------------------------------------- /module/module_unnamed.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | type UnnamedModule struct { 4 | BaseModule 5 | // TODO 6 | } 7 | -------------------------------------------------------------------------------- /module/path.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "sort" 5 | "strings" 6 | ) 7 | 8 | type Path []Module 9 | 10 | func (path Path) Sort() { 11 | sort.Sort(path) 12 | } 13 | 14 | // TODO 15 | func (path Path) ReadClass(className string) (string, []byte) { 16 | for _, m := range path { 17 | if data, err := m.ReadClass(className); err == nil { 18 | return "todo", data 19 | } 20 | } 21 | panic("class not found:" + className) 22 | } 23 | 24 | func (path Path) findModule(name string) Module { 25 | for _, module := range path { 26 | if module.GetName() == name { 27 | return module 28 | } 29 | } 30 | return nil 31 | } 32 | 33 | // sort.Interface 34 | func (path Path) Len() int { 35 | return len(path) 36 | } 37 | func (path Path) Less(i, j int) bool { 38 | name1 := path[i].GetName() 39 | name2 := path[j].GetName() 40 | return strings.Compare(name1, name2) < 0 41 | } 42 | func (path Path) Swap(i, j int) { 43 | path[i], path[j] = path[j], path[i] 44 | } 45 | -------------------------------------------------------------------------------- /module/path_checker.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | // TODO: check dependency loop 4 | func CheckDeps(path Path, rootModuleName string) Path { 5 | rootModule := path.findModule(rootModuleName) 6 | if rootModule == nil { 7 | panic("unknown module: " + rootModule.GetName() + "@" + rootModule.GetVersion()) 8 | } 9 | 10 | checkList := []Module{rootModule} 11 | checked := map[string]Module{} 12 | 13 | for len(checkList) > 0 { 14 | m := checkList[0] 15 | checkList = checkList[1:] 16 | 17 | if checked[m.GetName()] == nil { 18 | for _, require := range m.GetInfo().Requires { 19 | if checked[require.Name] == nil { 20 | if found := path.findModule(require.Name); found != nil { 21 | checkList = append(checkList, found) 22 | } else { 23 | panic("unknown module: " + require.Name + "@" + require.Version) 24 | } 25 | } 26 | } 27 | checked[m.GetName()] = m 28 | } 29 | } 30 | 31 | checkedList := make([]Module, 0, len(checked)) 32 | for _, m := range checked { 33 | checkedList = append(checkedList, m) 34 | } 35 | 36 | checkedPath := Path(checkedList) 37 | checkedPath.Sort() 38 | return checkedPath 39 | } 40 | -------------------------------------------------------------------------------- /native/all/init.go: -------------------------------------------------------------------------------- 1 | package all 2 | 3 | import ( 4 | _ "github.com/zxh0/jvm.go/native/java/awt" 5 | _ "github.com/zxh0/jvm.go/native/java/io" 6 | _ "github.com/zxh0/jvm.go/native/java/lang" 7 | _ "github.com/zxh0/jvm.go/native/java/lang/invoke" 8 | _ "github.com/zxh0/jvm.go/native/java/lang/reflect" 9 | _ "github.com/zxh0/jvm.go/native/java/net" 10 | _ "github.com/zxh0/jvm.go/native/java/security" 11 | _ "github.com/zxh0/jvm.go/native/java/util" 12 | _ "github.com/zxh0/jvm.go/native/java/util/concurrent/atomic" 13 | _ "github.com/zxh0/jvm.go/native/java/util/jar" 14 | _ "github.com/zxh0/jvm.go/native/java/util/zip" 15 | _ "github.com/zxh0/jvm.go/native/sun/awt" 16 | _ "github.com/zxh0/jvm.go/native/sun/io" 17 | _ "github.com/zxh0/jvm.go/native/sun/java2d/opengl" 18 | _ "github.com/zxh0/jvm.go/native/sun/management" 19 | _ "github.com/zxh0/jvm.go/native/sun/misc" 20 | _ "github.com/zxh0/jvm.go/native/sun/nio/ch" 21 | _ "github.com/zxh0/jvm.go/native/sun/reflect" 22 | ) 23 | -------------------------------------------------------------------------------- /native/java/awt/Component.go: -------------------------------------------------------------------------------- 1 | package awt 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | ) 6 | 7 | func init() { 8 | } 9 | 10 | func _comp(method native.Method, name, desc string) { 11 | native.Register("java/awt/Component", name, desc, method) 12 | } 13 | -------------------------------------------------------------------------------- /native/java/awt/Container.go: -------------------------------------------------------------------------------- 1 | package awt 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | ) 6 | 7 | func init() { 8 | } 9 | 10 | func _container(method native.Method, name, desc string) { 11 | native.Register("java/awt/Container", name, desc, method) 12 | } 13 | -------------------------------------------------------------------------------- /native/java/awt/Cursor.go: -------------------------------------------------------------------------------- 1 | package awt 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | ) 6 | 7 | func init() { 8 | } 9 | 10 | func _cursor(method native.Method, name, desc string) { 11 | native.Register("java/awt/Cursor", name, desc, method) 12 | } 13 | -------------------------------------------------------------------------------- /native/java/awt/Font.go: -------------------------------------------------------------------------------- 1 | package awt 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | ) 6 | 7 | func init() { 8 | } 9 | 10 | func _font(method native.Method, name, desc string) { 11 | native.Register("java/awt/Font", name, desc, method) 12 | } 13 | -------------------------------------------------------------------------------- /native/java/awt/Frame.go: -------------------------------------------------------------------------------- 1 | package awt 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | ) 6 | 7 | func _frame(method native.Method, name, desc string) { 8 | native.Register("java/awt/Frame", name, desc, method) 9 | } 10 | -------------------------------------------------------------------------------- /native/java/awt/Toolkit.go: -------------------------------------------------------------------------------- 1 | package awt 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | ) 6 | 7 | func init() { 8 | } 9 | 10 | func _tk(method native.Method, name, desc string) { 11 | native.Register("java/awt/Toolkit", name, desc, method) 12 | } 13 | -------------------------------------------------------------------------------- /native/java/awt/Window.go: -------------------------------------------------------------------------------- 1 | package awt 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | ) 6 | 7 | func _window(method native.Method, name, desc string) { 8 | native.Register("java/awt/Window", name, desc, method) 9 | } 10 | -------------------------------------------------------------------------------- /native/java/io/FileDescriptor.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _fd(set, "set", "(I)J") 10 | } 11 | 12 | func _fd(method native.Method, name, desc string) { 13 | native.Register("java/io/FileDescriptor", name, desc, method) 14 | } 15 | 16 | func set(frame *rtda.Frame) { 17 | frame.PushLong(0) 18 | } 19 | -------------------------------------------------------------------------------- /native/java/io/FileOutputStream.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/zxh0/jvm.go/native" 7 | "github.com/zxh0/jvm.go/rtda" 8 | ) 9 | 10 | func init() { 11 | _fos(writeBytes, "writeBytes", "([BIIZ)V") 12 | } 13 | 14 | func _fos(method native.Method, name, desc string) { 15 | native.Register("java/io/FileOutputStream", name, desc, method) 16 | } 17 | 18 | // private native void writeBytes(byte b[], int off, int len, boolean append) throws IOException; 19 | // ([BIIZ)V 20 | func writeBytes(frame *rtda.Frame) { 21 | fosObj := frame.GetRefVar(0) // this 22 | byteArrObj := frame.GetRefVar(1) // b 23 | offset := frame.GetIntVar(2) // off 24 | length := frame.GetIntVar(3) // len 25 | //frame.GetBooleanVar(4) // append 26 | 27 | fdObj := fosObj.GetFieldValue("fd", "Ljava/io/FileDescriptor;").Ref 28 | if fdObj.Extra == nil { 29 | goFd := fdObj.GetFieldValue("fd", "I").IntValue() 30 | switch goFd { 31 | case 0: 32 | fdObj.Extra = os.Stdin 33 | case 1: 34 | fdObj.Extra = os.Stdout 35 | case 2: 36 | fdObj.Extra = os.Stderr 37 | default: 38 | fdObj.Extra = os.Stdout 39 | } 40 | } 41 | goFile := fdObj.Extra.(*os.File) 42 | 43 | goBytes := byteArrObj.GetGoBytes() 44 | goBytes = goBytes[offset : offset+length] 45 | goFile.Write(goBytes) 46 | } 47 | -------------------------------------------------------------------------------- /native/java/io/FileSystem.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _fs(getFileSystem, "getFileSystem", "()Ljava/io/FileSystem;") 10 | } 11 | 12 | func _fs(method native.Method, name, desc string) { 13 | native.Register("java/io/FileSystem", name, desc, method) 14 | } 15 | 16 | // public static native FileSystem getFileSystem() 17 | // ()Ljava/io/FileSystem; 18 | func getFileSystem(frame *rtda.Frame) { 19 | thread := frame.Thread 20 | unixFsClass := frame.GetClassLoader().LoadClass("java/io/UnixFileSystem") 21 | if unixFsClass.InitializationNotStarted() { 22 | frame.NextPC = thread.PC // undo getFileSystem 23 | thread.InitClass(unixFsClass) 24 | return 25 | } 26 | 27 | unixFsObj := unixFsClass.NewObj() 28 | frame.PushRef(unixFsObj) 29 | 30 | // call 31 | frame.PushRef(unixFsObj) // this 32 | constructor := unixFsClass.GetDefaultConstructor() 33 | thread.InvokeMethod(constructor) 34 | } 35 | -------------------------------------------------------------------------------- /native/java/io/ObjectStreamClass.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _osc(initNative, "initNative", "()V") 10 | } 11 | 12 | func _osc(method native.Method, name, desc string) { 13 | native.Register("java/io/ObjectStreamClass", name, desc, method) 14 | } 15 | 16 | // private static native void initNative(); 17 | // ()V 18 | func initNative(frame *rtda.Frame) { 19 | // todo 20 | } 21 | -------------------------------------------------------------------------------- /native/java/lang/Class_annotations.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/rtda" 5 | "github.com/zxh0/jvm.go/vmutils" 6 | ) 7 | 8 | func init() { 9 | _class(getRawAnnotations, "getRawAnnotations", "()[B") 10 | } 11 | 12 | // native byte[] getRawAnnotations(); 13 | // ()[B 14 | func getRawAnnotations(frame *rtda.Frame) { 15 | this := frame.GetThis() 16 | 17 | class := this.GetGoClass() 18 | goBytes := class.AnnotationData 19 | if goBytes != nil { 20 | jBytes := vmutils.CastBytesToInt8s(goBytes) 21 | byteArr := frame.GetRuntime().NewByteArray(jBytes) 22 | frame.PushRef(byteArr) 23 | return 24 | } 25 | 26 | frame.PushRef(nil) 27 | } 28 | 29 | // native byte[] getRawTypeAnnotations(); 30 | // ()[B 31 | func getRawTypeAnnotations(frame *rtda.Frame) { 32 | // todo 33 | } 34 | -------------------------------------------------------------------------------- /native/java/lang/Double.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/zxh0/jvm.go/native" 7 | "github.com/zxh0/jvm.go/rtda" 8 | ) 9 | 10 | func init() { 11 | _double(doubleToRawLongBits, "doubleToRawLongBits", "(D)J") 12 | _double(longBitsToDouble, "longBitsToDouble", "(J)D") 13 | } 14 | 15 | func _double(method native.Method, name, desc string) { 16 | native.Register("java/lang/Double", name, desc, method) 17 | } 18 | 19 | // public static native long doubleToRawLongBits(double value); 20 | // (D)J 21 | func doubleToRawLongBits(frame *rtda.Frame) { 22 | value := frame.GetDoubleVar(0) 23 | 24 | // todo 25 | bits := math.Float64bits(value) 26 | frame.PushLong(int64(bits)) 27 | } 28 | 29 | // public static native double longBitsToDouble(long bits); 30 | // (J)D 31 | func longBitsToDouble(frame *rtda.Frame) { 32 | bits := frame.GetLongVar(0) 33 | 34 | // todo 35 | value := math.Float64frombits(uint64(bits)) 36 | frame.PushDouble(value) 37 | } 38 | -------------------------------------------------------------------------------- /native/java/lang/Float.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/zxh0/jvm.go/native" 7 | "github.com/zxh0/jvm.go/rtda" 8 | ) 9 | 10 | func init() { 11 | _float(floatToRawIntBits, "floatToRawIntBits", "(F)I") 12 | _float(intBitsToFloat, "intBitsToFloat", "(I)F") 13 | } 14 | 15 | func _float(method native.Method, name, desc string) { 16 | native.Register("java/lang/Float", name, desc, method) 17 | } 18 | 19 | // public static native int floatToRawIntBits(float value); 20 | // (F)I 21 | func floatToRawIntBits(frame *rtda.Frame) { 22 | value := frame.GetFloatVar(0) 23 | bits := math.Float32bits(value) 24 | 25 | frame.PushInt(int32(bits)) // todo 26 | } 27 | 28 | // public static native float intBitsToFloat(int bits); 29 | // (I)F 30 | func intBitsToFloat(frame *rtda.Frame) { 31 | bits := frame.GetIntVar(0) 32 | value := math.Float32frombits(uint32(bits)) 33 | 34 | frame.PushFloat(value) 35 | } 36 | -------------------------------------------------------------------------------- /native/java/lang/Package.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _package(getSystemPackage0, "getSystemPackage0", "(Ljava/lang/String;)Ljava/lang/String;") 10 | } 11 | 12 | func _package(method native.Method, name, desc string) { 13 | native.Register("java/lang/Package", name, desc, method) 14 | } 15 | 16 | // private static native String getSystemPackage0(String name); 17 | // (Ljava/lang/String;)Ljava/lang/String; 18 | func getSystemPackage0(frame *rtda.Frame) { 19 | // vars := frame. 20 | // name := frame.GetRefVar(0) 21 | 22 | sysPkg := frame.GetClassLoader().JLObjectClass().LoadedFrom.String() 23 | sysPkgObj := frame.GetRuntime().JSFromGoStr(sysPkg) 24 | 25 | frame.PushRef(sysPkgObj) 26 | } 27 | 28 | // private static native String[] getSystemPackages0(); 29 | -------------------------------------------------------------------------------- /native/java/lang/Runtime.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "runtime" 5 | 6 | "github.com/zxh0/jvm.go/native" 7 | "github.com/zxh0/jvm.go/rtda" 8 | ) 9 | 10 | func init() { 11 | _runtime(availableProcessors, "availableProcessors", "()I") 12 | _runtime(freeMemory, "freeMemory", "()J") 13 | } 14 | 15 | func _runtime(method native.Method, name, desc string) { 16 | native.Register("java/lang/Runtime", name, desc, method) 17 | } 18 | 19 | // public native int availableProcessors(); 20 | // ()I 21 | func availableProcessors(frame *rtda.Frame) { 22 | numCPU := runtime.NumCPU() 23 | 24 | frame.PushInt(int32(numCPU)) 25 | } 26 | 27 | // public native long freeMemory(); 28 | // ()J 29 | func freeMemory(frame *rtda.Frame) { 30 | var memStats runtime.MemStats 31 | runtime.ReadMemStats(&memStats) 32 | frees := memStats.Frees 33 | 34 | frame.PushLong(int64(frees)) 35 | } 36 | 37 | // public native long totalMemory(); 38 | // public native long maxMemory(); 39 | // public native void gc(); 40 | // private static native void runFinalization0(); 41 | // public native void traceInstructions(boolean on); 42 | // public native void traceMethodCalls(boolean on); 43 | -------------------------------------------------------------------------------- /native/java/lang/String.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _string(intern, "intern", "()Ljava/lang/String;") 10 | } 11 | 12 | func _string(method native.Method, name, desc string) { 13 | native.Register("java/lang/String", name, desc, method) 14 | } 15 | 16 | // public native String intern(); 17 | // ()Ljava/lang/String; 18 | func intern(frame *rtda.Frame) { 19 | jStr := frame.GetThis() 20 | 21 | goStr := jStr.JSToGoStr() 22 | internedStr := frame.GetRuntime().JSIntern(goStr, jStr) 23 | 24 | frame.PushRef(internedStr) 25 | } 26 | -------------------------------------------------------------------------------- /native/java/lang/Thread_static.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/zxh0/jvm.go/rtda" 7 | ) 8 | 9 | func init() { 10 | _thread(currentThread, "currentThread", "()Ljava/lang/Thread;") 11 | _thread(sleep, "sleep", "(J)V") 12 | } 13 | 14 | // public static native boolean holdsLock(Object obj); 15 | // public static native void yield(); 16 | // private native static StackTraceElement[][] dumpThreads(Thread[] threads); 17 | // private native static Thread[] getThreads(); 18 | 19 | // public static native Thread currentThread(); 20 | // ()Ljava/lang/Thread; 21 | func currentThread(frame *rtda.Frame) { 22 | jThread := frame.Thread.JThread() 23 | frame.PushRef(jThread) 24 | } 25 | 26 | // public static native void sleep(long millis) throws InterruptedException; 27 | // (J)V 28 | func sleep(frame *rtda.Frame) { 29 | millis := frame.GetLongVar(0) 30 | 31 | thread := frame.Thread 32 | if millis < 0 { 33 | thread.ThrowIllegalArgumentException("timeout value is negative") 34 | return 35 | } 36 | 37 | m := millis * int64(time.Millisecond) 38 | d := time.Duration(m) 39 | interrupted := thread.Sleep(d) 40 | 41 | if interrupted { 42 | thread.ThrowInterruptedException("sleep interrupted") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /native/java/lang/reflect/Proxy.go: -------------------------------------------------------------------------------- 1 | package reflect 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | "github.com/zxh0/jvm.go/vmutils" 7 | ) 8 | 9 | func init() { 10 | _proxy(defineClass0, "defineClass0", "(Ljava/lang/ClassLoader;Ljava/lang/String;[BII)Ljava/lang/Class;") 11 | } 12 | 13 | func _proxy(method native.Method, name, desc string) { 14 | native.Register("java/lang/reflect/Proxy", name, desc, method) 15 | } 16 | 17 | // private static native Class defineClass0(ClassLoader loader, String name, 18 | // byte[] b, int off, int len); 19 | // (Ljava/lang/ClassLoader;Ljava/lang/String;[BII)Ljava/lang/Class; 20 | func defineClass0(frame *rtda.Frame) { 21 | if frame.IsStackEmpty() { 22 | _loadClass(frame) 23 | } 24 | 25 | // init class 26 | class := frame.TopRef(0).GetGoClass() 27 | if class.InitializationNotStarted() { 28 | frame.RevertNextPC() 29 | frame.Thread.InitClass(class) 30 | } 31 | } 32 | 33 | func _loadClass(frame *rtda.Frame) { 34 | //loader := frame.GetRefVar(0) 35 | nameObj := frame.GetRefVar(1) 36 | byteArr := frame.GetRefVar(2) 37 | off := frame.GetIntVar(3) 38 | _len := frame.GetIntVar(4) 39 | 40 | name := nameObj.JSToGoStr() 41 | name = vmutils.DotToSlash(name) 42 | data := byteArr.GetGoBytes() 43 | data = data[off : off+_len] 44 | 45 | // todo 46 | class := frame.GetClassLoader().DefineClass(name, data) 47 | frame.PushRef(class.JClass) 48 | } 49 | -------------------------------------------------------------------------------- /native/java/net/Inet4Address.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _i4a(i4a_init, "init", "()V") 10 | } 11 | 12 | func _i4a(method native.Method, name, desc string) { 13 | native.Register("java/net/Inet4Address", name, desc, method) 14 | } 15 | 16 | func i4a_init(frame *rtda.Frame) { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /native/java/net/InetAddress.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _ia(ia_init, "init", "()V") 10 | } 11 | 12 | func _ia(method native.Method, name, desc string) { 13 | native.Register("java/net/InetAddress", name, desc, method) 14 | } 15 | 16 | func ia_init(frame *rtda.Frame) { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /native/java/net/InetAddressImplFactory.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _iaif(iaif_isIPv6Supported, "isIPv6Supported", "()Z") 10 | } 11 | 12 | func _iaif(method native.Method, name, desc string) { 13 | native.Register("java/net/InetAddressImplFactory", name, desc, method) 14 | } 15 | 16 | //static native boolean isIPv6Supported(); 17 | // ()Z 18 | func iaif_isIPv6Supported(frame *rtda.Frame) { 19 | frame.PushBoolean(true) 20 | } 21 | -------------------------------------------------------------------------------- /native/java/net/SocketInputStream.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "time" 7 | 8 | "github.com/zxh0/jvm.go/native" 9 | "github.com/zxh0/jvm.go/rtda" 10 | ) 11 | 12 | func init() { 13 | _sis(sis_init, "init", "()V") 14 | _sis(sis_socketRead0, "socketRead0", "(Ljava/io/FileDescriptor;[BIII)I") 15 | } 16 | 17 | func _sis(method native.Method, name, desc string) { 18 | native.Register("java/net/SocketInputStream", name, desc, method) 19 | } 20 | 21 | // native java/net/SocketOutputStream~init~()V 22 | func sis_init(frame *rtda.Frame) { 23 | } 24 | 25 | //private native int socketRead0(FileDescriptor fd, byte b[], int off, int len, int timeout) 26 | // java/net/SocketInputStream~socketRead0~(Ljava/io/FileDescriptor;[BIII)I 27 | func sis_socketRead0(frame *rtda.Frame) { 28 | //this := frame.GetThis() 29 | fd := frame.GetRefVar(1) 30 | buf := frame.GetRefVar(2) 31 | off := frame.GetIntVar(3) 32 | _len := frame.GetIntVar(4) 33 | 34 | conn := fd.Extra.(net.Conn) 35 | 36 | _timeout := frame.GetIntVar(5) 37 | if _timeout > 0 { 38 | conn.SetDeadline(time.Now().Add(time.Duration(_timeout) * time.Millisecond)) 39 | } 40 | 41 | goBuf := buf.GetGoBytes() 42 | goBuf = goBuf[off : off+_len] 43 | 44 | n, err := conn.Read(goBuf) 45 | if err == nil || n > 0 || err == io.EOF { 46 | frame.PushInt(int32(n)) 47 | } else { 48 | // todo 49 | panic(err.Error()) 50 | //frame.Thread.ThrowIOException(err.Error()) 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /native/java/net/SocketOutputStream.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/zxh0/jvm.go/native" 7 | "github.com/zxh0/jvm.go/rtda" 8 | ) 9 | 10 | func init() { 11 | _sos(sos_init, "init", "()V") 12 | _sos(sos_socketWrite0, "socketWrite0", "(Ljava/io/FileDescriptor;[BII)V") 13 | } 14 | 15 | func _sos(method native.Method, name, desc string) { 16 | native.Register("java/net/SocketOutputStream", name, desc, method) 17 | } 18 | 19 | // native java/net/SocketOutputStream~init~()V 20 | func sos_init(frame *rtda.Frame) { 21 | 22 | } 23 | 24 | // private native void socketWrite0(FileDescriptor fd, byte[] b, int off, 25 | // int len) throws IOException; 26 | // java/net/SocketOutputStream~socketWrite0~(Ljava/io/FileDescriptor; 27 | func sos_socketWrite0(frame *rtda.Frame) { 28 | //this := frame.GetThis() 29 | fd := frame.GetRefVar(1) 30 | b := frame.GetRefVar(2) 31 | offset := frame.GetIntVar(3) 32 | length := frame.GetIntVar(4) 33 | conn := fd.Extra.(net.Conn) 34 | goBytes := b.GetGoBytes() 35 | goBytes = goBytes[offset : offset+length] 36 | 37 | if _, err := conn.Write(goBytes); err != nil { 38 | frame.Thread.ThrowIOException(err.Error()) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /native/java/util/TimeZone.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "strings" 7 | "time" 8 | 9 | "github.com/zxh0/jvm.go/native" 10 | "github.com/zxh0/jvm.go/rtda" 11 | ) 12 | 13 | func init() { 14 | _tz(getSystemTimeZoneID, "getSystemTimeZoneID", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;") 15 | } 16 | 17 | func _tz(method native.Method, name, desc string) { 18 | native.Register("java/util/TimeZone", name, desc, method) 19 | } 20 | 21 | // private static native String getSystemTimeZoneID(String javaHome, String country); 22 | // (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; 23 | func getSystemTimeZoneID(frame *rtda.Frame) { 24 | //javaHomeObj := frame.GetRefVar(0) 25 | countryObj := frame.GetRefVar(1) 26 | 27 | //for osx read system timezone 28 | file, err := os.Open("/usr/share/zoneinfo/zone.tab") 29 | 30 | if err != nil { 31 | panic(err) 32 | } 33 | 34 | scanner := bufio.NewScanner(file) 35 | var timezone string 36 | for scanner.Scan() { 37 | line := scanner.Text() 38 | if line[0] == '#' { 39 | continue 40 | } 41 | zone := strings.Split(line, "\t") 42 | if zone[0] == countryObj.JSToGoStr() { 43 | timezone = zone[2] 44 | break 45 | } 46 | } 47 | 48 | location, _ := time.LoadLocation(timezone) 49 | zoneID := frame.GetRuntime().JSFromGoStr(location.String()) 50 | frame.PushRef(zoneID) 51 | } 52 | -------------------------------------------------------------------------------- /native/java/util/concurrent/atomic/AtomicLong.go: -------------------------------------------------------------------------------- 1 | package atomic 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _al(VMSupportsCS8, "VMSupportsCS8", "()Z") 10 | } 11 | 12 | func _al(method native.Method, name, desc string) { 13 | native.Register("java/util/concurrent/atomic/AtomicLong", name, desc, method) 14 | } 15 | 16 | // private static native boolean VMSupportsCS8(); 17 | // ()Z 18 | func VMSupportsCS8(frame *rtda.Frame) { 19 | frame.PushBoolean(false) // todo sync/atomic 20 | } 21 | -------------------------------------------------------------------------------- /native/java/util/jar/JarFile.go: -------------------------------------------------------------------------------- 1 | package jar 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _jf(getMetaInfEntryNames, "getMetaInfEntryNames", "()[Ljava/lang/String;") 10 | } 11 | 12 | func _jf(method native.Method, name, desc string) { 13 | native.Register("java/util/jar/JarFile", name, desc, method) 14 | } 15 | 16 | // private native String[] getMetaInfEntryNames(); 17 | // ()[Ljava/lang/String; 18 | func getMetaInfEntryNames(frame *rtda.Frame) { 19 | // todo 20 | frame.PushNull() 21 | } 22 | -------------------------------------------------------------------------------- /native/java/util/zip/CRC32.go: -------------------------------------------------------------------------------- 1 | package zip 2 | 3 | import ( 4 | "hash/crc32" 5 | 6 | "github.com/zxh0/jvm.go/native" 7 | "github.com/zxh0/jvm.go/rtda" 8 | ) 9 | 10 | func init() { 11 | _crc(updateBytes, "updateBytes", "(I[BII)I") 12 | } 13 | 14 | func _crc(method native.Method, name, desc string) { 15 | native.Register("java/util/zip/CRC32", name, desc, method) 16 | } 17 | 18 | // private native static int updateBytes(int crc, byte[] b, int off, int len); 19 | // (I[BII)I 20 | func updateBytes(frame *rtda.Frame) { 21 | crc := uint32(frame.GetIntVar(0)) 22 | byteArr := frame.GetRefVar(1) 23 | off := frame.GetIntVar(2) 24 | _len := frame.GetIntVar(3) 25 | 26 | goBytes := byteArr.GetGoBytes() 27 | goBytes = goBytes[off : off+_len] 28 | // func Update(crc uint32, tab *Table, p []byte) uint32 29 | crc = crc32.Update(crc, crc32.IEEETable, goBytes) 30 | 31 | frame.PushInt(int32(crc)) 32 | } 33 | -------------------------------------------------------------------------------- /native/java/util/zip/Inflater.go: -------------------------------------------------------------------------------- 1 | package zip 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _inflater(inflater_initIDs, "initIDs", "()V") 10 | } 11 | 12 | func _inflater(method native.Method, name, desc string) { 13 | native.Register("java/util/zip/Inflater", name, desc, method) 14 | } 15 | 16 | // private static native void initIDs(); 17 | // ()V 18 | func inflater_initIDs(frame *rtda.Frame) { 19 | // todo 20 | } 21 | -------------------------------------------------------------------------------- /native/registry.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/rtda" 5 | "github.com/zxh0/jvm.go/rtda/heap" 6 | ) 7 | 8 | // NativeMethod 9 | type Method func(frame *rtda.Frame) 10 | 11 | var registry = map[string]Method{} 12 | 13 | func emptyNativeMethod(frame *rtda.Frame) { 14 | // do nothing 15 | } 16 | 17 | func Register(className, methodName, methodDescriptor string, method Method) { 18 | key := className + "~" + methodName + "~" + methodDescriptor 19 | 20 | if _, ok := registry[key]; !ok { 21 | registry[key] = method 22 | } else { 23 | panic("native method:" + key + " has been registered !") 24 | } 25 | } 26 | 27 | func FindNativeMethod(method *heap.Method) Method { 28 | key := method.Class.Name + "~" + method.Name + "~" + method.Descriptor 29 | if nativeMethod, ok := registry[key]; ok { 30 | return nativeMethod 31 | } 32 | if method.IsRegisterNatives() || method.IsInitIDs() { 33 | return emptyNativeMethod 34 | } 35 | panic("native method not found: " + key) 36 | } 37 | -------------------------------------------------------------------------------- /native/sun/awt/CGraphicsEnvironment.go: -------------------------------------------------------------------------------- 1 | package awt 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _cge(cge_initCocoa, "initCocoa", "()V") 10 | _cge(cge_getMainDisplayID, "getMainDisplayID", "()I") 11 | } 12 | 13 | func _cge(method native.Method, name, desc string) { 14 | native.Register("sun/awt/CGraphicsEnvironment", name, desc, method) 15 | } 16 | 17 | func cge_initCocoa(frame *rtda.Frame) { 18 | //TODO 19 | } 20 | 21 | func cge_getMainDisplayID(frame *rtda.Frame) { 22 | frame.PushInt(1) 23 | } 24 | -------------------------------------------------------------------------------- /native/sun/io/Win32ErrorMode.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _fd(setErrorMode, "setErrorMode", "(J)J") 10 | } 11 | 12 | func _fd(method native.Method, name, desc string) { 13 | native.Register("sun/io/Win32ErrorMode", name, desc, method) 14 | } 15 | 16 | func setErrorMode(frame *rtda.Frame) { 17 | frame.PushLong(0) 18 | } 19 | -------------------------------------------------------------------------------- /native/sun/java2d/opengl/CGLGraphicsConfig.go: -------------------------------------------------------------------------------- 1 | package awt 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _cgl(cgl_initCGL, "initCGL", "()Z") 10 | } 11 | 12 | func _cgl(method native.Method, name, desc string) { 13 | native.Register("sun/java2d/opengl/CGLGraphicsConfig", name, desc, method) 14 | } 15 | 16 | func cgl_initCGL(frame *rtda.Frame) { 17 | //TODO 18 | frame.PushBoolean(true) 19 | } 20 | -------------------------------------------------------------------------------- /native/sun/management/VMManagementImpl.go: -------------------------------------------------------------------------------- 1 | package management 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _vmm(getUptime0, "getUptime0", "()J") 10 | _vmm(getStartupTime, "getStartupTime", "()J") 11 | _vmm(getVersion0, "getVersion0", "()Ljava/lang/String;") 12 | _vmm(initOptionalSupportFields, "initOptionalSupportFields", "()V") 13 | } 14 | 15 | func _vmm(method native.Method, name, desc string) { 16 | native.Register("sun/management/VMManagementImpl", name, desc, method) 17 | } 18 | 19 | // private native long getUptime0(); 20 | // ()J 21 | func getUptime0(frame *rtda.Frame) { 22 | // todo 23 | uptime := int64(0) 24 | 25 | frame.PushLong(uptime) 26 | } 27 | 28 | // public native long getStartupTime(); 29 | // ()J 30 | func getStartupTime(frame *rtda.Frame) { 31 | // todo 32 | startupTime := int64(0) 33 | 34 | frame.PushLong(startupTime) 35 | } 36 | 37 | // private static native String getVersion0(); 38 | // ()Ljava/lang/String; 39 | func getVersion0(frame *rtda.Frame) { 40 | // todo 41 | version := frame.GetRuntime().JSFromGoStr("0") 42 | 43 | frame.PushRef(version) 44 | } 45 | 46 | // private static native void initOptionalSupportFields(); 47 | // ()V 48 | func initOptionalSupportFields(frame *rtda.Frame) { 49 | // todo 50 | } 51 | -------------------------------------------------------------------------------- /native/sun/misc/Perf.go: -------------------------------------------------------------------------------- 1 | package misc 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _perf(createLong, "createLong", "(Ljava/lang/String;IIJ)Ljava/nio/ByteBuffer;") 10 | } 11 | 12 | func _perf(method native.Method, name, desc string) { 13 | native.Register("sun/misc/Perf", name, desc, method) 14 | } 15 | 16 | // public native ByteBuffer createLong(String name, int variability, int units, long value); 17 | // (Ljava/lang/String;IIJ)Ljava/nio/ByteBuffer; 18 | func createLong(frame *rtda.Frame) { 19 | bbClass := frame.GetClassLoader().LoadClass("java/nio/ByteBuffer") 20 | if bbClass.InitializationNotStarted() { 21 | frame.RevertNextPC() 22 | frame.Thread.InitClass(bbClass) 23 | return 24 | } 25 | 26 | frame.PushInt(8) 27 | 28 | allocate := bbClass.GetStaticMethod("allocate", "(I)Ljava/nio/ByteBuffer;") 29 | frame.Thread.InvokeMethod(allocate) 30 | } 31 | -------------------------------------------------------------------------------- /native/sun/misc/Signal.go: -------------------------------------------------------------------------------- 1 | package misc 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _signal(findSignal, "findSignal", "(Ljava/lang/String;)I") 10 | _signal(handle0, "handle0", "(IJ)J") 11 | } 12 | 13 | func _signal(method native.Method, name, desc string) { 14 | native.Register("sun/misc/Signal", name, desc, method) 15 | } 16 | 17 | // private static native int findSignal(String string); 18 | // (Ljava/lang/String;)I 19 | func findSignal(frame *rtda.Frame) { 20 | frame.GetRefVar(0) // name 21 | 22 | frame.PushInt(0) // todo 23 | } 24 | 25 | // private static native long handle0(int i, long l); 26 | // (IJ)J 27 | func handle0(frame *rtda.Frame) { 28 | // todo 29 | frame.GetIntVar(0) 30 | frame.GetLongVar(1) 31 | 32 | frame.PushLong(0) 33 | } 34 | 35 | // private static native void raise0(int i); 36 | -------------------------------------------------------------------------------- /native/sun/misc/URLClassPath.go: -------------------------------------------------------------------------------- 1 | package misc 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _urlcp(getLookupCacheURLs, "getLookupCacheURLs", "(Ljava/lang/ClassLoader;)[Ljava/net/URL;") 10 | } 11 | 12 | func _urlcp(method native.Method, name, desc string) { 13 | native.Register("sun/misc/URLClassPath", name, desc, method) 14 | } 15 | 16 | // private static native URL[] getLookupCacheURLs(ClassLoader var0); 17 | // (Ljava/lang/ClassLoader;)[Ljava/net/URL; 18 | func getLookupCacheURLs(frame *rtda.Frame) { 19 | frame.PushNull() 20 | } 21 | -------------------------------------------------------------------------------- /native/sun/misc/Unsafe_test.go: -------------------------------------------------------------------------------- 1 | package misc 2 | 3 | import "testing" 4 | 5 | func TestUnsafeMem(t *testing.T) { 6 | // TODO 7 | } 8 | -------------------------------------------------------------------------------- /native/sun/misc/VM.go: -------------------------------------------------------------------------------- 1 | package misc 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _vm(initialize, "initialize", "()V") 10 | } 11 | 12 | func _vm(method native.Method, name, desc string) { 13 | native.Register("sun/misc/VM", name, desc, method) 14 | } 15 | 16 | // private static native void initialize(); 17 | // ()V 18 | func initialize(frame *rtda.Frame) { 19 | // todo 20 | } 21 | -------------------------------------------------------------------------------- /native/sun/misc/malloc.go: -------------------------------------------------------------------------------- 1 | package misc 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | var _allocated = map[int64][]byte{} 8 | var _nextAddress = int64(64) // not zero! 9 | 10 | func allocate(size int64) (address int64) { 11 | mem := make([]byte, size) 12 | address = _nextAddress 13 | _allocated[address] = mem 14 | _nextAddress += size 15 | return 16 | } 17 | 18 | func reallocate(address, size int64) int64 { 19 | if size == 0 { 20 | return 0 21 | } else if address == 0 { 22 | return allocate(size) 23 | } else { 24 | mem := memoryAt(address) 25 | if len(mem) >= int(size) { 26 | return address 27 | } else { 28 | delete(_allocated, address) 29 | newAddress := allocate(size) 30 | newMem := memoryAt(newAddress) 31 | copy(newMem, mem) 32 | return newAddress 33 | } 34 | } 35 | } 36 | 37 | func free(address int64) { 38 | if _, ok := _allocated[address]; ok { 39 | delete(_allocated, address) 40 | } else { 41 | panic(fmt.Errorf("memory was not allocated: %v", address)) 42 | } 43 | } 44 | 45 | func memoryAt(address int64) []byte { 46 | for startAddress, mem := range _allocated { 47 | endAddress := startAddress + int64(len(mem)) 48 | if address >= startAddress && address < endAddress { 49 | offset := address - startAddress 50 | return mem[offset:] 51 | } 52 | } 53 | panic(fmt.Errorf("invalid address: %v", address)) 54 | } 55 | -------------------------------------------------------------------------------- /native/sun/nio/ch/IOUtil.go: -------------------------------------------------------------------------------- 1 | package ch 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _ioUtil(iou_iovMax, "iovMax", "()I") 10 | _ioUtil(iou_setfdVal, "setfdVal", "(Ljava/io/FileDescriptor;I)V") 11 | _ioUtil(iou_fdVal, "fdVal", "(Ljava/io/FileDescriptor;)I") 12 | _ioUtil(iou_makePipe, "makePipe", "(Z)J") 13 | } 14 | 15 | func _ioUtil(method native.Method, name, desc string) { 16 | native.Register("sun/nio/ch/IOUtil", name, desc, method) 17 | } 18 | 19 | func iou_iovMax(frame *rtda.Frame) { 20 | //read config _SC_IOV_MAX 21 | //default = 16 22 | frame.PushInt(16) 23 | } 24 | 25 | //static native void setfdVal(FileDescriptor var0, int var1) 26 | // (Ljava/io/FileDescriptor;I)V 27 | func iou_setfdVal(frame *rtda.Frame) { 28 | //vars := frame. 29 | //fdVal := frame.GetIntVar(1) 30 | //fd := frame.GetRefVar(0) 31 | //fmt.Println(fd) 32 | //fmt.Println(fdVal) 33 | } 34 | 35 | //static native int fdVal(FileDescriptor fd); 36 | //(Ljava/io/FileDescriptor;)I 37 | func iou_fdVal(frame *rtda.Frame) { 38 | frame.PushInt(100) 39 | } 40 | 41 | //makePipe~(Z)J 42 | func iou_makePipe(frame *rtda.Frame) { 43 | 44 | } 45 | -------------------------------------------------------------------------------- /native/sun/nio/ch/ServerSocketChannelImpl.go: -------------------------------------------------------------------------------- 1 | package ch 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | ) 6 | 7 | func init() { 8 | } 9 | 10 | func _ssci(method native.Method, name, desc string) { 11 | native.Register("sun/nio/ch/ServerSocketChannelImpl", name, desc, method) 12 | } 13 | -------------------------------------------------------------------------------- /native/sun/reflect/ConstantPool.go: -------------------------------------------------------------------------------- 1 | package reflect 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | "github.com/zxh0/jvm.go/rtda/heap" 7 | ) 8 | 9 | func init() { 10 | _cp(getLongAt0, "getLongAt0", "(Ljava/lang/Object;I)J") 11 | _cp(getUTF8At0, "getUTF8At0", "(Ljava/lang/Object;I)Ljava/lang/String;") 12 | } 13 | 14 | func _cp(method native.Method, name, desc string) { 15 | native.Register("sun/reflect/ConstantPool", name, desc, method) 16 | } 17 | 18 | // private native long getLongAt0(Object o, int i); 19 | // (Ljava/lang/Object;I)J 20 | func getLongAt0(frame *rtda.Frame) { 21 | cp, index := _getPop(frame) 22 | val := cp.GetConstant(index).(int64) 23 | frame.PushLong(val) 24 | } 25 | 26 | // private native String getUTF8At0(Object o, int i); 27 | // (Ljava/lang/Object;I)Ljava/lang/String; 28 | func getUTF8At0(frame *rtda.Frame) { 29 | cp, index := _getPop(frame) 30 | kUtf8 := cp.GetConstant(index).(string) 31 | jStr := frame.GetRuntime().JSFromGoStr(kUtf8) 32 | frame.PushRef(jStr) 33 | } 34 | 35 | func _getPop(frame *rtda.Frame) (cp *heap.ConstantPool, index uint) { 36 | this := frame.GetThis() 37 | index = uint(frame.GetIntVar(2)) 38 | cp = this.Extra.(*heap.ConstantPool) 39 | return 40 | } 41 | -------------------------------------------------------------------------------- /native/sun/reflect/NativeConstructorAccessorImpl.go: -------------------------------------------------------------------------------- 1 | package reflect 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _ncai(newInstance0, "newInstance0", "(Ljava/lang/reflect/Constructor;[Ljava/lang/Object;)Ljava/lang/Object;") 10 | } 11 | 12 | func _ncai(method native.Method, name, desc string) { 13 | native.Register("sun/reflect/NativeConstructorAccessorImpl", name, desc, method) 14 | } 15 | 16 | // private static native Object newInstance0(Constructor c, Object[] os) 17 | // throws InstantiationException, IllegalArgumentException, InvocationTargetException; 18 | // (Ljava/lang/reflect/Constructor;[Ljava/lang/Object;)Ljava/lang/Object; 19 | func newInstance0(frame *rtda.Frame) { 20 | constructorObj := frame.GetRefVar(0) 21 | argArrObj := frame.GetRefVar(1) 22 | 23 | goConstructor := getGoConstructor(constructorObj) 24 | goClass := goConstructor.Class 25 | if goClass.InitializationNotStarted() { 26 | frame.RevertNextPC() 27 | frame.Thread.InitClass(goClass) 28 | return 29 | } 30 | 31 | obj := goClass.NewObj() 32 | frame.PushRef(obj) 33 | 34 | // call 35 | args := convertArgs(obj, argArrObj, goConstructor) 36 | frame.Thread.InvokeMethodWithShim(goConstructor, args) 37 | } 38 | -------------------------------------------------------------------------------- /native/sun/reflect/Reflection.go: -------------------------------------------------------------------------------- 1 | package reflect 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/native" 5 | "github.com/zxh0/jvm.go/rtda" 6 | ) 7 | 8 | func init() { 9 | _reflection(getCallerClass, "getCallerClass", "()Ljava/lang/Class;") 10 | _reflection(getClassAccessFlags, "getClassAccessFlags", "(Ljava/lang/Class;)I") 11 | } 12 | 13 | func _reflection(method native.Method, name, desc string) { 14 | native.Register("sun/reflect/Reflection", name, desc, method) 15 | } 16 | 17 | // public static native Class getCallerClass(int i); 18 | // (I)Ljava/lang/Class; 19 | func getCallerClass(frame *rtda.Frame) { 20 | // top0 is sun/reflect/Reflection 21 | // top1 is the caller of getCallerClass() 22 | // top2 is the caller of method 23 | callerFrame := frame.Thread.TopFrameN(2) 24 | callerClass := callerFrame.GetClass().JClass 25 | frame.PushRef(callerClass) 26 | } 27 | 28 | // public static native int getClassAccessFlags(Class type); 29 | // (Ljava/lang/Class;)I 30 | func getClassAccessFlags(frame *rtda.Frame) { 31 | _type := frame.GetRefVar(0) 32 | goClass := _type.GetGoClass() 33 | frame.PushInt(int32(goClass.AccessFlags)) 34 | } 35 | -------------------------------------------------------------------------------- /rtda/frame_native.go: -------------------------------------------------------------------------------- 1 | package rtda 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/zxh0/jvm.go/rtda/heap" 7 | ) 8 | 9 | var ( 10 | _invokeNativeIReturn = []byte{0xfe, 0xac} 11 | _invokeNativeLReturn = []byte{0xfe, 0xad} 12 | _invokeNativeFReturn = []byte{0xfe, 0xae} 13 | _invokeNativeDReturn = []byte{0xfe, 0xaf} 14 | _invokeNativeAReturn = []byte{0xfe, 0xb0} 15 | _invokeNativeReturn = []byte{0xfe, 0xb1} 16 | ) 17 | 18 | func newNativeFrame(thread *Thread, method *heap.Method) *Frame { 19 | frame := &Frame{} 20 | frame.Thread = thread 21 | frame.Method = method 22 | frame.LocalVars = newLocalVars(method.ParamSlotCount) // todo 23 | frame.OperandStack = newOperandStack(4) // todo 24 | 25 | if method.Code == nil { 26 | method.Code = getHackCode(method.Descriptor) // hack! 27 | } 28 | 29 | return frame 30 | } 31 | 32 | func getHackCode(methodDescriptor string) []byte { 33 | rParenIdx := strings.IndexByte(methodDescriptor, ')') 34 | switch methodDescriptor[rParenIdx+1] { 35 | case 'V': 36 | return _invokeNativeReturn 37 | case 'L', '[': 38 | return _invokeNativeAReturn 39 | case 'D': 40 | return _invokeNativeDReturn 41 | case 'F': 42 | return _invokeNativeFReturn 43 | case 'J': 44 | return _invokeNativeLReturn 45 | default: 46 | return _invokeNativeIReturn 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /rtda/frame_test.go: -------------------------------------------------------------------------------- 1 | package rtda 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | "github.com/zxh0/jvm.go/rtda/heap" 9 | ) 10 | 11 | func TestLocalVars(t *testing.T) { 12 | lv := newLocalVars(16) 13 | obj := &heap.Object{} 14 | 15 | lv.SetIntVar(0, 123) 16 | lv.SetLongVar(1, 456) 17 | lv.SetFloatVar(2, 3.14) 18 | lv.SetDoubleVar(3, 2.71828) 19 | lv.SetRefVar(4, obj) 20 | 21 | require.Equal(t, int32(123), lv.GetIntVar(0)) 22 | require.Equal(t, int64(456), lv.GetLongVar(1)) 23 | require.Equal(t, float32(3.14), lv.GetFloatVar(2)) 24 | require.Equal(t, 2.71828, lv.GetDoubleVar(3)) 25 | require.Equal(t, obj, lv.GetRefVar(4)) 26 | } 27 | 28 | func TestOperandStack(t *testing.T) { 29 | obj := &heap.Object{} 30 | 31 | stack := newOperandStack(16) 32 | require.True(t, stack.IsStackEmpty()) 33 | 34 | stack.PushInt(123) 35 | stack.PushLong(456) 36 | stack.PushFloat(3.14) 37 | stack.PushDouble(2.71828) 38 | stack.PushRef(obj) 39 | 40 | require.False(t, stack.IsStackEmpty()) 41 | require.Equal(t, obj, stack.PopRef()) 42 | require.Equal(t, 2.71828, stack.PopDouble()) 43 | require.Equal(t, float32(3.14), stack.PopFloat()) 44 | require.Equal(t, int64(456), stack.PopLong()) 45 | require.Equal(t, int32(123), stack.PopInt()) 46 | require.True(t, stack.IsStackEmpty()) 47 | } 48 | -------------------------------------------------------------------------------- /rtda/heap/class_array.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | func (class *Class) IsArray() bool { 4 | return class.Name[0] == '[' 5 | } 6 | 7 | func (class *Class) IsPrimitiveArray() bool { 8 | return class.IsArray() && len(class.Name) == 2 9 | } 10 | 11 | func (class *Class) GetComponentClass() *Class { 12 | componentClassName := getComponentClassName(class.Name) 13 | return class.bootLoader.LoadClass(componentClassName) 14 | } 15 | 16 | func (class *Class) getArrayClass() *Class { 17 | arrayClassName := getArrayClassName(class.Name) 18 | return class.bootLoader.LoadClass(arrayClassName) 19 | } 20 | -------------------------------------------------------------------------------- /rtda/heap/class_hierarchy.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | func (class *Class) IsAssignableFrom(cls *Class) bool { 4 | return class == cls || 5 | class.isSuperClassOf(cls) || 6 | class.isSuperInterfaceOf(cls) 7 | } 8 | 9 | // class implements iface 10 | func (class *Class) isImplements(iface *Class) bool { 11 | for k := class; k != nil; k = k.SuperClass { 12 | for _, i := range k.Interfaces { 13 | if i == iface || i.isSubInterfaceOf(iface) { 14 | return true 15 | } 16 | } 17 | } 18 | return false 19 | } 20 | 21 | // iface extends class 22 | func (class *Class) isSuperInterfaceOf(iface *Class) bool { 23 | return iface.isSubInterfaceOf(class) 24 | } 25 | 26 | // class extends iface 27 | func (class *Class) isSubInterfaceOf(iface *Class) bool { 28 | for _, superInterface := range class.Interfaces { 29 | if superInterface == iface || superInterface.isSubInterfaceOf(iface) { 30 | return true 31 | } 32 | } 33 | return false 34 | } 35 | 36 | // c extends class 37 | func (class *Class) isSuperClassOf(c *Class) bool { 38 | return c.isSubClassOf(class) 39 | } 40 | 41 | // class extends c 42 | func (class *Class) isSubClassOf(c *Class) bool { 43 | for k := class.SuperClass; k != nil; k = k.SuperClass { 44 | if k == c { 45 | return true 46 | } 47 | } 48 | return false 49 | } 50 | -------------------------------------------------------------------------------- /rtda/heap/cp_class.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/classfile" 5 | ) 6 | 7 | type ConstantClass struct { 8 | class *Class 9 | name string 10 | resolved *Class 11 | } 12 | 13 | func newConstantClass(class *Class, cf *classfile.ClassFile, 14 | cfc classfile.ConstantClassInfo) *ConstantClass { 15 | 16 | return &ConstantClass{ 17 | class: class, 18 | name: cf.GetUTF8(cfc.NameIndex), 19 | } 20 | } 21 | 22 | func (ref *ConstantClass) GetClass() *Class { 23 | if ref.resolved == nil { 24 | ref.resolve() 25 | } 26 | return ref.resolved 27 | } 28 | 29 | // todo 30 | func (ref *ConstantClass) resolve() { 31 | // load class 32 | ref.resolved = ref.class.bootLoader.LoadClass(ref.name) 33 | } 34 | -------------------------------------------------------------------------------- /rtda/heap/cp_invoke_dynamic.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/classfile" 5 | ) 6 | 7 | type ConstantInvokeDynamic struct { 8 | name string 9 | _type string 10 | bootstrapMethodRef uint16 // method handle 11 | bootstrapArguments []uint16 12 | cp ConstantPool 13 | } 14 | 15 | func newConstantInvokeDynamic(cf *classfile.ClassFile, cp ConstantPool, indyInfo classfile.ConstantInvokeDynamicInfo) *ConstantInvokeDynamic { 16 | name, _type := cf.GetNameAndType(indyInfo.NameAndTypeIndex) 17 | bm := cf.GetBootstrapMethods()[indyInfo.BootstrapMethodAttrIndex] 18 | return &ConstantInvokeDynamic{ 19 | name: name, 20 | _type: _type, 21 | bootstrapMethodRef: bm.BootstrapMethodRef, 22 | bootstrapArguments: bm.BootstrapArguments, 23 | cp: cp, 24 | } 25 | } 26 | 27 | func (indy *ConstantInvokeDynamic) MethodHandle() { 28 | kMH := indy.cp.GetConstant(uint(indy.bootstrapMethodRef)).(*ConstantMethodHandle) 29 | println(kMH) 30 | } 31 | -------------------------------------------------------------------------------- /rtda/heap/cp_method_handle.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/classfile" 5 | ) 6 | 7 | type ConstantMethodHandle struct { 8 | referenceKind uint8 9 | referenceIndex uint16 10 | } 11 | 12 | func newConstantMethodHandle(mhInfo classfile.ConstantMethodHandleInfo) *ConstantMethodHandle { 13 | return &ConstantMethodHandle{ 14 | referenceKind: mhInfo.ReferenceKind, 15 | referenceIndex: mhInfo.ReferenceIndex, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rtda/heap/cp_method_type.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/classfile" 5 | ) 6 | 7 | type ConstantMethodType struct { 8 | // todo 9 | } 10 | 11 | func newConstantMethodType(mtInfo classfile.ConstantMethodTypeInfo) *ConstantMethodType { 12 | return &ConstantMethodType{ 13 | // todo 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rtda/heap/cp_ref_interface_method.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/classfile" 5 | ) 6 | 7 | type ConstantInterfaceMethodRef struct { 8 | ConstantMethodRef 9 | } 10 | 11 | func newConstantInterfaceMethodRef(class *Class, cf *classfile.ClassFile, 12 | cfRef classfile.ConstantInterfaceMethodRefInfo) *ConstantInterfaceMethodRef { 13 | 14 | ref := &ConstantInterfaceMethodRef{} 15 | ref.ConstantMemberRef = newConstantMemberRef(class, cf, cfRef.ClassIndex, cfRef.NameAndTypeIndex) 16 | ref.ParamSlotCount = calcParamSlotCount(ref.descriptor) 17 | return ref 18 | } 19 | 20 | // todo 21 | func (ref *ConstantInterfaceMethodRef) FindInterfaceMethod(obj *Object) *Method { 22 | for class := obj.Class; class != nil; class = class.SuperClass { 23 | method := class.getMethod(ref.name, ref.descriptor, false) 24 | if method != nil { 25 | return method 26 | } 27 | } 28 | 29 | if method := findInterfaceMethod(obj.Class.Interfaces, ref.name, ref.descriptor); method != nil { 30 | return method 31 | } else { 32 | //TODO 33 | panic("virtual method not found!") 34 | } 35 | } 36 | 37 | func findInterfaceMethod(interfaces []*Class, name, descriptor string) *Method { 38 | for i := 0; i < len(interfaces); i++ { 39 | if method := findInterfaceMethod(interfaces[i].Interfaces, name, descriptor); method != nil { 40 | return method 41 | } 42 | method := interfaces[i].getMethod(name, descriptor, false) 43 | if method != nil { 44 | return method 45 | } 46 | } 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /rtda/heap/cp_ref_member.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/classfile" 5 | ) 6 | 7 | type ConstantMemberRef struct { 8 | class *Class 9 | className string 10 | name string 11 | descriptor string 12 | } 13 | 14 | func newConstantMemberRef(class *Class, cf *classfile.ClassFile, 15 | classIdx, nameAndTypeIdx uint16) ConstantMemberRef { 16 | 17 | className := cf.GetClassName(classIdx) 18 | name, descriptor := cf.GetNameAndType(nameAndTypeIdx) 19 | return ConstantMemberRef{ 20 | class: class, 21 | className: className, 22 | name: name, 23 | descriptor: descriptor, 24 | } 25 | } 26 | 27 | func (ref *ConstantMemberRef) getBootLoader() *ClassLoader { 28 | return ref.class.bootLoader 29 | } 30 | -------------------------------------------------------------------------------- /rtda/heap/cp_string.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | type ConstantString struct { 4 | goStr string 5 | class *Class 6 | jStr *Object 7 | } 8 | 9 | func newConstantString(class *Class, str string) *ConstantString { 10 | return &ConstantString{class: class, goStr: str} 11 | } 12 | 13 | func (s *ConstantString) GetJString() *Object { 14 | if s.jStr == nil { 15 | rt := s.class.bootLoader.rt 16 | s.jStr = rt.JSFromGoStr(s.goStr) 17 | } 18 | return s.jStr 19 | } 20 | -------------------------------------------------------------------------------- /rtda/heap/descriptor.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | type TypeDescriptor string 4 | 5 | func (td TypeDescriptor) IsBaseType() bool { return len(td) == 1 } 6 | func (td TypeDescriptor) IsVoidType() bool { return td == "V" } 7 | func (td TypeDescriptor) IsObjectType() bool { return td[0] == 'L' } 8 | func (td TypeDescriptor) IsArrayType() bool { return td[0] == '[' } 9 | func (td TypeDescriptor) IsLongOrDouble() bool { return td == "J" || td == "D" } 10 | 11 | type MethodDescriptor struct { 12 | ParameterTypes []TypeDescriptor 13 | ReturnType TypeDescriptor 14 | } 15 | 16 | func (md MethodDescriptor) getParamCount() uint { 17 | return uint(len(md.ParameterTypes)) 18 | } 19 | 20 | func (md MethodDescriptor) getParamSlotCount() uint { 21 | slotCount := md.getParamCount() 22 | for _, paramType := range md.ParameterTypes { 23 | if paramType.IsLongOrDouble() { 24 | slotCount++ 25 | } 26 | } 27 | return slotCount 28 | } 29 | -------------------------------------------------------------------------------- /rtda/heap/descriptor_test.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestParseMethodDescriptor(t *testing.T) { 10 | testParseMethodDescriptor(t, "(IDLjava/lang/Thread;)Ljava/lang/Object;") 11 | testParseMethodDescriptor(t, "([Ljava/lang/Thread;)I") 12 | testParseMethodDescriptor(t, "()V") 13 | } 14 | 15 | func testParseMethodDescriptor(t *testing.T, md string) { 16 | parsed := parseMethodDescriptor(md) 17 | require.Equal(t, md, mdToStr(parsed)) 18 | } 19 | 20 | func mdToStr(md MethodDescriptor) string { 21 | str := "(" 22 | for _, paramType := range md.ParameterTypes { 23 | str += string(paramType) 24 | } 25 | str += ")" 26 | str += string(md.ReturnType) 27 | return str 28 | } 29 | -------------------------------------------------------------------------------- /rtda/heap/field.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/classfile" 5 | ) 6 | 7 | type Field struct { 8 | ClassMember 9 | IsLongOrDouble bool 10 | ConstValueIndex uint16 11 | SlotId uint 12 | _type *Class 13 | } 14 | 15 | func newField(class *Class, cf *classfile.ClassFile, cfMember classfile.MemberInfo) *Field { 16 | field := &Field{} 17 | field.Class = class 18 | field.copyMemberData(cf, cfMember) 19 | field.IsLongOrDouble = field.Descriptor == "J" || field.Descriptor == "D" 20 | field.ConstValueIndex = cfMember.GetConstantValueIndex() 21 | return field 22 | } 23 | 24 | func (field *Field) GetValue(ref *Object) Slot { 25 | fields := ref.Fields.([]Slot) 26 | return fields[field.SlotId] 27 | } 28 | func (field *Field) PutValue(ref *Object, val Slot) { 29 | fields := ref.Fields.([]Slot) 30 | fields[field.SlotId] = val 31 | } 32 | 33 | func (field *Field) GetStaticValue() Slot { 34 | return field.Class.StaticFieldSlots[field.SlotId] 35 | } 36 | func (field *Field) PutStaticValue(val Slot) { 37 | field.Class.StaticFieldSlots[field.SlotId] = val 38 | } 39 | 40 | // reflection 41 | func (field *Field) Type() *Class { 42 | if field._type == nil { 43 | field._type = field.resolveType() 44 | } 45 | return field._type 46 | } 47 | func (field *Field) resolveType() *Class { 48 | bootLoader := field.Class.bootLoader 49 | className := getClassName(field.Descriptor) 50 | return bootLoader.LoadClass(className) 51 | } 52 | -------------------------------------------------------------------------------- /rtda/heap/member.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/classfile" 5 | ) 6 | 7 | type ClassMember struct { 8 | classfile.AccessFlags 9 | Name string 10 | Descriptor string 11 | Signature string 12 | AnnotationData []byte // RuntimeVisibleAnnotations_attribute 13 | Class *Class 14 | } 15 | 16 | func (m *ClassMember) copyMemberData(cf *classfile.ClassFile, cfMember classfile.MemberInfo) { 17 | m.AccessFlags = classfile.AccessFlags(cfMember.AccessFlags) 18 | m.Name = cf.GetUTF8(cfMember.NameIndex) 19 | m.Descriptor = cf.GetUTF8(cfMember.DescriptorIndex) 20 | m.Signature = cf.GetUTF8(cfMember.GetSignatureIndex()) 21 | m.AnnotationData = cfMember.GetRuntimeVisibleAnnotationsAttributeData() 22 | } 23 | -------------------------------------------------------------------------------- /rtda/heap/method_reflection.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | func (method *Method) GetParameterTypes() []*Class { 4 | if method.ParamSlotCount == 0 { 5 | return nil 6 | } 7 | 8 | bootLoader := method.Class.bootLoader 9 | paramClasses := make([]*Class, 0, method.ParamSlotCount) 10 | for _, paramType := range method.ParameterTypes { 11 | paramClassName := getClassName(string(paramType)) 12 | paramClasses = append(paramClasses, bootLoader.LoadClass(paramClassName)) 13 | } 14 | 15 | return paramClasses 16 | } 17 | 18 | func (method *Method) GetReturnType() *Class { 19 | returnDescriptor := method.ReturnType 20 | returnClassName := getClassName(string(returnDescriptor)) 21 | returnClass := method.Class.bootLoader.LoadClass(returnClassName) 22 | return returnClass 23 | } 24 | 25 | func (method *Method) GetExceptionTypes() []*Class { 26 | if method.exIndexTable == nil { 27 | return nil 28 | } 29 | 30 | exClasses := make([]*Class, len(method.exIndexTable)) 31 | cp := method.Class.ConstantPool 32 | 33 | for i, exIndex := range method.exIndexTable { 34 | kClass := cp.GetConstantClass(uint(exIndex)) 35 | exClasses[i] = kClass.GetClass() 36 | } 37 | 38 | return exClasses 39 | } 40 | -------------------------------------------------------------------------------- /rtda/heap/object_instanceof.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | func (obj *Object) IsInstanceOf(class *Class) bool { 4 | s, t := obj.Class, class 5 | return _checkcast(s, t) 6 | } 7 | 8 | // jvms8-6.5.checkcast 9 | // jvms8-6.5.instanceof 10 | func _checkcast(s, t *Class) bool { 11 | if s == t { 12 | return true 13 | } 14 | 15 | if !s.IsArray() { 16 | if !s.IsInterface() { 17 | if !t.IsInterface() { 18 | return s.isSubClassOf(t) 19 | } else { 20 | return s.isImplements(t) 21 | } 22 | } else { 23 | if !t.IsInterface() { 24 | return t.isJlObject() 25 | } else { 26 | return t.isSuperInterfaceOf(s) 27 | } 28 | } 29 | } else { // s is array 30 | if !t.IsArray() { 31 | if !t.IsInterface() { 32 | return t.isJlObject() 33 | } else { 34 | return t.isJlCloneable() || t.isJioSerializable() 35 | } 36 | } else { // t is array 37 | sc := s.GetComponentClass() 38 | tc := t.GetComponentClass() 39 | return sc == tc || _checkcast(sc, tc) 40 | } 41 | } 42 | 43 | return false 44 | } 45 | -------------------------------------------------------------------------------- /rtda/heap/object_str.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/vmutils" 5 | ) 6 | 7 | // java.lang.String -> go string 8 | func (obj *Object) JSToGoStr() string { 9 | charArr := obj.GetFieldValue("value", "[C").Ref 10 | return vmutils.UTF16ToUTF8(charArr.GetChars()) 11 | } 12 | -------------------------------------------------------------------------------- /rtda/heap/object_test.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestClone(t *testing.T) { 10 | obj1 := newObj(nil, []int8{1, 2, 3}, nil) 11 | obj2 := obj1.Clone() 12 | obj2.Fields.([]int8)[1] = 9 13 | require.Equal(t, []int8{1, 2, 3}, obj1.Fields) 14 | require.Equal(t, []int8{1, 9, 3}, obj2.Fields) 15 | } 16 | 17 | func TestArrayLength(t *testing.T) { 18 | arr := newObj(nil, []int8{1, 2, 3, 4, 5}, nil) 19 | require.Equal(t, int32(5), arr.ArrayLength()) 20 | } 21 | 22 | func TestArrayCopy(t *testing.T) { 23 | arr1 := newObj(nil, []int8{1, 2, 3, 4, 5}, nil) 24 | arr2 := newObj(nil, []int8{6, 7, 8, 9, 0}, nil) 25 | ArrayCopy(arr2, arr1, 1, 2, 3) 26 | require.Equal(t, []int8{6, 7, 8, 9, 0}, arr2.Fields) 27 | require.Equal(t, []int8{1, 2, 7, 8, 9}, arr1.Fields) 28 | } 29 | -------------------------------------------------------------------------------- /rtda/heap/slot.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "math" 5 | ) 6 | 7 | var EmptySlot = Slot{0, nil} 8 | 9 | type Slot struct { 10 | Val int64 // big enough to hold any primitive value 11 | Ref *Object 12 | } 13 | 14 | func NewIntSlot(n int32) Slot { 15 | return Slot{Val: int64(n)} 16 | } 17 | func NewLongSlot(n int64) Slot { 18 | return Slot{Val: n} 19 | } 20 | func NewFloatSlot(n float32) Slot { 21 | return Slot{Val: int64(math.Float32bits(n))} 22 | } 23 | func NewDoubleSlot(n float64) Slot { 24 | return Slot{Val: int64(math.Float64bits(n))} 25 | } 26 | func NewRefSlot(ref *Object) Slot { 27 | return Slot{Ref: ref} 28 | } 29 | 30 | func (slot Slot) IntValue() int32 { 31 | return int32(slot.Val) 32 | } 33 | func (slot Slot) LongValue() int64 { 34 | return slot.Val 35 | } 36 | func (slot Slot) FloatValue() float32 { 37 | return math.Float32frombits(uint32(slot.Val)) 38 | } 39 | func (slot Slot) DoubleValue() float64 { 40 | return math.Float64frombits(uint64(slot.Val)) 41 | } 42 | 43 | // TODO 44 | func NewHackSlot(x interface{}) Slot { 45 | return NewRefSlot(&Object{Extra: x}) 46 | } 47 | func (slot Slot) GetHack() interface{} { 48 | return slot.Ref.Extra 49 | } 50 | -------------------------------------------------------------------------------- /rtda/jvm_stack.go: -------------------------------------------------------------------------------- 1 | package rtda 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // jvm stack 8 | type Stack struct { 9 | maxSize uint 10 | size uint 11 | _top *Frame // stack is implemented as linked list 12 | } 13 | 14 | func newStack(maxSize uint) *Stack { 15 | return &Stack{maxSize, 0, nil} 16 | } 17 | 18 | func (stack *Stack) isEmpty() bool { 19 | return stack._top == nil 20 | } 21 | 22 | func (stack *Stack) push(frame *Frame) { 23 | if stack.size >= stack.maxSize { 24 | // todo 25 | panic("StackOverflowError") 26 | } 27 | 28 | if stack._top != nil { 29 | frame.lower = stack._top 30 | } 31 | 32 | stack._top = frame 33 | stack.size++ 34 | } 35 | 36 | func (stack *Stack) pop() *Frame { 37 | if stack._top == nil { 38 | panic("jvm stack is empty!") 39 | } 40 | 41 | top := stack._top 42 | stack._top = top.lower 43 | top.lower = nil 44 | stack.size-- 45 | 46 | return top 47 | } 48 | 49 | func (stack *Stack) clear() { 50 | for !stack.isEmpty() { 51 | stack.pop() 52 | } 53 | } 54 | 55 | func (stack *Stack) top() *Frame { 56 | if stack._top == nil { 57 | panic("jvm stack is empty!") 58 | } 59 | 60 | return stack._top 61 | } 62 | 63 | func (stack *Stack) topN(n uint) *Frame { 64 | if stack.size < n { 65 | panic(fmt.Sprintf("jvm stack size:%v n:%v", stack.size, n)) 66 | } 67 | 68 | frame := stack._top 69 | for n > 0 { 70 | frame = frame.lower 71 | n-- 72 | } 73 | 74 | return frame 75 | } 76 | -------------------------------------------------------------------------------- /rtda/shim_frames.go: -------------------------------------------------------------------------------- 1 | package rtda 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/rtda/heap" 5 | ) 6 | 7 | // TODO 8 | func NewShimFrame(thread *Thread, args []heap.Slot) *Frame { 9 | return newShimFrame(thread, args) 10 | } 11 | 12 | func newShimFrame(thread *Thread, args []heap.Slot) *Frame { 13 | return &Frame{ 14 | Thread: thread, 15 | Method: shimReturnMethod, 16 | OperandStack: newOperandStackWithSlots(args), 17 | } 18 | } 19 | 20 | func newAthrowFrame(thread *Thread, ex *heap.Object, initArgs []heap.Slot) *Frame { 21 | // stackSlots := [ex, ex, initArgs] 22 | stackSlots := make([]heap.Slot, len(initArgs)+2) 23 | stackSlots[0] = heap.NewRefSlot(ex) 24 | stackSlots[1] = heap.NewRefSlot(ex) 25 | copy(stackSlots[2:], initArgs) 26 | 27 | return &Frame{ 28 | Thread: thread, 29 | Method: shimAThrowMethod, 30 | OperandStack: newOperandStackWithSlots(stackSlots), 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rtda/shim_methods.go: -------------------------------------------------------------------------------- 1 | package rtda 2 | 3 | import ( 4 | "github.com/zxh0/jvm.go/classfile" 5 | "github.com/zxh0/jvm.go/rtda/heap" 6 | ) 7 | 8 | var ( 9 | _shimClass = &heap.Class{Name: "~shim"} 10 | 11 | shimReturnMethod = &heap.Method{ 12 | ClassMember: newShimMember(""), 13 | MethodData: heap.MethodData{ 14 | Code: []byte{0xb1}, // return 15 | }, 16 | } 17 | 18 | shimAThrowMethod = &heap.Method{ 19 | ClassMember: newShimMember(""), 20 | MethodData: heap.MethodData{ 21 | Code: []byte{0xbf}, // athrow 22 | }, 23 | } 24 | 25 | ShimBootstrapMethod = &heap.Method{ 26 | ClassMember: newShimMember(""), 27 | MethodData: heap.MethodData{ 28 | Code: []byte{0xff, 0xb1}, // bootstrap, return 29 | MaxStack: 8, 30 | MaxLocals: 8, 31 | }, 32 | ParamSlotCount: 2, 33 | } 34 | ) 35 | 36 | func newShimMember(name string) heap.ClassMember { 37 | return heap.ClassMember{ 38 | AccessFlags: classfile.AccStatic, 39 | Name: name, 40 | Class: _shimClass, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/hw_module/src/hello.modules/hello/HelloWorld.java: -------------------------------------------------------------------------------- 1 | package hello; 2 | 3 | class HelloWorld { 4 | public static void main(String[] args) { 5 | System.out.println("Hello, world!"); 6 | } 7 | } -------------------------------------------------------------------------------- /test/hw_module/src/hello.modules/module-info.java: -------------------------------------------------------------------------------- 1 | module hello.modules { 2 | exports hello; 3 | } -------------------------------------------------------------------------------- /test/hw_module/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | alias javac='~/.sdkman/candidates/java/13.0.1-open/bin/javac' 5 | alias jlink='~/.sdkman/candidates/java/13.0.1-open/bin/jlink' 6 | 7 | javac -version | grep 'javac 13' 8 | jlink --version | grep 13 9 | 10 | OUT=myjre 11 | rm -rf $OUT 12 | javac --module-source-path src \ 13 | --module-version 0.1 \ 14 | -d out -m hello.modules 15 | jlink --module-path out --add-modules hello.modules,java.base --output $OUT 16 | ./$OUT/bin/java -m hello.modules/hello.HelloWorld 17 | -------------------------------------------------------------------------------- /test/testclasses/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * This generated file contains a sample Java Library project to get you started. 5 | * For more details take a look at the Java Libraries chapter in the Gradle 6 | * User Manual available at https://docs.gradle.org/5.6.2/userguide/java_library_plugin.html 7 | */ 8 | 9 | plugins { 10 | // Apply the java plugin to add support for Java 11 | id 'java' 12 | 13 | // Apply the application plugin to add support for building a CLI application 14 | id 'application' 15 | } 16 | 17 | repositories { 18 | // Use jcenter for resolving dependencies. 19 | // You can declare any Maven/Ivy/file repository here. 20 | jcenter() 21 | } 22 | 23 | dependencies { 24 | // This dependency is exported to consumers, that is to say found on their compile classpath. 25 | //api 'org.apache.commons:commons-math3:3.6.1' 26 | 27 | // This dependency is used internally, and not exposed to consumers on their own compile classpath. 28 | implementation 'com.google.guava:guava:28.0-jre' 29 | implementation 'com.google.code.gson:gson:2.3.1' 30 | implementation 'org.eclipse.jetty.aggregate:jetty-all:9.2.9.v20150224' 31 | 32 | // Use JUnit test framework 33 | implementation 'junit:junit:4.12' 34 | } 35 | 36 | application { 37 | // Define the main class for the application 38 | mainClassName = 'UnitTests' 39 | } -------------------------------------------------------------------------------- /test/testclasses/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/jvm.go/129b147ebcc820279748f6095c916da1986ebef1/test/testclasses/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /test/testclasses/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /test/testclasses/settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * 6 | * Detailed information about configuring a multi-project build in Gradle can be found 7 | * in the user manual at https://docs.gradle.org/5.6.2/userguide/multi_project_builds.html 8 | */ 9 | 10 | rootProject.name = 'testclasses' 11 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/HelloWorld.java: -------------------------------------------------------------------------------- 1 | public class HelloWorld { 2 | 3 | public static void main(String[] args) { 4 | System.out.println("Hello, world!"); 5 | } 6 | 7 | } 8 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/PrimeTest.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | 3 | public class PrimeTest { 4 | 5 | public static void main(String[] args) { 6 | ArrayList res = new ArrayList<>(); 7 | int last = 3; 8 | res.add(last); 9 | while (true) { 10 | last = last + 2; 11 | boolean prime = true; 12 | for (int v : res) { 13 | if (v * v > last) { 14 | break; 15 | } 16 | if (last % v == 0) { 17 | prime = false; 18 | break; 19 | } 20 | } 21 | if (prime) { 22 | res.add(last); 23 | if (res.size() % 100000 == 0) { 24 | System.out.println(last); 25 | } 26 | if (last > 9999999) { 27 | break; 28 | } 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/PrintArgs.java: -------------------------------------------------------------------------------- 1 | public class PrintArgs { 2 | 3 | public static void main(String[] args) { 4 | for (String arg : args) { 5 | System.out.println(arg); 6 | } 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/helper/MyAssert.java: -------------------------------------------------------------------------------- 1 | package helper; 2 | 3 | public class MyAssert { 4 | 5 | public static void assertEquals(Object expected, Object actual) { 6 | if (!expected.equals(actual)) { 7 | throw new AssertionError(actual.toString() + " != " + expected.toString()); 8 | } 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/helper/TestHelper.java: -------------------------------------------------------------------------------- 1 | package helper; 2 | 3 | public class TestHelper { 4 | 5 | public static Object nullObj() { 6 | return null; 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/helper/UnitTestRunner.java: -------------------------------------------------------------------------------- 1 | package helper; 2 | 3 | import org.junit.runner.JUnitCore; 4 | import org.junit.runner.Result; 5 | import org.junit.runner.notification.Failure; 6 | 7 | public class UnitTestRunner { 8 | 9 | // todo 10 | public static void run(Class... tests) { 11 | Result result = JUnitCore.runClasses(tests); 12 | System.out.println("total: " + result.getRunCount()); 13 | System.out.println("failures: " + result.getFailureCount()); 14 | 15 | if (!result.wasSuccessful()) { 16 | for (Failure f : result.getFailures()) { 17 | System.out.println(f); 18 | //f.getException().printStackTrace(); 19 | } 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jls8/StringOut.java: -------------------------------------------------------------------------------- 1 | package jls8; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | 6 | public class StringOut extends PrintWriter { 7 | 8 | public StringOut () { 9 | super(new StringWriter()); 10 | } 11 | 12 | @Override 13 | public String toString() { 14 | String str = out.toString(); 15 | return str.replaceAll("\r\n", "\n"); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jls8/ch12/Eg12_4_1_1.java: -------------------------------------------------------------------------------- 1 | package jls8.ch12; 2 | 3 | import jls8.StringOut; 4 | import helper.UnitTestRunner; 5 | import org.junit.Test; 6 | import static org.junit.Assert.*; 7 | 8 | /** 9 | * Example 12.4.1-1. 10 | * Superclasses Are Initialized Before Subclasses 11 | */ 12 | public class Eg12_4_1_1 { 13 | 14 | private static final StringOut out = new StringOut(); 15 | 16 | private static class Super { 17 | static { out.print("Super "); } 18 | } 19 | private static class One { 20 | static { out.print("One "); } 21 | } 22 | private static class Two extends Super { 23 | static { out.print("Two "); } 24 | } 25 | 26 | @Test 27 | public void test() { 28 | One o = null; 29 | Two t = new Two(); 30 | out.println((Object)o == (Object)t); 31 | assertEquals("Super Two false\n", out.toString()); 32 | } 33 | 34 | public static void main(String[] args) { 35 | UnitTestRunner.run(Eg12_4_1_1.class); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jls8/ch12/Eg12_4_1_2.java: -------------------------------------------------------------------------------- 1 | package jls8.ch12; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example 12.4.1-2. 9 | * Only The Class That Declares static Field Is Initialized 10 | */ 11 | public class Eg12_4_1_2 { 12 | 13 | private static class Super { 14 | static int taxi = 1729; 15 | } 16 | private static class Sub extends Super { 17 | static { 18 | System.out.print("Sub "); 19 | if (true) { 20 | throw new RuntimeException("BAD"); 21 | } 22 | } 23 | } 24 | 25 | @Test 26 | public void test() { 27 | assertEquals(1729, Sub.taxi); 28 | } 29 | 30 | public static void main(String[] args) { 31 | UnitTestRunner.run(Eg12_4_1_2.class); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jls8/ch12/Eg12_4_1_3.java: -------------------------------------------------------------------------------- 1 | package jls8.ch12; 2 | 3 | import jls8.StringOut; 4 | import helper.UnitTestRunner; 5 | import org.junit.Test; 6 | import static org.junit.Assert.*; 7 | 8 | /** 9 | * Example 12.4.1-3. 10 | * Interface Initialization Does Not Initialize Superinterfaces 11 | */ 12 | public class Eg12_4_1_3 { 13 | 14 | private static final StringOut sout = new StringOut(); 15 | 16 | private static interface I { 17 | int i = 1, ii = out("ii", 2); 18 | } 19 | private static interface J extends I { 20 | int j = out("j", 3), jj = out("jj", 4); 21 | } 22 | private static interface K extends J { 23 | int k = out("k", 5); 24 | } 25 | 26 | static int out(String s, int i) { 27 | sout.println(s + "=" + i); 28 | return i; 29 | } 30 | 31 | @Test 32 | public void test() { 33 | sout.println("" + J.i); 34 | sout.println("" + K.j); 35 | assertEquals("1\nj=3\njj=4\n3\n", sout.toString()); 36 | } 37 | 38 | public static void main(String[] args) { 39 | UnitTestRunner.run(Eg12_4_1_3.class); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jls8/ch12/Eg12_5_2.java: -------------------------------------------------------------------------------- 1 | package jls8.ch12; 2 | 3 | import jls8.StringOut; 4 | import helper.UnitTestRunner; 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example 12.5-2. 9 | * Dynamic Dispatch During Instance Creation 10 | */ 11 | public class Eg12_5_2 { 12 | 13 | private static final StringOut out = new StringOut(); 14 | 15 | private static class Super { 16 | Super() { printThree(); } 17 | void printThree() { out.println("three"); } 18 | } 19 | private static class Test extends Super { 20 | int three = (int)Math.PI; // That is, 3 21 | void printThree() { out.println(three); } 22 | } 23 | 24 | @org.junit.Test 25 | public void test() { 26 | Test t = new Test(); 27 | t.printThree(); 28 | assertEquals("0\n3\n", out.toString()); 29 | } 30 | 31 | public static void main(String[] args) { 32 | UnitTestRunner.run(Eg12_5_2.class); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/InheritanceTest.java: -------------------------------------------------------------------------------- 1 | package jvm; 2 | 3 | public class InheritanceTest { 4 | 5 | static class Super { 6 | int x; 7 | long y; 8 | } 9 | static class Sub extends Super { 10 | float a; 11 | double b; 12 | } 13 | 14 | public static void main(String[] args) { 15 | Sub sub = new Sub(); 16 | sub.x = 1; 17 | sub.y = 2L; 18 | sub.a = 3.14f; 19 | sub.b = 2.71828; 20 | 21 | int x = sub.x; 22 | long y = sub.y; 23 | float a = sub.a; 24 | double b = sub.b; 25 | 26 | Super sup = sub; 27 | long z = sup.x + sup.y; 28 | 29 | System.out.println("OK!"); 30 | } 31 | 32 | } 33 | 34 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/MethodCall.java: -------------------------------------------------------------------------------- 1 | package jvm; 2 | 3 | public class MethodCall { 4 | 5 | public static void main(String[] args) { 6 | f(1, 2L, 3.14f, 2.71828, args); 7 | new MethodCall().g(1, 2L, 3.14f, 2.71828, args); 8 | System.out.println("OK"); 9 | } 10 | 11 | private static void f(int a, long b, float c, double d, Object e) { 12 | int x = a; 13 | long y = b; 14 | float z = c; 15 | double u = d; 16 | Object v = e; 17 | } 18 | 19 | private void g(int a, long b, float c, double d, Object e) { 20 | int x = a; 21 | long y = b; 22 | float z = c; 23 | double u = d; 24 | Object v = e; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/ObjectInit.java: -------------------------------------------------------------------------------- 1 | package jvm; 2 | 3 | 4 | public class ObjectInit { 5 | 6 | char x; 7 | static long a; 8 | 9 | public static void main(String[] args) { 10 | ObjectInit o = new ObjectInit(); 11 | char y = o.x; 12 | if (y == 0) { 13 | System.out.println("OK1!"); 14 | } else { 15 | System.out.println("Fail1!"); 16 | } 17 | 18 | long b = a; 19 | if (b == 0) { 20 | System.out.println("OK2!"); 21 | } else { 22 | System.out.println("Fail2!"); 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/ex/ClassLibExTest.java: -------------------------------------------------------------------------------- 1 | package jvm.ex; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | public class ClassLibExTest { 8 | 9 | public static void main(String[] args) { 10 | UnitTestRunner.run(ClassLibExTest.class); 11 | } 12 | 13 | @Test 14 | public void threadSleep() throws InterruptedException { 15 | try { 16 | Thread.sleep(-1); 17 | fail(); 18 | } catch (IllegalArgumentException e) { 19 | assertEquals("timeout value is negative", e.getMessage()); 20 | } 21 | } 22 | 23 | @Test 24 | public void threadSleep2() throws InterruptedException { 25 | Thread.currentThread().interrupt(); 26 | assertTrue(Thread.currentThread().isInterrupted()); 27 | try { 28 | Thread.sleep(1000); 29 | fail(); 30 | } catch (InterruptedException e) { 31 | assertFalse(Thread.currentThread().isInterrupted()); 32 | assertEquals("sleep interrupted", e.getMessage()); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/ex/FinallyTest.java: -------------------------------------------------------------------------------- 1 | package jvm.ex; 2 | 3 | public class FinallyTest { 4 | 5 | public static void main(String[] args) { 6 | int x = 1; 7 | try { 8 | bad(); 9 | x = 100; 10 | } catch (Exception e) { 11 | x += 2; 12 | } finally { 13 | x *= 3; 14 | } 15 | 16 | if (x == (1 + 2) * 3) { 17 | System.out.println("OK!"); 18 | } 19 | } 20 | 21 | private static void bad() { 22 | throw new RuntimeException("BAD!"); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/ex/InstructionNpeTest.java: -------------------------------------------------------------------------------- 1 | package jvm.ex; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static helper.TestHelper.nullObj; 6 | 7 | public class InstructionNpeTest { 8 | 9 | private int i; 10 | 11 | public static void main(String[] args) { 12 | UnitTestRunner.run(InstructionNpeTest.class); 13 | } 14 | 15 | @Test(expected = NullPointerException.class) 16 | public void arraylength() { 17 | int[] x = (int[]) nullObj(); 18 | int y = x.length; 19 | } 20 | 21 | @Test(expected = NullPointerException.class) 22 | public void athrow() throws Exception { 23 | Exception x = (Exception) nullObj(); 24 | throw x; 25 | } 26 | 27 | @Test(expected = NullPointerException.class) 28 | public void getfield() { 29 | InstructionNpeTest x = (InstructionNpeTest) nullObj(); 30 | int y = x.i; 31 | } 32 | 33 | @Test(expected = NullPointerException.class) 34 | public void monitorenter() { 35 | Object x = nullObj(); 36 | synchronized(x) { 37 | System.out.println("BAD!"); 38 | } 39 | } 40 | 41 | @Test(expected = NullPointerException.class) 42 | public void invokevirtual() { 43 | Object x = nullObj(); 44 | x.toString(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/ex/StackTraceTest.java: -------------------------------------------------------------------------------- 1 | package jvm.ex; 2 | 3 | public class StackTraceTest { 4 | 5 | public static void main(String[] args) { 6 | try { 7 | foo(); 8 | } catch (Exception e) { 9 | e.printStackTrace(System.err); 10 | } 11 | } 12 | 13 | private static void foo() { 14 | bar(); 15 | } 16 | 17 | private static void bar() { 18 | bad(); 19 | } 20 | 21 | private static void bad() { 22 | throw new RuntimeException("BAD!"); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/ex/UncaughtTest.java: -------------------------------------------------------------------------------- 1 | package jvm.ex; 2 | 3 | public class UncaughtTest { 4 | 5 | public static void main(String[] args) { 6 | foo(); 7 | } 8 | 9 | private static void foo() { 10 | bar(); 11 | } 12 | 13 | private static void bar() { 14 | bad(); 15 | } 16 | 17 | private static void bad() { 18 | throw new RuntimeException("BAD!"); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/field/FieldAccessTest.java: -------------------------------------------------------------------------------- 1 | package jvm.field; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | public class FieldAccessTest { 8 | 9 | private static interface I { 10 | static int i = val(1); 11 | } 12 | private static interface J { 13 | static int j = val(2); 14 | } 15 | private static interface K extends I, J { 16 | static int k = val(3); 17 | } 18 | private static class A implements K { 19 | static int a = val(4); 20 | } 21 | private static class B extends A { 22 | static int b = val(5); 23 | } 24 | 25 | private static int val(int x) { 26 | return x; 27 | } 28 | 29 | @Test 30 | public void test() { 31 | assertEquals(1, B.i); 32 | assertEquals(2, B.j); 33 | assertEquals(3, B.k); 34 | assertEquals(4, B.a); 35 | assertEquals(5, B.b); 36 | } 37 | 38 | public static void main(String[] args) { 39 | UnitTestRunner.run(FieldAccessTest.class); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/field/FieldsTest.java: -------------------------------------------------------------------------------- 1 | package jvm.field; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | public class FieldsTest { 8 | 9 | static class Sup { 10 | static int x; 11 | int a; 12 | } 13 | 14 | static class Sub extends Sup { 15 | static int y; 16 | int b; 17 | } 18 | 19 | @Test 20 | public void staticFields() { 21 | int z = Sub.x + Sub.y; 22 | z += 100; 23 | Sub.y = z; 24 | Sub.x = z; 25 | assertTrue(Sub.x == 100 && Sub.y == 100); 26 | } 27 | 28 | @Test 29 | public void instanceFields() { 30 | Sub sub = new Sub(); 31 | int c = sub.a + sub.b; 32 | c += 100; 33 | sub.a = c; 34 | sub.b = c; 35 | assertTrue(sub.a == 100 && sub.b == 100); 36 | } 37 | 38 | public static void main(String[] args) { 39 | UnitTestRunner.run(FieldsTest.class); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/instructions/ANewArray.java: -------------------------------------------------------------------------------- 1 | package jvm.instructions; 2 | 3 | public class ANewArray { 4 | 5 | public static void main(String[] args) { 6 | test1(); 7 | test2(); 8 | } 9 | 10 | private static void test1() { 11 | Object[] arr = new Object[8]; 12 | if (arr.length == 8) { 13 | System.out.println("OK!"); 14 | } else { 15 | System.out.println("Fail!"); 16 | } 17 | } 18 | 19 | private static void test2() { 20 | int[][][] y = { 21 | { 22 | {1}, 23 | {1, 2}, 24 | {1, 2, 3} 25 | } 26 | }; 27 | System.out.println(y.length); 28 | System.out.println(y[0].length); 29 | System.out.println(y[0][0].length); 30 | System.out.println(y[0][1].length); 31 | System.out.println(y[0][2].length); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/instructions/AThrow.java: -------------------------------------------------------------------------------- 1 | package jvm.instructions; 2 | 3 | public class AThrow { 4 | 5 | public static void main(String[] args) { 6 | try { 7 | foo(); 8 | } catch (RuntimeException e) { 9 | System.out.println("main!!"); 10 | System.out.println(e.getMessage()); 11 | } 12 | } 13 | 14 | static void foo() { 15 | try { 16 | throw new RuntimeException("foo!"); 17 | } catch (RuntimeException e) { 18 | System.out.println(e.getMessage()); 19 | throw new RuntimeException("bar!"); 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/instructions/LookupSwitch.java: -------------------------------------------------------------------------------- 1 | package jvm.instructions; 2 | 3 | public class LookupSwitch { 4 | 5 | public static void main(String[] args) { 6 | switch (args.length) { 7 | case -100: System.out.println("-100"); break; 8 | case 0: System.out.println("0"); break; 9 | case 3: System.out.println("3"); break; 10 | case 5: System.out.println("5"); break; 11 | default: System.out.println("default"); 12 | } 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/instructions/MultiANewArrayTest.java: -------------------------------------------------------------------------------- 1 | package jvm.instructions; 2 | 3 | public class MultiANewArrayTest { 4 | 5 | public static void main(String[] args) { 6 | int[][][] x = new int[2][3][5]; 7 | System.out.println(x.length); 8 | System.out.println(x[0].length); 9 | System.out.println(x[1][2].length); 10 | 11 | x[1][2][3] = 7; 12 | System.out.println(x[1][2][3]); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/instructions/TableSwitch.java: -------------------------------------------------------------------------------- 1 | package jvm.instructions; 2 | 3 | public class TableSwitch { 4 | 5 | public static void main(String[] args) { 6 | switch (args.length) { 7 | case 3: System.out.println("3"); break; 8 | case 4: System.out.println("4"); break; 9 | case 5: System.out.println("5"); break; 10 | default: System.out.println("default"); 11 | } 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/jsr292/MemberNameTest.java: -------------------------------------------------------------------------------- 1 | package jvm.jsr292; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.Method; 6 | 7 | import static helper.MyAssert.assertEquals; 8 | 9 | public class MemberNameTest { 10 | 11 | public static void main(String[] args) throws Exception { 12 | Method mainMethod = MemberNameTest.class.getDeclaredMethod("main", String[].class); 13 | 14 | Class mnClass = Class.forName("java.lang.invoke.MemberName"); 15 | Constructor mnConstructor = mnClass.getConstructor(Method.class); 16 | mnConstructor.setAccessible(true); 17 | Field flags = mnClass.getDeclaredField("flags"); 18 | flags.setAccessible(true); 19 | Method getFieldType = mnClass.getDeclaredMethod("getMethodOrFieldType"); 20 | getFieldType.setAccessible(true); 21 | Method getRefKind = mnClass.getDeclaredMethod("getReferenceKind"); 22 | getRefKind.setAccessible(true); 23 | 24 | Object mn = mnConstructor.newInstance(mainMethod); 25 | assertEquals(100728841, flags.get(mn)); 26 | assertEquals((byte)6, getRefKind.invoke(mn)); 27 | assertEquals("(String[])void", getFieldType.invoke(mn).toString()); 28 | 29 | System.out.println("OK!"); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/jsr292/MethodHandleNativesTest.java: -------------------------------------------------------------------------------- 1 | package jvm.jsr292; 2 | 3 | import helper.ReflectionHelper; 4 | import helper.UnitTestRunner; 5 | import org.junit.Test; 6 | import static org.junit.Assert.*; 7 | 8 | public class MethodHandleNativesTest { 9 | 10 | public static void main(String[] args) { 11 | UnitTestRunner.run(MethodHandleNativesTest.class); 12 | } 13 | 14 | @Test 15 | public void getConstant() throws Exception { 16 | Class mhnClass = Class.forName("java.lang.invoke.MethodHandleNatives"); 17 | 18 | for (int i = 0; i < 100; i++) { 19 | Object x = ReflectionHelper.call(mhnClass, "getConstant", i); 20 | if (i == 4) { 21 | assertEquals(1, x); 22 | } else { 23 | assertEquals(0, x); 24 | } 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/jsr292/MethodHandleTest.java: -------------------------------------------------------------------------------- 1 | package jvm.jsr292; 2 | 3 | import java.lang.invoke.MethodHandle; 4 | import java.lang.invoke.MethodHandles; 5 | import java.lang.invoke.MethodType; 6 | 7 | import static helper.MyAssert.assertEquals; 8 | 9 | public class MethodHandleTest { 10 | 11 | private static int x; 12 | 13 | public static String test() { 14 | return "hi"; 15 | } 16 | 17 | public static void main(String[] args) throws Throwable { 18 | MethodHandles.Lookup lookup = MethodHandles.lookup(); 19 | 20 | MethodType mt = MethodType.methodType(String.class); 21 | MethodHandle mh = lookup.findStatic(MethodHandleTest.class, "test", mt); 22 | assertEquals("hi", mh.invoke()); 23 | assertEquals("hi", (String) mh.invokeExact()); 24 | 25 | MethodHandle getter = lookup.findStaticGetter(MethodHandleTest.class, "x", int.class); 26 | MethodHandle setter = lookup.findStaticSetter(MethodHandleTest.class, "x", int.class); 27 | setter.invoke(100); 28 | assertEquals(100, getter.invoke()); 29 | 30 | System.out.println("OK!"); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/jsr292/MethodTypeTest.java: -------------------------------------------------------------------------------- 1 | package jvm.jsr292; 2 | 3 | import java.util.Arrays; 4 | 5 | import static java.lang.invoke.MethodType.*; 6 | import static helper.MyAssert.assertEquals; 7 | 8 | public class MethodTypeTest { 9 | 10 | public static void main(String[] args) { 11 | assertEquals("()void", methodType(void.class).toString()); // methodType(Class rtype) 12 | assertEquals("(String)Object", methodType(Object.class, String.class).toString()); // methodType(Class rtype, Class ptype0) 13 | assertEquals("(int,long)String", methodType(String.class, int.class, long.class).toString()); // methodType(Class rtype, Class ptype0, Class... ptypes) 14 | assertEquals("(int)String", methodType(String.class, new Class[]{int.class}).toString()); // methodType(Class rtype, Class[] ptypes) 15 | assertEquals("(int)String", methodType(String.class, Arrays.asList(int.class)).toString()); // methodType(Class rtype, List> ptypes) 16 | assertEquals("()void", fromMethodDescriptorString("()V", null).toString()); 17 | System.out.println("OK!"); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/lambda/InterfaceMethodTest.java: -------------------------------------------------------------------------------- 1 | package jvm.lambda; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | public class InterfaceMethodTest { 8 | 9 | public interface If1 { 10 | static int x() { 11 | return 1; 12 | } 13 | default int y() { 14 | return 2; 15 | } 16 | } 17 | 18 | public static class Impl1 implements If1 { 19 | 20 | } 21 | 22 | public static class Impl2 implements If1 { 23 | @Override 24 | public int y() { 25 | return 12; 26 | } 27 | } 28 | 29 | public static class Impl3 implements If1 { 30 | @Override 31 | public int y() { 32 | return 100 + If1.super.y(); 33 | } 34 | } 35 | 36 | public static void main(String[] args) { 37 | UnitTestRunner.run(InterfaceMethodTest.class); 38 | } 39 | 40 | @Test 41 | public void staticMethod() { 42 | assertEquals(1, If1.x()); 43 | } 44 | 45 | @Test 46 | public void defaultMethod() { 47 | assertEquals(2, new Impl1().y()); 48 | assertEquals(12, new Impl2().y()); 49 | assertEquals(102, new Impl3().y()); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/jvm/lambda/LambdaTest.java: -------------------------------------------------------------------------------- 1 | package jvm.lambda; 2 | 3 | public class LambdaTest { 4 | 5 | public static void main(String[] args) { 6 | Runnable r = () -> System.out.println("Hello, World!"); 7 | r.run(); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/RuntimeTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic; 2 | 3 | public class RuntimeTest { 4 | 5 | public static void main(String[] args) { 6 | Runtime rt = Runtime.getRuntime(); 7 | System.out.println("availableProcessors:" + rt.availableProcessors()); 8 | System.out.println("freeMemory:" + rt.freeMemory()); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/SysProps.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic; 2 | 3 | public class SysProps { 4 | 5 | public static void main(String[] args) { 6 | String[] keys = { 7 | "java.version", 8 | "java.vendor", 9 | "java.vendor.url", 10 | "java.home", 11 | "java.class.version", 12 | "java.class.path", 13 | "os.name", 14 | "os.arch", 15 | "os.version", 16 | "file.separator", 17 | "path.separator", 18 | "line.separator", 19 | "user.name", 20 | "user.home", 21 | "user.dir", 22 | "file.encoding", 23 | "sun.stdout.encoding", 24 | "sun.stderr.encoding", 25 | }; 26 | 27 | for (String key : keys) { 28 | String val = System.getProperty(key); 29 | System.out.println(key + ": " + val); 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/SysPropsTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic; 2 | 3 | import org.junit.Test; 4 | 5 | public class SysPropsTest { 6 | 7 | public static void main(String[] args) { 8 | //UnitTestRunner.run(SysPropsTest.class); 9 | System.out.println(System.getProperty("os.name")); 10 | System.out.println(System.getProperty("os.arch")); 11 | System.out.println(System.getProperty("os.version")); 12 | } 13 | 14 | @Test 15 | public void test() { 16 | String osName = System.getProperty("os.name"); 17 | System.out.println(osName); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/TimeZoneTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic; 2 | 3 | 4 | import java.util.Calendar; 5 | import java.util.TimeZone; 6 | 7 | public class TimeZoneTest { 8 | 9 | public static void main(String[] args) { 10 | System.out.println(TimeZone.getDefault()); 11 | System.out.println(Calendar.getInstance().getTime()); 12 | } 13 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/cl/GetClassLoaderTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.cl; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | public class GetClassLoaderTest { 8 | 9 | public static void main(String[] args) { 10 | UnitTestRunner.run(GetClassLoaderTest.class); 11 | } 12 | 13 | @Test 14 | public void primitiveTypes() { 15 | assertNull(int.class.getClassLoader()); 16 | } 17 | 18 | @Test 19 | public void array() { 20 | assertNull(float[].class.getClassLoader()); 21 | assertNull(new int[0].getClass().getClassLoader()); 22 | assertNull(new Object[0].getClass().getClassLoader()); 23 | assertNull(int[][].class.getClassLoader()); 24 | } 25 | 26 | @Test 27 | public void bootCl() { 28 | assertNull(Object.class.getClassLoader()); 29 | assertNull("".getClass().getClassLoader()); 30 | } 31 | 32 | //@Test 33 | public void sysCl() { 34 | ClassLoader sysCl = ClassLoader.getSystemClassLoader(); 35 | assertSame(sysCl, GetClassLoaderTest.class.getClassLoader()); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/cl/UrlClassLoaderTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.cl; 2 | 3 | import java.net.URL; 4 | import java.net.URLClassLoader; 5 | 6 | public class UrlClassLoaderTest { 7 | 8 | public static void main(String[] args) throws Exception { 9 | URL gson = new URL("file:/Users/zxh/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.3.1/ecb6e1f8e4b0e84c4b886c2f14a1500caf309757/gson-2.3.1.jar"); 10 | URLClassLoader loader1 = new URLClassLoader(new URL[]{gson}); 11 | URLClassLoader loader2 = new URLClassLoader(new URL[]{gson}); 12 | 13 | Class c1 = loader1.loadClass("com.google.gson.JsonArray"); 14 | Class c2 = loader2.loadClass("com.google.gson.JsonArray"); 15 | System.out.println(c1 == c2); 16 | // 17 | // Object gson1 = c1.newInstance(); 18 | // Method toJson = c1.getMethod("toJson", Object.class); 19 | // System.out.println(toJson.invoke(gson1, "123")); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/reflection/ArrayClassTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.reflection; 2 | 3 | import java.io.Serializable; 4 | import org.junit.Test; 5 | import helper.UnitTestRunner; 6 | import static org.junit.Assert.*; 7 | 8 | public class ArrayClassTest { 9 | 10 | public static void main(String[] args) { 11 | UnitTestRunner.run(ArrayClassTest.class); 12 | } 13 | 14 | @Test 15 | public void test() { 16 | testArrayClass(boolean[].class, "[Z"); 17 | testArrayClass(byte[].class, "[B"); 18 | testArrayClass(char[].class, "[C"); 19 | testArrayClass(short[].class, "[S"); 20 | testArrayClass(int[].class, "[I"); 21 | testArrayClass(long[].class, "[J"); 22 | testArrayClass(float[].class, "[F"); 23 | testArrayClass(double[].class, "[D"); 24 | testArrayClass(int[][].class, "[[I"); 25 | testArrayClass(Object[].class, "[Ljava.lang.Object;"); 26 | testArrayClass(Object[][].class,"[[Ljava.lang.Object;"); 27 | } 28 | 29 | private void testArrayClass(Class c, String name) { 30 | assertEquals(name, c.getName()); 31 | assertEquals(Object.class, c.getSuperclass()); 32 | assertArrayEquals(new Class[]{Cloneable.class, Serializable.class}, c.getInterfaces()); 33 | assertEquals(0, c.getFields().length); 34 | assertEquals(0, c.getDeclaredFields().length); 35 | assertEquals(9, c.getMethods().length); 36 | assertEquals(0, c.getDeclaredMethods().length); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/reflection/CallerClassTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.reflection; 2 | 3 | import sun.reflect.Reflection; 4 | 5 | public class CallerClassTest { 6 | 7 | public static void main(String[] args) { 8 | Foo.test(); 9 | } 10 | 11 | static class Foo { 12 | static void test() { 13 | Bar.test(); 14 | } 15 | } 16 | 17 | static class Bar { 18 | static void test() { 19 | System.out.println(Reflection.getCallerClass(0).getName()); 20 | System.out.println(Reflection.getCallerClass(1).getName()); 21 | System.out.println(Reflection.getCallerClass(2).getName()); 22 | System.out.println(Reflection.getCallerClass(3).getName()); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/reflection/ClassInitTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.reflection; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | public class ClassInitTest { 8 | 9 | static class A { 10 | public static int a = 100; 11 | } 12 | 13 | public static void main(String[] args) { 14 | UnitTestRunner.run(ClassInitTest.class); 15 | } 16 | 17 | @Test 18 | public void getStatic() throws Exception { 19 | assertEquals(100, A.class.getField("a").get(null)); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/reflection/DeclaringClassTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.reflection; 2 | 3 | public class DeclaringClassTest { 4 | 5 | static class A { 6 | static class B { 7 | class C { 8 | 9 | } 10 | } 11 | } 12 | 13 | public static void main(String[] args) { 14 | System.out.println(DeclaringClassTest.class.getDeclaringClass()); 15 | System.out.println(A.class.getDeclaringClass()); 16 | System.out.println(A.B.class.getDeclaringClass()); 17 | System.out.println(A.B.C.class.getDeclaringClass()); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/reflection/FieldTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.reflection; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | public class FieldTest { 6 | 7 | static int x; 8 | 9 | public static void main(String[] args) throws Exception { 10 | Field f = String.class.getDeclaredField("value"); 11 | f.setAccessible(true); 12 | f.get("123"); 13 | 14 | Field fx = FieldTest.class.getDeclaredField("x"); 15 | fx.setAccessible(true); 16 | fx.get(null); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/reflection/GenericTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.reflection; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import helper.UnitTestRunner; 7 | import org.junit.Test; 8 | import static org.junit.Assert.*; 9 | 10 | public class GenericTest { 11 | 12 | private static class GenericClass { 13 | 14 | } 15 | 16 | private static final String str = "abc"; 17 | private static final List list = new ArrayList<>(); 18 | 19 | @Test 20 | public void typeParameter() { 21 | assertEquals(1, GenericClass.class.getTypeParameters().length); 22 | assertEquals("String", GenericClass.class.getTypeParameters()[0].toString()); 23 | } 24 | 25 | @Test 26 | public void fieldGenericType() throws NoSuchFieldException { 27 | assertEquals(String.class, 28 | GenericTest.class.getDeclaredField("str").getGenericType()); 29 | 30 | Field listField = GenericTest.class.getDeclaredField("list"); 31 | assertEquals("list", listField.getName()); 32 | assertEquals(List.class, listField.getType()); 33 | assertEquals(26, listField.getModifiers()); 34 | assertEquals("java.util.List", 35 | listField.getGenericType().toString()); 36 | } 37 | 38 | public static void main(String[] args) throws Exception { 39 | UnitTestRunner.run(GenericTest.class); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/reflection/InnerClassTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.reflection; 2 | 3 | public class InnerClassTest { 4 | 5 | private static class Inner {} 6 | 7 | public static void main(String[] args) { 8 | System.out.println(InnerClassTest.class.getModifiers()); // 1 9 | System.out.println(Inner.class.getModifiers()); // 10 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/reflection/MethodTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.reflection; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.concurrent.Callable; 5 | import helper.UnitTestRunner; 6 | import org.junit.Test; 7 | import static org.junit.Assert.*; 8 | 9 | public class MethodTest implements Callable { 10 | 11 | public static void main(String[] args) throws Exception { 12 | UnitTestRunner.run(MethodTest.class); 13 | } 14 | 15 | @Test 16 | public void boxReturn() throws Exception { 17 | Method m = MethodTest.class.getMethod("returnLong"); 18 | Object x = m.invoke(new MethodTest()); 19 | assertEquals(3L, x); 20 | } 21 | 22 | @Test 23 | public void invokeInterfaceMethod() throws Exception { 24 | Method m = Callable.class.getMethod("call"); 25 | Object x = m.invoke(new MethodTest()); 26 | assertEquals(7, x); 27 | } 28 | 29 | @Override 30 | public Integer call() { 31 | return 7; 32 | } 33 | 34 | public long returnLong() { 35 | return 3; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/reflection/PrimitiveClassTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.reflection; 2 | 3 | import org.junit.Test; 4 | import static org.junit.Assert.*; 5 | import helper.UnitTestRunner; 6 | 7 | public class PrimitiveClassTest { 8 | 9 | public static void main(String[] args) { 10 | UnitTestRunner.run(PrimitiveClassTest.class); 11 | } 12 | 13 | @Test 14 | public void test() { 15 | testPrimitiveClass(void.class, "void"); 16 | testPrimitiveClass(boolean.class, "boolean"); 17 | testPrimitiveClass(byte.class, "byte"); 18 | testPrimitiveClass(char.class, "char"); 19 | testPrimitiveClass(short.class, "short"); 20 | testPrimitiveClass(int.class, "int"); 21 | testPrimitiveClass(long.class, "long"); 22 | testPrimitiveClass(float.class, "float"); 23 | testPrimitiveClass(double.class, "double"); 24 | } 25 | 26 | private void testPrimitiveClass(Class c, String name) { 27 | assertEquals(name, c.getName()); 28 | assertEquals(null, c.getSuperclass()); 29 | assertEquals(0, c.getFields().length); 30 | assertEquals(0, c.getDeclaredFields().length); 31 | assertEquals(0, c.getMethods().length); 32 | assertEquals(0, c.getDeclaredMethods().length); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/string/Mutf8Test.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.string; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | // http://www.oracle.com/technetwork/articles/javase/supplementary-142654.html 8 | public class Mutf8Test { 9 | 10 | public static void main(String[] args) { 11 | UnitTestRunner.run(Mutf8Test.class); 12 | } 13 | 14 | @Test 15 | public void test() { 16 | assertEquals("A", 1, "A".length()); // U+0041 17 | assertEquals("NULL", 1, "\u0000".length()); 18 | assertEquals("ß", 1, "ß".length()); // U+00DF 19 | assertEquals("東", 1, "東".length()); // U+6771 20 | assertEquals("U+10400", 2, "\uD801\uDC00".length()); // U+10400 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/string/StringTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.string; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | public class StringTest { 8 | 9 | public static void main(String[] args) { 10 | UnitTestRunner.run(StringTest.class); 11 | } 12 | 13 | @Test 14 | public void test() { 15 | String s1 = "abc1"; 16 | String s2 = "abc1"; 17 | assertSame(s1, s2); 18 | 19 | int x = 1; 20 | String s3 = "abc" + x; 21 | assertNotSame(s1, s3); 22 | 23 | s3 = s3.intern(); 24 | assertSame(s1, s3); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/string/StringTest2.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.string; 2 | 3 | public class StringTest2 { 4 | 5 | public static void main(String[] args) { 6 | String s = "\uD800"; 7 | System.out.println(s.length()); 8 | System.out.println((int)s.charAt(0)); // 55296 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/sunmisc/UnsafeGetter.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.sunmisc; 2 | 3 | import java.lang.reflect.Field; 4 | import sun.misc.Unsafe; 5 | 6 | public class UnsafeGetter { 7 | 8 | public static Unsafe getUnsafe() { 9 | //Unsafe unsafe = Unsafe.getUnsafe(); 10 | try { 11 | Field f = Unsafe.class.getDeclaredField("theUnsafe"); 12 | f.setAccessible(true); 13 | Unsafe unsafe = (Unsafe) f.get(null); 14 | return unsafe; 15 | } catch (Exception e) { 16 | throw new RuntimeException(e); 17 | } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/thread/AliveTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.thread; 2 | 3 | public class AliveTest { 4 | 5 | public static void main(String[] args) throws InterruptedException { 6 | Thread t = new Thread() { 7 | 8 | @Override 9 | public void run() { 10 | try { 11 | Thread.sleep(2000); 12 | } catch (InterruptedException e) { 13 | e.printStackTrace(System.err); 14 | } 15 | } 16 | 17 | }; 18 | 19 | if (t.isAlive() != false) { 20 | System.out.println("t is alive!"); 21 | return; 22 | } 23 | 24 | t.start(); 25 | Thread.sleep(1000); 26 | if (t.isAlive() != true) { 27 | System.out.println("t is not alive!"); 28 | return; 29 | } 30 | 31 | t.join(); 32 | if (t.isAlive() != false) { 33 | System.out.println("t is not dead!"); 34 | return; 35 | } 36 | 37 | System.out.println("OK!"); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/thread/DaemonTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.thread; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | public class DaemonTest { 8 | 9 | public static void main(String[] args) { 10 | UnitTestRunner.run(DaemonTest.class); 11 | } 12 | 13 | @Test 14 | public void isDaemon() { 15 | Thread mainThread = Thread.currentThread(); 16 | assertFalse(mainThread.isDaemon()); 17 | 18 | Thread newThread = new Thread(); 19 | assertFalse(newThread.isDaemon()); 20 | 21 | newThread.setDaemon(true); 22 | assertTrue(newThread.isDaemon()); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/thread/InterruptFlagTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.thread; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | public class InterruptFlagTest { 8 | 9 | public static void main(String[] args) { 10 | UnitTestRunner.run(InterruptFlagTest.class); 11 | } 12 | 13 | @Test 14 | public void interruptFlag() { 15 | Thread t = Thread.currentThread(); 16 | assertFalse(t.isInterrupted()); 17 | 18 | t.interrupt(); 19 | assertTrue(t.isInterrupted()); 20 | assertTrue(t.isInterrupted()); 21 | 22 | assertTrue(Thread.interrupted()); 23 | assertFalse(Thread.interrupted()); 24 | assertFalse(t.isInterrupted()); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/thread/InterruptionTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.thread; 2 | 3 | public class InterruptionTest { 4 | 5 | public static void main(String[] args) throws InterruptedException { 6 | Thread t = new Thread(new Runnable() { 7 | 8 | @Override 9 | public void run() { 10 | try { 11 | Thread.sleep(1000); 12 | } catch (InterruptedException ex) { 13 | System.out.println(ex.getMessage()); 14 | } 15 | } 16 | 17 | }); 18 | t.start(); 19 | 20 | Thread.sleep(500); 21 | t.interrupt(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/thread/MainThreadTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.thread; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | public class MainThreadTest { 8 | 9 | public static void main(String[] args) { 10 | UnitTestRunner.run(MainThreadTest.class); 11 | } 12 | 13 | @Test 14 | public void mainThread() { 15 | Thread mainThread = Thread.currentThread(); 16 | assertEquals("main", mainThread.getName()); 17 | assertTrue("isAlive", mainThread.isAlive()); 18 | assertFalse("isDaemon", mainThread.isDaemon()); 19 | //System.out.println(mainThread.getThreadGroup()); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/thread/RunnableTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.thread; 2 | 3 | public class RunnableTest implements Runnable { 4 | 5 | public static void main(String[] args) { 6 | Thread t = new Thread(new RunnableTest()); 7 | t.start(); 8 | 9 | for (int i = 0; i < 100; i++) { 10 | System.out.println("main:" + i); 11 | } 12 | } 13 | 14 | @Override 15 | public void run() { 16 | for (int i = 0; i < 100; i++) { 17 | System.out.println("run:" + i); 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/thread/SleepTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.thread; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | public class SleepTest { 8 | 9 | public static void main(String[] args) { 10 | UnitTestRunner.run(SleepTest.class); 11 | } 12 | 13 | @Test 14 | public void sleep() throws InterruptedException { 15 | long beforeSleep = System.currentTimeMillis(); 16 | Thread.sleep(100); 17 | long afterSleep = System.currentTimeMillis(); 18 | assertTrue(afterSleep - beforeSleep >= 100); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/thread/SynchronizedTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.thread; 2 | 3 | public class SynchronizedTest { 4 | 5 | public static void main(String[] args) { 6 | synchronized (SynchronizedTest.class) { 7 | test(); 8 | } 9 | } 10 | 11 | private static synchronized void test() { 12 | new SynchronizedTest().foo(); 13 | } 14 | 15 | private synchronized void foo() { 16 | bar(); 17 | } 18 | 19 | private void bar() { 20 | System.out.println("OK!"); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/thread/ThreadSubClassTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.thread; 2 | 3 | public class ThreadSubClassTest extends Thread { 4 | 5 | public static void main(String[] args) { 6 | new ThreadSubClassTest().start(); 7 | for (int i = 0; i < 100; i++) { 8 | System.out.println("main:" + i); 9 | } 10 | } 11 | 12 | @Override 13 | public void run() { 14 | for (int i = 0; i < 100; i++) { 15 | System.out.println("run:" + i); 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/thread/WaitTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.thread; 2 | 3 | public class WaitTest { 4 | 5 | public static void main(String[] args) throws InterruptedException { 6 | args.wait(); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/wrapper/DoubleTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.wrapper; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | public class DoubleTest { 8 | 9 | public static void main(String[] args) { 10 | UnitTestRunner.run(DoubleTest.class); 11 | } 12 | 13 | @Test 14 | public void doubleToRawLongBits() { 15 | assertEquals(4614253070214989087L, Double.doubleToRawLongBits(3.14)); 16 | } 17 | 18 | @Test 19 | public void longBitsToDouble() { 20 | assertEquals(3.14, Double.longBitsToDouble(4614253070214989087L), 0); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/basic/wrapper/FloatTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.basic.wrapper; 2 | 3 | import helper.UnitTestRunner; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | public class FloatTest { 8 | 9 | public static void main(String[] args) { 10 | UnitTestRunner.run(FloatTest.class); 11 | } 12 | 13 | @Test 14 | public void floatToRawIntBits() { 15 | assertEquals(1076754509, Float.floatToRawIntBits(2.71828f)); 16 | } 17 | 18 | @Test 19 | public void intBitsToFloat() { 20 | // assertEquals(2.71828f, Float.intBitsToFloat(1076754509), 0); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/gui/awt/AwtTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.gui.awt; 2 | 3 | import java.awt.Button; 4 | import java.awt.Frame; 5 | import java.awt.event.WindowAdapter; 6 | import java.awt.event.WindowEvent; 7 | 8 | public class AwtTest extends WindowAdapter { 9 | 10 | public static void main(String[] args) { 11 | Frame frame = new Frame("AWT Test"); 12 | Button button = new Button("Click me!"); 13 | frame.add("Center", button); 14 | frame.addWindowListener(new AwtTest()); 15 | frame.pack(); 16 | frame.setVisible(true); 17 | } 18 | 19 | @Override 20 | public void windowClosing(WindowEvent e) { 21 | System.exit(0); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/gui/swing/SwingTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.gui.swing; 2 | 3 | import javax.swing.JButton; 4 | import javax.swing.JFrame; 5 | 6 | public class SwingTest { 7 | 8 | public static void main(String[] args) { 9 | JFrame frame = new JFrame("Swing Test"); 10 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 11 | frame.getContentPane().add(new JButton("Click me!")); 12 | frame.setLocationRelativeTo(null); 13 | frame.pack(); 14 | frame.setVisible(true); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/juc/atomic/AtomicIntegerTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.juc.atomic; 2 | 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | 5 | public class AtomicIntegerTest { 6 | 7 | private static AtomicInteger x = new AtomicInteger(0); 8 | // private static AtomicInteger y = new AtomicInteger(0); 9 | 10 | private static class Runner extends Thread { 11 | 12 | @Override 13 | public void run() { 14 | // for (int i = 0; i < 100; i++) { 15 | // sleepMS(10); 16 | // x.getAndIncrement(); 17 | // } 18 | // System.out.println(y.incrementAndGet()); 19 | } 20 | 21 | } 22 | 23 | public static void main(String[] args) { 24 | // for (int i = 0; i < 100; i++) { 25 | // new Runner().start(); 26 | // } 27 | // while (y.get() != 100) { 28 | // sleepMS(100); 29 | // } 30 | // AtomicInteger y = new AtomicInteger(0); 31 | // System.out.println("y=" + y.get()); 32 | } 33 | 34 | private static void sleepMS(long millis) { 35 | try { 36 | Thread.sleep(millis); 37 | } catch (InterruptedException ex) { 38 | ex.printStackTrace(System.err); 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/net/InetAddressTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.net; 2 | 3 | import java.net.InetAddress; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: beyond 8 | * Email: beyondblog@outlook.com 9 | * Date: 15/4/10 10 | * Time: 上午8:32 11 | * Description: 12 | */ 13 | public class InetAddressTest { 14 | 15 | public static void main(String[] args) throws Exception { 16 | //InetAddress test 17 | InetAddress inetAddress = InetAddress.getByName("127.0.0.1"); 18 | System.out.println(inetAddress.getHostName()); 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/net/SocketConnectTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.net; 2 | 3 | import java.io.*; 4 | import java.net.Socket; 5 | 6 | /** 7 | * Created with IntelliJ IDEA. 8 | * User: beyond 9 | * Email: beyondblog@outlook.com 10 | * Date: 15/3/24 11 | * Time: 上午9:35 12 | * Description: 13 | */ 14 | public class SocketConnectTest { 15 | public static void main(String[] args) throws Exception { 16 | try { 17 | Socket client = new Socket("127.0.0.1", 5457); 18 | Writer writer = new OutputStreamWriter(client.getOutputStream()); 19 | writer.write("Hello beyond."); 20 | writer.flush(); 21 | writer.close(); 22 | // String str = "Hello beyond."; 23 | // client.getOutputStream().write(str.getBytes()); 24 | client.close(); 25 | } catch (Exception e) { 26 | System.out.println(e); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/net/SocketListenTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.net; 2 | 3 | import java.net.ServerSocket; 4 | import java.net.Socket; 5 | 6 | /** 7 | * Created with IntelliJ IDEA. 8 | * User: beyond 9 | * Email: beyondblog@outlook.com 10 | * Date: 15/3/21 11 | * Time: 下午9:24 12 | * Description: 13 | * Socket 14 | */ 15 | public class SocketListenTest { 16 | 17 | public static void main(String[] args) throws Exception { 18 | try { 19 | ServerSocket serverSocket = new ServerSocket(5457); 20 | System.out.println("Accept:"); 21 | Socket socket = serverSocket.accept(); 22 | //read from client 23 | //char chars[] = new char[64]; 24 | byte[] buffer = new byte[1024]; 25 | int len; 26 | StringBuilder sb = new StringBuilder(); 27 | //Reader reader = new InputStreamReader(socket.getInputStream()); 28 | socket.setSoTimeout(2000); 29 | while ((len = socket.getInputStream().read(buffer)) != -1) { 30 | sb.append(new String(buffer, 0, len)); 31 | } 32 | System.out.println("from client: " + sb); 33 | //write to client 34 | String s = "Hi i'm beyond\r\n"; 35 | socket.getOutputStream().write(s.getBytes()); 36 | socket.close(); 37 | } catch (Exception e) { 38 | System.out.println(e.toString()); 39 | } 40 | //telnet 127.0.0.1 5457 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/net/UrlTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.net; 2 | 3 | import java.io.InputStream; 4 | import java.net.URL; 5 | 6 | public class UrlTest { 7 | 8 | public static void main(String[] args) throws Exception { 9 | URL url = new URL("http://cn.bing.com"); 10 | try (InputStream is = url.openStream()) { 11 | byte[] bytes = new byte[is.available()]; 12 | int n = is.read(bytes); 13 | System.out.println(new String(bytes)); 14 | } 15 | System.out.println("OK!"); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/nio/ByteBufferTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.nio; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | public class ByteBufferTest { 6 | 7 | public static void main(String[] args) { 8 | ByteBuffer.allocate(8); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/nio/io/file/FileDescriptorTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.nio.io.file; 2 | 3 | import java.io.FileDescriptor; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | 7 | public class FileDescriptorTest { 8 | 9 | public static void main(String[] args) throws IOException { 10 | FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out); 11 | fdOut.write("OK!\n".getBytes()); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/nio/io/file/FileIoTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.nio.io.file; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.FileNotFoundException; 5 | import helper.UnitTestRunner; 6 | import org.junit.Test; 7 | import static org.junit.Assert.*; 8 | 9 | public class FileIoTest { 10 | 11 | public static void main(String[] args) throws Exception { 12 | UnitTestRunner.run(FileIoTest.class); 13 | } 14 | 15 | @Test 16 | public void fileNotFoundException() { 17 | try { 18 | FileInputStream fis = new FileInputStream("a/b/foo.txt"); 19 | } catch (FileNotFoundException e) { 20 | assertEquals("a/b/foo.txt (No such file or directory)", e.getMessage()); 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/nio/io/file/FileTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.nio.io.file; 2 | 3 | import java.nio.file.FileSystems; 4 | 5 | public class FileTest { 6 | 7 | public static void main(String[] args) { 8 | // File file = new File("test.txt"); 9 | // String p = file.getAbsolutePath(); 10 | // System.out.println(p); 11 | FileSystems.getDefault(); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/util/TimeZoneTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.util; 2 | 3 | import java.util.TimeZone; 4 | 5 | public class TimeZoneTest { 6 | 7 | public static void main(String[] args) { 8 | System.out.println(TimeZone.getDefault()); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/stdlib/util/ZipFileTest.java: -------------------------------------------------------------------------------- 1 | package stdlib.util; 2 | 3 | import java.util.Collections; 4 | import java.util.zip.ZipEntry; 5 | import java.util.zip.ZipFile; 6 | 7 | public class ZipFileTest { 8 | 9 | public static void main(String[] args) throws Exception { 10 | ZipFile zf = new ZipFile("/Users/zxh/Work/GitHub/jvm.go/testclasses/build/libs/testclasses.jar"); 11 | 12 | for (ZipEntry x : Collections.list(zf.entries())) { 13 | System.out.println(x); 14 | } 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /test/testclasses/src/main/java/thirdparty/gson/GsonTest.java: -------------------------------------------------------------------------------- 1 | package thirdparty.gson; 2 | 3 | import com.google.gson.Gson; 4 | import helper.UnitTestRunner; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | public class GsonTest { 9 | 10 | public static void main(String[] args) { 11 | UnitTestRunner.run(GsonTest.class); 12 | } 13 | 14 | @Test 15 | public void gson() { 16 | JsonTest2 obj = new JsonTest2(); 17 | obj.x = 100; 18 | obj.y = 3.14f; 19 | Assert.assertEquals("{\"x\":100,\"y\":3.14}", new Gson().toJson(obj)); 20 | } 21 | 22 | @Test 23 | public void testGenericsType() { 24 | try { 25 | JsonTest jsonTest = new JsonTest(); 26 | jsonTest.x = "beyond"; 27 | Assert.assertEquals("{\"x\":\"beyond\"}", new Gson().toJson(jsonTest)); 28 | } catch (Exception e) { 29 | e.printStackTrace(); 30 | } 31 | } 32 | } 33 | 34 | class JsonTest { 35 | public T x; 36 | } 37 | 38 | class JsonTest2 { 39 | public int x; 40 | public float y; 41 | } 42 | -------------------------------------------------------------------------------- /test/testdata/java13/HelloWorld.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/jvm.go/129b147ebcc820279748f6095c916da1986ebef1/test/testdata/java13/HelloWorld.class -------------------------------------------------------------------------------- /test/testdata/java13/module-info.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/jvm.go/129b147ebcc820279748f6095c916da1986ebef1/test/testdata/java13/module-info.class -------------------------------------------------------------------------------- /test/testdata/java8/HelloWorld.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/jvm.go/129b147ebcc820279748f6095c916da1986ebef1/test/testdata/java8/HelloWorld.class -------------------------------------------------------------------------------- /vm/err_class_not_found.go: -------------------------------------------------------------------------------- 1 | package vm 2 | 3 | type ClassNotFoundError struct { 4 | name string 5 | } 6 | 7 | func NewClassNotFoundError(name string) ClassNotFoundError { 8 | return ClassNotFoundError{name} 9 | } 10 | 11 | func (err ClassNotFoundError) Error() string { 12 | return err.name 13 | } 14 | -------------------------------------------------------------------------------- /vmutils/bytes_reader.go: -------------------------------------------------------------------------------- 1 | package vmutils 2 | 3 | import ( 4 | "encoding/binary" 5 | ) 6 | 7 | type BytesReader struct { 8 | byteOrder binary.ByteOrder 9 | data []byte 10 | position int 11 | } 12 | 13 | func NewBytesReader(data []byte, byteOrder binary.ByteOrder) BytesReader { 14 | return BytesReader{ 15 | byteOrder: byteOrder, 16 | data: data, 17 | position: 0, 18 | } 19 | } 20 | 21 | func (reader *BytesReader) Position() int { 22 | return reader.position 23 | } 24 | 25 | func (reader *BytesReader) ReadUint8() uint8 { 26 | i := reader.data[reader.position] 27 | reader.position++ 28 | return i 29 | } 30 | 31 | func (reader *BytesReader) ReadUint16() uint16 { 32 | i := reader.byteOrder.Uint16(reader.data[reader.position:]) 33 | reader.position += 2 34 | return i 35 | } 36 | 37 | func (reader *BytesReader) ReadUint32() uint32 { 38 | i := reader.byteOrder.Uint32(reader.data[reader.position:]) 39 | reader.position += 4 40 | return i 41 | } 42 | 43 | func (reader *BytesReader) ReadUint64() uint64 { 44 | i := reader.byteOrder.Uint64(reader.data[reader.position:]) 45 | reader.position += 8 46 | return i 47 | } 48 | 49 | func (reader *BytesReader) ReadBytes(n int) []byte { 50 | bytes := reader.data[reader.position : reader.position+n] 51 | reader.position += n 52 | return bytes 53 | } 54 | -------------------------------------------------------------------------------- /vmutils/dir.go: -------------------------------------------------------------------------------- 1 | package vmutils 2 | 3 | import ( 4 | "io/ioutil" 5 | "path/filepath" 6 | ) 7 | 8 | type Dir struct { 9 | absPath string 10 | } 11 | 12 | func NewDir(path string) (*Dir, error) { 13 | if absPath, err := filepath.Abs(path); err != nil { 14 | return nil, err 15 | } else { 16 | return &Dir{absPath: absPath}, nil 17 | } 18 | } 19 | 20 | func (dir *Dir) AbsPath() string { 21 | return dir.absPath 22 | } 23 | 24 | func (dir *Dir) ReadFile(filename string) ([]byte, error) { 25 | absFilename := filepath.Join(dir.absPath, filename) 26 | if data, err := ioutil.ReadFile(absFilename); err != nil { 27 | return nil, err 28 | } else { 29 | return data, nil 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /vmutils/file_jmod.go: -------------------------------------------------------------------------------- 1 | package vmutils 2 | 3 | import ( 4 | "archive/zip" 5 | "bytes" 6 | "io/ioutil" 7 | "path/filepath" 8 | ) 9 | 10 | type JModFile struct { 11 | absPath string 12 | r *zip.Reader 13 | } 14 | 15 | func OpenJModFile(path string) (*JModFile, error) { 16 | if jmodFile, err := NewJModFile(path); err != nil { 17 | return nil, err 18 | } else if err := jmodFile.Open(); err != nil { 19 | return nil, err 20 | } else { 21 | return jmodFile, nil 22 | } 23 | } 24 | 25 | func NewJModFile(path string) (*JModFile, error) { 26 | if absPath, err := filepath.Abs(path); err != nil { 27 | return nil, err 28 | } else { 29 | return &JModFile{absPath: absPath}, nil 30 | } 31 | } 32 | 33 | func (mf *JModFile) AbsPath() string { 34 | return mf.absPath 35 | } 36 | func (mf *JModFile) IsOpen() bool { 37 | return mf.r != nil 38 | } 39 | 40 | func (mf *JModFile) Close() { 41 | mf.r = nil 42 | } 43 | 44 | func (mf *JModFile) Open() error { 45 | data, err := ioutil.ReadFile(mf.absPath) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | data = data[4:] // skip 0x4a4d0100 51 | r, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) 52 | if err == nil { 53 | mf.r = r 54 | } 55 | return err 56 | } 57 | 58 | func (mf *JModFile) ReadFile(filename string) ([]byte, error) { 59 | return readFileInZip(mf.r, filename) 60 | } 61 | 62 | func (mf *JModFile) ListFiles() []string { 63 | files := make([]string, 0, 100) 64 | for _, f := range mf.r.File { 65 | files = append(files, f.Name) 66 | } 67 | return files 68 | } 69 | -------------------------------------------------------------------------------- /vmutils/files.go: -------------------------------------------------------------------------------- 1 | package vmutils 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | ) 7 | 8 | func IsDir(path string) bool { 9 | if fileInfo, err := os.Stat(path); err == nil { 10 | return fileInfo.IsDir() 11 | } 12 | return false 13 | } 14 | 15 | func IsExists(path string) bool { 16 | if _, err := os.Stat(path); err == nil { 17 | return true 18 | //} else if os.IsNotExist(err) { 19 | // return false 20 | } else { 21 | return false 22 | } 23 | } 24 | 25 | func IsZipFile(name string) bool { 26 | return strings.HasSuffix(name, ".zip") || 27 | strings.HasSuffix(name, ".ZIP") 28 | } 29 | 30 | func IsJarFile(name string) bool { 31 | return strings.HasSuffix(name, ".jar") || 32 | strings.HasSuffix(name, ".JAR") 33 | } 34 | 35 | func IsJModFile(name string) bool { 36 | return strings.HasSuffix(name, ".jmod") 37 | } 38 | -------------------------------------------------------------------------------- /vmutils/native_endian.go: -------------------------------------------------------------------------------- 1 | package vmutils 2 | 3 | import ( 4 | "encoding/binary" 5 | "unsafe" 6 | ) 7 | 8 | var NativeEndian nativeEndian 9 | var _ binary.ByteOrder = NativeEndian 10 | 11 | type nativeEndian struct{} 12 | 13 | func (n nativeEndian) Uint16(b []byte) uint16 { 14 | _ = b[1] 15 | return *(*uint16)(unsafe.Pointer(&b[0])) 16 | } 17 | 18 | func (n nativeEndian) Uint32(b []byte) uint32 { 19 | _ = b[3] 20 | return *(*uint32)(unsafe.Pointer(&b[0])) 21 | } 22 | 23 | func (n nativeEndian) Uint64(b []byte) uint64 { 24 | _ = b[7] 25 | return *(*uint64)(unsafe.Pointer(&b[0])) 26 | } 27 | 28 | func (n nativeEndian) PutUint16([]byte, uint16) { 29 | panic("not implemented") 30 | } 31 | 32 | func (n nativeEndian) PutUint32([]byte, uint32) { 33 | panic("not implemented") 34 | } 35 | 36 | func (n nativeEndian) PutUint64([]byte, uint64) { 37 | panic("not implemented") 38 | } 39 | 40 | func (n nativeEndian) String() string { 41 | panic("not implemented") 42 | } 43 | -------------------------------------------------------------------------------- /vmutils/string_builder.go: -------------------------------------------------------------------------------- 1 | package vmutils 2 | 3 | import "strings" 4 | 5 | type StringBuilder struct { 6 | ss []string 7 | } 8 | 9 | func NewStringBuilder() *StringBuilder { 10 | return &StringBuilder{ss: make([]string, 0, 8)} 11 | } 12 | 13 | func (sb *StringBuilder) Append(ss ...string) { 14 | sb.ss = append(sb.ss, ss...) 15 | } 16 | 17 | func (sb *StringBuilder) String() string { 18 | return strings.Join(sb.ss, "") 19 | } 20 | -------------------------------------------------------------------------------- /vmutils/strings_test.go: -------------------------------------------------------------------------------- 1 | package vmutils 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestDecodeMUTF8(t *testing.T) { 10 | require.Equal(t, "abc", DecodeMUTF8([]byte("abc"))) 11 | require.Equal(t, "foo\u0000bar", DecodeMUTF8([]byte("foo\u0000bar"))) // ? 12 | require.Equal(t, "foo\u0000bar", DecodeMUTF8([]byte("foo\xc0\x80bar"))) 13 | //require.Equal(t, "foo\U00010000bar", DecodeMUTF8([]byte("foo\uD800\uDC00bar"))) 14 | } 15 | -------------------------------------------------------------------------------- /vmutils/unsafe.go: -------------------------------------------------------------------------------- 1 | package vmutils 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | // []int8 <-> []byte 9 | func CastInt8sToBytes(s []int8) []byte { 10 | ptr := unsafe.Pointer(&s) 11 | return *((*[]byte)(ptr)) 12 | } 13 | func CastBytesToInt8s(s []byte) []int8 { 14 | ptr := unsafe.Pointer(&s) 15 | return *((*[]int8)(ptr)) 16 | } 17 | 18 | // []int8 <-> []uint16 19 | func CastInt8sToUint16s(s []int8) []uint16 { 20 | ptr := unsafe.Pointer(&s) 21 | (*reflect.SliceHeader)(ptr).Len /= 2 22 | return *((*[]uint16)(ptr)) 23 | } 24 | func CastUint16sToInt8s(s []uint16) []int8 { 25 | ptr := unsafe.Pointer(&s) 26 | (*reflect.SliceHeader)(ptr).Len *= 2 27 | return *((*[]int8)(ptr)) 28 | } 29 | 30 | func CastBytesToUint32s(s []byte) []uint32 { 31 | ptr := unsafe.Pointer(&s) 32 | (*reflect.SliceHeader)(ptr).Len /= 4 33 | return *((*[]uint32)(ptr)) 34 | } 35 | 36 | func CastBytesToInt32s(s []byte) []int32 { 37 | ptr := unsafe.Pointer(&s) 38 | (*reflect.SliceHeader)(ptr).Len /= 4 39 | return *((*[]int32)(ptr)) 40 | } 41 | -------------------------------------------------------------------------------- /vmutils/unsafe_test.go: -------------------------------------------------------------------------------- 1 | package vmutils 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestCasts(t *testing.T) { 10 | bytes := []byte("hello, world") 11 | int8s := CastBytesToInt8s(bytes) 12 | uint16s := CastInt8sToUint16s(int8s) 13 | uint32s := CastBytesToUint32s(bytes) 14 | int32s := CastBytesToInt32s(bytes) 15 | 16 | require.Equal(t, 12, len(int8s)) 17 | require.Equal(t, 6, len(uint16s)) 18 | require.Equal(t, 3, len(uint32s)) 19 | require.Equal(t, 3, len(int32s)) 20 | 21 | require.Equal(t, bytes, CastInt8sToBytes(int8s)) 22 | require.Equal(t, int8s, CastUint16sToInt8s(uint16s)) 23 | } 24 | --------------------------------------------------------------------------------